MVVM 模式激光直写控制软件完整实现

下面我将使用 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; }
}

三、如何运行项目

  1. 安装必要的NuGet包:

Install-Package Prism.Unity
Install-Package Prism.Wpf
  1. 实现具体的服务类 (LaserService 和 MotionService) 连接实际硬件

  2. 在 App.xaml.cs 中配置依赖注入

  3. 运行应用程序

四、MVVM 模式优势在本项目中的体现

  1. 可测试性:可以轻松对 ViewModel 进行单元测试,无需依赖实际硬件

  2. 可维护性:视图逻辑与业务逻辑分离,便于修改和扩展

  3. 可替换性:可以轻松替换服务实现(如模拟服务用于开发,真实服务用于生产)

  4. 数据绑定:自动同步UI与数据状态,减少样板代码

这个实现完整展示了如何使用 MVVM 模式构建激光直写控制软件,您可以根据实际硬件接口进一步完善服务层的实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值