WPF MVVM系统入门-上

简介: 本文详细讲解WPF,MVVM开发,实现UI与逻辑的解耦。

WPF MVVM系统入门-上

本文详细讲解WPF,MVVM开发,实现UI与逻辑的解耦。

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

Views:可视化界面

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

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

案例入门

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

Model

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

publicclassMainModel

{

   publicdoubleValue1 { get; set; }

   publicdoubleValue2 { get; set; }

   publicdoubleValue3 { get; set; }

}

ViewModel

编写相加的逻辑代码

publicclassMainViewModel

{

   //声明一个Model类型的属性

   publicMainModelmainModel { get; set; } =newMainModel();

   //业务逻辑

   publicvoidAdd()

   {

       mainModel.Value3=mainModel.Value2+mainModel.Value1;

   }

   publicMainViewModel()

   {

       //业务逻辑的调用

       //为了便于观察,延迟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>

       <TextBoxText="{Binding mainModel.Value1, UpdateSourceTrigger=PropertyChanged}"/>

       <Slider

           Maximum="100"

           Minimum="0"

           Value="{Binding mainModel.Value2}"/>

       <TextBoxText="{Binding mainModel.Value3}"/>

   </StackPanel>

</Grid>

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

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

改进Model

publicclassMainModel:INotifyPropertyChanged

{

   publicdoubleValue1 { get; set; }

   publicdoubleValue2 { get; set; }

   privatedouble_value3;

   publicdoubleValue3

   {

       get { return_value3; }

       set

       {

           _value3=value;

           PropertyChanged?.Invoke(this, newPropertyChangedEventArgs("Value3"));

       }

   }

   publiceventPropertyChangedEventHandler?PropertyChanged;

}

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

PropertyChanged.Fody插件

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

[AddINotifyPropertyChangedInterface]

publicclassMainModel

{

   publicdoubleValue1 { get; set; }

   publicdoubleValue2 { get; set; }

   publicdoubleValue3 { get; set; }

}

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

[AddINotifyPropertyChangedInterface]

publicclassMainModel

{

   [DoNotNotify]

   publicdoubleValue1 { get; set; }

   [DoNotNotify]

   publicdoubleValue2 { get; set; }

   publicdoubleValue3 { get; set; }

}

//其他的特性

/// AlsoNotifyFoAttribute     实现通知的时候,同时通知其属性

/// DoNotNotify 指定不需要通知相关的代码

/// DependsOn 指定哪些属性变化的时候,通知当前属性变化

/// DoNotCheckEquality    强制不做旧值比对(默认情况会自动添加比对代码)

命令Command

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

命令的用途

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

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

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

案例入门

  1. 定义一个类实现ICommand接口

publicclassCommandBase : ICommand

{

   publiceventEventHandler?CanExecuteChanged;

   publicboolCanExecute(object?parameter)

   {

       returntrue;//返回true表示命令可用

   }

   //这样做可以在外部给DoExecute委托赋值,根据不同的逻辑业务赋予不同的值

   publicAction<object>DoExecute { get; set; }

   publicvoidExecute(object?parameter)

   {

       DoExecute?.Invoke(parameter);

   }

}

  1. 在ViewModel中定义一个CommandBase属性

publicclassMainViewModel

{

   publicMainModelmainModel { get; set; } =newMainModel();

   publicvoidAdd(objectobj)

   {

       mainModel.Value3=mainModel.Value2+mainModel.Value1;

   }

   publicCommandBaseBtnCommand { get; set; }//命令

   publicMainViewModel()

   {

       BtnCommand=newCommandBase() {DoExecute   =newAction<object>(Add) };

   }

}

  1. 页面增加一个Button按键并进行绑定<Button Command="{Binding BtnCommand}" Content="Ok" />

检测是否可执行

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

将上面的CommandBase改为

publicFunc<object,bool>DoCanExecute { get; set; }

publicboolCanExecute(object?parameter)

{

   returnDoCanExecute?.Invoke(parameter) ==true;

}

更改ViewModel

publicclassMainViewModel

{

   publicMainModelmainModel { get; set; } =newMainModel();

   publicvoidAdd(objectobj)

   {

       mainModel.Value3=mainModel.Value2+mainModel.Value1;

   }

   publicboolCanCal(objectobj)

   {

       returnmainModel.Value1!=0;

   }

   publicCommandBaseBtnCommand { get; set; }//命令

   publicMainViewModel()

   {

       BtnCommand=newCommandBase() {

           DoExecute=newAction<object>(Add),

           DoCanExecute=newFunc<object, bool>(CanCal) };

   }

}

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

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

publicvoidDoCanExecuteChanged()

{

   CanExecuteChanged?.Invoke(this, EventArgs.Empty);

}

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

publicclassMainModel:INotifyPropertyChanged

{

   privatedouble_value1;

   publicdoubleValue1

   {

       get { return_value1; }

       set {

           _value1=value;

           BtnCommand.DoCanExecuteChanged();//在更改时触发CanExecuteChanged事件,该事件触发后会执行CanExecute方法

       }

   }

   publicdoubleValue2 { get; set; }

   privatedouble_value3;

   publicdoubleValue3

   {

       get { return_value3; }

       set

       {

           _value3=value;

           PropertyChanged?.Invoke(this, newPropertyChangedEventArgs("Value3"));

       }

   }

   publiceventPropertyChangedEventHandler?PropertyChanged;

   publicCommandBaseBtnCommand { get; set; }//命令

   publicMainModel()

   {

       BtnCommand=newCommandBase()

       {

           DoExecute=newAction<object>(Add),

           DoCanExecute=newFunc<object, bool>(CanCal)

       };

   }

   publicvoidAdd(objectobj)

   {

       Value3=Value2+Value1;

   }

   publicboolCanCal(objectobj)

   {

       returnValue1!=0;

   }

}

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

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

相关文章
|
2月前
|
C# 开发者 Windows
WPF 应用程序开发:一分钟入门
本文介绍 Windows Presentation Foundation (WPF),这是一种用于构建高质量、可缩放的 Windows 桌面应用程序的框架,支持 XAML 语言,方便 UI 设计与逻辑分离。文章涵盖 WPF 基础概念、代码示例,并深入探讨常见问题及解决方案,包括数据绑定、控件样式与模板、布局管理等方面,帮助开发者高效掌握 WPF 开发技巧。
159 65
|
2月前
|
设计模式 前端开发 C#
WPF 项目中 MVVM模式 的简单例子说明
本文通过WPF项目中的加法操作示例,讲解了MVVM模式的结构和实现方法,包括数据模型、视图、视图模型的创建和数据绑定,以及命令的实现和事件通知机制。
|
3月前
|
开发框架 缓存 前端开发
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
|
3月前
|
C# 开发者 Windows
全面指南:WPF无障碍设计从入门到精通——让每一个用户都能无障碍地享受你的应用,从自动化属性到焦点导航的最佳实践
【8月更文挑战第31天】为了确保Windows Presentation Foundation (WPF) 应用程序对所有用户都具备无障碍性,开发者需关注无障碍设计原则。这不仅是法律要求,更是社会责任,旨在让技术更人性化,惠及包括视障、听障及行动受限等用户群体。
79 0
|
3月前
|
前端开发 C# 设计模式
“深度剖析WPF开发中的设计模式应用:以MVVM为核心,手把手教你重构代码结构,实现软件工程的最佳实践与高效协作”
【8月更文挑战第31天】设计模式是在软件工程中解决常见问题的成熟方案。在WPF开发中,合理应用如MVC、MVVM及工厂模式等能显著提升代码质量和可维护性。本文通过具体案例,详细解析了这些模式的实际应用,特别是MVVM模式如何通过分离UI逻辑与业务逻辑,实现视图与模型的松耦合,从而优化代码结构并提高开发效率。通过示例代码展示了从模型定义、视图模型管理到视图展示的全过程,帮助读者更好地理解并应用这些模式。
91 0
|
3月前
|
前端开发 开发者 C#
WPF开发者必读:MVVM模式实战,轻松实现现代桌面应用架构,让你的代码更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,MVVM(Model-View-ViewModel)模式通过分离应用程序的逻辑和界面,提高了代码的可维护性和可扩展性。本文介绍了MVVM模式的三个核心组件:Model(数据模型)、View(用户界面)和ViewModel(处理数据绑定和逻辑),并通过示例代码展示了如何在WPF项目中实现MVVM模式。通过这种方式,开发者可以构建更加高效和可扩展的桌面应用程序。
147 0
|
3月前
|
前端开发 C# 开发者
WPF开发者必读:MVVM模式实战,轻松构建可维护的应用程序,让你的代码更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,MVVM(Model-View-ViewModel)模式通过分离关注点,提高了代码的可维护性和可扩展性。本文详细介绍了MVVM模式的三个核心组件:Model(数据模型)、View(用户界面)和ViewModel(处理数据绑定与逻辑),并通过示例代码展示了如何在WPF项目中实现MVVM模式。通过这种模式,开发者可以更高效地构建桌面应用程序。希望本文能帮助你在WPF开发中更好地应用MVVM模式。
163 0
|
3月前
|
C# Windows IDE
WPF入门实战:零基础快速搭建第一个应用程序,让你的开发之旅更上一层楼!
【8月更文挑战第31天】在软件开发领域,WPF(Windows Presentation Foundation)是一种流行的图形界面技术,用于创建桌面应用程序。本文详细介绍如何快速搭建首个WPF应用,包括安装.NET Framework和Visual Studio、理解基础概念、创建新项目、设计界面、添加逻辑及运行调试等关键步骤,帮助初学者顺利入门并完成简单应用的开发。
89 0
|
3月前
|
C# Windows 开发者
超越选择焦虑:深入解析WinForms、WPF与UWP——谁才是打造顶级.NET桌面应用的终极利器?从开发效率到视觉享受,全面解读三大框架优劣,助你精准匹配项目需求,构建完美桌面应用生态系统
【8月更文挑战第31天】.NET框架为开发者提供了多种桌面应用开发选项,包括WinForms、WPF和UWP。WinForms简单易用,适合快速开发基本应用;WPF提供强大的UI设计工具和丰富的视觉体验,支持XAML,易于实现复杂布局;UWP专为Windows 10设计,支持多设备,充分利用现代硬件特性。本文通过示例代码详细介绍这三种框架的特点,帮助读者根据项目需求做出明智选择。以下是各框架的简单示例代码,便于理解其基本用法。
125 0
|
3月前
|
设计模式 前端开发 C#
WPF/C#:理解与实现WPF中的MVVM模式
WPF/C#:理解与实现WPF中的MVVM模式
156 0