设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间00 现在时间00 缓存数据 对自己狠一点,逼自己努力,再过几年你将会感谢今天发狠的自己、恨透今天懒惰自卑的自己。晚安!

对自己狠一点,逼自己努力,再过几年你将会感谢今天发狠的自己、恨透今天懒惰自卑的自己。晚安!

查看: 1711|回复: 2

WPF中MVVM模式的理解与实现

[复制链接]

  离线 

TA的专栏

  • 打卡等级:热心大叔
  • 打卡总天数:229
  • 打卡月天数:1
  • 打卡总奖励:3383
  • 最近打卡:2025-12-03 20:53:34
等级头衔

等級:晓枫资讯-上等兵

在线时间
0 小时

积分成就
威望
0
贡献
420
主题
396
精华
0
金钱
4675
积分
879
注册时间
2023-1-6
最后登录
2025-12-3

发表于 2024-6-16 09:54:55 | 显示全部楼层 |阅读模式
目录


  • MVVM模式的介绍
  • 不使用MVVM模式的例子
  • 使用MVVM的例子

    • 开始使用MVVM模式

      • RelayCommand
      • ICommand
      • RelayCommand
      • View—ViewModel
      • ViewModel—Model


  • 使用MVVM库
  • 总结

MVVM模式的介绍

MVVM(Model-View-ViewModel)是一种设计模式,特别适用于WPF(Windows Presentation Foundation)等XAML-based的应用程序开发。MVVM模式主要包含三个部分:Model(模型)、View(视图)和ViewModel(视图模型)。

  • Model(模型):模型代表的是业务逻辑和数据。它包含了应用程序中用于处理的核心数据对象。模型通常包含业务规则、数据访问和存储逻辑。
  • View(视图):视图是用户看到和与之交互的界面。在WPF中,视图通常由XAML定义,并且包含各种用户界面元素,如按钮、文本框、列表等。
  • ViewModel(视图模型):视图模型是视图的抽象,它包含视图所需的所有数据和命令。视图模型通过实现
    1. INotifyPropertyChanged
    复制代码
    接口和使用
    1. ICommand
    复制代码
    对象,将视图的状态和行为抽象化,从而实现了视图和模型的解耦。
MVVM模式的主要优点是分离了视图和模型,使得视图和业务逻辑之间的依赖性降低,提高了代码的可维护性和可测试性。此外,通过数据绑定和命令绑定,MVVM模式可以减少大量的样板代码,使得代码更加简洁和易于理解。
1.png


不使用MVVM模式的例子

要真正理解为什么要使用MVVM,使用MVVM有什么好处,肯定要与不使用MVVM的情况进行对比。在Winform中我们使用了事件驱动编程,同样在WPF中我们也可以使用事件驱动编程。
Windows Forms(WinForms)是一种基于事件驱动的图形用户界面(GUI)框架。在WinForms中,用户与应用程序的交互主要通过事件来驱动。
事件驱动编程是一种编程范式,其中程序的执行流程由外部事件(如用户操作或系统事件)决定。在WinForms中,事件可以是用户的各种操作,如点击按钮、选择菜单项、输入文本等,也可以是系统的事件,如窗口加载、大小改变等。
当一个事件发生时,会触发与之关联的事件处理器(Event Handler)。事件处理器是一个函数或方法,用于响应特定的事件。例如,当用户点击一个按钮时,可以触发一个事件处理器,执行一些特定的操作。
在WinForms中,你可以为各种控件添加事件处理器,以响应用户的操作。这种事件驱动的方式使得你可以很容易地创建交互式的GUI应用程序,而无需关心程序的执行流程。
事件驱动的简图如下图所示:
2.png


  • 事件源(Event Source):事件源是产生事件的对象。在WinForms中,事件源通常是用户界面元素,如按钮、文本框、菜单项等。当用户与这些元素进行交互(如点击按钮、输入文本等)时,这些元素就会产生相应的事件。
  • 事件(Event):事件是由事件源产生的一个信号,表示某种特定的事情已经发生。例如,当用户点击一个按钮时,按钮就会产生一个Click事件。事件通常包含一些关于该事件的信息,例如事件发生的时间、事件的源对象等。
  • 事件处理器(Event Handler):事件处理器是一个函数或方法,用于响应特定的事件。当一个事件发生时,与该事件关联的事件处理器就会被调用。在事件处理器中,你可以编写代码来定义当事件发生时应该执行的操作。例如,你可以在按钮的Click事件处理器中编写代码,定义当用户点击按钮时应该执行的操作。
