MVVM设计模式教程[一].手动建立MVVM地方法

 随着WPF开发应用的普及.越来越多的人关注MVVM的构建. 这种设计模式能使逻辑与界面很彻底的解耦.为并行开发提供可能,在提高了开发效率的同时,使得程序更容易维护与修改

,而且这种设计模式可以更好的测试更好的分离关注点,模块与模块的之间的耦合大幅度降低. 使得测试更加方便.

作为一个WPF 开发人员,很有必要了解MVVM.

MVVM 是由 Model, ViewModel  View 组成. 他之所以能比MVC,MVP耦合度更低,取决于WPF 2个新出的重要概念.命令与数据绑定. 这2个使得完全解耦成为可能.

下面我们将有一个实例 在演示下 用MVVM 创建一个WPF应用实例

    一  首先让我们打开VisualStudio2010. 新建一个WPF项目.

                                

   实际工作中的MVVM 结构图.                                       

                             

根据上图 我们先建立几个文件夹,为MVVM做准备.

 

二  开始编写 Model层. 我们在Model层 先添加一个类. Employee 

 代码如下

View Code
namespace MVVMDemo.Model
{
   public class Employee
    {
        public static Employee CreateEmployee(string name, string age)
        {
            return new Employee() { Name = name, Age = age };
        }
       
        public string Name { get; set; }

        public string Age { get; set; }
    }
}

然后我在 数据层  写下虚拟数据的加载 创建一个类 . EmployeeRespository 

代码如下

Respository.cs
using System.Collections.Generic;
using MVVMDemo.Model;

namespace MVVMDemo.DataAccess
{
   public class EmployeeRespository
   {
       readonly List<Employee> _employees;

       public EmployeeRespository()
       {
           if (_employees == null)
           {
               _employees = new List<Employee>();
           }
           _employees.Add(new Employee() { Age = "33", Name = "Tom" });
           _employees.Add(new Employee() { Age = "32", Name = "Gravin" });
           _employees.Add(new Employee() { Age = "28", Name = "Alex" });
       }

       public List<Employee> GetEmployeeRespository()
       {
           return _employees;
       }
   }
}

 三 编写我们的一个演示界面 View. 我们直接写在Mainwindow 的xaml里面

MainWindows.xaml
<Window x:Class="MVVMDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:MVVMDemo.ViewModel"
        Title="MainWindow" Height="Auto" Width="330">
    <Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>
    <StackPanel>
        <ListBox ItemsSource="{Binding AllEmployees}" SelectedIndex="0" SelectedItem="{Binding SelectEmployee}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}" Margin="5"/>
                        <TextBlock Text="{Binding Age}" Margin="5"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <StackPanel  Orientation="Horizontal">
            <TextBlock Text="Name" Margin="5,10"/>
            <TextBox Width="100" Margin="5,10" Text="{Binding NewEmployee.Name}"/>
            <TextBlock Text="Age" Margin="5,10"/>
            <TextBox Width="100" Margin="5,10" Text="{Binding NewEmployee.Age}"/>
        </StackPanel>
        <StackPanel  Orientation="Horizontal">
            <Button Content="ADD" Width="100" Margin="10" Command="{Binding AddCommand}"/>
            <Button Content="Remove" Width="100" Margin="10" Command="{Binding RemoveCommand}"/>
        </StackPanel>
      
    </StackPanel>
</Window>

现在界面和数据 Model都好了.. 我们来考虑下这个Demo的逻辑.

首先界面是个ListBox 用于加载 Employee里面的虚拟数据. 然后下面的2个文本框 用于写入要添加新数据的Name信息与Age信息  最下面的2个按钮 传递操作,添加数据 和 移除数据. 如图.

 添加数据 要防止数据的重复.  移除数据 是移除选中的一行. 现在我们 用MVVM 来实现这个列子

前面说道 MVVM 之所以能够 比MVC MVP更好的解耦 ,其原因是WPF 的2大新功能 数据的绑定Data Binding和命令Command  这种机制使得逻辑与界面最大化的解耦

 四 我们开始编写ViewModel.  实际项目中 通常我们会为ViewModel写个基类   然后其余的对应View的ViewModel继承此基类

 ViewModelBase 代码如下

ViewModelBase.cs
using System.Text;
using System.ComponentModel;

namespace MVVMDemo.ViewModel
{
  public abstract class ViewModelBase:INotifyPropertyChanged,IDisposable
  {
      protected ViewModelBase()
      { 
      
      }

      public event PropertyChangedEventHandler PropertyChanged;

      protected virtual void OnpropertyChanged(string propertyName)
      {
          PropertyChangedEventHandler handler = this.PropertyChanged;
         
          if (handler != null)
          {
              var e = new PropertyChangedEventArgs(propertyName);
              
              handler(this, e);
          }
      }


