(一)定义:MVVM(Model-View-ViewModel)是在MVC(Model-View-Control)模式之后引出的新的开发模式,他与MVC模式一样用于把视图(界面)和数据进行解耦,不同的是采用ViewModel来完成数据与视图的双向绑定,通过自动化的方式承担大部分数据工作,来解决由于界面复杂化和快速迭代带来的问题。它的技术模型如下图所示

(二)MVVM技术框架
根据MVVM模型框架定义的逻辑体系, MVVM技术框架提供视图模型(ViewModel)、数据模型(Model、Collection)和服务(Service)四个组件来构建开发者的MVVM开发体系。
MVVM.Service
Service主要负责与后端服务器进行通讯的处理。
MVVM.Model
Model就是系统中的业务实体,是对现实中事物的抽象,开发过程中涉及到的事物都可以抽象为Model,例如客户实体中含有客户的姓名、编号、电话、住址等属性也对应了实体类中的Property,客户的下订单、付款等行为对应了实体中的方法。
MVVM.Collection
Collection 是用来维护一个 Model的集合,它主要用于维护的是一个可以增加、删除、排序筛选的数据列表。例如订单列表、商品列表等。这些数据列表都不会在一次访问中加载完成,用户需要进行翻页等操作,这些操作触发数据更新并附加或更新到 Collection 中。Model 中的数组主要用于维护一个对象的一些不经常变化的列表属性,例如衣服对象的尺码列表、一个电脑的硬盘大小选项等,这些属性经常不需要动态添加,而是直接一次获取和展现。
MVVM.ViewModel
ViewModel是Model、Collection与XAML(View)的中间处理机,他首要完成数据到界面、界面到数据的自动化操作。同时也是用户交互行为事件的处理中心。我们通过代码来了解MVVM对象的使用方式
MVVM.View
View是系统界面展示,在WPF中即为XAML页面。
(三)INotifyPropertyChanged实现原理
3.1 类层次图

3.2 应用场景举例
项目用到DataGridView,用它绑定数据源后,如果数据源中的数据修改无法及时刷新到控件上,必须切换单元格的焦点才能导致刷新显示新数值,用INotifyPropertyChanged解决此问题。


代码分析//Step1:定义一个类:Customer
public class Customer : INotifyPropertyChanged
{
private string _customerName;
private string _phoneNumber;
public string CustomerName
{
get { return _customerName; }
set
{
if (_customerName != value)
{
_customerName = value;
PropertyChanged(this, new PropertyChangedEventArgs("CustomerName"));
}
}
}
public string PhoneNumber
{
get { return _phoneNumber; }
set
{
if (_phoneNumber != value)
{
_phoneNumber = value;
PropertyChanged(this, new PropertyChangedEventArgs("PhoneNumber"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate{};
}
//Step2:创建一个绑定数据集
private BindingList<Customer> customers = new BindingList<Customer>();
//Step3:向数据集中添加数据并绑定到控件
customers.Add(new Customer() { CustomerName = "张三", PhoneNumber = "010-5263" });
customers.Add(new Customer() { CustomerName = "李四", PhoneNumber = "010-8823" });
dataGridView1.DataSource = customers;
//Step4:修改数据源
customers[0].CustomerName = "王五";
3.3 案例分析

关于public event PropertyChangedEventHandler PropertyChanged在子类实现的三种方式

(1) 在构造中实现public Customer()
{
PropertyChanged += Customer_PropertyChanged;
}
private void Customer_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine(e.PropertyName);
}

(2) 无参匿名委托public event PropertyChangedEventHandler PropertyChanged = delegate {};

(3) 有参匿名委托public event PropertyChangedEventHandler PropertyChanged = delegate(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine(e.PropertyName);
};
(四)Microsoft.Practices.Prism.dll>>Microsoft.Practices.Prism.Commands>>DelegateCommand类原理分析

DelegateCommand源码public class DelegateCommand : DelegateCommandBase
{
public DelegateCommand(Action executeMethod) : this(executeMethod, () => true)
{
}
public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod) : base(action, func)
{
Action<object> action = null;
Func<object, bool> func = null;
if (action == null)
{
action = o => executeMethod();
}
if (func == null)
{
func = o => canExecuteMethod();
}
if ((executeMethod == null) || (canExecuteMethod == null))
{
throw new ArgumentNullException("executeMethod", Resources.DelegateCommandDelegatesCannotBeNull);
}
}
public bool CanExecute()
{
return base.CanExecute(null);
}
public void Execute()
{
base.Execute(null);
}
}

DelegateCommand《T》源码public class DelegateCommand<T> : DelegateCommandBase
{
public DelegateCommand(Action<T> executeMethod) : this(executeMethod, o => true)
{
}
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod) : base(action, func)
{
Action<object> action = null;
Func<object, bool> func = null;
if (action == null)
{
action = o => executeMethod((T) o);
}
if (func == null)
{
func = o => canExecuteMethod((T) o);
}
if ((executeMethod == null) || (canExecuteMethod == null))
{
throw new ArgumentNullException("executeMethod", Resources.DelegateCommandDelegatesCannotBeNull);
}
Type type = typeof(T);
if (type.get_IsValueType() && !(type.get_IsGenericType() && typeof(Nullable<>).IsAssignableFrom(type.GetGenericTypeDefinition())))
{
throw new InvalidCastException(Resources.DelegateCommandInvalidGenericPayloadType);
}
}
public bool CanExecute(T parameter)
{
return base.CanExecute(parameter);
}
public void Execute(T parameter)
{
base.Execute(parameter);
}
}
参考资料:
http://www.cnblogs.com/guofeiji/p/5277834.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人