WPF入门:从空白项目到MVVM骨架搭建
这是「从零搭建工业控制系统」系列第1篇。从零开始,手把手搭建一个可维护的WPF项目骨架。
为什么你需要MVVM?
三年前我接手一个半导体设备控制软件项目,打开 MainWindow.xaml.cs,映入眼帘的是2000行代码:按钮点击事件、设备通信、UI更新、定时器逻辑,全堆在一个文件里。
改一个按钮的文字颜色,要在一堆业务逻辑里小心翼翼地找。加一个新功能,牵一发动全身。
MVVM(Model-View-ViewModel)解决的就是这个问题:
- View(界面):只管长什么样
- ViewModel(逻辑):只管数据和命令
- Model(数据):只管业务模型
三者通过数据绑定连接,各司其职,互不干扰。
CommunityToolkit.Mvvm:微软官方的MVVM工具包
传统MVVM写法,每个属性都要手写INotifyPropertyChanged,每个命令都要实现ICommand接口,代码又臭又长。
CommunityToolkit.Mvvm是微软官方维护的轻量级MVVM库,用源代码生成器技术自动生成样板代码。你只需要加一个[ObservableProperty]特性,编译器就帮你搞定一切。
对比一下:
| 对比项 | 传统手写 | CommunityToolkit.Mvvm |
|---|---|---|
| 属性通知 | 15行重复代码 | 1行特性搞定 |
| 命令绑定 | 实现ICommand接口 | 1行特性搞定 |
| 学习曲线 | 低但代码繁琐 | 最低 |
| 包体积 | 无依赖 | 很小,按需引用 |
第一步:创建项目 + 安装包
1.1 创建WPF项目
打开 Visual Studio 2022 → 新建项目 →WPF应用程序→ 命名MvvmTutorial→ .NET 8。
1.2 安装 CommunityToolkit.Mvvm
工具 → NuGet包管理器 → 程序包管理器控制台输入:
Install-PackageCommunityToolkit.Mvvm1.3 建立项目目录结构
MvvmTutorial/ ├── Models/ # 数据模型 ├── ViewModels/ # 视图模型 ├── Views/ # 界面 └── App.xaml # 应用入口第二步:写你的第一个ViewModel
在ViewModels/下新建MainViewModel.cs:
usingCommunityToolkit.Mvvm.ComponentModel;usingCommunityToolkit.Mvvm.Input;namespaceMvvmTutorial.ViewModels{// 关键1:partial类,源代码生成器才能工作// 关键2:继承 ObservableObject,自带属性变更通知publicpartialclassMainViewModel:ObservableObject{// 关键3:[ObservableProperty] 自动生成公共属性 + 通知[ObservableProperty]privateintcount=0;[ObservableProperty]privatestringstatus="就绪";// 关键4:[RelayCommand] 自动生成命令属性[RelayCommand]privatevoidIncrement(){Count++;Status=$"已点击{Count}次";}[RelayCommand]privatevoidReset(){Count=0;Status="已重置";}}}3个关键点解读:
①partial class— 必须加,源代码生成器在编译时生成另一半代码。
②[ObservableProperty]— 标记在私有字段上,编译器自动生成公共属性(count→Count),赋值时自动触发界面刷新。你不用写一行OnPropertyChanged。
③[RelayCommand]— 标记在方法上,编译器自动生成同名命令属性(Increment→IncrementCommand),可直接绑定到按钮。
第三步:写界面 + 绑定
在Views/下新建MainWindow.xaml:
<Windowx:Class="MvvmTutorial.Views.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MVVM入门"Height="300"Width="400"><StackPanelVerticalAlignment="Center"HorizontalAlignment="Center"><!-- 显示计数 --><TextBlockText="{Binding Count}"FontSize="48"HorizontalAlignment="Center"/><!-- 显示状态 --><TextBlockText="{Binding Status}"FontSize="16"Margin="0,10"HorizontalAlignment="Center"/><!-- 按钮绑定命令 --><StackPanelOrientation="Horizontal"HorizontalAlignment="Center"Margin="0,20"><ButtonContent="点击+1"Command="{Binding IncrementCommand}"Padding="20,10"Margin="5"/><ButtonContent="重置"Command="{Binding ResetCommand}"Padding="20,10"Margin="5"/></StackPanel></StackPanel></Window>绑定语法解读:
{Binding Count}— 把TextBlock的Text绑定到ViewModel的Count属性,Count变了,界面自动更新{Binding IncrementCommand}— 把Button的Command绑定到ViewModel的IncrementCommand命令,点击按钮自动调用Increment方法
第四步:连接View和ViewModel
这一步是MVVM最关键的一步— 把View和ViewModel绑在一起。
打开MainWindow.xaml.cs:
usingMvvmTutorial.ViewModels;usingSystem.Windows;namespaceMvvmTutorial.Views{publicpartialclassMainWindow:Window{publicMainWindow(){InitializeComponent();// 这一行就是MVVM的灵魂DataContext=newMainViewModel();}}}DataContext = new MainViewModel()— 设置数据上下文后,XAML里所有{Binding xxx}都会去ViewModel里找对应属性和命令。
第五步:运行!
按 F5 运行,你会看到:
- 点击「点击+1」按钮,数字递增,状态文字更新
- 点击「重置」按钮,数字归零,状态变为「已重置」
全程没有一行手动刷新UI的代码。这就是MVVM的魔力 — 数据变了,界面自动变。
进阶:带条件判断的命令
实际项目中,按钮不是永远能点的。比如「设备运行中时禁止再点启动」。
CommunityToolkit.Mvvm 原生支持:
publicpartialclassMainViewModel:ObservableObject{[ObservableProperty]privateboolisRunning=false;// CanExecute 方法名 = 命令方法名 + "CanExecute"[RelayCommand(CanExecute=nameof(CanStart))]privatevoidStart(){IsRunning=true;}privateboolCanStart()=>!IsRunning;// 当 IsRunning 变化时,通知命令刷新按钮状态partialvoidOnIsRunningChanged(boolvalue){StartCommand.NotifyCanExecuteChanged();}}XAML中按钮绑定不变,当IsRunning = true时,按钮自动变灰禁用。
真实项目长什么样?
以上是入门示例。来看看我在实际工业项目中怎么用:
// 真实项目:半导体设备控制软件 HomeViewModelpublicpartialclassHomeViewModel:ObservableObject,IDisposable{// === 属性:20+个 [ObservableProperty] ===[ObservableProperty]privateboolisAutoMode=true;[ObservableProperty]privateboolisManualMode=false;[ObservableProperty]privateintalarmCount=0;[ObservableProperty]privatedoublechamberPressure=0;// 腔体压力[ObservableProperty]privatedoubleheater1TempAct=0;// 加热器实际温度// === 命令:模式切换、设备控制 ===[RelayCommand]privatevoidAutoMode(){IsAutoMode=true;IsManualMode=false;}[RelayCommand]privatevoidManualMode(){IsAutoMode=false;IsManualMode=true;}[RelayCommand(CanExecute=nameof(CanRunWhenIdle))]privatevoidRunSequence(){// 执行工艺配方...}privateboolCanRunWhenIdle()=>!_commandService.IsBusy;}对应的XAML绑定:
<!-- 模式切换控件 --><operator:AutoManualSwitchIsLeftActive="{Binding IsAutoMode, Mode=OneWay}"LeftCommand="{Binding AutoModeCommand}"RightCommand="{Binding ManualModeCommand}"/><!-- 报警控件 --><operator:WarningAlarmCount="{Binding AlarmCount}"WarningCommand="{Binding WarningCommand}"/>一个 ViewModel 管理几十个属性和命令,但代码依然清晰可读。这就是 CommunityToolkit.Mvvm 的威力。
本篇小结
| 知识点 | 关键代码 |
|---|---|
| 属性自动通知 | [ObservableProperty] private int count; |
| 命令自动生成 | [RelayCommand] private void DoSomething() |
| 命令条件判断 | [RelayCommand(CanExecute = nameof(CanDo))] |
| 连接View和VM | DataContext = new MainViewModel(); |
| XAML绑定 | {Binding Count}/{Binding DoSomethingCommand} |
5个核心知识点,搭建一个完整的MVVM骨架。
下期预告
第2篇:MVVM实战 — RelayCommand绑定按钮到ViewModel方法
我们将深入命令系统:异步命令、带参数命令、命令的CanExecute动态刷新,以及在真实项目中如何用反射批量通知命令状态。