简介:面向窗体应用开发者的MVVM框架升级版资源,核心特点是借助Castle动态代理自动拦截属性访问并触发变更通知,将Vue风格的双向绑定引入窗体开发环境。资源包含128个文件,以动态链接库、源代码、调试符号、配置文件和可执行程序为主,分别对应运行依赖、业务实现、排错信息、参数设置和演示示例;压缩包仅3.52MB,目录结构紧凑,便于直接阅读和二次改造。目前已有1695人学习,适合希望在窗体应用中实践MVVM、减少手写属性通知样板代码的.NET开发者。通过示例工程可以理解动态代理如何统一处理属性监听与变更通知,提取可复用的绑定机制,并迁移到命令绑定、依赖注入等场景;同时还能看到类似Vue的数据同步思想在非WPF平台的落地方式。此类设计能显著降低界面与业务逻辑的耦合,使属性变化自动同步到控件,测试和重构也更方便,对提升窗体项目的可维护性与开发效率有直接参考价值。 WinForm 项目做久了,谁没被 UI 和业务逻辑缠成一团的状态坑过。代码越写越多,Form 里按钮点击事件直接写 SQL、直接调接口的情况到处都是。我前前后后在几个工业软件项目里试过把 MVVM 引到 WinForms,第一版用最朴素的 INotifyPropertyChanged 手写方案,属性一多全是复制粘贴的样板代码,后来改成动态代理方案,才算真正觉得“能用且好用”。这篇就把这套升级版的思路、代码、坑全部讲透,适合正在 WinForms 项目里挣扎、想用 MVVM 但又不想迁移到 WPF 的朋友。
说白了,MVVM 在 WinForms 里能落地,核心就两件事:属性变更通知(INotifyPropertyChanged)和命令绑定(ICommand)。动态代理解决第一件事——让 ViewModel 的属性自动具备通知能力,少写几万行 setter 样板代码;命令用一个通用 RelayCommand 解决。两个拼起来,加上 WinForms 自带的 DataBindings,写起来的手感已经很接近 WPF 绑定了。
这篇不劝你从 WinForms 迁到 WPF。很多工控、设备类项目里 WinForms 之所以替代不掉,是因为历史代码多、客户机器老旧、很多 PLC 通信库和驱动只有 WinForms 版本、部署又简单。与其折腾重构,不如把 MVVM 的架构思路直接搬进 WinForms 里面来。
1. 为什么要在 WinForms 里折腾 MVVM
1.1 事件驱动写起来爽,维护起来痛
WinForms 的事件驱动模型是它容易上手的关键,也是项目变大的第一杀手。双击按钮生成 Click 事件,往里填代码,没人拦你。填到五千行的时候,你连哪个按钮改了哪个状态都分不清了。
我拆过一个设备管理系统的界面:主窗体三十多个控件、十几个按钮、若干定时器,事件方法互相调用,状态靠一个全局类跑来跑去。后来按 MVVM 把每个页面的状态收敛到对应 ViewModel,Form 后面的代码从八百多行砍到一百多行,只剩控件和 ViewModel 的绑定关系。那是我第一次确定,WinForms 需要 MVVM。
MVVM 在 WinForms 里能做的事比很多人想的多:按钮启用禁用、颜色、进度条、列表选中项、文本框联动刷新、表单校验提示,都能通过绑定处理。如果你的项目里有大量“某个值变了,一堆控件跟着变”的需求,收益会非常明显。
1.2 WinForms 的绑定能力没你想的弱
很多人一提 WinForms 绑定就说不如 WPF。差距确实存在,但没到“没法用”的程度。WinForms 的 DataBindings 支持控件属性到数据源属性的双向绑定,配合 INotifyPropertyChanged,控件能自动监听属性变化并刷新。
真正的问题只有两个。一个是设计期绑定体验差,在 VS 里拖 BindingSource、配 DataBindings 的 DataSource 和 DataMember 很繁琐;另一个是手动实现 INotifyPropertyChanged 太痛苦。前一个靠代码里写绑定辅助类解决,后一个就是这篇要说的动态代理方案。
2. 升级版核心思路:动态代理到底代理了什么
2.1 旧方案的样板代码让人崩溃
先看传统写法:
public class DemoViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private string _name; public string Name { get => _name; set { if (_name == value) return; _name = value; OnPropertyChanged(nameof(Name)); } } private int _progress; public int Progress { get => _progress; set { if (_progress == value) return; _progress = value; OnPropertyChanged(nameof(Progress)); } } protected void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); }一个属性四行,十个属性四十行,基本都是重复代码。用 CallerMemberName 能省一点,还是避不开每个 setter 写通知逻辑。最怕的是属性多了以后复制粘贴漏改属性名,运行时界面不刷新,查半天发现是字符串写错了。
2.2 动态代理方案:让代理替你写样板
动态代理的核心理念:你不再直接持有 ViewModel 实例,而是持有一个代理对象。代理在运行时拦截所有属性赋值,自动完成“判断值是否变化 -> 存储新值 -> 触发 PropertyChanged”这套流程。
听起来玄,其实你早就在用类似的东西。ORM 延迟加载的实体、Mock 框架生成的替身对象,都是动态代理。C# 里有两条主流路线:Castle.Core 的 DynamicProxy 和 .NET 自带的 DispatchProxy。前者是成熟第三方库,Moq 的底层就是它;后者是官方实现,.NET Framework 4.6.1+ 通过 NuGet 包也能用。
使用前提只有一个:ViewModel 里要参与通知的属性必须声明为virtual。因为代理是继承你的 ViewModel 生成子类,然后覆写所有 virtual 属性的 getter/setter,在覆写方法里插入拦截逻辑。非 virtual 属性代理管不着,这个规则要提前和团队说清楚。
3. 动手实现:两套动态代理方案
3.
本文还有配套的精品资源,点击获取