当前位置: 首页 > news >正文

MVVM架构与ICommand核心笔记

一、MVVM 架构核心理论

1. 定义与本质

MVVM(Model-View-ViewModel)是基于 MVC 改进的 UI 架构模式,核心是通过 ViewModel 实现 View 与 Model 的解耦,适用于支持数据绑定的框架(如 WPF、.NET MAUI、Vue.js),核心目标是提升代码可维护性、可测试性与复用性。

2. 三大组件职责与数据流向
组件核心职责关键特性与注意事项
Model(模型)1. 存储业务数据(如用户信息、订单数据);2. 封装业务逻辑(如数据校验、计算);3. 数据持久化(读写数据库、XML 等)。- 完全独立于 UI,不依赖任何视图组件;- 数据来源:外部存储(数据库 / XML)、初始值、View 收集后同步;- 示例:User实体类、OrderService业务逻辑类。
View(视图)1. 展示 UI 界面(按钮、输入框、列表等);2. 收集用户输入(如表单填写);3. 渲染界面外观(样式、布局)。- 仅保留简单数据校验(如非空判断、格式校验),不包含业务逻辑;- 通过数据绑定关联 ViewModel,不直接操作 Model;- 示例:WPF 的 XAML 页面、.NET MAUI 的页面布局。
ViewModel(视图模型)1. 作为 View 与 Model 的 “桥梁”,传递数据;2. 暴露 View 可绑定的属性(如格式化后的数据);3. 响应 View 的用户操作(如按钮点击)。- 实现INotifyPropertyChanged接口,确保属性变更同步到 View;- 不依赖 UI 组件,可独立编写单元测试;- 核心作用:解耦 View 与 Model,隔离业务逻辑。
3. 数据交互流程(以 “用户信息展示与修改” 为例)
  1. 读取数据(Model→View)

Model 从数据库读取用户信息(如User{Id=1, Name="张三", Age=25})→ ViewModel 接收 Model 数据并封装为可绑定属性(如UserName“张三”)→ View 通过数据绑定展示UserName。

  1. 保存数据(View→Model)

View 收集用户输入(如修改UserName为 “李四”)→ ViewModel 接收输入并验证→ 调用 Model 的持久化方法→ Model 将 “李四” 同步到数据库。

4. MVVM 与 MVP 的差异
架构核心组件关键区别适用场景
MVVMModel-View-ViewModel1. View 与 ViewModel 通过数据绑定关联,无直接代码依赖;2. ViewModel 独立于 UI,可测试性更强。WPF、.NET MAUI、前端框架
MVPModel-View-Presenter1. Presenter(如 WinForm 的 Form.cs)直接持有 View 引用,耦合度高;2. View 需暴露方法供 Presenter 调用更新。WinForm、传统桌面应用

核心结论:MVVM 是 MVP 的优化,用 ViewModel 替代 Presenter,通过数据绑定彻底解耦 View 与业务逻辑。

二、ViewModel 核心技术:INotifyPropertyChanged

1. 作用

当 ViewModel 的属性值变更时,通过INotifyPropertyChanged接口触发PropertyChanged事件,通知 View 自动更新 UI(实现 “数据驱动 UI”)。