现在我们通过一个例子在WPF中使用事件驱动编程。
首先看一下我们的示例xaml页面:
  1. <Window x:Class="WPF_MVVM_Pattern.MainWindow"
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5.         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6.         xmlns:local="clr-namespace:WPF_MVVM_Pattern"
  7.         mc:Ignorable="d"
  8.         Title="MainWindow" Height="450" Width="800"
  9.         Loaded="Window_Loaded">
  10.     <StackPanel>
  11.         <ToolBar>
  12.             <Label Content="姓名:"></Label>
  13.             <TextBox x:Name="nameTextBox" Width="50"></TextBox>
  14.             <Label Content="邮箱:"></Label>
  15.             <TextBox x:Name="emailTextBox" Width="100"></TextBox>
  16.             <Button Content="添加"
  17.                     Click="AddUser"></Button>
  18.        </ToolBar>
  19.         <StackPanel>
  20.             <DataGrid x:Name="dataGrid1"></DataGrid>
  21.             
  22.         </StackPanel>
  23.     </StackPanel>
  24. </Window>
复制代码
3.png

使用了两个事件,分别是窗体加载事件:
  1. Loaded="Window_Loaded"
复制代码
与button点击事件:
  1. <Button Content="添加"
  2.         Click="AddUser"></Button>
复制代码
实现该操作与两个类有关:
  1. public class User
  2. {
  3.      public string? Name { get; set; }
  4.      public string? Email { get; set; }
  5. }
复制代码
  1. public static class UserManager
  2. {
  3.      public static ObservableCollection<User> DataBaseUsers = new       ObservableCollection<User>()
  4.      {
  5.          new User() { Name = "小王", Email = "123@qq.com" },
  6.          new User() { Name = "小红", Email = "456@qq.com" },
  7.          new User() { Name = "小五", Email = "789@qq.com" }
  8.      };

  9.      public static ObservableCollection<User> GetUsers()
  10.      {
  11.          return DataBaseUsers;
  12.      }

  13.      public static void AddUser(User user)
  14.      {
  15.          DataBaseUsers.Add(user);
  16.      }
  17. }
复制代码
窗体加载事件处理程序:
  1. private void Window_Loaded(object sender, RoutedEventArgs e)
  2. {
  3.     dataGrid1.ItemsSource =  UserManager.GetUsers();
  4. }
复制代码
"添加"按钮点击事件处理程序:
  1. private void AddUser(object sender, RoutedEventArgs e)
  2. {
  3.      User user = new User();
  4.      user.Name = nameTextBox.Text;
  5.      user.Email = emailTextBox.Text;
  6.      UserManager.AddUser(user);
  7.      MessageBox.Show("成功添加用户!");
  8. }
复制代码
实现的效果如下所示:
4.gif


使用MVVM的例子

刚刚我们使用的是事件驱动编程,我们在winform开发中经常这样干。对于一些小项目或者demo程序这样做很方便,但是如果业务逻辑很多,这样做就不好维护,因为UI与业务逻辑严重耦合了。
我们经常在cs文件中使用xaml中的元素,也就是经常在cs中引用xaml中的元素,如下所示:
5.png

在C#代码文件中直接引用XAML元素,会导致代码与界面元素之间的耦合度增加,这是一种不良的编程实践。以下是这种做法的一些潜在问题:

  • 耦合度高:代码与界面元素紧密耦合,这使得代码更难以维护和重用。如果你更改了界面元素(例如更改了元素的名称或类型),你可能需要修改引用这个元素的所有代码。
  • 测试困难:由于代码直接依赖于界面元素,这使得单元测试变得困难。你可能需要创建一个界面元素的实例,或者使用复杂的模拟技术,才能测试这些代码。
  • 违反MVVM模式:在WPF中,推荐使用MVVM(Model-View-ViewModel)模式来组织代码。在MVVM模式中,视图(View)和模型(Model)之间的交互是通过视图模型(ViewModel)来进行的,而不是直接在代码中引用界面元素。

