WinForm 三层架构 MVVM 工业自动化上位机 开发详解 主界面Form 多个UserControl 完整代码示例
本方案为构建一个基于**WinForm**、融合**三层架构**与**MVVM(Model-View-ViewModel)** 模式的工业自动化上位机(HMI/SCADA)提供一套完整、详细、可直接落地的代码实现。核心是**一个主窗体(MainForm)管理多个用户控件(UserControl)**,通过数据绑定和命令实现彻底解耦,确保代码的可维护性、可测试性和扩展性[ref_1][ref_3][ref_4]。以下将从项目结构、核心类实现、交互模式到具体代码示例进行全方位拆解。
### **1. 解决方案与项目结构规划**
首先,在Visual Studio中创建解决方案,并按以下结构组织项目,这清晰体现了“三层”与“MVVM”的融合:
```
HMI.Solution (解决方案)
│
├── HMI.Core (类库)
│ ├── Models/ # 数据模型,实现 INotifyPropertyChanged
│ ├── ViewModels/ # ViewModel 基类及具体ViewModel
│ ├── Business/ # 业务逻辑层 (BLL) 接口与实现
│ ├── DataAccess/ # 数据访问层 (DAL) 接口与实现
│ ├── Common/ # 通用助手类 (RelayCommand, EventAggregator等)
│ └── Services/ # 应用层服务 (如设备通信服务)
│
├── HMI.Infrastructure (类库)
│ ├── DeviceDrivers/ # PLC/设备通信具体实现 (如Modbus, OPC UA)
│ └── Repositories/ # 数据持久化具体实现 (如EF Core DbContext)
│
└── HMI.UI (WinForms 应用程序)
├── Views/ # WinForm窗体和UserControl
│ ├── MainForm.cs
│ ├── UC_StatusPanel.ascx
│ ├── UC_ControlPanel.ascx
│ └── UC_AlarmViewer.ascx
├── Presenters/ # (可选) 如果需要更经典的MVP,可作为View和ViewModel的桥梁
└── Program.cs
```
**职责说明**:
* **`HMI.Core`**:包含核心业务模型、视图逻辑和抽象接口。它是MVVM模式的核心承载层。
* **`HMI.Infrastructure`**:是**数据访问层(DAL)** 的具体实现,依赖于Core层定义的接口。
* **`HMI.UI`**:是**表示层**,纯粹负责UI展示。其`Views`目录下的窗体和控制器的后台代码应极简,仅负责初始化和数据绑定。
### **2. 核心基础架构代码实现**
#### **2.1. 可观察对象基类 (HMI.Core/Common/ObservableObject.cs)**
所有Model和ViewModel的基类,实现属性变更通知。
```csharp
// 文件名:ObservableObject.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace HMI.Core.Common
{
/// <summary>
/// 实现 INotifyPropertyChanged 接口的基类,用于支持数据绑定。
/// </summary>
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// 设置属性值并在值更改时触发 PropertyChanged 事件。
/// </summary>
/// <typeparam name="T">属性类型</typeparam>
/// <param name="field">后台字段的引用</param>
/// <param name="value">新值</param>
/// <param name="propertyName">属性名(自动获取)</param>
/// <returns>如果值被更改,返回 true;否则返回 false。</returns>
protected virtual 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;
}
/// <summary>
/// 触发 PropertyChanged 事件。
/// </summary>
/// <param name="propertyName">发生更改的属性名称</param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
```
#### **2.2. 命令实现 (HMI.Core/Common/RelayCommand.cs)**
用于将UI事件(如按钮点击)绑定到ViewModel中的方法。
```csharp
// 文件名:RelayCommand.cs
using System;
using System.Windows.Input;
namespace HMI.Core.Common
{
/// <summary>
/// 一个通用的、无参数的ICommand实现。
/// </summary>
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;
public void Execute(object parameter) => _execute();
}
/// <summary>
/// 一个通用的、带参数的ICommand实现。
/// </summary>
/// <typeparam name="T">命令参数类型</typeparam>
public class RelayCommand<T> : ICommand
{
private readonly Action<T> _execute;
private readonly Predicate<T> _canExecute;
public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (parameter == null && typeof(T).IsValueType)
return _canExecute?.Invoke(default(T)) ?? true;
return _canExecute?.Invoke((T)parameter) ?? true;
}
public void Execute(object parameter)
{
if (parameter == null && typeof(T).IsValueType)
_execute(default(T));
else
_execute((T)parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
}
```
#### **2.3. 业务模型定义 (HMI.Core/Models/)**
定义应用程序的核心数据对象。
```csharp
// 文件名:DeviceStatus.cs
namespace HMI.Core.Models
{
/// <summary>
/// 设备状态模型,代表从PLC或传感器读取的一个设备单元的状态。
/// </summary>
public class DeviceStatus : ObservableObject
{
private string _deviceId;
private string _deviceName;
private double _currentValue;
private bool _isOnline;
private DateTime _lastUpdated;
public string DeviceId
{
get => _deviceId;
set => SetProperty(ref _deviceId, value);
}
public string DeviceName
{
get => _deviceName;
set => SetProperty(ref _deviceName, value);
}
public double CurrentValue
{
get => _currentValue;
set => SetProperty(ref _currentValue, value);
}
public bool IsOnline
{
get => _isOnline;
set => SetProperty(ref _isOnline, value);
}
public DateTime LastUpdated
{
get => _lastUpdated;
set => SetProperty(ref _lastUpdated, value);
}
}
}
```
```csharp
// 文件名:AlarmInfo.cs
namespace HMI.Core.Models
{
/// <summary>
/// 报警信息模型。
/// </summary>
public class AlarmInfo : ObservableObject
{
private int _id;
private string _message;
private AlarmLevel _level;
private DateTime _triggerTime;
private DateTime? _ackTime;
public int Id { get => _id; set => SetProperty(ref _id, value); }
public string Message { get => _message; set => SetProperty(ref _message, value); }
public AlarmLevel Level { get => _level; set => SetProperty(ref _level, value); }
public DateTime TriggerTime { get => _triggerTime; set => SetProperty(ref _triggerTime, value); }
public DateTime? AckTime { get => _ackTime; set => SetProperty(ref _ackTime, value); }
public bool IsAcknowledged => AckTime.HasValue;
}
public enum AlarmLevel { Info, Warning, Error, Critical }
}
```
### **3. 数据访问与业务逻辑层接口定义 (BLL/DAL)**
#### **3.1. 数据访问层接口 (HMI.Core/DataAccess/)**
定义与底层数据源(数据库、PLC)交互的契约。
```csharp
// 文件名:IDeviceDataRepository.cs
using HMI.Core.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace HMI.Core.DataAccess
{
/// <summary>
/// 设备数据访问接口。
/// </summary>
public interface IDeviceDataRepository
{
/// <summary>
/// 异步获取所有设备的最新状态。
/// </summary>
Task<IEnumerable<DeviceStatus>> GetAllDeviceStatusAsync();
/// <summary>
/// 异步向指定设备写入一个控制值。
/// </summary>
Task<bool> WriteControlValueAsync(string deviceId, double value);
}
}
```
#### **3.2. 业务逻辑层接口与实现 (HMI.Core/Business/)**
封装核心业务规则,它依赖于DAL接口,并为ViewModel提供服务。
```csharp
// 文件名:IDeviceMonitoringService.cs
using HMI.Core.Models;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
namespace HMI.Core.Business
{
/// <summary>
/// 设备监控业务服务接口。
/// </summary>
public interface IDeviceMonitoringService
{
/// <summary>
/// 当前活动的设备状态列表(可绑定)。
/// </summary>
ObservableCollection<DeviceStatus> ActiveDevices { get; }
/// <summary>
/// 异步启动数据监控。
/// </summary>
Task StartMonitoringAsync();
/// <summary>
/// 停止数据监控。
/// </summary>
void StopMonitoring();
/// <summary>
/// 手动请求刷新所有设备数据。
/// </summary>
Task RefreshAllAsync();
}
}
```
```csharp
// 文件名:DeviceMonitoringService.cs
using HMI.Core.DataAccess;
using HMI.Core.Models;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
namespace HMI.Core.Business
{
/// <summary>
/// 设备监控业务服务的具体实现。
/// </summary>
public class DeviceMonitoringService : IDeviceMonitoringService
{
private readonly IDeviceDataRepository _deviceRepo;
private readonly Timer _pollingTimer;
private bool _isMonitoring;
public ObservableCollection<DeviceStatus> ActiveDevices { get; private set; }
public DeviceMonitoringService(IDeviceDataRepository deviceRepo)
{
_deviceRepo = deviceRepo;
ActiveDevices = new ObservableCollection<DeviceStatus>();
_pollingTimer = new Timer(2000) { AutoReset = true }; // 2秒轮询一次
_pollingTimer.Elapsed += async (s, e) => await PollDevicesAsync();
}
public async Task StartMonitoringAsync()
{
if (_isMonitoring) return;
await RefreshAllAsync(); // 启动时先获取一次
_pollingTimer.Start();
_isMonitoring = true;
}
public void StopMonitoring()
{
_pollingTimer.Stop();
_isMonitoring = false;
}
public async Task RefreshAllAsync()
{
var devices = await _deviceRepo.GetAllDeviceStatusAsync();
// 注意:需要在UI线程上更新ObservableCollection以确保绑定正确更新。
// 此处简化处理,实际应用中应通过同步上下文(SynchronizationContext)返回到UI线程。
System.Windows.Forms.Application.Current?.Invoke((Action)(() =>
{
ActiveDevices.Clear();
foreach (var device in devices)
{
ActiveDevices.Add(device);
}
}));
}
private async Task PollDevicesAsync()
{
// 模拟轮询逻辑,实际可能只读取变化的数据
await RefreshAllAsync();
}
}
}
```
### **4. ViewModel层实现**
ViewModel是连接View和Model/BLL的桥梁。
#### **4.1. 主窗体ViewModel (HMI.Core/ViewModels/MainViewModel.cs)**
协调整个应用程序,持有各个子模块的ViewModel实例。
```csharp
// 文件名:MainViewModel.cs
using HMI.Core.Business;
using HMI.Core.Common;
using System.Windows.Input;
namespace HMI.Core.ViewModels
{
/// <summary>
/// 主窗体的ViewModel,作为应用程序的根ViewModel。
/// </summary>
public class MainViewModel : ObservableObject
{
// 业务服务(通过构造函数注入)
private readonly IDeviceMonitoringService _monitoringService;
// 子模块的ViewModels
public DeviceStatusViewModel StatusVM { get; }
public AlarmOverviewViewModel AlarmVM { get; }
// 命令
public ICommand StartMonitoringCommand { get; }
public ICommand StopMonitoringCommand { get; }
public ICommand ExitApplicationCommand { get; }
private string _systemStatus = "就绪";
public string SystemStatus
{
get => _systemStatus;
set => SetProperty(ref _systemStatus, value);
}
public MainViewModel(IDeviceMonitoringService monitoringService)
{
_monitoringService = monitoringService;
// 初始化子ViewModel,并传入它们所需的服务
StatusVM = new DeviceStatusViewModel(_monitoringService);
AlarmVM = new AlarmOverviewViewModel();
// 初始化命令
StartMonitoringCommand = new RelayCommand(async () =>
{
SystemStatus = "监控中...";
await _monitoringService.StartMonitoringAsync();
}, () => !_monitoringService.ActiveDevices.Any()); // 简单示例:未启动时可执行
StopMonitoringCommand = new RelayCommand(() =>
{
_monitoringService.StopMonitoring();
SystemStatus = "已停止";
});
ExitApplicationCommand = new RelayCommand(() =>
{
// 清理资源
_monitoringService.StopMonitoring();
System.Windows.Forms.Application.Exit();
});
}
}
}
```
#### **4.2. 设备状态面板ViewModel (HMI.Core/ViewModels/DeviceStatusViewModel.cs)**
专门用于设备状态显示的用户控件。
```csharp
// 文件名:DeviceStatusViewModel.cs
using HMI.Core.Business;
using HMI.Core.Common;
using HMI.Core.Models;
using System.Collections.ObjectModel;
using System.Windows.Input;
namespace HMI.Core.ViewModels
{
public class DeviceStatusViewModel : ObservableObject
{
private readonly IDeviceMonitoringService _monitoringService;
private DeviceStatus _selectedDevice;
// 直接暴露业务层的可观察集合以供绑定
public ObservableCollection<DeviceStatus> Devices => _monitoringService.ActiveDevices;
public DeviceStatus SelectedDevice
{
get => _selectedDevice;
set => SetProperty(ref _selectedDevice, value);
}
// 命令示例:手动刷新、控制设备
public ICommand RefreshCommand { get; }
public ICommand ToggleDeviceCommand { get; }
public DeviceStatusViewModel(IDeviceMonitoringService monitoringService)
{
_monitoringService = monitoringService;
RefreshCommand = new RelayCommand(async () => await _monitoringService.RefreshAllAsync());
ToggleDeviceCommand = new RelayCommand<DeviceStatus>(async (device) =>
{
if (device != null)
{
// 调用业务逻辑,例如通过服务写入控制值
// await _someControlService.ToggleDeviceAsync(device.DeviceId);
device.IsOnline = !device.IsOnline; // 仅为示例,直接修改模型
}
});
}
}
}
```
### **5. View层实现:WinForm与UserControl**
#### **5.1. 程序入口与依赖注入配置 (HMI.UI/Program.cs)**
使用简单的依赖注入容器(如Microsoft.Extensions.DependencyInjection)来组装应用程序。
```csharp
// 文件名:Program.cs
using HMI.Core.Business;
using HMI.Core.DataAccess;
using HMI.Core.ViewModels;
using HMI.Infrastructure.DeviceDrivers;
using HMI.UI.Views;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Windows.Forms;
namespace HMI.UI
{
internal static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 配置依赖注入容器
var services = new ServiceCollection();
ConfigureServices(services);
// 构建服务提供者
using (var serviceProvider = services.BuildServiceProvider())
{
// 解析主窗体,其依赖会自动注入
var mainForm = serviceProvider.GetRequiredService<MainForm>();
Application.Run(mainForm);
}
}
static void ConfigureServices(IServiceCollection services)
{
// 注册数据访问层实现(具体驱动)
services.AddSingleton<IDeviceDataRepository, SimulatedDeviceRepository>(); // 模拟仓库
// 注册业务逻辑层服务
services.AddSingleton<IDeviceMonitoringService, DeviceMonitoringService>();
// 注册ViewModels (Transient 或 Scoped,取决于需求)
services.AddTransient<MainViewModel>();
services.AddTransient<DeviceStatusViewModel>();
services.AddTransient<AlarmOverviewViewModel>();
// 注册主窗体
services.AddSingleton<MainForm>();
}
}
}
```
#### **5.2. 主窗体 (HMI.UI/Views/MainForm.cs)**
主窗体的后台代码应极其精简,主要职责是承载控件和设置绑定上下文。
```csharp
// 文件名:MainForm.cs
using HMI.Core.ViewModels;
using System.Windows.Forms;
namespace HMI.UI.Views
{
public partial class MainForm : Form
{
// 通过依赖注入获得主ViewModel
private readonly MainViewModel _mainViewModel;
// 子用户控件
private UC_DeviceStatus _ucStatus;
private UC_AlarmOverview _ucAlarm;
public MainForm(MainViewModel mainViewModel)
{
_mainViewModel = mainViewModel;
InitializeComponent();
InitializeLayout();
SetupDataBinding();
}
private void InitializeLayout()
{
this.Text = "工业自动化上位机 HMI";
this.WindowState = FormWindowState.Maximized;
// 使用 SplitContainer 或 Panel 来布局多个UserControl[ref_2]
var mainSplit = new SplitContainer
{
Dock = DockStyle.Fill,
Orientation = Orientation.Vertical
};
this.Controls.Add(mainSplit);
// 左侧:设备状态面板
_ucStatus = new UC_DeviceStatus();
_ucStatus.Dock = DockStyle.Fill;
mainSplit.Panel1.Controls.Add(_ucStatus);
// 右侧:报警总览面板
_ucAlarm = new UC_AlarmOverview();
_ucAlarm.Dock = DockStyle.Fill;
mainSplit.Panel2.Controls.Add(_ucAlarm);
// 可以添加工具栏、状态栏等
InitializeToolbar();
}
private void InitializeToolbar()
{
var toolStrip = new ToolStrip();
var btnStart = new ToolStripButton("启动监控") { DisplayStyle = ToolStripItemDisplayStyle.Text };
var btnStop = new ToolStripButton("停止监控") { DisplayStyle = ToolStripItemDisplayStyle.Text };
var btnExit = new ToolStripButton("退出") { DisplayStyle = ToolStripItemDisplayStyle.Text };
// **关键:将WinForm控件事件绑定到ViewModel的ICommand**
btnStart.Click += (s, e) => _mainViewModel.StartMonitoringCommand.Execute(null);
btnStop.Click += (s, e) => _mainViewModel.StopMonitoringCommand.Execute(null);
btnExit.Click += (s, e) => _mainViewModel.ExitApplicationCommand.Execute(null);
// 命令的可执行状态可以绑定到按钮的Enabled属性(这里简化处理)
toolStrip.Items.AddRange(new ToolStripItem[] { btnStart, btnStop, new ToolStripSeparator(), btnExit });
this.Controls.Add(toolStrip);
toolStrip.Dock = DockStyle.Top;
}
private void SetupDataBinding()
{
// **核心:将子用户控件的ViewModel设置为MainViewModel中对应的子ViewModel**
_ucStatus.SetViewModel(_mainViewModel.StatusVM);
_ucAlarm.SetViewModel(_mainViewModel.AlarmVM);
// 绑定主窗体的系统状态到Label(原生数据绑定示例)
lblSystemStatus.DataBindings.Add("Text", _mainViewModel, nameof(MainViewModel.SystemStatus));
}
}
}
```
#### **5.3. 用户控件示例:设备状态面板 (HMI.UI/Views/UC_DeviceStatus.ascx.cs)**
用户控件的后台代码同样精简,专注于UI控件与ViewModel的绑定。
```csharp
// 文件名:UC_DeviceStatus.ascx.cs
using HMI.Core.ViewModels;
using System.Windows.Forms;
namespace HMI.UI.Views
{
public partial class UC_DeviceStatus : UserControl
{
private DeviceStatusViewModel _viewModel;
private BindingSource _bindingSource;
public UC_DeviceStatus()
{
InitializeComponent();
_bindingSource = new BindingSource();
dataGridView1.AutoGenerateColumns = false;
dataGridView1.DataSource = _bindingSource;
// 配置列...
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn { DataPropertyName = "DeviceName", HeaderText = "设备名" });
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn { DataPropertyName = "CurrentValue", HeaderText = "当前值" });
dataGridView1.Columns.Add(new DataGridViewCheckBoxColumn { DataPropertyName = "IsOnline", HeaderText = "在线状态" });
}
// 关键方法:由主窗体调用,注入ViewModel
public void SetViewModel(DeviceStatusViewModel viewModel)
{
_viewModel = viewModel;
BindData();
WireCommands();
}
private void BindData()
{
if (_viewModel == null) return;
// 将BindingSource的数据源设置为ViewModel的Devices集合
_bindingSource.DataSource = _viewModel.Devices;
// 将DataGridView的选择项绑定到ViewModel的SelectedDevice
dataGridView1.SelectionChanged += (s, e) =>
{
if (dataGridView1.CurrentRow?.DataBoundItem is HMI.Core.Models.DeviceStatus device)
{
_viewModel.SelectedDevice = device;
}
};
// 绑定其他控件(如选中设备的详情Label)
lblSelectedDevice.DataBindings.Clear();
lblSelectedDevice.DataBindings.Add("Text", _viewModel, "SelectedDevice.DeviceName", true, DataSourceUpdateMode.OnPropertyChanged, "N/A");
}
private void WireCommands()
{
// 将按钮点击事件绑定到ViewModel的命令
btnRefresh.Click += (s, e) => _viewModel.RefreshCommand.Execute(null);
// 注意:Toggle命令需要参数,这里绑定到选中项
btnToggle.Click += (s, e) => _viewModel.ToggleDeviceCommand.Execute(_viewModel.SelectedDevice);
}
}
}
```
### **6. 数据访问层模拟实现 (HMI.Infrastructure/DeviceDrivers/SimulatedDeviceRepository.cs)**
```csharp
// 文件名:SimulatedDeviceRepository.cs
using HMI.Core.DataAccess;
using HMI.Core.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace HMI.Infrastructure.DeviceDrivers
{
/// <summary>
/// 模拟设备数据仓库,用于开发和测试。
/// </summary>
public class SimulatedDeviceRepository : IDeviceDataRepository
{
private readonly Random _random = new Random();
private readonly List<DeviceStatus> _simulatedDevices;
public SimulatedDeviceRepository()
{
_simulatedDevices = new List<DeviceStatus>
{
new DeviceStatus { DeviceId = "M001", DeviceName = "电机1", CurrentValue = 25.5, IsOnline = true },
new DeviceStatus { DeviceId = "V002", DeviceName = "阀门2", CurrentValue = 60.0, IsOnline = false },
new DeviceStatus { DeviceId = "T003", DeviceName = "温度传感器3", CurrentValue = 37.2, IsOnline = true },
};
}
public async Task<IEnumerable<DeviceStatus>> GetAllDeviceStatusAsync()
{
// 模拟异步延迟
await Task.Delay(50);
// 模拟数据变化
foreach (var device in _simulatedDevices)
{
if (device.IsOnline)
{
device.CurrentValue += (_random.NextDouble() - 0.5) * 2; // 微小波动
device.LastUpdated = DateTime.Now;
}
}
// 返回深拷贝,避免直接修改内部集合
return _simulatedDevices.ConvertAll(d => new DeviceStatus
{
DeviceId = d.DeviceId,
DeviceName = d.DeviceName,
CurrentValue = d.CurrentValue,
IsOnline = d.IsOnline,
LastUpdated = d.LastUpdated
});
}
public async Task<bool> WriteControlValueAsync(string deviceId, double value)
{
await Task.Delay(30);
var device = _simulatedDevices.Find(d => d.DeviceId == deviceId);
if (device != null)
{
device.CurrentValue = value;
return true;
}
return false;
}
}
}
```
### **7. 总结与进阶建议**
通过以上详细代码,构建了一个结构清晰的WinForm MVVM应用程序框架[ref_1]。主`MainForm`通过依赖注入获取`MainViewModel`,并将不同的子`ViewModel`分配给对应的`UserControl`,实现了彻底的关注点分离。
| 层/组件 | 技术要点 | 关键代码文件 |
| :--- | :--- | :--- |
| **Model** | 可观察对象,纯数据实体。 | `DeviceStatus.cs`, `AlarmInfo.cs` |
| **ViewModel** | 业务逻辑的UI抽象,持有Model和命令。 | `MainViewModel.cs`, `DeviceStatusViewModel.cs` |
| **View** | 纯UI展示,后台代码极简,仅负责绑定。 | `MainForm.cs`, `UC_DeviceStatus.ascx.cs` |
| **BLL** | 封装核心业务规则,操作Model。 | `DeviceMonitoringService.cs` |
| **DAL** | 负责与外部系统通信,实现接口。 | `SimulatedDeviceRepository.cs` |
**进阶建议**:
1. **使用更强大的绑定**:对于复杂UI(如树形图、图表),考虑使用`DevExpress`、`Telerik`等第三方控件库,它们提供了更完善的WinForm MVVM支持和数据绑定引擎[ref_4]。
2. **实现事件聚合器**:对于跨多个`UserControl`的复杂通信(如一个控件中的操作需要更新另一个控件),实现一个轻量级的事件聚合器(`EventAggregator`),让`ViewModel`之间进行松耦合的发布/订阅通信[ref_4]。
3. **异步与线程安全**:工业通信常涉及异步操作。确保在`ViewModel`中正确使用`async/await`,并通过`SynchronizationContext`或控件的`Invoke/BeginInvoke`方法确保对`ObservableCollection`的更新在UI线程上执行。
4. **结合三层架构**:本方案中的`IDeviceDataRepository`和`DeviceMonitoringService`已经体现了两层(DAL和BLL)。可以进一步将数据库操作、文件IO等更底层的持久化逻辑抽象到独立的`Infrastructure`项目中,使`Core`层完全不依赖于具体技术实现。
此架构为工业自动化上位机开发提供了坚实的、可扩展的基础,能够有效管理复杂的界面交互和业务逻辑,并极大提升代码的可测试性[ref_3][ref_4]。