以下是对半导体可靠性测试机上位机系统的更详细 UI 设计和代码实现,基于 .NET Core + WPF + Prism + MVVM + MaterialDesignThemes,涵盖首页、配置界面、监控界面、日志界面和硬件控制界面。每个页面都会提供完整的 XAML 和 ViewModel 代码,并附带中文解释,确保界面大气、现代、用户友好。代码将使用 MaterialDesignThemes 美化界面,LiveCharts 绘制曲线,Prism 实现模块化导航,Autofac 管理依赖。以下按页面逐一展开。


一、UI 设计总体原则

  • 风格:采用 MaterialDesignThemes,界面现代化,配色柔和,按钮和控件有动画效果。

  • 布局:使用 Grid 和 StackPanel 实现自适应布局,确保不同分辨率下显示正常。

  • 交互:支持鼠标点击、键盘导航,界面响应快速,异步加载数据避免卡顿。

  • 提示:每个页面包含帮助按钮,点击显示操作指引。

  • 图表:使用 LiveCharts 绘制实时曲线,支持动态更新。


二、项目结构与依赖

确保项目已添加以下 NuGet 包:

  • Prism.Unity(模块化和导航)

  • MaterialDesignThemes(UI 美化)

  • LiveCharts.Wpf(图表)

  • Autofac(依赖注入)

  • Serilog.Sinks.File 和 Serilog.Sinks.Console(日志)

  • Microsoft.EntityFrameworkCore.Sqlite(数据库)

  • NumSharp(计算)

appsettings.json(示例配置)

json

{
  "Storage": {
    "Type": "SQLite",
    "ConnectionString": "Data Source=testdata.db"
  },
  "Hardware": {
    "PowerSupply": { "Type": "TektronixPowerSupply", "Port": "COM1" },
    "Oven": { "Type": "CustomOven", "Address": "192.168.1.100", "Protocol": "TCP" }
  }
}

Bootstrapper.cs(Prism 启动配置,已在上一回答中提供,略)


三、UI 页面详细设计与代码

3.1 主窗口

主窗口包含导航栏和内容区域,使用 Prism 的 Region 动态加载各模块视图。

MainWindow.xaml

xaml

<Window x:Class="SemiconductorTestSystem.UI.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://prismlibrary.com/"
        xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
        Title="半导体可靠性测试系统" Height="720" Width="1280" WindowStartupLocation="CenterScreen">
    <Grid>
        <!-- 导航栏 -->
        <materialDesign:Card Background="{DynamicResource MaterialDesignPaper}" Margin="0,0,0,10">
            <StackPanel Orientation="Horizontal" Height="60" VerticalAlignment="Top">
                <Button Content="首页" Margin="10" Style="{StaticResource MaterialDesignRaisedButton}"
                        Command="{Binding NavigateCommand}" CommandParameter="HomeView"/>
                <Button Content="配置" Margin="10" Style="{StaticResource MaterialDesignRaisedButton}"
                        Command="{Binding NavigateCommand}" CommandParameter="ConfigView"/>
                <Button Content="监控" Margin="10" Style="{StaticResource MaterialDesignRaisedButton}"
                        Command="{Binding NavigateCommand}" CommandParameter="MonitorView"/>
                <Button Content="日志" Margin="10" Style="{StaticResource MaterialDesignRaisedButton}"
                        Command="{Binding NavigateCommand}" CommandParameter="LogView"/>
                <Button Content="硬件控制" Margin="10" Style="{StaticResource MaterialDesignRaisedButton}"
                        Command="{Binding NavigateCommand}" CommandParameter="HardwareControlView"/>
            </StackPanel>
        </materialDesign:Card>

        <!-- 内容区域 -->
        <ContentControl prism:RegionManager.RegionName="MainRegion" Margin="10,70,10,10"/>
    </Grid>
</Window>

MainWindowViewModel.cs

csharp

using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;