开始使用MVVM模式



RelayCommand

首先新建一个Commands文件夹,新建一个RelayComand类:
6.png

RelayCommand如下:
  1. public class RelayCommand : ICommand
  2. {
  3.   
  4.     public event EventHandler? CanExecuteChanged;

  5.     private Action<object> _Excute { get; set; }

  6.     private Predicate<object> _CanExcute { get;set; }

  7.     public RelayCommand(Action<object> ExcuteMethod, Predicate<object> CanExcuteMethod)
  8.     {
  9.         _Excute = ExcuteMethod;
  10.         _CanExcute = CanExcuteMethod;
  11.     }

  12.     public bool CanExecute(object? parameter)
  13.     {
  14.        return _CanExcute(parameter);
  15.     }

  16.     public void Execute(object? parameter)
  17.     {
  18.        _Excute(parameter);
  19.     }
  20. }
复制代码
RelayCommand实现了ICommand接口。
先来介绍一下
  1. ICommand
复制代码
接口。

ICommand

在WPF(Windows Presentation Foundation)中,
  1. ICommand
复制代码
是一个接口,它定义了一种机制,用于在用户界面(UI)中处理事件,这种机制与用户界面的具体行为进行了解耦。这是实现MVVM(Model-View-ViewModel)设计模式的关键部分。
  1. ICommand
复制代码
接口包含两个方法和一个事件:

    1. Execute(object parameter)
    复制代码
    :当调用此命令时,应执行的操作。
    1. CanExecute(object parameter)
    复制代码
    :如果可以执行
    1. Execute
    复制代码
    方法,则返回
    1. true
    复制代码
    ;否则返回
    1. false
    复制代码
    。这可以用于启用或禁用控件,例如按钮。
    1. CanExecuteChanged
    复制代码
    事件:当
    1. CanExecute
    复制代码
    的返回值可能发生更改时,应引发此事件。
ICommand的结构图如下所示:
7.png

代码如下所示:
  1. public interface ICommand
  2. {
  3.   
  4.      event EventHandler? CanExecuteChanged;
  5.   
  6.      bool CanExecute(object? parameter);
  7.    
  8.      void Execute(object? parameter);
  9. }
复制代码
现在再来看看
  1. RelayCommand
复制代码

RelayCommand
  1. RelayCommand
复制代码
是一种常用于WPF和MVVM模式的设计模式,它是一种特殊的命令类型。在MVVM模式中,
  1. RelayCommand
复制代码
允许将命令的处理逻辑从视图模型中分离出来,使得视图模型不需要知道命令的具体执行逻辑,从而实现了视图模型和命令处理逻辑的解耦。
  1. RelayCommand
复制代码
通常包含两个主要部分:
  1. CanExecute
复制代码
  1. Execute
复制代码
  1. CanExecute
复制代码
是一个返回布尔值的函数,用于确定命令是否可以执行。
  1. Execute
复制代码
是一个执行命令的函数,当
  1. CanExecute
复制代码
返回
  1. true
复制代码
时,
  1. Execute
复制代码
将被调用。
这种设计模式使得你可以在不改变视图模型的情况下,更改命令的处理逻辑,提高了代码的可维护性和可重用性。
简单理解就是
  1. RelayCommand
复制代码
  1. ICommand
复制代码
接口的一个常见实现,它允许你将
  1. Execute
复制代码
  1. CanExecute
复制代码
的逻辑定义为委托,从而实现对命令的灵活处理。
在RelayCommand中我们定义了两个委托:
  1. private Action<object> _Excute { get; set; }

  2. private Predicate<object> _CanExcute { get;set; }
复制代码
  1. Action<object>
