翼度科技»论坛 编程开发 .net 查看内容

MVVM - Model和ViewModel的创建和配置

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
MVVM-Model和ViewModel的创建和配置

本文同时为b站的笔记,相关示例代码
简介

MVVM:Model-View-ViewModel,是一种软件架构的模式。通过引入一个中间层ViewModel,分离用户界面的表示层(View)和业务逻辑层(Model)。
需要手动实现MVVM,可以通过以下方法。
定义Model

创建一个模型(Model)类,用来定义需要的数据结构。
这个类包含了想要在应用中使用和展示的数据。
这里就创建LoginModel类
将需要的属性放到这个类当中
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. namespace WPF_Study
  7. {
  8.     public class LoginModel
  9.     {
  10.         private string _UserName;
  11.         public string UserName
  12.         {
  13.             get { return _UserName; }
  14.             set
  15.             {
  16.                 _UserName = value;
  17.             }
  18.         }
  19.         private string _Password;
  20.         public string Password
  21.         {
  22.             get { return _Password; }
  23.             set
  24.             {
  25.                 _Password = value;
  26.             }
  27.         }
  28.     }
  29. }
复制代码
在这里,我放入了UserName和Password用于存储账号与密码,这两个属性会在xaml中绑定到TextBlock的Text上,方便与外界做交互。
定义ViewModel

创建ViewModel

创建一个ViewModel类(这里就叫做LoginVM),这个类将作为View(用户界面)和Model(数据)之间的桥梁。
在这个类中创建属性LoginModel:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. namespace WPF_Study
  7. {
  8.     public class LoginVM
  9.     {
  10.         private LoginModel _loginModel;
  11.         public LoginModel loginModel
  12.         {
  13.             get
  14.             {
  15.                 return _loginModel;
  16.             }
  17.             set
  18.             {
  19.                 _loginModel = value;
  20.             }
  21.         }
  22.     }
  23. }
复制代码
指定MainWindow上下文

在MainWindow.xaml.cs中,将ViewModel指定给当前界面的上下文:
  1. LoginVM loginVM;
  2. public MainWindow()
  3. {
  4.     InitializeComponent();
  5.     loginVM = new LoginVM();
  6.     this.DataContext = loginVM;
  7. }
复制代码
绑定到xaml控件属性

同时修改xaml里面需要绑定的属性。别忘记在xaml中绑定的不再是UserName和Password了,而是loginModel.UserName和loginModel.Password。
后端代码访问属性

目前,loginVM是存放所有我们需要访问的属性的一个类,如果我们需要访问某个属性,那么就是到loginVM下面的loginVM.loginModel当中去访问UserName和Password。
也就是说,欲想访问这些属性,需要通过:
  1. loginVM.loginModel.UserName = "";
  2. loginVM.loginModel.Password = "";
复制代码
这样的方法。
比如以下定义一个登录按钮:
  1. private void Button_Click(object sender, RoutedEventArgs e)
  2. {
  3.     if (loginVM.loginModel.UserName == "wpf" && loginVM.loginModel.Password == "777")
  4.     {
  5.         //MessageBox.Show("Login");
  6.         Index index = new Index();
  7.         index.Show();
  8.         this.Hide();
  9.     }
  10.     else
  11.     {
  12.         MessageBox.Show("Error");
  13.         loginVM.loginModel.UserName = "";
  14.         loginVM.loginModel.Password = "";
  15.     }
  16. }
复制代码
这个时候尝试运行,会发现程序报错:
loginVM.loginModel.UserName:未将对象引用设置到对象的实例。
出现“未将对象引用设置到对象的实例”错误通常是因为尝试访问一个还未初始化的对象的属性或方法。
这是因为,我们确实在MainWindow.xaml.cs中实例化了loginVM = new LoginVM();,但是我们没有实例化loginModel。此时直接访问loginVM.loginModel的成员时,因为LoginVM类中的_LoginModel成员变量没有被初始化。
那么怎么办呢?只需要在loginModel的访问器中加入是否实例化的特判即可:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. namespace WPF_Study
  7. {
  8.     public class LoginVM
  9.     {
  10.         private LoginModel _loginModel;
  11.         public LoginModel loginModel
  12.         {
  13.             get
  14.             {
  15.                 if(_loginModel == null)
  16.                     _loginModel = new LoginModel();
  17.                 return _loginModel;
  18.             }
  19.             set
  20.             {
  21.                 _loginModel = value;
  22.             }
  23.         }
  24.     }
  25. }