namespace SemiconductorTestSystem.UI.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        private readonly IRegionManager _regionManager;
        public DelegateCommand<string> NavigateCommand { get; private set; }

        public MainWindowViewModel(IRegionManager regionManager)
        {
            _regionManager = regionManager;
            NavigateCommand = new DelegateCommand<string>(Navigate);
        }

        private void Navigate(string viewName)
        {
            _regionManager.RequestNavigate("MainRegion", viewName);
        }
    }
}

中文解释:

  • 主窗口使用 MaterialDesign 的 Card 组件包装导航栏,增加立体感。

  • 导航按钮使用 MaterialDesignRaisedButton 样式,点击触发 Prism 导航。

  • 内容区域通过 ContentControl 的 Region 动态加载视图。

  • 窗口大小设置为 1280x720,居中显示,适配大多数屏幕。


3.2 首页

功能:显示多个通道信息(通道名称、状态、测试模式等),点击通道显示数据表格和实时曲线,展示实验进度。

HomeView.xaml

xaml

<UserControl x:Class="SemiconductorTestSystem.UI.Modules.Home.Views.HomeView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             xmlns:lc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf">
    <Grid Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="250"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <!-- 通道列表 -->
        <materialDesign:Card Grid.Column="0" Padding="10">
            <StackPanel>
                <TextBlock Text="通道列表" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0,0,0,10"/>
                <ListBox ItemsSource="{Binding Channels}" SelectedItem="{Binding SelectedChannel}" Height="600">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <materialDesign:Card Margin="5" Padding="10">
                                <StackPanel>
                                    <TextBlock Text="{Binding ChannelName}" FontWeight="Bold"/>
                                    <TextBlock Text="{Binding Status}"/>
                                    <TextBlock Text="{Binding TestMode}"/>
                                </StackPanel>
                            </materialDesign:Card>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </StackPanel>
        </materialDesign:Card>

        <!-- 数据展示 -->
        <materialDesign:Card Grid.Column="1" Margin="10,0,0,0" Padding="10">
            <StackPanel>
                <TextBlock Text="通道数据" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0,0,0,10"/>
                <DataGrid ItemsSource="{Binding ChannelData}" AutoGenerateColumns="True" Height="200" Margin="0,0,0,10"/>
                <lc:CartesianChart Series="{Binding SeriesCollection}" Height="300" Margin="0,0,0,10">
                    <lc:CartesianChart.AxisX>
                        <lc:Axis Title="时间 (s)"/>
                    </lc:CartesianChart.AxisX>
                    <lc:CartesianChart.AxisY>
                        <lc:Axis Title="温度 (°C)"/>
                    </lc:CartesianChart.AxisY>
                </lc:CartesianChart>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="实验进度: " FontWeight="Bold"/>
                    <TextBlock Text="{Binding TestProgress}"/>
                </StackPanel>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="测试模式: " FontWeight="Bold"/>
                    <TextBlock Text="{Binding TestMode}"/>
                </StackPanel>
                <Button Content="帮助" Style="{StaticResource MaterialDesignFloatingActionMiniButton}"
                        Command="{Binding ShowHelpCommand}" HorizontalAlignment="Right" Margin="0,10,0,0"/>
            </StackPanel>
        </materialDesign:Card>
    </Grid>
</UserControl>

HomeViewModel.cs

csharp

using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using LiveCharts;
using LiveCharts.Wpf;
using SemiconductorTestSystem.Core.Models;
using SemiconductorTestSystem.Core.Services;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows;

namespace SemiconductorTestSystem.UI.Modules.Home.ViewModels
{
    public class HomeViewModel : BindableBase, INavigationAware
    {
        private readonly ITestController _testController;
        private Channel _selectedChannel;
        private ObservableCollection<Channel> _channels;
        private ObservableCollection<TestData> _channelData;
        private SeriesCollection _seriesCollection;
        private string _testProgress;
        private string _testMode;

        public DelegateCommand ShowHelpCommand { get; private set; }