2. 实现示例(以 “用户信息 ViewModel” 为例)
using System.ComponentModel;
using System.Runtime.CompilerServices;
​
// ViewModel基类:封装INotifyPropertyChanged,供所有ViewModel继承
public class BaseViewModel : INotifyPropertyChanged
{// 事件:属性变更时触发public event PropertyChangedEventHandler? PropertyChanged;
​// 触发事件的辅助方法([CallerMemberName]自动获取调用属性名)protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}
​// 简化属性赋值:赋值后自动触发事件protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string? propertyName = null){if (EqualityComparer<T>.Default.Equals(field, value)) return false;field = value;OnPropertyChanged(propertyName);return true;}
}
​
// 具体ViewModel:用户信息视图模型
public class UserViewModel : BaseViewModel
{// 持有Model实例(业务数据来源)private readonly User _userModel;
​// 可绑定属性:用户名(供View绑定)private string _userName;public string UserName{get => _userName;set => SetProperty(ref _userName, value); // 赋值后自动触发UI更新}
​// 可绑定属性:年龄(格式化展示,如“25岁”)public string FormattedAge => $"{_userModel.Age}岁";
​// 构造函数:初始化Model并同步数据到ViewModelpublic UserViewModel(User userModel){_userModel = userModel;// 将Model数据同步到ViewModel属性_userName = _userModel.Name;}
​// 业务方法:保存用户信息(View调用此方法同步到Model)public void SaveUserInfo(){// 验证数据(ViewModel层的业务逻辑)if (string.IsNullOrWhiteSpace(UserName))throw new ArgumentException("用户名不能为空");// 同步ViewModel数据到Model_userModel.Name = UserName;// 调用Model的持久化方法(如保存到数据库)_userModel.SaveToDatabase();}
}
​
// Model:用户实体类
public class User
{public int Id { get; set; }public string Name { get; set; } = string.Empty;public int Age { get; set; }
​// Model层业务逻辑:保存到数据库(模拟)public void SaveToDatabase(){Console.WriteLine($"用户{Name}(ID:{Id})已保存到数据库");}
}

三、ICommand:ViewModel 响应 View 操作的核心

1. 定义与作用

ICommand 是.NET 中用于解耦用户操作(如按钮点击)与业务逻辑的接口,核心能力:

  • 判断操作是否可执行(CanExecute);

  • 执行操作逻辑(Execute);

  • 通知 UI 更新操作可用性(CanExecuteChanged事件)。

2. 接口核心成员
public interface ICommand
{// 1. 判断命令是否可执行(返回true则按钮启用,false则禁用)bool CanExecute(object? parameter);
​// 2. 执行命令对应的业务逻辑void Execute(object? parameter);
​// 3. 当命令可执行状态变化时触发,通知UI更新按钮状态event EventHandler? CanExecuteChanged;
}
3. 经典实现:RelayCommand(通用命令封装)

由于 ICommand 是接口,需自定义实现类(RelayCommand),通过委托灵活接收业务逻辑:

using System.Windows.Input;
​
public class RelayCommand : ICommand
{// 委托:存储命令执行逻辑private readonly Action<object?> _execute;// 委托:存储命令可执行状态判断逻辑(可选)private readonly Func<object?, bool>? _canExecute;
​// 构造函数:传入执行逻辑,可执行判断逻辑可选(默认始终可执行)public RelayCommand(Action<object?> execute, Func<object?, bool>? canExecute = null){_execute = execute ?? throw new ArgumentNullException(nameof(execute));_canExecute = canExecute;}
​// 实现ICommand:判断命令是否可执行public bool CanExecute(object? parameter){return _canExecute?.Invoke(parameter) ?? true; // 无判断逻辑则默认可执行}
​// 实现ICommand:执行命令逻辑public void Execute(object? parameter){_execute(parameter);}
​// 实现ICommand:订阅/取消订阅CanExecuteChanged事件// 依赖CommandManager自动监测UI变化,更新可执行状态public event EventHandler? CanExecuteChanged{add => CommandManager.RequerySuggested += value;remove => CommandManager.RequerySuggested -= value;}
​// 手动触发CanExecuteChanged事件(可选,强制更新UI状态)public void RaiseCanExecuteChanged(){CommandManager.InvalidateRequerySuggested();}
}
4. ICommand 在 ViewModel 中的应用示例
public class UserViewModel : BaseViewModel
{private readonly User _userModel;private string _userName;
​// 1. 定义命令属性(供View绑定按钮点击)public ICommand SaveCommand { get; }
​public string UserName{get => _userName;set {SetProperty(ref _userName, value);// 用户名变化时,手动更新SaveCommand的可执行状态((RelayCommand)SaveCommand).RaiseCanExecuteChanged();}}
​public UserViewModel(User userModel){_userModel = userModel;_userName = _userModel.Name;
​// 2. 初始化命令:绑定执行逻辑与可执行判断SaveCommand = new RelayCommand(execute: parameter => SaveUserInfo(), // 执行“保存用户”逻辑canExecute: parameter => !string.IsNullOrWhiteSpace(UserName) // 用户名非空才可用);}
​// 3. 命令对应的业务逻辑private void SaveUserInfo(){_userModel.Name = UserName;_userModel.SaveToDatabase();// 可添加UI反馈逻辑(如弹出提示)Console.WriteLine("用户信息保存成功!");}
}
5. View 与 ICommand 的绑定(WPF/XAML 示例)
<!-- 1. 引用ViewModel命名空间 -->
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:vm="clr-namespace:YourProject.ViewModels"Title="用户信息管理" Height="300" Width="400"><!-- 2. 设置DataContext为ViewModel实例(绑定上下文) --><Window.DataContext><vm:UserViewModel><!-- 初始化Model参数 --><vm:UserViewModel.UserModel><vm:User Id="1" Name="张三" Age="25" /></vm:UserViewModel.UserModel></vm:UserViewModel></Window.DataContext>
​<!-- 3. 绑定ViewModel的属性与命令 --><StackPanel Margin="20"><!-- 输入框:绑定UserName属性(双向绑定,输入变化同步到ViewModel) --><TextBox Text="{Binding UserName, Mode=TwoWay}" Margin="0 0 0 10" Hint="请输入用户名" /><!-- 按钮:绑定SaveCommand命令(仅当UserName非空时启用) --><Button Content="保存用户信息" Command="{Binding SaveCommand}" /></StackPanel>
</Window>

四、MVVM 完整流程示例(用户信息修改场景)

  1. 初始化:View 加载时,ViewModel 接收 Model 的初始数据(“张三”),并通过数据绑定展示在 TextBox 中。

  2. 用户操作:用户在 TextBox 中修改用户名为 “李四”,输入变化通过双向绑定同步到 ViewModel 的UserName属性,触发PropertyChanged事件。

  3. 命令状态更新:UserName变化后,SaveCommand的CanExecute重新判断(“李四” 非空,返回 true),按钮保持启用。

  4. 执行命令:用户点击 “保存” 按钮,SaveCommand的Execute方法调用SaveUserInfo,将 “李四” 同步到 Model 并保存到数据库。

  5. 反馈:Model 保存成功后,ViewModel 可通过弹窗、日志等方式反馈结果(如控制台输出 “用户信息保存成功!”)。

http://www.dtcms.com/a/553077.html

相关文章:

  • Web后端开发学习总结
  • 萍乡做网站的公司有哪些门户网站建设方案ppt 百度文库
  • Wireshark抓包教程:获取网站登录凭证
  • 销售驱动的黄昏:医药商业化模式的效率悖论与转型必然
  • 【mysql】锁机制 - 2.行锁间隙锁临键锁
  • 做网站制作需要多少钱网络设计公司有哪些
  • 外卖骑手的Python转型指南:从送餐到编程的实战路径
  • 一款端侧TTS:NeuTTS-Air,3秒语音克隆,声音听起来没有生硬感,语气和节奏感相对自然
  • 网站建设网站软件页面设计属于什么知识产权
  • 网站管理的含义长春做网站哪家好
  • Nacos和Nginx集群,项目启动失败问题
  • Opencv(五): 腐蚀和膨胀
  • 17.React获取DOM的方式
  • 编码器读写操作方式
  • WEB服务
  • 2025年10月31日 AI大事件
  • Rust开发中泛型结构体定义与使用(通用容器)
  • 9-SpringCloud-服务网关 Gateway-高级特性之 Filter-2
  • Electron中使用exceljs+Node模块编写
  • 优秀服装网站设计业务接单网站
  • 构造/析构/赋值运算理解
  • 给予虚拟成像台尝鲜版九,支持 HTML 原型模式
  • 区块链技术在生产数据管理中的应用:Hyperledger Fabric与蚂蚁链智能合约设计
  • 可以用手机建设网站吗wordpress程序
  • deepin Ubuntu/Debian系统 环境下安装nginx,php,mysql,手动安装,配置自己的项目
  • SDC命令详解:使用set_dont_touch_network命令进行约束
  • CI/CD(三)—— 【保姆级实操】Jenkins+Docker GitLab+Tomcat 实现微服务CI/CD全流程部署
  • 20. Portals和Fragment
  • 企业网站管理wordpress必备插件
  • 数据结构之栈和队列-栈