      public void Dispose()
      {
          this.OnDispose();
      }

      protected virtual void OnDispose()
      {
          throw new NotImplementedException();
      }


  }

}

ViewModel 其中一个非常常用的功能就 对属性的变化能够即使反映到UI View.  这需要 就是要继承 一个接口 INotifyPropertyChanged  

 随后就是命令, 我也同样编写一个命令的基类 

CommandBase
using System;
using System.Windows.Input;

namespace MVVMDemo.Command
{
   public class CommandBase:ICommand
    {

        #region ICommand 成员

        public bool CanExecute(object parameter)
        {
           return _canExecute==null? true:_canExecute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add 
            { 
                CommandManager.RequerySuggested += value; 
            
            }
            remove
            {
                CommandManager.RequerySuggested -= value;
            }
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }

        #endregion

        readonly Action<object> _execute;

        readonly Predicate<object> _canExecute; 

        public CommandBase(Action<object> execute):this(execute,null)
        {

        }

        public CommandBase(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;

        }

    }
}

命令有2个很重要的方法 一个执行Execute 要做操作的内容 一个CanExecute在操作前 是否能执行改命令 
这和好的用到了我们这个Demo的 业务逻辑, 比如Add加入数据 我们很容易想到 Execute中写具体加入数据的操作, 但是要避免录入的数据与原来数据源的数据重复

所以我们在条件 CanExecute中做出判断看是否能Add,

MainWindowViewModel代码如下

MainWindowViewModel.cs
using MVVMDemo.Model;
using System.Collections.ObjectModel;
using MVVMDemo.DataAccess;
using System.Windows.Input;
using MVVMDemo.Command;

namespace MVVMDemo.ViewModel
{
   public class MainWindowViewModel:ViewModelBase
   {
       private ICommand _removeCommand;

       private Employee _newEmployee;
       
       public ObservableCollection<Employee> AllEmployees { get; set; }

       public Employee SelectEmployee{get;set;}

       public Employee NewEmployee { get { return _newEmployee; } }

       public ICommand RemoveCommand
       {
           get { return _removeCommand = new CommandBase(RemoveExecute,RemoveCanExecute);}
       
       }
       public ICommand AddCommand
       {
           get { return _removeCommand = new CommandBase(AddExecute, AddCanExecute); }

       }
    
       public MainWindowViewModel()
       {
           if (AllEmployees == null)
           {
               AllEmployees = new ObservableCollection<Employee>(new EmployeeRespository().GetEmployeeRespository()); 
           }

           _newEmployee = new Employee();
       }

       void RemoveExecute(object param)
       {
           AllEmployees.Remove(SelectEmployee);
       } 

      bool RemoveCanExecute(object param)
      {
          if(SelectEmployee!=null)
          {
             return true;
          }
          
          return false;
      }

      void AddExecute(object param)
      {
          AllEmployees.Add(NewEmployee);
      }

      bool AddCanExecute(object param)
      {
          if (NewEmployee.Name != null && NewEmployee.Age != null)
          {
              foreach (var item in AllEmployees)
              {

                  if (item.Name.Trim() == NewEmployee.Name.Trim())
                  {
                      return false;
                  }
              }
          }
          else
          {
              return false;
          }
          return true;
      }   
   }
}

这样这个View'所有的逻辑都在这个文件里面了.  我们看看MainWindow中的UI元素. 现在可以很清楚感觉到MVVM的解耦. UI元素可以随时变换,在本质的业务逻辑不改变的情况下我们的 ViewModel是不需要更改的,这样设计师和 工程师 不但可以并行开发而且 即使面对UI的变动 逻辑基本不需要修改什么.

我们看看View  只要把ListBox 的itemsource绑定 MainWindowViewModel的 数据源属性. 其他依次绑定 里面的属性 指定好数据源

 DataContext.数据就能正确的Load出来. 按钮绑定ViewModel中的Command属性  当填写的新数据信息不复合要求的时候 Button就会处于 Enabel状态. 非常方便

当我们把View UI(比如ListBox,Button,textbox)的内容随意删除  编译一样不会报错 因为逻辑中MainWindowViewModel并没有任何UI元素. 逻辑与界面 解耦  就是这个意思,而其他的设计模式多少会不可避免的与UI 纠缠在一起,UI一变化化 逻辑势必变化.这一根本原因是

WPF的MVVM模式是数据驱动UI的理念. 数据变化呈现在UI上, 而MVC MVP 以及以往的各种模式 都是事件驱动UI  事件里必定有UI元素 

 当然 这只是个小列子 仅仅只是演示下MVVM的意思.

源代码下载 MVVMDemo

 实际开发要注意的东西还很多.  相关内容我们会在以后的时间陆续更新.

原文地址:https://www.cnblogs.com/Utionsoft/p/2617017.html