        public HomeViewModel(ITestController testController)
        {
            _testController = testController;
            Channels = new ObservableCollection<Channel>();
            ChannelData = new ObservableCollection<TestData>();
            SeriesCollection = new SeriesCollection();
            ShowHelpCommand = new DelegateCommand(ShowHelp);
            LoadChannelsAsync();
        }

        public Channel SelectedChannel
        {
            get => _selectedChannel;
            set
            {
                SetProperty(ref _selectedChannel, value);
                UpdateChannelDataAsync();
            }
        }

        public ObservableCollection<Channel> Channels
        {
            get => _channels;
            set => SetProperty(ref _channels, value);
        }

        public ObservableCollection<TestData> ChannelData
        {
            get => _channelData;
            set => SetProperty(ref _channelData, value);
        }

        public SeriesCollection SeriesCollection
        {
            get => _seriesCollection;
            set => SetProperty(ref _seriesCollection, value);
        }

        public string TestProgress
        {
            get => _testProgress;
            set => SetProperty(ref _testProgress, value);
        }

        public string TestMode
        {
            get => _testMode;
            set => SetProperty(ref _testMode, value);
        }

        private async void LoadChannelsAsync()
        {
            var channels = await _testController.GetChannelsAsync();
            Channels.Clear();
            foreach (var channel in channels)
            {
                Channels.Add(channel);
            }
        }

        private async void UpdateChannelDataAsync()
        {
            if (SelectedChannel != null)
            {
                var data = await _testController.GetChannelDataAsync(SelectedChannel.Id);
                ChannelData.Clear();
                foreach (var item in data)
                {
                    ChannelData.Add(item);
                }

                SeriesCollection.Clear();
                SeriesCollection.Add(new LineSeries
                {
                    Title = "温度",
                    Values = new ChartValues<double>(data.Select(d => d.Temperature))
                });

                TestProgress = $"已完成 {data.Count / 100.0:P0}";
                TestMode = SelectedChannel.TestMode;
            }
        }

        private void ShowHelp()
        {
            MessageBox.Show("首页帮助:\n1. 左侧选择通道查看数据。\n2. 右侧显示数据表格和曲线。\n3. 实验进度和测试模式实时更新。", 
                "帮助", MessageBoxButton.OK, MessageBoxImage.Information);
        }

        public void OnNavigatedTo(NavigationContext navigationContext) { }
        public bool IsNavigationTarget(NavigationContext navigationContext) => true;
        public void OnNavigatedFrom(NavigationContext navigationContext) { }
    }
}

中文解释:

  • 布局:左侧使用 ListBox 显示通道列表,右侧显示数据表格(DataGrid)和曲线(LiveCharts)。

  • 美化:通道列表和数据区域使用 MaterialDesign 的 Card 组件,增加层次感。

  • 交互:选择通道后异步加载数据,更新表格和曲线;帮助按钮显示操作说明。

  • 图表:LiveCharts 显示温度曲线,X 轴为时间,Y 轴为温度,标题和单位清晰。

  • 异步:数据加载使用 async/await,确保 UI 不卡顿。


3.3 配置界面

功能:设置实验流程(如升温、升压、延时等),支持添加、删除、调整步骤顺序。

ConfigView.xaml

xaml