复制代码
是一个委托,它封装了一个接受单个参数并且没有返回值的方法。这个参数的类型是
  1. object
复制代码

对应于这一部分:
8.png
  1. Predicate<object>
复制代码
是一个委托,它封装了一个接受单个参数并返回一个
  1. bool
复制代码
值的方法。这个参数的类型是
  1. object
复制代码

对应于这一部分:
9.png

RelayCommand的构造函数为:
  1. public RelayCommand(Action<object> ExcuteMethod, Predicate<object> CanExcuteMethod) {     _Excute = ExcuteMethod;     _CanExcute = CanExcuteMethod; }
复制代码
现在去看看
  1. View—ViewModel
复制代码


View—ViewModel

ViewModel是一个抽象,它代表了View的状态和行为。ViewModel包含了View所需的数据,并提供了命令以响应View上的用户操作。ViewModel不知道View的具体实现,它只知道如何提供View所需的状态和行为。
ViewModel的主要职责包括:

  • 数据绑定:ViewModel提供了View所需的数据。这些数据通常是以属性的形式提供的,当这些属性的值改变时,ViewModel会通过实现
    1. INotifyPropertyChanged
    复制代码
    接口来通知View。
  • 命令绑定:ViewModel提供了命令以响应View上的用户操作。这些命令通常是以
    1. ICommand
    复制代码
    接口的实现的形式提供的。
  • 视图逻辑:ViewModel包含了View的逻辑,例如,决定何时显示或隐藏某个元素,何时启用或禁用某个按钮等。
新建一个ViewModel文件夹,在该文件夹中新建一个MainViewModel类:
10.png

目前写的MainViewModel如下:
  1. public class MainViewModel
  2. {
  3.     public ObservableCollection<User> Users { get; set; }
  4.     public ICommand AddUserCommand { get; set; }
  5.     public string? Name { get; set; }
  6.     public string? Email { get; set; }

  7.     public MainViewModel()
  8.     {
  9.         Users = UserManager.GetUsers();
  10.         AddUserCommand = new RelayCommand(AddUser, CanAddUser);
  11.     }

  12.     private bool CanAddUser(object obj)
  13.     {
  14.         return true;
  15.     }

  16.     private void AddUser(object obj)
  17.     {
  18.         User user = new User();
  19.         user.Name = Name;
  20.         user.Email = Email;
  21.         UserManager.AddUser(user);
  22.     }
  23. }
复制代码
现在我们结合这张图,理解View与ViewModel之间的关系:
11.png

一个一个来理解,首先最重要的就是数据绑定。
现在View的xaml如下:
  1. <Window x:Class="WPF_MVVM_Pattern.MainWindow"
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5.         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6.         xmlns:local="clr-namespace:WPF_MVVM_Pattern"
  7.         mc:Ignorable="d"
  8.         Title="MainWindow" Height="450" Width="800">
  9.     <StackPanel>
  10.         <ToolBar>
  11.             <Label Content="姓名:"></Label>
  12.             <TextBox Text="{Binding Name}"  Width="50"></TextBox>
  13.             <Label Content="邮箱:"></Label>
  14.             <TextBox Text="{Binding Email}" Width="100"></TextBox>
  15.             <Button Content="添加"
  16.                     Command="{Binding AddUserCommand }"></Button>         
  17.         </ToolBar>
  18.         <StackPanel>
  19.             <DataGrid ItemsSource="{Binding Users}"></DataGrid>
  20.             
  21.         </StackPanel>
  22.     </StackPanel>
  23. </Window>
复制代码
cs如下:
  1. public partial class MainWindow : Window
  2. {      
  3.     public MainWindow()
  4.     {
  5.         InitializeComponent();
  6.         MainViewModel mainViewModel = new MainViewModel();
  7.         this.DataContext = mainViewModel;
  8.     }
  9.    
  10. }
复制代码
将MainWindow的DataContext赋值给了mainViewModel。
  1. <TextBox Text="{Binding Name}"  Width="50"></TextBox>
  2. <TextBox Text="{Binding Email}" Width="100"></TextBox>
  3. <DataGrid ItemsSource="{Binding Users}"></DataGrid>
