首页 > 其他分享 >WPF MVVM系统入门-上

WPF MVVM系统入门-上

时间:2023-02-18 12:37:43浏览次数:33  
标签:set 入门 MVVM get double mainModel Value1 WPF public


WPF MVVM系统入门-上

Models:存放数据的模型,实体对象

Views:可视化界面

ViewModels:业务逻辑。ViewModels与Models的联系会更紧密,而Views页面会主动绑定ViewModels中的数据,原则上ViewModels不要直接去操作Views,被动的被Views来获取数据即可。

一般遵循MVVM模式的项目下,都会有​​Models、Views、ViewModels​​三个文件夹来存放不同的代码工程。

案例入门

实现一个加法计算器,输入两个值,进行相加,并返回结果。

Model

一共需要三个double类型,两个输入,一个输出。

public class MainModel
{
public double Value1 { get; set; }
public double Value2 { get; set; }
public double Value3 { get; set; }
}

ViewModel

编写相加的逻辑代码

public class MainViewModel
{
//声明一个Model类型的属性
public MainModel mainModel { get; set; } = new MainModel();
//业务逻辑
public void Add()
{
mainModel.Value3 = mainModel.Value2 + mainModel.Value1;
}
public MainViewModel()
{
//业务逻辑的调用
//为了便于观察,延迟3s后调用方法
Task.Factory.StartNew(() =>
{
Task.Delay(3000).Wait();
Add();
});
}
}

View

view中,利用一个Textbox和一个slider作为输入分别绑定Value1和value2,使用另一个TextBox绑定value3

<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<Grid>
<StackPanel>
<TextBox Text="{Binding mainModel.Value1, UpdateSourceTrigger=PropertyChanged}" />
<Slider
Maximum="100"
Minimum="0"
Value="{Binding mainModel.Value2}" />
<TextBox Text="{Binding mainModel.Value3}" />
</StackPanel>
</Grid>

这样等待3秒,下面的TextBox中的数值一直没有更新

WPF MVVM系统入门-上_C#

造成这样的原因是,Value3虽然更新了,但是并没有通知绑定他的控件,所以Value3作为输出,需要在更新时触发一个事件,让该事件的订阅者(也就是绑定该值得控件)进行响应。

改进Model