<UserControl x:Class="SemiconductorTestSystem.UI.Modules.Config.Views.ConfigView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!-- 工具栏 -->
        <materialDesign:Card Padding="10">
            <StackPanel Orientation="Horizontal">
                <ComboBox ItemsSource="{Binding StepTypes}" SelectedItem="{Binding SelectedStepType}" 
                          Width="150" Margin="10" materialDesign:HintAssist.Hint="步骤类型"/>
                <TextBox Text="{Binding StepValue}" Width="100" Margin="10" materialDesign:HintAssist.Hint="参数值"/>
                <Button Content="添加步骤" Style="{StaticResource MaterialDesignRaisedButton}" 
                        Command="{Binding AddStepCommand}" Margin="10"/>
                <Button Content="保存配置" Style="{StaticResource MaterialDesignRaisedButton}" 
                        Command="{Binding SaveConfigCommand}" Margin="10"/>
                <Button Content="帮助" Style="{StaticResource MaterialDesignFloatingActionMiniButton}" 
                        Command="{Binding ShowHelpCommand}" Margin="10"/>
            </StackPanel>
        </materialDesign:Card>

        <!-- 步骤列表 -->
        <materialDesign:Card Grid.Row="1" Margin="0,10,0,0" Padding="10">
            <ListBox ItemsSource="{Binding TestSteps}" Height="550">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <materialDesign:Card Margin="5" Padding="10">
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding Type}" Width="150"/>
                                <TextBlock Text="{Binding Value}" Width="100"/>
                                <Button Content="删除" Style="{StaticResource MaterialDesignFlatButton}" 
                                        Command="{Binding DataContext.DeleteStepCommand, RelativeSource={RelativeSource AncestorType=ListBox}}" 
                                        CommandParameter="{Binding}"/>
                            </StackPanel>
                        </materialDesign:Card>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </materialDesign:Card>
    </Grid>
</UserControl>

ConfigViewModel.cs

csharp

using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using SemiconductorTestSystem.Core.Models;
using SemiconductorTestSystem.Core.Services;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;

namespace SemiconductorTestSystem.UI.Modules.Config.ViewModels
{
    public class ConfigViewModel : BindableBase, INavigationAware
    {
        private readonly ITestController _testController;
        private ObservableCollection<TestStep> _testSteps;
        private string _selectedStepType;
        private double _stepValue;

        public DelegateCommand AddStepCommand { get; private set; }
        public DelegateCommand<TestStep> DeleteStepCommand { get; private set; }
        public DelegateCommand SaveConfigCommand { get; private set; }
        public DelegateCommand ShowHelpCommand { get; private set; }

        public ConfigViewModel(ITestController testController)
        {
            _testController = testController;
            TestSteps = new ObservableCollection<TestStep>();
            StepTypes = new ObservableCollection<string> { "Temperature", "Voltage", "Delay" };
            AddStepCommand = new DelegateCommand(AddStep);
            DeleteStepCommand = new DelegateCommand<TestStep>(DeleteStep);
            SaveConfigCommand = new DelegateCommand(SaveConfig);
            ShowHelpCommand = new DelegateCommand(ShowHelp);
        }

        public ObservableCollection<TestStep> TestSteps
        {
            get => _testSteps;
            set => SetProperty(ref _testSteps, value);
        }

        public ObservableCollection<string> StepTypes { get; private set; }

        public string SelectedStepType
        {
            get => _selectedStepType;
            set => SetProperty(ref _selectedStepType, value);
        }

        public double StepValue
        {
            get => _stepValue;
            set => SetProperty(ref _stepValue, value);
        }

        private void AddStep()
        {
            if (!string.IsNullOrEmpty(SelectedStepType))
            {
                TestSteps.Add(new TestStep { Type = SelectedStepType, Value = StepValue });
            }
        }

        private void DeleteStep(TestStep step)
        {
            TestSteps.Remove(step);
        }

        private async void SaveConfig()
        {
            var config = new TestConfig { Steps = TestSteps.ToList() };
            await _testController.SaveConfigAsync(config);
            MessageBox.Show("配置已保存!", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
        }

        private void ShowHelp()
        {
            MessageBox.Show("配置界面帮助:\n1. 选择步骤类型和参数值添加实验步骤。\n2. 点击删除移除步骤。\n3. 保存配置后可用于实验控制。", 
                "帮助", MessageBoxButton.OK, MessageBoxImage.Information);
        }

        public void OnNavigatedTo(NavigationContext navigationContext) { }
        public bool IsNavigationTarget(NavigationContext navigationContext) => true;
        public void OnNavigatedFrom(NavigationContext navigationContext) { }
    }
}

中文解释:

  • 布局:顶部工具栏用于添加步骤,包含步骤类型(ComboBox)、参数值(TextBox)和按钮;下方 ListBox 显示步骤列表。