复制代码
中进行了数据绑定,对应于图中的这一部分:
12.png

现在来看看命令绑定。
  1. <Button Content="添加"
  2.          Command="{Binding AddUserCommand }"></Button>  
复制代码
进行了命令绑定,对应于图中这一部分:
13.png

现在先来看看效果:
14.gif

现在已经实现了与前面基于事件驱动同样的效果,但是上面那张图中的Send Notifications还没有体现。
Send Notifications表示ViewModel中的更改会通知View。
现在我们来以一个例子说明一下Send Notifications是如何实现的。
首先添加一个测试命令:
  1. public ICommand TestCommand { get; set; }
复制代码
在构造函数中添加:
  1. TestCommand = new RelayCommand(Test, CanTest);
复制代码
实现Test与CanTest方法:
  1. private bool CanTest(object obj)
  2. {
  3.     return true;
  4. }

  5. private void Test(object obj)
  6. {
  7.     Name = "小1";
  8.     Email = "111@qq.com";
  9. }
复制代码
View中修改如下:
  1. <Button Content="测试"
  2.          Command="{Binding TestCommand }"></Button>
复制代码
现在去尝试,我们会发现没有效果,原因是我们的ViewModel没有实现
  1. INotifyPropertyChanged
复制代码
接口。
INotifyPropertyChanged接口介绍
在WPF(Windows Presentation Foundation)中,
  1. INotifyPropertyChanged
复制代码
接口用于实现数据绑定中的属性更改通知。当绑定到UI元素的数据源中的属性值发生更改时,
  1. INotifyPropertyChanged
复制代码
接口可以通知UI元素更新。
  1. INotifyPropertyChanged
复制代码
接口只定义了一个事件:
  1. PropertyChanged
复制代码
。当属性值发生更改时,应触发此事件。事件参数
  1. PropertyChangedEventArgs
复制代码
包含更改的属性的名称。
现在我们的MainViewModel实现一下INotifyPropertyChanged接口,如下所示:
  1. public class MainViewModel : INotifyPropertyChanged
  2. {
  3.      public ObservableCollection<User> Users { get; set; }
  4.      public ICommand AddUserCommand { get; set; }
  5.      public ICommand TestCommand { get; set; }

  6.      private string? _name;
  7.      public string? Name
  8.      {
  9.          get { return _name; }
  10.          set
  11.          {
  12.              if (_name != value)
  13.              {
  14.                  _name = value;
  15.                  OnPropertyChanged(nameof(Name));
  16.              }
  17.          }
  18.      }

  19.      private string? _email;
  20.      public string? Email
  21.      {
  22.          get { return _email; }
  23.          set
  24.          {
  25.              if (_email != value)
  26.              {
  27.                  _email = value;
  28.                  OnPropertyChanged(nameof(Email));
  29.              }
  30.          }
  31.      }

  32.      public MainViewModel()
  33.      {
  34.          Users = UserManager.GetUsers();
  35.          AddUserCommand = new RelayCommand(AddUser, CanAddUser);
  36.          TestCommand = new RelayCommand(Test, CanTest);
  37.      }

  38.      private bool CanTest(object obj)
  39.      {
  40.          return true;
  41.      }

  42.      private void Test(object obj)
  43.      {
  44.          Name = "小1";
  45.          Email = "111@qq.com";
  46.      }

  47.      private bool CanAddUser(object obj)
  48.      {
  49.          return true;
  50.      }

  51.      private void AddUser(object obj)
  52.      {
  53.          User user = new User();
  54.          user.Name = Name;
  55.          user.Email = Email;
  56.          UserManager.AddUser(user);
  57.      }

  58.      public event PropertyChangedEventHandler? PropertyChanged;

  59.      protected virtual void OnPropertyChanged(string propertyName)
  60.      {
  61.          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  62.      }
  63. }
复制代码
现在再尝试一下,会发现ViewModel中的更改会成功通知View了,如下所示:
15.gif

