下面我将使用 MVVM 模式重构之前的激光直写控制软件,提供完整的工程结构。我们将使用 Prism 框架来实现 MVVM 模式。
一、项目结构
LaserDirectWriteApp/
├── Models/ # 数据模型
│ ├── LaserParameters.cs
│ ├── MotionParameters.cs
│ └── SystemStatus.cs
├── ViewModels/ # 视图模型
│ ├── MainViewModel.cs
│ ├── LaserControlViewModel.cs
│ ├── MotionControlViewModel.cs
│ └── PreviewViewModel.cs
├── Views/ # 视图
│ ├── MainWindow.xaml
│ ├── LaserControlView.xaml
│ ├── MotionControlView.xaml
│ └── PreviewView.xaml
├── Services/ # 服务层
│ ├── ILaserService.cs
│ ├── IMotionService.cs
│ ├── LaserService.cs
│ └── MotionService.cs
├── Converters/ # 值转换器
│ └── BooleanToStatusConverter.cs
├── Commands/ # 自定义命令
│ └── AsyncRelayCommand.cs
└── App.xaml # 应用入口
二、核心代码实现
1. 应用入口 (App.xaml)
<prism:PrismApplication x:Class="LaserDirectWriteApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Prism.Wpf;component/Resources/PrismResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</prism:PrismApplication>
using Prism.Ioc;
using Prism.Unity;
using LaserDirectWriteApp.Views;
using LaserDirectWriteApp.ViewModels;
using LaserDirectWriteApp.Services;
public partial class App : PrismApplication
{
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// 注册服务
containerRegistry.RegisterSingleton<ILaserService, LaserService>();
containerRegistry.RegisterSingleton<IMotionService, MotionService>();
// 注册视图和ViewModel
containerRegistry.RegisterForNavigation<MainWindow, MainViewModel>();
containerRegistry.RegisterForNavigation<LaserControlView, LaserControlViewModel>();
containerRegistry.RegisterForNavigation<MotionControlView, MotionControlViewModel>();
containerRegistry.RegisterForNavigation<PreviewView, PreviewViewModel>();
}
}
2. 主窗口视图模型 (MainViewModel.cs)
using Prism.Mvvm;
using Prism.Commands;
using LaserDirectWriteApp.Services;
public class MainViewModel : BindableBase
{
private readonly ILaserService _laserService;
private readonly IMotionService _motionService;
private string _statusMessage = "系统就绪";
public string StatusMessage
{
get => _statusMessage;
set => SetProperty(ref _statusMessage, value);
}
private bool _isConnected;
public bool IsConnected
{
get => _isConnected;
set => SetProperty(ref _isConnected, value);
}
public DelegateCommand ConnectCommand { get; }
public DelegateCommand DisconnectCommand { get; }
public DelegateCommand ExitCommand { get; }
public MainViewModel(ILaserService laserService, IMotionService motionService)
{
_laserService = laserService;
_motionService = motionService;
ConnectCommand = new DelegateCommand(OnConnect);
DisconnectCommand = new DelegateCommand(OnDisconnect).ObservesCanExecute(() => IsConnected);
ExitCommand = new DelegateCommand(OnExit);
}
private async void OnConnect()
{
try
{
StatusMessage = "正在连接设备...";
await Task.Run(() => _motionService.Connect());
await Task.Run(() => _laserService.Connect());
IsConnected = true;
StatusMessage = "设备连接成功";
}
catch (Exception ex)
{
StatusMessage = $"连接失败: {ex.Message}";
}
}
private void OnDisconnect()
{
try
{
StatusMessage = "正在断开设备...";
_laserService.Disconnect();
_motionService.Disconnect();
IsConnected = false;
StatusMessage = "设备已断开";
}
catch (Exception ex)
{
StatusMessage = $"断开失败: {ex.Message}";
}
}
private void OnExit()
{
if (IsConnected) OnDisconnect();
Application.Current.Shutdown();
}
}
3. 主窗口视图 (MainWindow.xaml)
<Window x:Class="LaserDirectWriteApp.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="激光直写控制系统" Height="800" Width="1200">
<DockPanel>
<!-- 顶部菜单 -->
<Menu DockPanel.Dock="Top">
<MenuItem Header="文件">
<MenuItem Command="{Binding ExitCommand}" Header="退出"/>
</MenuItem>
<MenuItem Header="设备">
<MenuItem Command="{Binding ConnectCommand}" Header="连接"/>
<MenuItem Command="{Binding DisconnectCommand}" Header="断开"/>
</MenuItem>
</Menu>
<!-- 状态栏 -->
<StatusBar DockPanel.Dock="Bottom">
<StatusBarItem>
<TextBlock Text="{Binding StatusMessage}"/>
</StatusBarItem>
</StatusBar>
<!-- 主内容区 -->
<TabControl>
<TabItem Header="激光控制">
<ContentControl prism:RegionManager.RegionName="LaserControlRegion"/>
</TabItem>
<TabItem Header="运动控制">
<ContentControl prism:RegionManager.RegionName="MotionControlRegion"/>
</TabItem>
<TabItem Header="预览">
<ContentControl prism:RegionManager.RegionName="PreviewRegion"/>
</TabItem>
</TabControl>
</DockPanel>
</Window>
4. 激光控制视图模型 (LaserControlViewModel.cs)
using Prism.Mvvm;
using Prism.Commands;
using LaserDirectWriteApp.Models;
using LaserDirectWriteApp.Services;
public class LaserControlViewModel : BindableBase
{
private readonly ILaserService _laserService;
private LaserParameters _parameters = new LaserParameters();
public LaserParameters Parameters
{
get => _parameters;
set => SetProperty(ref _parameters, value);
}
public DelegateCommand EnableLaserCommand { get; }
public DelegateCommand ApplySettingsCommand { get; }
public LaserControlViewModel(ILaserService laserService)
{
_laserService = laserService;
EnableLaserCommand = new DelegateCommand(OnEnableLaser);
ApplySettingsCommand = new DelegateCommand(OnApplySettings);
}
private void OnEnableLaser()
{
try
{
_laserService.SetEnabled(Parameters.IsEnabled);
}
catch (Exception ex)
{
// 处理异常
}
}
private void OnApplySettings()
{
try
{
_laserService.SetPower(Parameters.Power);
_laserService.SetFrequency(Parameters.Frequency);
_laserService.SetPulseWidth(Parameters.PulseWidth);
}
catch (Exception ex)
{
// 处理异常
}
}
}
5. 激光控制视图 (LaserControlView.xaml)
<UserControl x:Class="LaserDirectWriteApp.Views.LaserControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid Margin="10">
<StackPanel>
<GroupBox Header="激光开关">
<ToggleButton IsChecked="{Binding Parameters.IsEnabled, Mode=TwoWay}"
Command="{Binding EnableLaserCommand}"
Content="{Binding Parameters.IsEnabled,
Converter={StaticResource BooleanToStatusConverter},
ConverterParameter='激光|关闭|开启'}"/>
</GroupBox>
<GroupBox Header="功率设置" Margin="0,10,0,0">
<StackPanel>
<Slider Minimum="0" Maximum="100"
Value="{Binding Parameters.Power, Mode=TwoWay}"/>
<TextBlock Text="{Binding Parameters.Power, StringFormat='功率: {0}%'}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="脉冲参数" Margin="0,10,0,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Text="频率 (kHz)"/>
<TextBox Text="{Binding Parameters.Frequency, Mode=TwoWay}"/>
</StackPanel>
<StackPanel Grid.Column="1">
<TextBlock Text="脉宽 (ns)"/>
<TextBox Text="{Binding Parameters.PulseWidth, Mode=TwoWay}"/>
</StackPanel>
</Grid>
</GroupBox>
<Button Content="应用设置" Command="{Binding ApplySettingsCommand}"
Margin="0,20,0,0" HorizontalAlignment="Right"/>
</StackPanel>
</Grid>
</UserControl>
6. 运动控制视图模型 (MotionControlViewModel.cs)
using Prism.Mvvm;
using Prism.Commands;
using LaserDirectWriteApp.Models;
using LaserDirectWriteApp.Services;
public class MotionControlViewModel : BindableBase
{
private readonly IMotionService _motionService;
private MotionParameters _parameters = new MotionParameters();
public MotionParameters Parameters
{
get => _parameters;
set => SetProperty(ref _parameters, value);
}
public AsyncRelayCommand MoveCommand { get; }
public AsyncRelayCommand HomeCommand { get; }
public MotionControlViewModel(IMotionService motionService)
{
_motionService = motionService;
MoveCommand = new AsyncRelayCommand(OnMove, CanMove);
HomeCommand = new AsyncRelayCommand(OnHome);
// 订阅位置更新
_motionService.PositionUpdated += (s, e) =>
{
Parameters.XPosition = e.X;
Parameters.YPosition = e.Y;
Parameters.ZPosition = e.Z;
};
}
private bool CanMove() => !_motionService.IsMoving;
private async Task OnMove()
{
try
{
await _motionService.MoveTo(
Parameters.XTarget,
Parameters.YTarget,
Parameters.ZTarget);
}
catch (Exception ex)
{
// 处理异常
}
}
private async Task OnHome()
{
try
{
await _motionService.Home();
}
catch (Exception ex)
{
// 处理异常
}
}
}
7. 运动控制视图 (MotionControlView.xaml)
<UserControl x:Class="LaserDirectWriteApp.Views.MotionControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid Margin="10">
<StackPanel>
<GroupBox Header="X轴">
<StackPanel>
<TextBlock Text="{Binding Parameters.XPosition, StringFormat='当前位置: {0:F2} mm'}"/>
<Slider Minimum="0" Maximum="500"
Value="{Binding Parameters.XTarget, Mode=TwoWay}"/>
<TextBlock Text="{Binding Parameters.XTarget, StringFormat='目标位置: {0:F2} mm'}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="Y轴" Margin="0,10,0,0">
<StackPanel>
<TextBlock Text="{Binding Parameters.YPosition, StringFormat='当前位置: {0:F2} mm'}"/>
<Slider Minimum="0" Maximum="500"
Value="{Binding Parameters.YTarget, Mode=TwoWay}"/>
<TextBlock Text="{Binding Parameters.YTarget, StringFormat='目标位置: {0:F2} mm'}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="Z轴" Margin="0,10,0,0">
<StackPanel>
<TextBlock Text="{Binding Parameters.ZPosition, StringFormat='当前位置: {0:F2} mm'}"/>
<Slider Minimum="0" Maximum="100"
Value="{Binding Parameters.ZTarget, Mode=TwoWay}"/>
<TextBlock Text="{Binding Parameters.ZTarget, StringFormat='目标位置: {0:F2} mm'}"/>
</StackPanel>
</GroupBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,20,0,0">
<Button Content="回原点" Command="{Binding HomeCommand}" Margin="0,0,10,0"/>
<Button Content="移动" Command="{Binding MoveCommand}"/>
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
8. 服务接口 (ILaserService.cs 和 IMotionService.cs)
// ILaserService.cs
public interface ILaserService
{
bool IsConnected { get; }
void Connect();
void Disconnect();
void SetEnabled(bool enabled);
void SetPower(double percentage);
void SetFrequency(double frequency);
void SetPulseWidth(double pulseWidth);
}
// IMotionService.cs
public interface IMotionService
{
bool IsConnected { get; }
bool IsMoving { get; }
void Connect();
void Disconnect();
Task MoveTo(double x, double y, double z);
Task Home();
event EventHandler<PositionUpdatedEventArgs> PositionUpdated;
}
public class PositionUpdatedEventArgs : EventArgs
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
三、如何运行项目
-
安装必要的NuGet包:
Install-Package Prism.Unity
Install-Package Prism.Wpf
-
实现具体的服务类 (
LaserService
和MotionService
) 连接实际硬件 -
在
App.xaml.cs
中配置依赖注入 -
运行应用程序
四、MVVM 模式优势在本项目中的体现
-
可测试性:可以轻松对 ViewModel 进行单元测试,无需依赖实际硬件
-
可维护性:视图逻辑与业务逻辑分离,便于修改和扩展
-
可替换性:可以轻松替换服务实现(如模拟服务用于开发,真实服务用于生产)
-
数据绑定:自动同步UI与数据状态,减少样板代码
这个实现完整展示了如何使用 MVVM 模式构建激光直写控制软件,您可以根据实际硬件接口进一步完善服务层的实现。