  • 交互:支持动态添加/删除步骤,保存配置到 TestController;帮助按钮提供指引。

  • 美化:使用 MaterialDesign 控件,步骤列表以 Card 形式展示,删除按钮为 Flat 样式,减少视觉干扰。

  • 功能:支持灵活配置实验流程(如先升温后升压,或加延时)。


3.4 监控界面

功能:实时查看硬件状态(温箱、通道、辅控板、老化板、电源),颜色指示状态。

MonitorView.xaml

xaml

<UserControl x:Class="SemiconductorTestSystem.UI.Modules.Monitor.Views.MonitorView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
    <Grid Margin="10">
        <materialDesign:Card Padding="10">
            <StackPanel>
                <TextBlock Text="硬件状态监控" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0,0,0,10"/>
                <DataGrid ItemsSource="{Binding Devices}" AutoGenerateColumns="False" Height="550">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="设备ID" Binding="{Binding DeviceId}" Width="150"/>
                        <DataGridTextColumn Header="设备名称" Binding="{Binding DeviceName}" Width="200"/>
                        <DataGridTemplateColumn Header="状态" Width="100">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <materialDesign:Chip Content="{Binding Status}" 
                                                        Background="{Binding Status, Converter={StaticResource StatusToColorConverter}}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Header="消息" Binding="{Binding StatusMessage}" Width="*"/>
                    </DataGrid.Columns>
                </DataGrid>
                <Button Content="帮助" Style="{StaticResource MaterialDesignFloatingActionMiniButton}" 
                        Command="{Binding ShowHelpCommand}" HorizontalAlignment="Right" Margin="0,10,0,0"/>
            </StackPanel>
        </materialDesign:Card>
    </Grid>
</UserControl>

StatusToColorConverter.cs(状态颜色转换器)

csharp

using SemiconductorTestSystem.Hardware.Interfaces;
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;

namespace SemiconductorTestSystem.UI.Converters
{
    public class StatusToColorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is DeviceStatus status)
            {
                return status switch
                {
                    DeviceStatus.Idle => Brushes.Gray,
                    DeviceStatus.Running => Brushes.Green,
                    DeviceStatus.Error => Brushes.Red,
                    DeviceStatus.Disconnected => Brushes.Orange,
                    _ => Brushes.Gray
                };
            }
            return Brushes.Gray;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

MonitorViewModel.cs

csharp

using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using SemiconductorTestSystem.Core.Services;
using SemiconductorTestSystem.Hardware.Interfaces;
using System.Collections.ObjectModel;
using System.Windows;

namespace SemiconductorTestSystem.UI.Modules.Monitor.ViewModels
{
    public class MonitorViewModel : BindableBase, INavigationAware
    {
        private readonly IHardwareService _hardwareService;
        private ObservableCollection<IHardwareDevice> _devices;

        public DelegateCommand ShowHelpCommand { get; private set; }

        public MonitorViewModel(IHardwareService hardwareService)
        {
            _hardwareService = hardwareService;
            Devices = new ObservableCollection<IHardwareDevice>();
            ShowHelpCommand = new DelegateCommand(ShowHelp);
            LoadDevices();
        }

        public ObservableCollection<IHardwareDevice> Devices
        {
            get => _devices;
            set => SetProperty(ref _devices, value);
        }

        private void LoadDevices()
        {
            Devices.Clear();
            foreach (var device in _hardwareService.GetAllDevices())
            {
                Devices.Add(device);
                device.StatusChanged += (s, e) =>
                {
                    RaisePropertyChanged(nameof(Devices));
                };
            }
        }

        private void ShowHelp()
        {
            MessageBox.Show("监控界面帮助:\n1. 查看所有硬件的实时状态。\n2. 状态颜色说明:绿色-运行,灰色-空闲,红色-错误,橙色-断开。", 
                "帮助", MessageBoxButton.OK, MessageBoxImage.Information);
        }