对应于图中的这一部分:
16.png

现在我们来看看ViewModel—Model。

ViewModel—Model

现在我们来看看ViewModel与Model之间的关系,可以根据下面这张图进行理解:
17.png

Model(模型):Model代表了业务逻辑和数据。它包含了应用程序中的数据和对数据的操作,例如,从数据库中获取数据,或者向数据库中添加数据。Model是独立于UI的,它不知道UI的存在。
ViewModel(视图模型):ViewModel是Model和View之间的桥梁。它包含了View所需的数据(这些数据来自于Model),并提供了命令以响应View上的用户操作。ViewModel将Model的数据转换为View可以显示的数据,同时,它也将View上的用户操作转换为对Model的操作。
在我们这个例子中我们的数据来源于Model文件夹下的User类与UserManager类:
18.png

这里的Send Notifications又该如何理解呢?
我们也是以一个小例子进行说明。
首先将ViewModel中的Test方法修改为:
  1. private void Test(object obj)
  2. {
  3.      Users[0].Name = "小1";
  4.      Users[0].Email = "111@qq.com";
  5. }
复制代码
会发现现在并不会发送通知,实现View上的修改,这是因为User类并没有实现INotifyPropertyChanged接口,现在修改User类实现INotifyPropertyChanged接口:
  1. public class User : INotifyPropertyChanged
  2. {
  3.     private string? _name;
  4.     public string? Name
  5.     {
  6.         get { return _name; }
  7.         set
  8.         {
  9.             if (_name != value)
  10.             {
  11.                 _name = value;
  12.                 OnPropertyChanged(nameof(Name));
  13.             }
  14.         }
  15.     }

  16.     private string? _email;
  17.     public string? Email
  18.     {
  19.         get { return _email; }
  20.         set
  21.         {
  22.             if (_email != value)
  23.             {
  24.                 _email = value;
  25.                 OnPropertyChanged(nameof(Email));
  26.             }
  27.         }
  28.     }

  29.     public event PropertyChangedEventHandler? PropertyChanged;

  30.     protected virtual void OnPropertyChanged(string propertyName)
  31.     {
  32.         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  33.     }
  34. }
复制代码
现在可以实现通知了,效果如下所示:
19.gif


使用MVVM库

我们会发现如果全部都手动实现MVVM模式的话,代码有点多,有点麻烦。这时候就可以使用一些MVVM库来简化我们的操作。
这里以CommunityToolkit.Mvvm为例,进行说明。
CommunityToolkit.Mvvm介绍
  1. CommunityToolkit.Mvvm
复制代码
是Microsoft Community Toolkit的一部分,它是一个轻量级但功能强大的MVVM(Model-View-ViewModel)库,旨在帮助开发者更容易地实现MVVM设计模式。
该库提供了一些基础类,如
  1. ObservableObject
复制代码
  1. ObservableRecipient
复制代码
,这些类实现了
  1. INotifyPropertyChanged
复制代码
接口,并提供了
  1. SetProperty
复制代码
方法,可以在属性值改变时触发
  1. PropertyChanged
复制代码
事件。这使得数据绑定变得更加简单和高效。
此外,该库还提供了
  1. ICommand
复制代码
接口的实现,如
  1. RelayCommand
复制代码
  1. AsyncRelayCommand
复制代码
,这些类可以帮助你创建命令,命令是MVVM模式中的一个重要组成部分。
  1. CommunityToolkit.Mvvm
复制代码
还提供了一些其他有用的特性,如消息传递、设计时数据支持等,这些特性可以帮助你更好地组织和管理你的代码。
  1. CommunityToolkit.Mvvm
复制代码
是一个强大的工具,它可以帮助你更容易地实现MVVM模式,从而提高你的代码质量和开发效率。
20.png

