WPF中MVVM设计模式的应用

本文案例是一个基于C#编程并模拟了数据获取服务的简单加法计算器
环境:Visual Studio 2019 WPF应用程序模板(.NET5)

本文MVVM设计模式的思路

WPF中MVVM设计模式的应用

如图所示,用户与View进行交互,在View中所需的数据全部来自于View Model,而用户输入的数据,也全部交给View Model进行处理,这些数据全部由实例化的Model进行封装。当程序需要获取外部数据时,操作均由Service提供,比如从数据库获取数据和将数据保存到数据库等,其所操作的数据模型就是Model。
View与View Model有两个交互通道,一是数据,一是命令。
数据是双向的,View到View Model的数据绑定依赖于Binding命令,View Model到View的数据实时更新则需要View Model实现INotifyPropertyChanged接口,一般我们会创建一个实现这个接口的基类,称为NotificationObject,再让View Model去继承它。
命令是单向的,由View发送给ViewModel,需要将这个操作由View中的事件源绑定到View中实现了ICommand接口的命令属性,一般我们会创建实现此接口的类,称为DelegateCommand,View Model的每个命令属性都是这个类的一个实例。

示例程序的目录结构和说明

WPF中MVVM设计模式的应用

  1. Assets,这里保存的是所需引用的静态文件,其中Data中的xml文件为模拟的外部数据源;
  2. Base,这里存放的是项目中的公共文件,比如Class中的两个类,一个实现的通知接口,一个实现了命令接口。
  3. Models,项目中的数据模型;
  4. Services,提供从数据源获取内容或者将程序中数据输出的功能,其中一个是接口,一个是实现接口的实际类;
  5. ViewModels,项目的核心内容,前后端的控制器;
  6. Views,界面

部分代码说明

通知和命令两个接口的简单实现

//通知
public class NotificationObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        //当有数据发生改变时,会触发ProPertyChanged事件
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

//命令
public class DelegateCommand : ICommand
{
    public event EventHandler CanExecuteChanged;

    //当命令发出后,会首先调用CanExcute方法,判断此时命令是否可用
    public bool CanExecute(object parameter)
    {
        if (DoCanexcute != null)
        {
            return DoCanexcute.Invoke(parameter);
        }

        return false;
    }

    //如果命令可用,会继续执行Excute方法,这是真正的命令执行
    public void Execute(object parameter)
    {
        if (DoExcute != null)
        {
            DoExcute.Invoke(parameter);
        }
    }
    
    //通过两个委托,从外部将判断和执行命令的方法传递进来
    public Func<object, bool> DoCanexcute { get; set; }

    public Action<object> DoExcute { get; set; }
}

数据模型

public class CalculatorModel
{
    //本例很简答,就只需要加法的两个加数即可
    public double FirstNumber { get; set; }

    public double SecondNumber { get; set; }

}

【核心】ViewModel的实现

//首先,View Model需要继承通知的基类,使其具有通知的方法
public class MainWindowViewModel : NotificationObject
{
    //计算器三个属性
    private double _firstNumber;

    public double FirstNumber
        {
            get { return _firstNumber; }
            set 
            { 
                _firstNumber = value;
                //当属性获得新的值以后,便调用通知方法,传入此时更改的属性名称
                RaisePropertyChanged(nameof(FirstNumber));
            }
        }

    private double _secondNumber;

    public double SeconeNumber
    {
        get { return _secondNumber; }
        set 
        { 
            _secondNumber = value;
            RaisePropertyChanged(nameof(SeconeNumber));
        }
    }

    private double _result;

    public double Result
        {
            get { return _result; }
            set 
            { 
                _result = value;
                RaisePropertyChanged(nameof(Result));
            }
        }

    //计算器的加法方法
    public DelegateCommand SumDelegateCommand { get; set; }

    //计算器从数据源获取数据的方法
    public DelegateCommand GetNumbersDelegateCommand { get; set; }


    //初始化计算器的View Model
    public MainWindowViewModel()
    {
        //初始化加法方法
        SumDelegateCommand = new DelegateCommand();
        SumDelegateCommand.DoCanexcute = new Func<object, bool>(o => true);
        SumDelegateCommand.DoExcute = new Action<object>(o => { Result = FirstNumber + SeconeNumber; });

        //初始化获取数据方法
        GetNumbersDelegateCommand = new DelegateCommand();
        GetNumbersDelegateCommand.DoCanexcute = new Func<object, bool>(o => true);
        GetNumbersDelegateCommand.DoExcute = new Action<object>(o =>
          {
              GetNumbersAction getNumbersAction = new GetNumbersAction();
              //数据获取服务很简单,就是用xml文件中得到两个加数,然后返回一个Model
              CalculatorModel calculatorModel = getNumbersAction.GetNumbers();
              FirstNumber = calculatorModel.FirstNumber;
              SeconeNumber = calculatorModel.SecondNumber;
          });
    }
}

View中的Binding命令

WPF中MVVM设计模式的应用

<UniformGrid Rows="4">
    <!--第一个加数-->
    <TextBox FontSize="15" Padding="20 0 0 0" VerticalContentAlignment="Center" Text="{Binding FirstNumber}"/>
    <!--第二个加数-->
    <TextBox FontSize="15" Padding="20 0 0 0" VerticalContentAlignment="Center" Text="{Binding SeconeNumber}"/>
    <!--和-->
    <TextBlock FontSize="15" Padding="20 0 0 0" VerticalAlignment="Center" Text="{Binding Result}"/>
    <UniformGrid Columns="2">
        <!--
            点击获取数据,VideModel会通过Service拿到数据源中的数据模型,并赋值给自己的属性。当属性值更改时,会通知View进行实时 
            更新。
        -->
        <Button FontSize="15" Content="获取数据" Command="{Binding GetNumbersDelegateCommand}"/>
        <!--
            点击计算,ViewModel会拿到界面上两个加数的和并复制给Result,此时该属性的值发生变化,即通知View实时更新。
        -->
        <Button FontSize="15" Content="计算加法" Command="{Binding SumDelegateCommand}"/>
    </UniformGrid>
</UniformGrid>

总结

我是一个新手,本文设计思路来自于刘铁猛老师的视频教程,此总结也是为了检验自己所学,难免有错误出现,恳请指正。除了这个以外,我还跟着教程复刻了刘铁猛老师的“Crazy大象”点单系统,主要对界面进行的重新设计,这两个程序的源码很快会上传到我的Github,感兴趣的可以一并看看。

WPF中MVVM设计模式的应用

上一篇:WPF学习——创建一个基于mvvm模式的简单小示例


下一篇:关于Vue的MVVM模式理解