public class MainModel:INotifyPropertyChanged
{
public double Value1 { get; set; }
public double Value2 { get; set; }

private double _value3;

public double Value3
{
get { return _value3; }
set
{
_value3 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value3"));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
}

WPF MVVM系统入门-上_.net_02

在本案例中,只有三个属性,但是属性很多,难道都需要这样触发事件吗?有没有简单的方式?

PropertyChanged.Fody插件

PropertyChanged.Fody插件可让用户简化事件的通知,Nuget安装​​PropertyChanged.Fody​

[AddINotifyPropertyChangedInterface]
public class MainModel
{
public double Value1 { get; set; }
public double Value2 { get; set; }
public double Value3 { get; set; }
}

只需要这样就可以完成上面的工作,它的原理是在编译的时候,把所有的属性都会使用​​PropertyChanged.Invoke​​来触发事件。但有时有些属性不需要进行触发,比如本案例的Value1和Value2,所以可以这样

[AddINotifyPropertyChangedInterface]
public class MainModel
{
[DoNotNotify]
public double Value1 { get; set; }
[DoNotNotify]
public double Value2 { get; set; }
public double Value3 { get; set; }
}
//其他的特性
/// AlsoNotifyFoAttribute 实现通知的时候,同时通知其属性
/// DoNotNotify 指定不需要通知相关的代码
/// DependsOn 指定哪些属性变化的时候,通知当前属性变化
/// DoNotCheckEquality 强制不做旧值比对(默认情况会自动添加比对代码)

命令Command

在上面案例中是使用了构造函数调用了​​Add​​​方法,但是如果我想增加一个按钮,在点击的时候才执行​​Add​​​方法要怎么办,当然可以绑定按钮的Click事件,但是这样的话Click事件要放置在View的后台类中,不能很好的利用绑定与ViewModel建立联系,所以引出了​​命令Command​​。

命令的用途

  1. 将调用命令的对象与执行命令的逻辑分开,这允许多个源调用相同的命令逻辑
  2. 可以指示命令是否可用,如登陆时,用户名为空则登陆按钮不可用

命令要实现ICommand接口,该接口中包含

  • CanExecute:是否可执行方法
  • Execute:主要执行逻辑
  • CanExecuteChanged:触发检查是否可执行

案例入门

  1. 定义一个类实现ICommand接口
public class CommandBase : ICommand
{
public event EventHandler? CanExecuteChanged;

public bool CanExecute(object? parameter)
{
return true;//返回true表示命令可用
}

//这样做可以在外部给DoExecute委托赋值,根据不同的逻辑业务赋予不同的值
public Action<object> DoExecute { get; set; }
public void Execute(object? parameter)
{
DoExecute?.Invoke(parameter);
}
}
  1. 在ViewModel中定义一个CommandBase属性
public class MainViewModel
{
public MainModel mainModel { get; set; } = new MainModel();

public void Add(object obj)
{
mainModel.Value3 = mainModel.Value2 + mainModel.Value1;
}

public CommandBase BtnCommand { get; set; }//命令
public MainViewModel()
{
BtnCommand = new CommandBase() {DoExecute = new Action<object>(Add) };
}
}
  1. 页面增加一个Button按键并进行绑定​​<Button Command="{Binding BtnCommand}" Content="Ok" />​

WPF MVVM系统入门-上_C#_03

检测是否可执行

上面的CommandBase中,直接将CanExecute返回为true,其实可以利用这个方法来实现检测是当前按钮是否可以使用

将上面的CommandBase改为

public Func<object,bool> DoCanExecute { get; set; }
public bool CanExecute(object? parameter)
{
return DoCanExecute?.Invoke(parameter) == true;
}

更改ViewModel

public class MainViewModel
{
public MainModel mainModel { get; set; } = new MainModel();

public void Add(object obj)
{
mainModel.Value3 = mainModel.Value2 + mainModel.Value1;
}

public bool CanCal(object obj)
{
return mainModel.Value1 != 0;
}

public CommandBase BtnCommand { get; set; }//命令
public MainViewModel()
{
BtnCommand = new CommandBase() {
DoExecute = new Action<object>(Add),
DoCanExecute = new Func<object, bool>(CanCal) };
}
}

运行后可以看出OK按钮为不可用状态,但是将上面的文本框改为非零状态,仍然是不可用,这是因为在更改value1后,并没有触发检测事件​​CanExecuteChanged​​​也就是说此时并没有再次执行CanExecute方法。所以需要触发​​CanExecuteChanged事件​​,框架会自动执行CanExecute方法。

WPF MVVM系统入门-上_C#_04

因为事件必须在本类中进行触发,所以在CommandBase中增加DoCanExecuteChanged方法并触发CanExecuteChanged事件。

public void DoCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

因为需要在Value1赋值的时候,触发 CanExecuteChanged事件,所以需要将Model中定义一个CommandBase。并将ViewModel中的CommandBase删除。

public class MainModel:INotifyPropertyChanged
{
private double _value1;

public double Value1
{
get { return _value1; }
set {
_value1 = value;
BtnCommand.DoCanExecuteChanged();//在更改时触发CanExecuteChanged事件,该事件触发后会执行CanExecute方法
}
}

public double Value2 { get; set; }
private double _value3;
public double Value3
{
get { return _value3; }
set
{
_value3 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value3"));
}
}
public event PropertyChangedEventHandler? PropertyChanged;

public CommandBase BtnCommand { get; set; }//命令

public MainModel()
{
BtnCommand = new CommandBase()
{
DoExecute = new Action<object>(Add),
DoCanExecute = new Func<object, bool>(CanCal)
};
}

public void Add(object obj)
{
Value3 =Value2 + Value1;
}

public bool CanCal(object obj)
{
return Value1 != 0;
}
}

View中的Button更换command绑定​​<Button Command="{Binding mainModel.BtnCommand}" Content="Ok" />​

WPF MVVM系统入门-上_wpf_05

但是这样的话就必须将Command定义在Model中。那Command如何定义在ViewModel中,请看MVVM系统入门-下。


标签:set,入门,MVVM,get,double,mainModel,Value1,WPF,public
From: https://blog.51cto.com/u_15943685/6065289

相关文章

  • Solidity极简入门#17. 库合约
    这一讲,我们用ERC721的引用的库合约String为例介绍solidity中的库合约(library),并总结了常用的库函数。库函数库函数是一种特殊的合约,为了提升solidity代码的复用性和减少gas而......
  • Vue急速入门-4
    组件其他根组件和组件的一些问题归总: 1.newVue()el:'xx'管理的Div就算根组件 2.父子组件的数据是无法共享的 3.组件有自己的html,css,js 4.在组件中,this代指当......
  • MyBatisPlus--入门
    入门案例MyBatisPlus(MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率.1、新建springboot项目(版本2.5.0),仅保留JDBC 添加mybatisplus起步依赖和drui......
  • 嵌入式Linux入门级板卡的神经网络框架ncnn移植与测试-米尔i.MX6UL开发板
    本篇测评由电子发烧友的优秀测评者“ALSET”提供。米尔MYD-Y6ULX-V2开发板,基于NXPi.MX6UL/i.MX6ULL处理器,该开发板被米尔称之为经典王牌产品。本次测试目标是在此开发......
  • 万字长文带你入门增量学习
    前言本文介绍了引入增量学习的必要性,继而引出最新的三种能够有效利用有效标签数据范式的增量学习方法。其次,针对这三大范式进行一个全面的调研,让大家对这个领域的整个发......
  • [WPF]MVVM的数据绑定
    啥是MVVM?我理解的MVVM是Model(数据),View(界面),ViewModel(数据与界面之间的桥梁)的缩写,是一种编程模式。前期需要多花一些时间去编辑绑定,在后期维护方便。只需要关注数据即可。如......
  • Blender 图像软件入门教程 导出glb ,gltf格式文件 blender导入glb格式文件
    环境:3.4.1实现功能:导出glb格式文件点击视图左上方文件按钮,点击导出,点击gltf2.0(.glb/gltf)  blender导入glb格式文件视图右上方,选择Collection,鼠标右键,点击删除......
  • WPF中MVVM模式下loaded无法触发问题
    经过实践检测,当时View上设置了Window.SizeToContent="WidthAndHeight"时候<i:Interaction.Triggers><i:EventTriggerEventName="Loaded"><i:Invoke......
  • 【AI入门】C++构建BP神经网络,并实现手写数字识别
    目录BP神经网络的基本原理BP神经网络的C++实现将BP神经网络应用于手写数字识别存在的疑惑BP神经网络的基本原理参考资料:机器学习(西瓜书)-周志华如图所示,一个简单的BP......
  • stm32入门--流程
    //使用固件库点亮LEDintmain(void){//定义一个GPIO_InitTypeDef类型的结构体GPIO_InitTypeDefGPIO_InitStructure;//开启GPIO端口时钟......