修改之后的ViewModel如下所示:
  1. public partial class MainViewModel : ObservableObject
  2. {
  3.      public ObservableCollection<User> Users { get; set; }   

  4.      [ObservableProperty]
  5.      private string? name;

  6.      [ObservableProperty]
  7.      private string? email;
  8.    

  9.      public MainViewModel()
  10.      {
  11.          Users = UserManager.GetUsers();         
  12.      }
  13.   
  14.      [RelayCommand]
  15.      private void Test(object obj)
  16.      {
  17.          Users[0].Name = "小1";
  18.          Users[0].Email = "111@qq.com";
  19.      }
  20.    
  21.      [RelayCommand]
  22.      private void AddUser(object obj)
  23.      {
  24.          User user = new User();
  25.          user.Name = Name;
  26.          user.Email = Email;
  27.          UserManager.AddUser(user);
  28.      }
  29.    
  30. }
复制代码
修改之后的User类如下所示:
  1. public partial class User : ObservableObject
  2. {
  3.      [ObservableProperty]
  4.      private string? _name;

  5.      [ObservableProperty]
  6.      private string? _email;            
  7. }
复制代码
用到了
  1. CommunityToolkit.Mvvm
复制代码
库中的三个东西,分别是ObservableObject、[ObservableProperty]与[RelayCommand]。
先来看一下ObservableObject。
  1. ObservableObject
复制代码
  1. CommunityToolkit.Mvvm
复制代码
库中的一个基础类,它实现了
  1. INotifyPropertyChanged
复制代码
接口。这个接口是.NET数据绑定基础架构的一部分,当对象的一个属性改变时,它会通知绑定到该属性的任何元素。
21.png

22.png

具体见:ObservableObject - Community Toolkits for .NET | Microsoft Learn
在这里我们使用
  1. [ObservableProperty]
  2. private string? name;
复制代码
它将生成一个像这样的可观察属性:
  1. public string? Name
  2. {
  3.     get => name;
  4.     set => SetProperty(ref name, value);
  5. }
复制代码
具体见:ObservableProperty attribute - Community Toolkits for .NET | Microsoft Learn
我们使用
  1. [RelayCommand]
  2. private void AddUser(object obj)
  3. {
  4.    User user = new User();
  5.    user.Name = Name;
  6.    user.Email = Email;
  7.    UserManager.AddUser(user);
  8. }
复制代码
代码生成器会生成一个命令如下所示:
  1. private RelayCommand? addUserCommand;

  2. public IRelayCommand AddUserCommand => addUserCommand ??= new RelayCommand(AddUser);
复制代码
具体见:RelayCommand attribute - Community Toolkits for .NET | Microsoft Learn
现在我们的ViewModel与Model就可以简化了,现在再来看看效果:
23.gif


总结

本文先总体介绍了一下MVVM模式,关于MVVM模式可以根据这张图帮助理解:
24.png

由于很多同学可能与我一样,是从winform到wpf的,因此在wpf中使用winform中的事件驱动编程范式完成了一个小例子,关于事件驱动编程,可以根据这张图帮助理解:
25.png

由于这种模式耦合比较多,我们想要松耦合,因此开始学习MVVM模式。我们创建了实现
  1. ICommand
复制代码
接口的
  1. RelayCommand
复制代码
类,实现
  1. INotifyPropertyChanged
复制代码
接口的
  1. MainViewModel
复制代码
类与
  1. User
复制代码
类。使用数据绑定与命令绑定改写xaml页面。
最后由于手动实现MVVM模式,需要写很多代码,看过去比较复杂与麻烦,我们可以使用MVVM库来简化MVVM模式的实现。
以上就是WPF中MVVM模式的理解与实现的详细内容,更多关于WPF MVVM模式的资料请关注晓枫资讯其它相关文章!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
18
积分
16
注册时间
2022-12-28
最后登录
2022-12-28

发表于 2025-2-8 02:06:18 | 显示全部楼层
感谢楼主分享。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

  • 打卡等级:即来则安
  • 打卡总天数:25
  • 打卡月天数:1
  • 打卡总奖励:368
  • 最近打卡:2025-12-12 05:30:50
等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
418
积分
56
注册时间
2023-2-1
最后登录
2025-12-12

发表于 昨天 15:09 | 显示全部楼层
顶顶更健康!!!
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1楼
2楼
3楼

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表