        public void OnNavigatedTo(NavigationContext navigationContext) { }
        public bool IsNavigationTarget(NavigationContext navigationContext) => true;
        public void OnNavigatedFrom(NavigationContext navigationContext) { }
    }
}

中文解释:

  • 布局:使用 DataGrid 显示硬件列表,包含设备 ID、名称、状态和消息。

  • 美化:状态列使用 Chip 控件,颜色通过 StatusToColorConverter 动态转换(绿色表示运行,红色表示错误等)。

  • 交互:硬件状态变化通过事件触发界面更新;帮助按钮提供指引。

  • 功能:实时监控温箱、通道、电源等硬件状态,异步更新确保流畅。


3.5 日志界面

功能:显示设备和实验日志,支持筛选和导出。

LogView.xaml

xaml

<UserControl x:Class="SemiconductorTestSystem.UI.Modules.Log.Views.LogView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
    <Grid Margin="10">
        <materialDesign:Card Padding="10">
            <StackPanel>
                <TextBlock Text="日志查看" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0,0,0,10"/>
                <StackPanel Orientation="Horizontal" Margin="0,0,0,10">
                    <TextBox Text="{Binding FilterText, UpdateSourceTrigger=PropertyChanged}" Width="200" 
                             materialDesign:HintAssist.Hint="筛选日志"/>
                    <Button Content="导出日志" Style="{StaticResource MaterialDesignRaisedButton}" 
                            Command="{Binding ExportLogCommand}" Margin="10,0,0,0"/>
                    <Button Content="帮助" Style="{StaticResource MaterialDesignFloatingActionMiniButton}" 
                            Command="{Binding ShowHelpCommand}" Margin="10,0,0,0"/>
                </StackPanel>
                <DataGrid ItemsSource="{Binding Logs}" AutoGenerateColumns="False" Height="500">
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="时间" Binding="{Binding Timestamp}" Width="150"/>
                        <DataGridTextColumn Header="级别" Binding="{Binding Level}" Width="100"/>
                        <DataGridTextColumn Header="消息" Binding="{Binding Message}" Width="*"/>
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>
        </materialDesign:Card>
    </Grid>
</UserControl>

LogViewModel.cs

csharp

using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using SemiconductorTestSystem.Core.Services;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows;

namespace SemiconductorTestSystem.UI.Modules.Log.ViewModels
{
    public class LogViewModel : BindableBase, INavigationAware
    {
        private readonly ILogService _logService;
        private ObservableCollection<LogEntry> _logs;
        private string _filterText;

        public DelegateCommand ExportLogCommand { get; private set; }
        public DelegateCommand ShowHelpCommand { get; private set; }

        public LogViewModel(ILogService logService)
        {
            _logService = logService;
            Logs = new ObservableCollection<LogEntry>();
            ExportLogCommand = new DelegateCommand(ExportLog);
            ShowHelpCommand = new DelegateCommand(ShowHelp);
            LoadLogs();
        }

        public ObservableCollection<LogEntry> Logs
        {
            get => _logs;
            set => SetProperty(ref _logs, value);
        }

        public string FilterText
        {
            get => _filterText;
            set
            {
                SetProperty(ref _filterText, value);
                FilterLogs();
            }
        }

        private void LoadLogs()
        {
            // 模拟从日志服务加载
            Logs.Add(new LogEntry { Timestamp = DateTime.Now, Level = "INFO", Message = "系统启动" });
            Logs.Add(new LogEntry { Timestamp = DateTime.Now.AddSeconds(-10), Level = "ERROR", Message = "设备连接失败" });
        }

        private void FilterLogs()
        {
            if (string.IsNullOrEmpty(FilterText))
            {
                LoadLogs();
            }
            else
            {
                var filtered = Logs.Where(l => l.Message.Contains(FilterText, StringComparison.OrdinalIgnoreCase)).ToList();
                Logs.Clear();
                foreach (var log in filtered)
                {
                    Logs.Add(log);
                }
            }
        }

