在工业4.0时代,设备监控系统的开发需求日益增长。你是否遇到过这样的困扰:界面逻辑与业务逻辑混杂、数据更新卡顿、多线程操作导致界面假死?今天,我将通过一个完整的工业设备监控系统,为你深度解析C# MVVM模式的生产级应用,解决实际开发中的核心痛点。
本文将带你构建一个具备实时数据采集、多线程安全更新、命令模式交互的完整监控系统,让你的WinForms应用达到企业级标准。
C#[AddINotifyPropertyChangedInterface]
public class MainViewModel : INotifyPropertyChanged
{
// 神奇之处:无需手动实现PropertyChanged通知
public string DeviceStatus { get; set; } = "离线";
public double Temperature { get; set; } = 25.0;
public bool IsRunning { get; set; } = false;
// Fody在编译时自动注入通知代码
public event PropertyChangedEventHandler PropertyChanged;
}
技术亮点:
C#// 线程安全的属性更新核心方法
private void UpdatePropertiesSafe(Action updateAction)
{
if (_syncContext != null)
{
// 将更新操作调度到UI线程执行
_syncContext.Post(_ => updateAction(), null);
}
else
{
updateAction();
}
}
// 实际应用示例
private async Task SimulateDataCollection(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested && IsDataCollecting)
{
// 在后台线程中进行数据处理
var newData = await GetSensorDataAsync();
// 线程安全地更新UI绑定的属性
UpdatePropertiesSafe(() =>
{
Temperature = newData.Temperature;
Pressure = newData.Pressure;
LastUpdate = DateTime.Now;
});
await Task.Delay(CollectionInterval, cancellationToken);
}
}
关键技术点:
C#public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
// 关键扩展:手动触发状态变化通知
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
智能状态管理:
C#private void UpdateButtonStates()
{
// 根据设备状态智能更新按钮可用性
CanConnect = !IsConnected;
CanDisconnect = IsConnected;
CanStart = IsConnected && !IsRunning;
CanStop = IsConnected && IsRunning;
// 批量通知所有命令更新状态
_connectCmd?.RaiseCanExecuteChanged();
_disconnectCmd?.RaiseCanExecuteChanged();
_startCmd?.RaiseCanExecuteChanged();
_stopCmd?.RaiseCanExecuteChanged();
}
C#private void SetupDataBindings()
{
// 温度数据绑定 - 自动格式化为一位小数
lblTemperature.DataBindings.Add("Text", _viewModel,
nameof(_viewModel.Temperature),
true, DataSourceUpdateMode.OnPropertyChanged, "0", "N1");
// 时间戳绑定 - 精确到毫秒显示
lblLastUpdate.DataBindings.Add("Text", _viewModel,
nameof(_viewModel.LastUpdate),
true, DataSourceUpdateMode.OnPropertyChanged, null, "HH:mm:ss.fff");
// 按钮状态绑定 - 实时响应ViewModel变化
btnConnect.DataBindings.Add("Enabled", _viewModel,
nameof(_viewModel.CanConnect),
false, DataSourceUpdateMode.OnPropertyChanged);
}
性能优化技巧:
C#using PropertyChanged;
using System;
using System.ComponentModel;
using System.Windows.Input;
using System.Threading.Tasks;
using System.Threading;
namespace AppFody
{
[AddINotifyPropertyChangedInterface]
public class MainViewModel : INotifyPropertyChanged
{
// 同步上下文,用于线程安全的属性更新
private readonly SynchronizationContext _syncContext;
private CancellationTokenSource _cancellationTokenSource;
// 设备状态属性
public string DeviceStatus { get; set; } = "离线";
public double Temperature { get; set; } = 25.0;
public double Pressure { get; set; } = 0.0;
public int MotorSpeed { get; set; } = 0;
public bool IsRunning { get; set; } = false;
public bool IsConnected { get; set; } = false;
public string ErrorMessage { get; set; } = "";
public DateTime LastUpdate { get; set; } = DateTime.Now;
public double Voltage { get; set; } = 0.0;
public double Current { get; set; } = 0.0;
public double Power { get; set; } = 0.0;
public double Vibration { get; set; } = 0.0;
public int ProductCount { get; set; } = 0;
public double Efficiency { get; set; } = 0.0;
public string AlarmStatus { get; set; } = "正常";
// 按钮启用状态属性
public bool CanConnect { get; set; } = true;
public bool CanDisconnect { get; set; } = false;
public bool CanStart { get; set; } = false;
public bool CanStop { get; set; } = false;
public bool CanReset { get; set; } = false;
public bool CanStartDataCollection { get; set; } = false;
public bool CanStopDataCollection { get; set; } = false;
// 数据采集状态
public bool IsDataCollecting { get; set; } = false;
public string DataCollectionStatus { get; set; } = "未启动";
public int CollectionInterval { get; set; } = 1000;
// 私有命令字段
private RelayCommand _connectCmd;
private RelayCommand _disconnectCmd;
private RelayCommand _startCmd;
private RelayCommand _stopCmd;
private RelayCommand _resetCmd;
private RelayCommand _startDataCollectionCmd;
private RelayCommand _stopDataCollectionCmd;
// 命令属性
public ICommand ConnectCommand => _connectCmd;
public ICommand DisconnectCommand => _disconnectCmd;
public ICommand StartCommand => _startCmd;
public ICommand StopCommand => _stopCmd;
public ICommand ResetCommand => _resetCmd;
public ICommand StartDataCollectionCommand => _startDataCollectionCmd;
public ICommand StopDataCollectionCommand => _stopDataCollectionCmd;
// PropertyChanged 事件
public event PropertyChangedEventHandler PropertyChanged;
public MainViewModel()
{
// 捕获当前同步上下文(通常是UI线程的同步上下文)
_syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
_cancellationTokenSource = new CancellationTokenSource();
InitializeCommands();
UpdateButtonStates();
}
private void InitializeCommands()
{
_connectCmd = new RelayCommand(
execute: _ => ConnectToDevice(),
canExecute: _ => CanConnect
);
_disconnectCmd = new RelayCommand(
execute: _ => DisconnectFromDevice(),
canExecute: _ => CanDisconnect
);
_startCmd = new RelayCommand(
execute: _ => StartDevice(),
canExecute: _ => CanStart
);
_stopCmd = new RelayCommand(
execute: _ => StopDevice(),
canExecute: _ => CanStop
);
_resetCmd = new RelayCommand(
execute: _ => ResetDevice(),
canExecute: _ => CanReset
);
_startDataCollectionCmd = new RelayCommand(
execute: _ => StartDataCollection(),
canExecute: _ => CanStartDataCollection
);
_stopDataCollectionCmd = new RelayCommand(
execute: _ => StopDataCollection(),
canExecute: _ => CanStopDataCollection
);
}
// 线程安全的属性更新方法
private void UpdatePropertySafe<T>(Action<T> updateAction, T value)
{
if (_syncContext != null)
{
_syncContext.Post(_ => updateAction(value), null);
}
else
{
updateAction(value);
}
}
// 线程安全的多属性更新方法
private void UpdatePropertiesSafe(Action updateAction)
{
if (_syncContext != null)
{
_syncContext.Post(_ => updateAction(), null);
}
else
{
updateAction();
}
}
private async void ConnectToDevice()
{
try
{
DeviceStatus = "连接中...";
ErrorMessage = "";
CanConnect = false;
UpdateButtonStates();
await Task.Delay(2000);
UpdatePropertiesSafe(() =>
{
IsConnected = true;
DeviceStatus = "已连接";
Temperature = 25.5;
Pressure = 1.2;
Voltage = 220.0;
Current = 0.0;
Power = 0.0;
Vibration = 0.1;
AlarmStatus = "正常";
LastUpdate = DateTime.Now;
UpdateButtonStates();
});
// 开始基础数据更新
_ = Task.Run(() => SimulateBasicDataUpdate(_cancellationTokenSource.Token));
}
catch (Exception ex)
{
UpdatePropertiesSafe(() =>
{
ErrorMessage = $"连接失败: {ex.Message}";
DeviceStatus = "连接失败";
IsConnected = false;
UpdateButtonStates();
});
}
}
private void DisconnectFromDevice()
{
_cancellationTokenSource?.Cancel();
_cancellationTokenSource = new CancellationTokenSource();
IsConnected = false;
IsRunning = false;
IsDataCollecting = false;
DeviceStatus = "离线";
DataCollectionStatus = "未启动";
ResetAllParameters();
UpdateButtonStates();
}
private async void StartDevice()
{
if (!IsConnected) return;
DeviceStatus = "启动中...";
UpdateButtonStates();
await Task.Delay(1000);
UpdatePropertiesSafe(() =>
{
IsRunning = true;
DeviceStatus = "运行中";
MotorSpeed = 1500;
Current = 15.5;
Power = Current * Voltage / 1000; // kW
Efficiency = 85.0;
UpdateButtonStates();
});
}
private async void StopDevice()
{
if (!IsConnected) return;
DeviceStatus = "停止中...";
UpdateButtonStates();
await Task.Delay(1000);
UpdatePropertiesSafe(() =>
{
IsRunning = false;
DeviceStatus = "已停止";
MotorSpeed = 0;
Current = 0.0;
Power = 0.0;
Efficiency = 0.0;
UpdateButtonStates();
});
}
private void ResetDevice()
{
if (!IsConnected) return;
IsRunning = false;
IsDataCollecting = false;
DataCollectionStatus = "未启动";
ResetAllParameters();
DeviceStatus = "已重置";
ErrorMessage = "";
UpdateButtonStates();
}
private void StartDataCollection()
{
if (!IsConnected) return;
IsDataCollecting = true;
DataCollectionStatus = "采集中";
UpdateButtonStates();
// 开始高频数据采集
_ = Task.Run(() => SimulateDataCollection(_cancellationTokenSource.Token));
}
private void StopDataCollection()
{
IsDataCollecting = false;
DataCollectionStatus = "已停止";
UpdateButtonStates();
}
private void ResetAllParameters()
{
Temperature = 0;
Pressure = 0;
MotorSpeed = 0;
Voltage = 0;
Current = 0;
Power = 0;
Vibration = 0;
ProductCount = 0;
Efficiency = 0;
AlarmStatus = "正常";
}
private void UpdateButtonStates()
{
CanConnect = !IsConnected;
CanDisconnect = IsConnected;
CanStart = IsConnected && !IsRunning;
CanStop = IsConnected && IsRunning;
CanReset = IsConnected;
CanStartDataCollection = IsConnected && !IsDataCollecting;
CanStopDataCollection = IsConnected && IsDataCollecting;
// 触发命令的 CanExecuteChanged 事件
_connectCmd?.RaiseCanExecuteChanged();
_disconnectCmd?.RaiseCanExecuteChanged();
_startCmd?.RaiseCanExecuteChanged();
_stopCmd?.RaiseCanExecuteChanged();
_resetCmd?.RaiseCanExecuteChanged();
_startDataCollectionCmd?.RaiseCanExecuteChanged();
_stopDataCollectionCmd?.RaiseCanExecuteChanged();
}
private async Task SimulateBasicDataUpdate(CancellationToken cancellationToken)
{
var random = new Random();
while (!cancellationToken.IsCancellationRequested && IsConnected)
{
try
{
if (!IsDataCollecting)
{
UpdatePropertiesSafe(() =>
{
if (IsRunning)
{
Temperature = 25 + random.NextDouble() * 50;
Pressure = 1.0 + random.NextDouble() * 2.0;
MotorSpeed = 1450 + random.Next(0, 100);
Voltage = 220 + random.NextDouble() * 10 - 5;
Current = 15 + random.NextDouble() * 5;
Power = Current * Voltage / 1000;
Vibration = 0.5 + random.NextDouble() * 2.0;
Efficiency = 80 + random.NextDouble() * 15;
}
LastUpdate = DateTime.Now;
});
}
await Task.Delay(2000, cancellationToken); // 基础更新间隔2秒
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex)
{
UpdatePropertySafe<string>(msg => ErrorMessage = msg, $"基础数据更新错误: {ex.Message}");
break;
}
}
}
private async Task SimulateDataCollection(CancellationToken cancellationToken)
{
var random = new Random();
while (!cancellationToken.IsCancellationRequested && IsConnected && IsDataCollecting)
{
try
{
UpdatePropertiesSafe(() =>
{
if (IsRunning)
{
// 高频数据采集 - 使用时间函数模拟真实波动
var timeMs = DateTime.Now.Millisecond;
var timeSec = DateTime.Now.Second;
Temperature = 30 + Math.Sin(timeMs / 1000.0 * Math.PI) * 20 + random.NextDouble() * 5;
Pressure = 1.5 + Math.Cos(timeMs / 800.0 * Math.PI) * 0.8 + random.NextDouble() * 0.2;
MotorSpeed = 1500 + (int)(Math.Sin(timeMs / 600.0 * Math.PI) * 50) + random.Next(-20, 20);
Voltage = 220 + Math.Sin(timeMs / 1200.0 * Math.PI) * 8 + random.NextDouble() * 3;
Current = 15 + Math.Cos(timeMs / 900.0 * Math.PI) * 3 + random.NextDouble() * 1;
Power = Current * Voltage / 1000;
Vibration = 0.8 + Math.Abs(Math.Sin(timeMs / 300.0 * Math.PI)) * 1.5 + random.NextDouble() * 0.3;
Efficiency = 85 + Math.Sin(timeSec / 10.0 * Math.PI) * 10 + random.NextDouble() * 3;
// 产品计数
if (timeMs % 100 < 50 && random.NextDouble() > 0.7)
{
ProductCount++;
}
// 报警模拟
if (Temperature > 70)
AlarmStatus = "温度报警";
else if (Vibration > 2.0)
AlarmStatus = "振动报警";
else if (Pressure > 2.5)
AlarmStatus = "压力报警";
else
AlarmStatus = "正常";
}
else
{
// 设备停止时的数据
Temperature = Math.Max(25, Temperature - 0.5);
Pressure = Math.Max(0, Pressure - 0.1);
MotorSpeed = 0;
Current = 0;
Power = 0;
Vibration = Math.Max(0.1, Vibration - 0.05);
Efficiency = 0;
AlarmStatus = "正常";
}
LastUpdate = DateTime.Now;
});
await Task.Delay(CollectionInterval, cancellationToken);
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex)
{
UpdatePropertySafe<string>(msg => ErrorMessage = msg, $"数据采集错误: {ex.Message}");
break;
}
}
UpdatePropertySafe<string>(status => DataCollectionStatus = status, "已停止");
}
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// 释放资源
public void Dispose()
{
_cancellationTokenSource?.Cancel();
_cancellationTokenSource?.Dispose();
}
}
}
C#using System;
using System.Windows.Input;
namespace AppFody
{
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
/// <summary>
/// 手动触发 CanExecuteChanged 事件
/// </summary>
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}
C#using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace AppFody
{
public partial class Form1 : Form
{
private MainViewModel _viewModel;
public Form1()
{
InitializeComponent();
_viewModel = new MainViewModel();
SetupDataBindings();
SetupEventHandlers();
}
private void SetupDataBindings()
{
// 基础状态绑定
lblDeviceStatus.DataBindings.Add("Text", _viewModel, nameof(_viewModel.DeviceStatus),
false, DataSourceUpdateMode.OnPropertyChanged);
lblTemperature.DataBindings.Add("Text", _viewModel, nameof(_viewModel.Temperature),
true, DataSourceUpdateMode.OnPropertyChanged, "0", "N1");
lblPressure.DataBindings.Add("Text", _viewModel, nameof(_viewModel.Pressure),
true, DataSourceUpdateMode.OnPropertyChanged, "0", "N2");
lblMotorSpeed.DataBindings.Add("Text", _viewModel, nameof(_viewModel.MotorSpeed),
false, DataSourceUpdateMode.OnPropertyChanged);
lblLastUpdate.DataBindings.Add("Text", _viewModel, nameof(_viewModel.LastUpdate),
true, DataSourceUpdateMode.OnPropertyChanged, null, "HH:mm:ss.fff");
lblErrorMessage.DataBindings.Add("Text", _viewModel, nameof(_viewModel.ErrorMessage),
false, DataSourceUpdateMode.OnPropertyChanged);
// 新增参数绑定
lblVoltage.DataBindings.Add("Text", _viewModel, nameof(_viewModel.Voltage),
true, DataSourceUpdateMode.OnPropertyChanged, "0", "N1");
lblCurrent.DataBindings.Add("Text", _viewModel, nameof(_viewModel.Current),
true, DataSourceUpdateMode.OnPropertyChanged, "0", "N1");
lblPower.DataBindings.Add("Text", _viewModel, nameof(_viewModel.Power),
true, DataSourceUpdateMode.OnPropertyChanged, "0", "N2");
lblVibration.DataBindings.Add("Text", _viewModel, nameof(_viewModel.Vibration),
true, DataSourceUpdateMode.OnPropertyChanged, "0", "N2");
lblProductCount.DataBindings.Add("Text", _viewModel, nameof(_viewModel.ProductCount),
false, DataSourceUpdateMode.OnPropertyChanged);
lblEfficiency.DataBindings.Add("Text", _viewModel, nameof(_viewModel.Efficiency),
true, DataSourceUpdateMode.OnPropertyChanged, "0", "N1");
lblAlarmStatus.DataBindings.Add("Text", _viewModel, nameof(_viewModel.AlarmStatus),
false, DataSourceUpdateMode.OnPropertyChanged);
// 数据采集状态绑定
lblDataCollectionStatus.DataBindings.Add("Text", _viewModel, nameof(_viewModel.DataCollectionStatus),
false, DataSourceUpdateMode.OnPropertyChanged);
nudCollectionInterval.DataBindings.Add("Value", _viewModel, nameof(_viewModel.CollectionInterval),
false, DataSourceUpdateMode.OnPropertyChanged);
// 按钮状态绑定
btnConnect.DataBindings.Add("Enabled", _viewModel, nameof(_viewModel.CanConnect),
false, DataSourceUpdateMode.OnPropertyChanged);
btnDisconnect.DataBindings.Add("Enabled", _viewModel, nameof(_viewModel.CanDisconnect),
false, DataSourceUpdateMode.OnPropertyChanged);
btnStart.DataBindings.Add("Enabled", _viewModel, nameof(_viewModel.CanStart),
false, DataSourceUpdateMode.OnPropertyChanged);
btnStop.DataBindings.Add("Enabled", _viewModel, nameof(_viewModel.CanStop),
false, DataSourceUpdateMode.OnPropertyChanged);
btnReset.DataBindings.Add("Enabled", _viewModel, nameof(_viewModel.CanReset),
false, DataSourceUpdateMode.OnPropertyChanged);
btnStartDataCollection.DataBindings.Add("Enabled", _viewModel, nameof(_viewModel.CanStartDataCollection),
false, DataSourceUpdateMode.OnPropertyChanged);
btnStopDataCollection.DataBindings.Add("Enabled", _viewModel, nameof(_viewModel.CanStopDataCollection),
false, DataSourceUpdateMode.OnPropertyChanged);
// 订阅 PropertyChanged 事件以更新 UI 样式
_viewModel.PropertyChanged += OnViewModelPropertyChanged;
}
private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// 确保在UI线程上执行
if (InvokeRequired)
{
// 使用BeginInvoke提高性能,避免阻塞
BeginInvoke(new Action<object, PropertyChangedEventArgs>(OnViewModelPropertyChanged), sender, e);
return;
}
try
{
switch (e.PropertyName)
{
case nameof(_viewModel.DeviceStatus):
UpdateStatusColor();
break;
case nameof(_viewModel.IsRunning):
UpdateRunningIndicator();
break;
case nameof(_viewModel.Temperature):
UpdateTemperatureColor();
break;
case nameof(_viewModel.AlarmStatus):
UpdateAlarmColor();
break;
case nameof(_viewModel.IsDataCollecting):
UpdateDataCollectionIndicator();
break;
case nameof(_viewModel.Vibration):
UpdateVibrationColor();
break;
}
}
catch (Exception ex)
{
// 防止UI更新错误影响整个应用
Console.WriteLine($"UI更新错误: {ex.Message}");
}
}
private void UpdateStatusColor()
{
if (lblDeviceStatus.IsDisposed) return;
switch (_viewModel.DeviceStatus)
{
case "离线":
case "连接失败":
lblDeviceStatus.ForeColor = Color.Gray;
break;
case "连接中...":
case "启动中...":
case "停止中...":
lblDeviceStatus.ForeColor = Color.Orange;
break;
case "已连接":
case "已停止":
case "已重置":
lblDeviceStatus.ForeColor = Color.Blue;
break;
case "运行中":
lblDeviceStatus.ForeColor = Color.Green;
break;
default:
lblDeviceStatus.ForeColor = Color.Black;
break;
}
}
private void UpdateRunningIndicator()
{
if (this.IsDisposed) return;
if (_viewModel.IsRunning)
{
this.Text = "MVVM设备监控 v1.0 - 运行中";
}
else
{
this.Text = "MVVM设备监控 v1.0";
}
}
private void UpdateTemperatureColor()
{
if (lblTemperature.IsDisposed) return;
if (_viewModel.Temperature > 70)
lblTemperature.ForeColor = Color.Red;
else if (_viewModel.Temperature > 50)
lblTemperature.ForeColor = Color.Orange;
else if (_viewModel.Temperature > 30)
lblTemperature.ForeColor = Color.Green;
else
lblTemperature.ForeColor = Color.Blue;
}
private void UpdateAlarmColor()
{
if (lblAlarmStatus.IsDisposed) return;
switch (_viewModel.AlarmStatus)
{
case "正常":
lblAlarmStatus.ForeColor = Color.Green;
lblAlarmStatus.Font = new Font(lblAlarmStatus.Font, FontStyle.Regular);
break;
case "温度报警":
case "振动报警":
case "压力报警":
lblAlarmStatus.ForeColor = Color.Red;
lblAlarmStatus.Font = new Font(lblAlarmStatus.Font, FontStyle.Bold);
break;
default:
lblAlarmStatus.ForeColor = Color.Orange;
lblAlarmStatus.Font = new Font(lblAlarmStatus.Font, FontStyle.Regular);
break;
}
}
private void UpdateDataCollectionIndicator()
{
if (lblDataCollectionStatus.IsDisposed || btnStartDataCollection.IsDisposed || btnStopDataCollection.IsDisposed)
return;
if (_viewModel.IsDataCollecting)
{
lblDataCollectionStatus.ForeColor = Color.Green;
lblDataCollectionStatus.Font = new Font(lblDataCollectionStatus.Font, FontStyle.Bold);
btnStartDataCollection.BackColor = Color.LightGray;
btnStopDataCollection.BackColor = Color.LightCoral;
}
else
{
lblDataCollectionStatus.ForeColor = Color.Gray;
lblDataCollectionStatus.Font = new Font(lblDataCollectionStatus.Font, FontStyle.Regular);
btnStartDataCollection.BackColor = Color.LightBlue;
btnStopDataCollection.BackColor = Color.LightGray;
}
}
private void UpdateVibrationColor()
{
if (lblVibration.IsDisposed) return;
if (_viewModel.Vibration > 2.0)
lblVibration.ForeColor = Color.Red;
else if (_viewModel.Vibration > 1.5)
lblVibration.ForeColor = Color.Orange;
else
lblVibration.ForeColor = Color.Green;
}
private void SetupEventHandlers()
{
btnConnect.Click += (s, e) => ExecuteCommand(_viewModel.ConnectCommand);
btnDisconnect.Click += (s, e) => ExecuteCommand(_viewModel.DisconnectCommand);
btnStart.Click += (s, e) => ExecuteCommand(_viewModel.StartCommand);
btnStop.Click += (s, e) => ExecuteCommand(_viewModel.StopCommand);
btnReset.Click += (s, e) => ExecuteCommand(_viewModel.ResetCommand);
btnStartDataCollection.Click += (s, e) => ExecuteCommand(_viewModel.StartDataCollectionCommand);
btnStopDataCollection.Click += (s, e) => ExecuteCommand(_viewModel.StopDataCollectionCommand);
// 采集间隔变化事件
nudCollectionInterval.ValueChanged += (s, e) =>
{
if (!nudCollectionInterval.IsDisposed)
{
_viewModel.CollectionInterval = (int)nudCollectionInterval.Value;
}
};
}
private void ExecuteCommand(System.Windows.Input.ICommand command)
{
try
{
if (command?.CanExecute(null) == true)
{
command.Execute(null);
}
}
catch (Exception ex)
{
MessageBox.Show($"命令执行错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
try
{
if (_viewModel != null)
{
_viewModel.PropertyChanged -= OnViewModelPropertyChanged;
if (_viewModel.IsConnected)
{
_viewModel.DisconnectCommand?.Execute(null);
}
// 释放ViewModel资源
_viewModel.Dispose();
}
}
catch (Exception ex)
{
Console.WriteLine($"窗体关闭错误: {ex.Message}");
}
base.OnFormClosing(e);
}
}
}
跨线程操作异常
C#// 错误做法 - 直接在后台线程更新UI属性
Task.Run(() => {
Temperature = newValue; // 抛出InvalidOperationException
});
// 正确做法 - 使用同步上下文调度
Task.Run(() => {
_syncContext.Post(_ => Temperature = newValue, null);
});
内存泄漏风险
C#// 潜在风险 - PropertyChanged事件未正确解除订阅
_viewModel.PropertyChanged += OnViewModelPropertyChanged;
// 安全做法 - 在窗体关闭时解除订阅
protected override void OnFormClosing(FormClosingEventArgs e)
{
_viewModel.PropertyChanged -= OnViewModelPropertyChanged;
_viewModel.Dispose(); // 释放资源
base.OnFormClosing(e);
}
UI更新性能问题
C#// 性能优化 - 使用BeginInvoke避免阻塞
private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (InvokeRequired)
{
// BeginInvoke异步调度,避免阻塞调用线程
BeginInvoke(new Action<object, PropertyChangedEventArgs>(OnViewModelPropertyChanged), sender, e);
return;
}
// 在UI线程中安全更新界面
UpdateUIElement(e.PropertyName);
}
通过这个完整的工业监控系统案例,我们深度实践了C# MVVM模式的生产级应用。三个关键收获:
这套架构不仅适用于工业监控,同样可以应用于金融交易系统、物联网数据展示、实时监控大屏等场景。掌握了这些核心技术,你就拥有了构建高质量WinForms应用的完整武器库。
你在实际项目中遇到过哪些MVVM实现难点?欢迎在评论区分享你的经验,一起探讨更多实战技巧!如果觉得这篇文章对你有帮助,请转发给更多需要的同行朋友! 🚀
相关信息
通过网盘分享的文件:AppFody.zip 链接: https://pan.baidu.com/s/11B_oUHvmOF-gV-XJDy9ETg?pwd=4w6y 提取码: 4w6y --来自百度网盘超级会员v9的分享
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!