本文案例是一个基于C#编程并模拟了数据获取服务的简单加法计算器
环境:Visual Studio 2019 WPF应用程序模板(.NET5)
本文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的每个命令属性都是这个类的一个实例。
示例程序的目录结构和说明
- Assets,这里保存的是所需引用的静态文件,其中Data中的xml文件为模拟的外部数据源;
- Base,这里存放的是项目中的公共文件,比如Class中的两个类,一个实现的通知接口,一个实现了命令接口。
- Models,项目中的数据模型;
- Services,提供从数据源获取内容或者将程序中数据输出的功能,其中一个是接口,一个是实现接口的实际类;
- ViewModels,项目的核心内容,前后端的控制器;
- 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命令
<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,感兴趣的可以一并看看。