为什么要用 MVVM
不用 MVVM 的 WPF,典型问题是:按钮点击事件里既读 PLC、又改界面、又写数据库,通讯协议一改,界面代码跟着崩。MVVM 的价值是界面(View)和业务逻辑(ViewModel)分离,逻辑可测试、界面可替换。
三者分工
- Model:设备、点位、数据模型,以及通讯/存储服务(与界面无关)
- View:XAML 界面,只负责展示和绑定,后台代码尽量不写业务
- ViewModel:连接两者,暴露界面要绑定的属性和命令,内部调用 Model
核心机制:数据绑定与通知
ViewModel 实现 INotifyPropertyChanged,属性变化时界面自动刷新;界面操作通过 ICommand 绑定到 ViewModel 方法,不在 XAML.cs 里写业务。
public class MainViewModel : INotifyPropertyChanged
{
private double _temperature;
public double Temperature
{
get => _temperature;
set { _temperature = value; OnPropertyChanged(); }
}
public ICommand StartCommand { get; }
// ...
}
XAML 里只做绑定:Text="{Binding Temperature}"、Command="{Binding StartCommand}"。
推荐项目结构
Models/ 数据模型
Services/ 通讯服务、存储服务(接口化,可替换/可Mock)
ViewModels/ 各页面 ViewModel
Views/ XAML 页面
Common/ 转换器、事件聚合、基类
与通讯层配合
通讯服务在后台线程持续更新点位缓存,ViewModel 只订阅缓存变化并刷新绑定属性。注意:非 UI 线程更新绑定属性时,WPF 绑定机制会自动调度到 UI 线程,但集合(ObservableCollection)更新仍需 Dispatcher。
状态灯的做法
用值转换器(Converter)把状态枚举转成颜色:运行绿、报警黄、故障红、离线灰,XAML 绑定状态即可,不在后台代码里逐个改控件颜色。
落地建议
- 先写一个 ViewModelBase 封装 PropertyChanged
- 通讯服务抽接口,开发时用 MockService 假数据,没 PLC 也能做界面
- 用依赖注入把服务注入 ViewModel
- 业务逻辑放 ViewModel/Service,做到 ViewModel 可单元测试
前期多搭一层架子,换来的是界面、通讯、业务各自独立演进,这正是上位机能维护十年的基础。