MVVM - Model和ViewModel的创建和配置
MVVM-Model和ViewModel的创建和配置
简介
MVVM:Model-View-ViewModel,是一种软件架构的模式。通过引入一个中间层ViewModel,分离用户界面的表示层(View)和业务逻辑层(Model)。
需要手动实现MVVM,可以通过以下方法。
定义Model
创建一个模型(Model)类,用来定义需要的数据结构。
这个类包含了想要在应用中使用和展示的数据。
这里就创建LoginModel
类
将需要的属性放到这个类当中
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WPF_Study
{
public class LoginModel
{
private string _UserName;
public string UserName
{
get { return _UserName; }
set
{
_UserName = value;
}
}
private string _Password;
public string Password
{
get { return _Password; }
set
{
_Password = value;
}
}
}
}
在这里,我放入了UserName
和Password
用于存储账号
与密码
,这两个属性会在xaml
中绑定到TextBlock
的Text
上,方便与外界做交互。
定义ViewModel
创建ViewModel
创建一个ViewModel类(这里就叫做LoginVM),这个类将作为View(用户界面)和Model(数据)之间的桥梁。
在这个类中创建属性LoginModel
:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WPF_Study
{
public class LoginVM
{
private LoginModel _loginModel;
public LoginModel loginModel
{
get
{
return _loginModel;
}
set
{
_loginModel = value;
}
}
}
}
指定MainWindow上下文
在MainWindow.xaml.cs
中,将ViewModel指定给当前界面的上下文:
LoginVM loginVM;
public MainWindow()
{
InitializeComponent();
loginVM = new LoginVM();
this.DataContext = loginVM;
}
绑定到xaml控件属性
同时修改xaml
里面需要绑定的属性。别忘记在xaml
中绑定的不再是UserName
和Password
了,而是loginModel.UserName
和loginModel.Password
。
后端代码访问属性
目前,loginVM
是存放所有我们需要访问的属性的一个类,如果我们需要访问某个属性,那么就是到loginVM
下面的loginVM.loginModel
当中去访问UserName
和Password
。
也就是说,欲想访问这些属性,需要通过:
loginVM.loginModel.UserName = "";
loginVM.loginModel.Password = "";
这样的方法。
比如以下定义一个登录按钮:
private void Button_Click(object sender, RoutedEventArgs e)
{
if (loginVM.loginModel.UserName == "wpf" && loginVM.loginModel.Password == "777")
{
//MessageBox.Show("Login");
Index index = new Index();
index.Show();
this.Hide();
}
else
{
MessageBox.Show("Error");
loginVM.loginModel.UserName = "";
loginVM.loginModel.Password = "";
}
}
这个时候尝试运行,会发现程序报错:
loginVM.loginModel.UserName
:未将对象引用设置到对象的实例。
出现“未将对象引用设置到对象的实例”错误通常是因为尝试访问一个还未初始化的对象的属性或方法。
这是因为,我们确实在MainWindow.xaml.cs
中实例化了loginVM = new LoginVM();
,但是我们没有实例化loginModel
。此时直接访问loginVM.loginModel
的成员时,因为LoginVM
类中的_LoginModel
成员变量没有被初始化。
那么怎么办呢?只需要在loginModel
的访问器中加入是否实例化的特判即可:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WPF_Study
{
public class LoginVM
{
private LoginModel _loginModel;
public LoginModel loginModel
{
get
{
if(_loginModel == null)
_loginModel = new LoginModel();
return _loginModel;
}
set
{
_loginModel = value;
}
}
}
}
(这个应该是更好的解决方案)也可以使用构造函数的方式,添加了一个构造函数LoginVM()
,初始化_LoginModel
对象。这样,创建一个LoginVM
的实例时,它会自动拥有一个初始化了的LoginModel
实例。
public LoginVM()
{
_loginModel = new LoginModel();
}
实现INotifyPropertyChanged
接口
ViewModel
应该实现INotifyPropertyChanged
接口,这样当属性的值改变时能够通知UI进行更新。
给ViewModel
继承INotifyPropertyChanged
类:
public class LoginVM:INotifyPropertyChanged
...
以及INotifyPropertyChanged
接口实现的核心:定义PropertyChanged
事件、实现RaisePropertyChanged
方法
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyChanged)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyChanged));
}
接下来在需要的地方调用RaisePropertyChanged()
,就可以实现刷新UI
那么我们需要在什么时候刷新呢?我们需要在UserName
和Password
发生了改变的时候对吧。或者简单一点,当LoginModel
发生了变化的时候。(这是不太对的,后面会说)
那么我们在LoginMV.cs
中的LoginModel loginModel
访问器set
中,设置RaisePropertyChanged(nameof(LoginModel));
即可。
现在LoginMV.cs
中的关于LoginModel
数据结构的部分:
private LoginModel _loginModel;
public LoginModel loginModel
{
get
{
if (_loginModel == null)
_loginModel = new LoginModel();
return _loginModel;
}
set
{
_loginModel = value;
RaisePropertyChanged(nameof(LoginModel));
}
}
但是这时候在代码中修改UserName
和Password
,发现界面并不会刷新?
loginVM.loginModel.UserName = "";
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
loginVM.loginModel.UserName = "";
loginVM.loginModel.Password = "";
loginVM.loginModel = loginVM.loginModel;
- 在
LoginModel
类中也实现INotifyPropertyChanged
接口,并且给UserName
和Password
的get
也添加了调用接口的代码。这样,当UserName
或Password
属性发生变化时,它们可以通知视图进行更新。
小结
到目前为止,我们已经创建了 Model (LoginModel
) 和 ViewModel (LoginVM
),并在 ViewModel 中处理了属性变化通知(通过实现INotifyPropertyChanged
)。接下来需要完善 View 的部分了。这个对应接下来的课程,将在下一篇笔记中记录。MVVM 整个体系较为庞大,这两节课也主要从改编代码的角度切入,在之后我还会写一篇 MVVM 总结,从头开始理清楚 MVVM 该怎么构架。
MVVM - Model和ViewModel的创建和配置的更多相关文章
- MVVM模式中ViewModel和View、Model有什么区别
Model:很简单,就是业务逻辑相关的数据对象,通常从数据库映射而来,我们可以说是与数据库对应的model. View:也很简单,就是展现出来的用户界面. 基本上,绝大多数软件所做的工作无非就是从数据 ...
- django 简易博客开发 1 安装、创建、配置、admin使用
首先贴一下项目地址吧 https://github.com/goodspeedcheng/sblog 到现在位置项目实现的功能有: 1.后台管理使用Admin ,前端显示使用bootstrap 2. ...
- Servlet过滤器,Servlet过滤器创建和配置
第一:Servlet的过滤器的创建和配置,创建一个过滤器对象需要实现javax.servlet.Filter接口,同时实现Filter的3个方法. 第一方法是过滤器中的init()方法用 ...
- IIS负载均衡-Application Request Route详解第二篇:创建与配置Server Farm(转载)
IIS负载均衡-Application Request Route详解第二篇:创建与配置Server Farm 自从本系列发布之后,收到了很多的朋友的回复!非常感谢,同时很多朋友问到了一些问题,有些问 ...
- Acitivity创建与配置
•Activity的创建和配置 –Activity提供了和用户交互的可视化界面.创建一个Activity一般是继承Activity(当然也可以继承ListActivity.MapActivity等), ...
- 什么是Servlet,Servlet的作用,生命周期,如何创建、配置Servlet
什么是Servlet,作用是? servlet是一个基于java技术的WEB组件,运行在服务器端,我们利用 sevlet可以很轻松的扩展WEB服务器的功能,使它满足特定的应用需要.servlet由se ...
- ios中pch文件的创建与配置
PCH文件(Precompile Prefix Header File),也就是预编译头文件,其作用就是,方便你一次性导入在多个文件中同时用到的头文件.宏或者URL地址等(全局使用),可以有效的帮你 ...
- IDEA如何创建及配置Web项目(多图)
正文之前 在学习Java Web时,第一个遇到的问题就是如何创建或配置Web项目了,今天,就用IntelliJ IDEA 来进行Web项目配置: 创建Web项目 配置web项目 正文 创建Web项目 ...
- Spring Boot 多模块项目创建与配置 (一) (转)
Spring Boot 多模块项目创建与配置 (一) 最近在负责的是一个比较复杂项目,模块很多,代码中的二级模块就有9个,部分二级模块下面还分了多个模块.代码中的多模块是用maven管理的,每个模块都 ...
- Spring Boot 多模块项目创建与配置 (一)
最近在负责的是一个比较复杂项目,模块很多,代码中的二级模块就有9个,部分二级模块下面还分了多个模块.代码中的多模块是用maven管理的,每个模块都使用spring boot框架.之前有零零散散学过一些 ...
随机推荐
- [第二章]ABAQUS CM插件中文手册
ABAQUS Composite Modeler User Manual(zh-CN) Dassault Systèmes, 2018 注: 源文档的交叉引用链接,本文无效 有些语句英文表达更易理解, ...
- Netty基础—7.Netty实现消息推送服务
大纲 1.Netty实现HTTP服务器 2.Netty实现WebSocket 3.Netty实现的消息推送系统 (1)基于WebSocket的消息推送系统说明 (2)消息推送系统的PushServer ...
- 面试题-RabbitMQ
前言 在面试题系列文章中,笔者本着效率的原则,没有总结RabbitMQ相关的知识,但是当其他知识点都总结完毕后,我发现如果面试中针对我们实际使用的RabbitMQ进行深入原理的提问或者说说框架使用的注 ...
- Redis-过期删除策略和内存淘汰策略
简介.我们先来看如下几个问题: ①.如何设置Redis键的过期时间 ? ②.设置完一个键的过期时间后,到了这个时间,这个键还能获取到么?假如获取不到那这个键还占据着内存吗 ? ③.如何设置Redis的 ...
- Spring Boot 根据配置决定服务(集群、单机)是否使用某些主件
比如:在集群模式下,我想用 Nacos 组件,单机版不想用它. server: name: VipSoft Server Dev port: 8193 cloud: nacos: discovery: ...
- 你了解 Java 的逃逸分析吗?
Java 的逃逸分析 1. 定义 逃逸分析(Escape Analysis)是 JVM 的一种优化技术,用于分析对象的作用域,从而决定对象的分配方式或优化手段. 主要目的是判断一个对象是否会逃离当前方 ...
- 2025dsfz集训Day11:数位DP、状态压缩DP、单调队列优化DP
Day11:数位DP.状压DP.单调队列优化DP 经典题目:AccodersP2195 |[一本通提高数位动态规划]Amount of Degrees 题意: 求出区间 \([x,y]\) 中满足下面 ...
- 2025dsfz集训Day7: KMP与Trie树
Day7: KMP与Trie树 \[Designed\ By\ FrankWkd\ -\ Luogu@Lwj54joy,uid=845400 \] 特别感谢 此次课的主讲 - Kwling KMP算法 ...
- 适用于LixtBox的,开启UI虚拟化时,某些时候需要定位到还没加载的项,比如自动选中某项,视图自动移过去等等
1 /// <summary> 2 /// 将指定父级的下级索引元素,显示在视野下,使其可见 3 /// </summary> 4 /// <param name=&qu ...
- servlet 读取表单数据
通过post和get两种方式提交表单数据. form.html <!DOCTYPE html> <html lang="en"> <head> ...