        private void ExportLog()
        {
            using var writer = new StreamWriter("logs/exported_log.txt");
            foreach (var log in Logs)
            {
                writer.WriteLine($"{log.Timestamp} [{log.Level}] {log.Message}");
            }
            MessageBox.Show("日志已导出到 exported_log.txt", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
        }

        private void ShowHelp()
        {
            MessageBox.Show("日志界面帮助:\n1. 使用筛选框搜索日志。\n2. 点击导出日志保存到文件。\n3. 日志包含时间、级别和消息。", 
                "帮助", MessageBoxButton.OK, MessageBoxImage.Information);
        }

        public void OnNavigatedTo(NavigationContext navigationContext) { }
        public bool IsNavigationTarget(NavigationContext navigationContext) => true;
        public void OnNavigatedFrom(NavigationContext navigationContext) { }
    }

    public class LogEntry
    {
        public DateTime Timestamp { get; set; }
        public string Level { get; set; }
        public string Message { get; set; }
    }
}

中文解释:

  • 布局:顶部包含筛选框和导出按钮,下方 DataGrid 显示日志。

  • 美化:日志表格使用 MaterialDesign 的 Card 包装,筛选框支持实时更新。

  • 交互:支持日志筛选(实时响应)和导出到文件;帮助按钮提供指引。

  • 功能:展示设备和实验日志,未来可与 Serilog 集成实时读取。


3.6 硬件控制界面

功能:显示硬件信息,选择硬件查看状态和执行操作(如设置电源电压)。

HardwareControlView.xaml

xaml

<UserControl x:Class="SemiconductorTestSystem.UI.Modules.HardwareControl.Views.HardwareControlView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
    <Grid Margin="10">
        <materialDesign:Card Padding="10">
            <StackPanel>
                <TextBlock Text="硬件控制" Style="{StaticResource MaterialDesignHeadline6TextBlock}" Margin="0,0,0,10"/>
                <ComboBox ItemsSource="{Binding Devices}" DisplayMemberPath="DeviceName" 
                          SelectedItem="{Binding SelectedDevice}" Width="200" Margin="10" 
                          materialDesign:HintAssist.Hint="选择硬件"/>
                <StackPanel Orientation="Horizontal" Margin="0,10,0,10">
                    <TextBox Text="{Binding ParameterName}" Width="150" materialDesign:HintAssist.Hint="参数名"/>
                    <TextBox Text="{Binding ParameterValue}" Width="100" materialDesign:HintAssist.Hint="参数值"/>
                    <Button Content="设置" Style="{StaticResource MaterialDesignRaisedButton}" 
                            Command="{Binding SetParameterCommand}" Margin="10,0,0,0"/>
                    <Button Content="读取" Style="{StaticResource MaterialDesignRaisedButton}" 
                            Command="{Binding ReadParameterCommand}" Margin="10,0,0,0"/>
                </StackPanel>
                <TextBlock Text="{Binding ParameterResult}" Margin="0,0,0,10"/>
                <Button Content="帮助" Style="{StaticResource MaterialDesignFloatingActionMiniButton}" 
                        Command="{Binding ShowHelpCommand}" HorizontalAlignment="Right"/>
            </StackPanel>
        </materialDesign:Card>
    </Grid>
</UserControl>

HardwareControlViewModel.cs

csharp

using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using SemiconductorTestSystem.Core.Services;
using SemiconductorTestSystem.Hardware.Interfaces;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows;

namespace SemiconductorTestSystem.UI.Modules.HardwareControl.ViewModels
{
    public class HardwareControlViewModel : BindableBase, INavigationAware
    {
        private readonly IHardwareService _hardwareService;
        private ObservableCollection<IHardwareDevice> _devices;
        private IHardwareDevice _selectedDevice;
        private string _parameterName;
        private string _parameterValue;
        private string _parameterResult;

        public DelegateCommand SetParameterCommand { get; private set; }
        public DelegateCommand ReadParameterCommand { get; private set; }
        public DelegateCommand ShowHelpCommand { get; private set; }