复制代码
(这个应该是更好的解决方案)也可以使用构造函数的方式,添加了一个构造函数LoginVM(),初始化_LoginModel对象。这样,创建一个LoginVM的实例时,它会自动拥有一个初始化了的LoginModel实例。
  1. public LoginVM()
  2. {
  3.     _loginModel = new LoginModel();
  4. }
复制代码
实现INotifyPropertyChanged接口

ViewModel应该实现INotifyPropertyChanged接口,这样当属性的值改变时能够通知UI进行更新。
给ViewModel继承INotifyPropertyChanged类:
  1. public class LoginVM:INotifyPropertyChanged
  2. ...
复制代码
以及INotifyPropertyChanged接口实现的核心:定义PropertyChanged事件、实现RaisePropertyChanged方法
  1. public event PropertyChangedEventHandler PropertyChanged;
  2. private void RaisePropertyChanged(string propertyChanged)
  3. {
  4.     PropertyChangedEventHandler handler = this.PropertyChanged;
  5.     if (handler != null)
  6.         handler(this, new PropertyChangedEventArgs(propertyChanged));
  7. }
复制代码
接下来在需要的地方调用RaisePropertyChanged(),就可以实现刷新UI
那么我们需要在什么时候刷新呢?我们需要在UserName和Password发生了改变的时候对吧。或者简单一点,当LoginModel发生了变化的时候。(这是不太对的,后面会说)
那么我们在LoginMV.cs中的LoginModel loginModel访问器set中,设置RaisePropertyChanged(nameof(LoginModel));即可。
现在LoginMV.cs中的关于LoginModel数据结构的部分:
  1. private LoginModel _loginModel;
  2. public LoginModel loginModel
  3. {
  4.     get
  5.     {
  6.         if (_loginModel == null)
  7.             _loginModel = new LoginModel();
  8.         return _loginModel;
  9.     }
  10.     set
  11.     {
  12.         _loginModel = value;
  13.         RaisePropertyChanged(nameof(LoginModel));
  14.     }
  15. }
复制代码
但是这时候在代码中修改UserName和Password,发现界面并不会刷新?
  1. loginVM.loginModel.UserName = "";
  2. loginVM.loginModel.Password = "";
复制代码
这是因为我们确实添加了调用接口的代码,但是仅仅修改UserName和Password并不会引起LoginModel对象本身的更改——UserName和Password只是LoginModel的内部属性。
换句话说,仅仅改变LoginModel内部的UserName和Password并不会触发INotifyPropertyChanged的PropertyChanged事件,因为这个事件是和LoginModel对象的属性关联的,而不是和LoginModel内部的属性UserName和Password关联的。
两种解决方法:

  • 在修改完loginVM.loginModel.UserName和loginVM.loginModel.Password之后,手动“修改”loginVM.loginModel
  1. loginVM.loginModel.UserName = "";
  2. loginVM.loginModel.Password = "";loginVM.loginModel = loginVM.loginModel;
复制代码

  • 在LoginModel类中也实现INotifyPropertyChanged接口,并且给UserName和Password的get也添加了调用接口的代码。这样,当UserName或Password属性发生变化时,它们可以通知视图进行更新。
小结

到目前为止,我们已经创建了 Model (LoginModel) 和 ViewModel (LoginVM),并在 ViewModel 中处理了属性变化通知(通过实现INotifyPropertyChanged)。接下来需要完善 View 的部分了。这个对应接下来的课程,将在下一篇笔记中记录。MVVM 整个体系较为庞大,这两节课也主要从改编代码的角度切入,在之后我还会写一篇 MVVM 总结,从头开始理清楚 MVVM 该怎么构架。

来源:https://www.cnblogs.com/Vanilla-chan/p/17988487/MVVM-Model-and-ViewModel
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具