        public HardwareControlViewModel(IHardwareService hardwareService)
        {
            _hardwareService = hardwareService;
            Devices = new ObservableCollection<IHardwareDevice>();
            SetParameterCommand = new DelegateCommand(async () => await SetParameterAsync());
            ReadParameterCommand = new DelegateCommand(async () => await ReadParameterAsync());
            ShowHelpCommand = new DelegateCommand(ShowHelp);
            LoadDevices();
        }

        public ObservableCollection<IHardwareDevice> Devices
        {
            get => _devices;
            set => SetProperty(ref _devices, value);
        }

        public IHardwareDevice SelectedDevice
        {
            get => _selectedDevice;
            set => SetProperty(ref _selectedDevice, value);
        }

        public string ParameterName
        {
            get => _parameterName;
            set => SetProperty(ref _parameterName, value);
        }

        public string ParameterValue
        {
            get => _parameterValue;
            set => SetProperty(ref _parameterValue, value);
        }

        public string ParameterResult
        {
            get => _parameterResult;
            set => SetProperty(ref _parameterResult, value);
        }

        private void LoadDevices()
        {
            Devices.Clear();
            foreach (var device in _hardwareService.GetAllDevices())
            {
                Devices.Add(device);
            }
        }

        private async Task SetParameterAsync()
        {
            if (SelectedDevice != null && !string.IsNullOrEmpty(ParameterName) && !string.IsNullOrEmpty(ParameterValue))
            {
                await SelectedDevice.SetParameterAsync(ParameterName, ParameterValue);
                ParameterResult = $"设置 {ParameterName} = {ParameterValue} 成功";
            }
        }

        private async Task ReadParameterAsync()
        {
            if (SelectedDevice != null && !string.IsNullOrEmpty(ParameterName))
            {
                var result = await SelectedDevice.ReadParameterAsync(ParameterName);
                ParameterResult = $"读取 {ParameterName} = {result}";
            }
        }

        private void ShowHelp()
        {
            MessageBox.Show("硬件控制界面帮助:\n1. 选择硬件并输入参数名和值。\n2. 点击设置发送参数,点击读取获取参数值。\n3. 支持电源、温箱等硬件操作。", 
                "帮助", MessageBoxButton.OK, MessageBoxImage.Information);
        }

        public void OnNavigatedTo(NavigationContext navigationContext) { }
        public bool IsNavigationTarget(NavigationContext navigationContext) => true;
        public void OnNavigatedFrom(NavigationContext navigationContext) { }
    }
}

中文解释:

  • 布局:ComboBox 选择硬件,TextBox 输入参数名和值,按钮执行设置/读取操作。

  • 美化:使用 MaterialDesign 控件,界面简洁,参数结果显示在 TextBlock 中。

  • 交互:异步设置和读取硬件参数,实时显示结果;帮助按钮提供指引。

  • 功能:支持动态选择硬件(如电源、温箱),执行调试操作。


四、UI 设计要点总结

  • 一致性:所有页面使用 MaterialDesignThemes,保持统一的现代化风格。

  • 响应性:异步加载数据(async/await),确保高频数据采集不卡顿。

  • 模块化:每个页面作为一个 Prism 模块,独立开发和维护。

  • 用户体验:帮助按钮提供操作指引,颜色和动画增强交互性。

  • 扩展性:支持动态添加新页面,Prism 导航灵活。


五、后续补充

如果需要以下内容,请告诉我,我可以进一步扩展:

  • 更详细的业务逻辑:如 TestController 的具体实现,涵盖 HTRB、HTGB 等测试。

  • 计算模块:热阻、结构函数等公式的详细实现。

  • 多设备优化:支持 200+ 设备的高效管理。

  • 云端部署:集成 Azure 或 AWS 实现远程监控。

  • 深度学习:使用 ML.NET 分析实验数据。

请确认具体需求,我会提供更详细的代码或分篇说明!

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