添加项目文件。

This commit is contained in:
“hsc”
2026-07-29 13:12:27 +08:00
parent d6da766e2d
commit 8cf45d36b9
297 changed files with 35814 additions and 0 deletions

25
MainModule/MainModule.cs Normal file
View File

@@ -0,0 +1,25 @@
using DeviceCommand.Base;
using DeviceCommand.Devices;
using MainModule.Views;
using System.Reflection;
using UIShare.GlobalVariable;
namespace MainModule
{
[Module(OnDemand=true)]
public class MainModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
IRegionManager regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("ShellViewManager", typeof(MainView));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<MainView>("MainView");
containerRegistry.RegisterForNavigation<AutomatedTestingView>("AutomatedTestingView");
containerRegistry.RegisterForNavigation<ProtocolStartView>("ProtocolStartView");
}
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\TestingModule\TestingModule.csproj" />
<ProjectReference Include="..\UIShare\UIShare.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,302 @@
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Input;
using DeviceCommand.Devices;
using Logger;
using Prism.Ioc;
using TestingModule.ViewModels;
using UIShare;
using UIShare.GlobalVariable;
using UIShare.PubEvent;
using UIShare.UIViewModel;
using UIShare.ViewModelBase;
using static System.Formats.Asn1.AsnWriter;
namespace MainModule.ViewModels
{
public class AutomatedTestingViewModel : NavigateViewModelBase, IRegionMemberLifetime, IDisposable
{
#region
private string _testStatus;
private readonly IScopedProvider _scope;
private bool IsInitialized =false;
private bool _isRunningErrorSteps = false;
private SubscriptionToken? _alarmToken;
#endregion
#region
public bool KeepAlive => true; // 保持存活
public string TestStatus
{
get => _testStatus;
set => SetProperty(ref _testStatus, value);
}
// 该 AutomatedTestingView 实例独占的 ScopedContext5 个子面板共享
public ScopedContext _scopedContext { get; }
public StepRunning _stepRunning { get; }
public GlobalInfo _globalInfo { get; }
public SystemConfig _systemConfig { get; set; }
public DeviceManager _deviceManager { get; set; }
// 5 个子 ViewModel 全部从同一个 scope 解析,自动注入同一个 ScopedContext
public CommandTreeViewModel CommandTreeVM { get; }
public StepsManagerViewModel StepsManagerVM { get; }
public SingleStepEditViewModel SingleStepEditVM { get; }
public LogAreaViewModel LogAreaVM { get; }
public ParametersManagerViewModel ParametersManagerVM { get; }
#endregion
public ICommand RefreshCommand { get; set; }
public ICommand BackToProtocolCommand { get; set; }
public ICommand LoadedCommand { get; set; }
public AutomatedTestingViewModel(IContainerExtension container) : base(container)
{
// 每个 AutomatedTestingViewModel 实例创建独立的容器作用域
_scope = container.CreateScope();
_globalInfo =container.Resolve<GlobalInfo>();
_systemConfig = _scope.Resolve<SystemConfig>();
//加载Json数据
if (ConfigService.IsExit(_globalInfo.CurrentOpeningScope))
{
string filePath = System.IO.Path.Combine(_systemConfig.SystemPath, $"{_globalInfo.CurrentOpeningScope}.json");
if (System.IO.File.Exists(filePath))
{
string json = System.IO.File.ReadAllText(filePath);
Newtonsoft.Json.JsonConvert.PopulateObject(json, _systemConfig);
}
}
//容器解析顺序不要改变!!!
_scopedContext = _scope.Resolve<ScopedContext>();
_deviceManager = _scope.Resolve<DeviceManager>();
_stepRunning = _scope.Resolve<StepRunning>();
// 从同一个 _scope 解析 5 个子 VMDI 会把同一个 ScopedContext 注入它们
CommandTreeVM = _scope.Resolve<CommandTreeViewModel>();
StepsManagerVM = _scope.Resolve<StepsManagerViewModel>();
SingleStepEditVM = _scope.Resolve<SingleStepEditViewModel>();
LogAreaVM = _scope.Resolve<LogAreaViewModel>();
ParametersManagerVM = _scope.Resolve<ParametersManagerViewModel>();
RefreshCommand = new DelegateCommand(OnRefresh);
BackToProtocolCommand = new DelegateCommand(OnBackToProtocol);
LoadedCommand = new AsyncDelegateCommand(OnLoad);
_eventAggregator.GetEvent<SilenceBuzzerEvent>().Subscribe(async (scope) => await SilenceBuzzer(scope));
_alarmToken = _eventAggregator.GetEvent<AlarmEvent>().Subscribe(OnAlarmTriggered);
}
private async Task SilenceBuzzer(string scope)
{
if (_deviceManager.IOGroup == null) return;
if (scope == "default")
{
for(int i = 1; i <= 8; i++)
{
await _deviceManager.IOGroup.Async(i, ., false);
}
}
else if (scope == TestStatus)
{
int = _systemConfig.SharedParameterList
.FirstOrDefault(x => x.ParameterName == "台架序号")?.Value ?? 0;
if ( >= 1 && <= 8)
{
await _deviceManager.IOGroup.Async(, ., false);
}
}
}
/// <summary>
/// AlarmEvent 回调:
/// 上下限 → 蜂鸣器报警 true
/// 上下极限 → 蜂鸣器报警 true + 执行对应 scope 的异常流程
/// </summary>
private async void OnAlarmTriggered((string Scope, string Fingerprint, string Status) args)
{
if (args.Scope != TestStatus) return;
if (_deviceManager?.IOGroup == null) return;
int = _systemConfig.SharedParameterList
.FirstOrDefault(x => x.ParameterName == "台架序号")?.Value ?? 0;
if ( < 1 || > 8) return;
// 上下限 / 上下极限 均触发蜂鸣器报警
await _deviceManager.IOGroup.Async(, ., true);
LoggerHelper.WarnWithNotify(args.Scope, $"[{args.Scope}] 报警触发: {args.Status} ({args.Fingerprint}),蜂鸣器已开启。");
// 上下极限额外触发异常流程
if (args.Status == "超上极限" || args.Status == "超下极限")
{
_ = TriggerErrorStepsAsync();
}
}
/// <summary>
/// 执行异常流程(带并发保护,同一时间仅允许一次)。
/// 如果正常流程正在运行,先取消并等待其停止,再执行异常流程。
/// </summary>
private async Task TriggerErrorStepsAsync()
{
if (_isRunningErrorSteps) return;
_isRunningErrorSteps = true;
try
{
// 1. 如果正常流程正在运行,先停止它
// IsStop == false 表示正在运行IsStop == null 表示未启动或已结束
if (_scopedContext.IsStop == false)
{
LoggerHelper.WarnWithNotify(TestStatus, $"[{TestStatus}] 报警触发,正在停止正常流程...");
_stepRunning.stepCTS.Cancel();
// 等待 ShellViewModel 清理完毕IsStop 变回 null、stepCTS 被重置)
// ExecuteMethodStep 内部硬编码使用 stepCTS.Token必须等它重置后才能跑异常流程
var deadline = DateTime.Now.AddSeconds(5);
while ((_scopedContext.IsStop != null || _stepRunning.stepCTS.IsCancellationRequested)
&& DateTime.Now < deadline)
{
await Task.Delay(50);
}
// 超时后仍未重置则强制重置,确保异常流程的设备命令能拿到有效 Token
if (_stepRunning.stepCTS.IsCancellationRequested)
{
_stepRunning.stepCTS = new CancellationTokenSource();
_scopedContext.IsStop = null;
_scopedContext.RunState = "运行";
}
LoggerHelper.WarnWithNotify(TestStatus, $"[{TestStatus}] 正常流程已停止,开始执行异常流程");
}
// 2. 执行异常流程
await _stepRunning.ExecuteErrorSteps(
_scopedContext.Program,
cancellationToken: _stepRunning.errorStepCTS.Token);
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify(TestStatus, $"[{TestStatus}] 报警触发异常流程失败: {ex.Message}");
}
finally
{
_isRunningErrorSteps = false;
if (_stepRunning.errorStepCTS.IsCancellationRequested)
_stepRunning.errorStepCTS = new CancellationTokenSource();
}
}
public void Dispose()
{
// 1. 如果 TestStatus 为空,说明还没走到 OnNavigatedTo 赋值,直接释放 scope 即可
if (string.IsNullOrEmpty(TestStatus))
{
_scope?.Dispose();
return;
}
try
{
if (_alarmToken != null)
_eventAggregator.GetEvent<AlarmEvent>().Unsubscribe(_alarmToken);
_eventAggregator.GetEvent<SilenceBuzzerEvent>().Unsubscribe(async (scope) => await SilenceBuzzer(scope));
// 2. 显式释放硬件资源(防止端口占用/死锁)
if (_deviceManager is IDisposable disposableDevice)
{
disposableDevice.Dispose();
}
(CommandTreeVM as IDisposable)?.Dispose();
(StepsManagerVM as IDisposable)?.Dispose();
(SingleStepEditVM as IDisposable)?.Dispose();
(LogAreaVM as IDisposable)?.Dispose();
(ParametersManagerVM as IDisposable)?.Dispose();
_globalInfo.ContextDic?.Remove(TestStatus);
_globalInfo.StepRunningDic?.Remove(TestStatus);
_globalInfo.ConfigDic?.Remove(TestStatus);
_globalInfo.ScopeDic?.Remove(TestStatus);
}
catch (Exception ex)
{
Logger.LoggerHelper.ErrorWithNotify(TestStatus, $"卸载机台 [{TestStatus}] 全局引用或资源失败: {ex.Message}");
}
finally
{
_scope?.Dispose();
}
}
#region
private async Task OnLoad()
{
if (!IsInitialized)
{
await _deviceManager.ConnectAllDevices();
IsInitialized = true;
}
}
private void OnRefresh()
{
// 双击:把自己的名字扔出去
_globalInfo.CurrentScope = TestStatus;
_eventAggregator.GetEvent<ExpandViewEvent>().Publish(TestStatus);
}
private void OnBackToProtocol()
{
// 返回:扔个空字符串出去
_eventAggregator.GetEvent<ExpandViewEvent>().Publish("");
}
#endregion
#region
public override void OnNavigatedTo(NavigationContext navigationContext)
{
base.OnNavigatedTo(navigationContext);
if (navigationContext.Parameters.ContainsKey("Name"))
{
TestStatus = navigationContext.Parameters.GetValue<string>("Name");
_globalInfo.ContextDic.Add(TestStatus, _scopedContext);
_globalInfo.StepRunningDic.Add(TestStatus, _stepRunning);
_globalInfo.ScopeDic.Add(TestStatus, _scope);
_globalInfo.ConfigDic.Add(TestStatus, _systemConfig);
if(_systemConfig.DefaultProgramFilePath != null&&File.Exists(_systemConfig.DefaultProgramFilePath))
{
var filePath = _systemConfig.DefaultProgramFilePath;
_systemConfig.CurrentACPFile= _systemConfig.DefaultProgramFilePath;
// 读取 JSON 文件
string json = File.ReadAllText(filePath);
// 反序列化为 ProgramVM
var program = Newtonsoft.Json.JsonConvert.DeserializeObject<ProgramVM>(json);
if (program == null)
{
LoggerHelper.WarnWithNotify(_globalInfo.CurrentScope, $"文件格式不正确或为空: {filePath}");
return;
}
// 💡 2. 严格赋值给快照锁定下的当前工位上下文,实现数据完全隔离
_scopedContext.Program.Parameters = program.Parameters;
_scopedContext.Program.StepCollection = program.StepCollection;
_scopedContext.Program.ErrorStepCollection = program.ErrorStepCollection;
_scopedContext.CurrentFilePath = filePath;
foreach (var item in _systemConfig.SharedParameterList)
{
var parameter = _scopedContext?.Program?.Parameters?.FirstOrDefault(x => x.Name == item.ParameterName);
if (parameter != null)
{
parameter.Value = item.Value;
}
}
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,67 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using UIShare.PubEvent;
using MainModule.ViewModels;
using UIShare.ViewModelBase;
namespace MainModule.ViewModels
{
public class MainViewModel : NavigateViewModelBase, IRegionMemberLifetime
{
#region
private bool IsInitialized = false;
private string _expandedCellName = string.Empty;
#endregion
#region
public bool KeepAlive => true; // 保持存活
public string ExpandedCellName
{
get => _expandedCellName;
set => SetProperty(ref _expandedCellName, value);
}
#endregion
#region
public ICommand LoadedCommand { get; set; }
#endregion
public MainViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
LoadedCommand = new DelegateCommand(OnLoaded);
_eventAggregator.GetEvent<ExpandViewEvent>().Subscribe(OnCellExpandRequested);
}
#region
private void OnLoaded()
{
if (IsInitialized) return;
for (int i = 1; i <= 9; i++)
{
var parameters = new NavigationParameters { { "Name", $"TestCell{i}" } };
_regionManager.RequestNavigate($"TestCell{i}", "ProtocolStartView", parameters);
}
IsInitialized = true;
}
// 不再物理搬迁视图,只需修改一个字符串属性 ExpandedCellName
// XAML 里每个单元的 Style.Triggers 会根据该值处理隐藏 / 跨越 3x3。
private void OnCellExpandRequested(string cellName)
{
ExpandedCellName = cellName ?? string.Empty;
}
#endregion
#region
public override bool IsNavigationTarget(NavigationContext navigationContext)
{
// 之前帮你改好的单例复用逻辑(防止重复 new 和初始化视图)
return true;
}
#endregion
}
}

View File

@@ -0,0 +1,107 @@
using Prism.Navigation.Regions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using UIShare.GlobalVariable;
using UIShare.ViewModelBase;
namespace MainModule.ViewModels
{
public class ProtocolStartViewModel : NavigateViewModelBase
{
#region
private string _testStatus;
private string _moduleColor;
private GlobalInfo _globalInfo;
#endregion
#region
public string TestStatus
{
get => _testStatus;
set => SetProperty(ref _testStatus, value);
}
#endregion
#region
public DelegateCommand StartProtocolCommand { get; }
#endregion
public ProtocolStartViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
_globalInfo = containerProvider.Resolve<GlobalInfo>();
StartProtocolCommand = new DelegateCommand(OnStart);
}
#region
private void OnStart()
{
// 只在当前格子所属的 Cell Region 内完成切换,不会影响其他格子位置
SwitchNavigate("AutomatedTestingView");
}
/// <summary>
/// 定位当前视图所在的 Cell RegionTestCell1..TestCell9
/// 在同一个 Region 内完成跳转。Region 位置由 XAML Grid 锁定,永远不会错位。
/// </summary>
public void SwitchNavigate(string viewName)
{
// 1. 反向查找:哪个 Cell Region 当前托着“我”这个 ProtocolStartView
for (int i = 1; i <= 9; i++)
{
var regionName = $"TestCell{i}";
if (!_regionManager.Regions.ContainsRegionWithName(regionName)) continue;
var region = _regionManager.Regions[regionName];
var myView = region.Views
.OfType<FrameworkElement>()
.FirstOrDefault(v => v.DataContext == this);
if (myView == null) continue;
// 2. 透传名称与颜色参数,使 AutomatedTestingViewModel 能正确初始化
var parameters = new NavigationParameters();
parameters.Add("Name", TestStatus);
_globalInfo.CurrentOpeningScope = TestStatus;
// 3. 在本格子 Region 内请求导航,导航成功后再移除旧的 ProtocolStartView 以释放资源
_regionManager.RequestNavigate(regionName, viewName, navResult =>
{
if (navResult.Success == true)
{
region.Remove(myView);
}
}, parameters);
_moduleManager.LoadModule("MonitorModule");
_moduleManager.LoadModule("SettingModule");
return;
}
}
#endregion
#region
public override bool IsNavigationTarget(NavigationContext navigationContext)
{
if (navigationContext.Parameters.ContainsKey("Name"))
return TestStatus == navigationContext.Parameters.GetValue<string>("Name");
return true;
}
public override void OnNavigatedTo(NavigationContext navigationContext)
{
base.OnNavigatedTo(navigationContext);
// 接收导航传参:名称与颜色
if (navigationContext.Parameters.ContainsKey("Name"))
TestStatus = navigationContext.Parameters.GetValue<string>("Name");
}
#endregion
}
}

View File

@@ -0,0 +1,133 @@
<UserControl x:Class="MainModule.Views.AutomatedTestingView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:MainModule.Views"
xmlns:b="clr-namespace:UIShare.Behaviors;assembly=UIShare"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:vs="clr-namespace:TestingModule.Views;assembly=TestingModule"
xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<converters:LessThanConverter x:Key="LessThanConverter" />
</UserControl.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Border >
<i:Interaction.Behaviors>
<b:MouseDoubleClickBehavior
Command="{Binding DataContext.RefreshCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</i:Interaction.Behaviors>
<!-- 给最外层 Grid 命名,方便子控件通过 Binding 找到它的 ActualWidth -->
<Grid x:Name="RootGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1.5*"/>
<!-- 给第二行取个名字 Row1方便触发器控制它 -->
<RowDefinition x:Name="Row1">
<RowDefinition.Style>
<Style TargetType="RowDefinition">
<!-- 正常情况下是占 1* 比例 -->
<Setter Property="Height" Value="1*" />
<Style.Triggers>
<!-- 当宽度小于 600 时,把第二行的高度死死锁在 0 像素 -->
<DataTrigger Binding="{Binding ActualWidth, ElementName=RootGrid, Converter={StaticResource LessThanConverter}, ConverterParameter=600}" Value="True">
<Setter Property="Height" Value="0" />
</DataTrigger>
</Style.Triggers>
</Style>
</RowDefinition.Style>
</RowDefinition>
</Grid.RowDefinitions>
<!-- ==================== 1. 左侧:命令树 ==================== -->
<vs:CommandTree DataContext="{Binding CommandTreeVM}"
Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Margin="5">
<vs:CommandTree.Style>
<Style TargetType="UserControl">
<Style.Triggers>
<!-- 核心逻辑:当最外层 Grid 宽度小于 600 时,隐藏自己 -->
<DataTrigger Binding="{Binding ActualWidth, ElementName=RootGrid, Converter={StaticResource LessThanConverter}, ConverterParameter=600}" Value="True">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</vs:CommandTree.Style>
</vs:CommandTree>
<!-- ==================== 2. 中上:步骤管理(核心显示) ==================== -->
<!-- 1. 注意:标签上不要再写任何 Grid.Row、Grid.Column、Grid.ColumnSpan 属性了! -->
<vs:StepsManager DataContext="{Binding StepsManagerVM}"
Margin="5">
<vs:StepsManager.Style>
<Style TargetType="UserControl">
<!-- 2. 【核心】把默认的网格位置写在这里(低优先级) -->
<Setter Property="Grid.Row" Value="0"/>
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Grid.RowSpan" Value="1"/>
<Setter Property="Grid.ColumnSpan" Value="2"/>
<Style.Triggers>
<!-- 3. 当宽度小于 600 时,触发器就会顺理成章地覆盖上面的默认值,变成全屏 -->
<DataTrigger Binding="{Binding ActualWidth, ElementName=RootGrid, Converter={StaticResource LessThanConverter}, ConverterParameter=600}" Value="True">
<Setter Property="Grid.Row" Value="0" />
<Setter Property="Grid.Column" Value="0" />
<!-- 附加属性在 Trigger 里的标准全称写法就是这样,只要上面没有本地值冲突,它就能完美生效 -->
<Setter Property="Grid.RowSpan" Value="2" />
<Setter Property="Grid.ColumnSpan" Value="4" />
</DataTrigger>
</Style.Triggers>
</Style>
</vs:StepsManager.Style>
</vs:StepsManager>
<!-- ==================== 3. 右上:单步编辑 ==================== -->
<vs:SingleStepEdit DataContext="{Binding SingleStepEditVM}"
Grid.Row="0" Grid.Column="3" Margin="5">
<vs:SingleStepEdit.Style>
<Style TargetType="UserControl">
<Style.Triggers>
<DataTrigger Binding="{Binding ActualWidth, ElementName=RootGrid, Converter={StaticResource LessThanConverter}, ConverterParameter=600}" Value="True">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</vs:SingleStepEdit.Style>
</vs:SingleStepEdit>
<!-- ==================== 4. 中下:日志显示 ==================== -->
<vs:LogArea DataContext="{Binding LogAreaVM}"
Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="5">
<!-- 这里不需要写 Visibility 触发器了,保持默认即可 -->
</vs:LogArea>
<!-- ==================== 5. 右下:参数管理 ==================== -->
<vs:ParametersManager DataContext="{Binding ParametersManagerVM}"
Grid.Row="1" Grid.Column="3" Margin="5">
<vs:ParametersManager.Style>
<Style TargetType="UserControl">
<Style.Triggers>
<DataTrigger Binding="{Binding ActualWidth, ElementName=RootGrid, Converter={StaticResource LessThanConverter}, ConverterParameter=600}" Value="True">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</vs:ParametersManager.Style>
</vs:ParametersManager>
</Grid>
</Border>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace MainModule.Views
{
/// <summary>
/// AutomatedTestingView.xaml 的交互逻辑
/// </summary>
public partial class AutomatedTestingView : UserControl
{
public AutomatedTestingView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,258 @@
<UserControl x:Class="MainModule.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:prism="http://prismlibrary.com/"
xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d" d:DesignHeight="1080" d:DesignWidth="1920">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<UserControl.Resources>
<converters:StringToVisibilityConverter x:Key="StringToVisibilityConverter"/>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 单元 1TestCell1默认 (0,0) -->
<ContentControl prism:RegionManager.RegionName="TestCell1" Margin="2">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Grid.Row" Value="0"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.RowSpan" Value="1"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ExpandedCellName, Converter={StaticResource StringToVisibilityConverter}}" Value="Visible">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding ExpandedCellName}" Value="TestCell1">
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="Grid.Row" Value="0"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.RowSpan" Value="3"/>
<Setter Property="Grid.ColumnSpan" Value="3"/>
<Setter Property="Panel.ZIndex" Value="99"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<!-- 单元 2TestCell2默认 (0,1) -->
<ContentControl prism:RegionManager.RegionName="TestCell2" Margin="2">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Grid.Row" Value="0"/>
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Grid.RowSpan" Value="1"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ExpandedCellName, Converter={StaticResource StringToVisibilityConverter}}" Value="Visible">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding ExpandedCellName}" Value="TestCell2">
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="Grid.Row" Value="0"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.RowSpan" Value="3"/>
<Setter Property="Grid.ColumnSpan" Value="3"/>
<Setter Property="Panel.ZIndex" Value="99"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<!-- 单元 3TestCell3默认 (0,2) -->
<ContentControl prism:RegionManager.RegionName="TestCell3" Margin="2">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Grid.Row" Value="0"/>
<Setter Property="Grid.Column" Value="2"/>
<Setter Property="Grid.RowSpan" Value="1"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ExpandedCellName, Converter={StaticResource StringToVisibilityConverter}}" Value="Visible">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding ExpandedCellName}" Value="TestCell3">
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="Grid.Row" Value="0"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.RowSpan" Value="3"/>
<Setter Property="Grid.ColumnSpan" Value="3"/>
<Setter Property="Panel.ZIndex" Value="99"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<!-- 单元 4TestCell4默认 (1,0) -->
<ContentControl prism:RegionManager.RegionName="TestCell4" Margin="2">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Grid.Row" Value="1"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.RowSpan" Value="1"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ExpandedCellName, Converter={StaticResource StringToVisibilityConverter}}" Value="Visible">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding ExpandedCellName}" Value="TestCell4">
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="Grid.Row" Value="0"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.RowSpan" Value="3"/>
<Setter Property="Grid.ColumnSpan" Value="3"/>
<Setter Property="Panel.ZIndex" Value="99"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<!-- 单元 5TestCell5默认 (1,1) -->
<ContentControl prism:RegionManager.RegionName="TestCell5" Margin="2">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Grid.Row" Value="1"/>
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Grid.RowSpan" Value="1"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ExpandedCellName, Converter={StaticResource StringToVisibilityConverter}}" Value="Visible">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding ExpandedCellName}" Value="TestCell5">
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="Grid.Row" Value="0"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.RowSpan" Value="3"/>
<Setter Property="Grid.ColumnSpan" Value="3"/>
<Setter Property="Panel.ZIndex" Value="99"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<!-- 单元 6TestCell6默认 (1,2) -->
<ContentControl prism:RegionManager.RegionName="TestCell6" Margin="2">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Grid.Row" Value="1"/>
<Setter Property="Grid.Column" Value="2"/>
<Setter Property="Grid.RowSpan" Value="1"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ExpandedCellName, Converter={StaticResource StringToVisibilityConverter}}" Value="Visible">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding ExpandedCellName}" Value="TestCell6">
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="Grid.Row" Value="0"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.RowSpan" Value="3"/>
<Setter Property="Grid.ColumnSpan" Value="3"/>
<Setter Property="Panel.ZIndex" Value="99"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<!-- 单元 7TestCell7默认 (2,0) -->
<ContentControl prism:RegionManager.RegionName="TestCell7" Margin="2">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Grid.Row" Value="2"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.RowSpan" Value="1"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ExpandedCellName, Converter={StaticResource StringToVisibilityConverter}}" Value="Visible">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding ExpandedCellName}" Value="TestCell7">
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="Grid.Row" Value="0"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.RowSpan" Value="3"/>
<Setter Property="Grid.ColumnSpan" Value="3"/>
<Setter Property="Panel.ZIndex" Value="99"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<!-- 单元 8TestCell8默认 (2,1) -->
<ContentControl prism:RegionManager.RegionName="TestCell8" Margin="2">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Grid.Row" Value="2"/>
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Grid.RowSpan" Value="1"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ExpandedCellName, Converter={StaticResource StringToVisibilityConverter}}" Value="Visible">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding ExpandedCellName}" Value="TestCell8">
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="Grid.Row" Value="0"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.RowSpan" Value="3"/>
<Setter Property="Grid.ColumnSpan" Value="3"/>
<Setter Property="Panel.ZIndex" Value="99"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<!-- 单元 9TestCell9默认 (2,2) -->
<!--<ContentControl prism:RegionManager.RegionName="TestCell9" Margin="2">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Grid.Row" Value="2"/>
<Setter Property="Grid.Column" Value="2"/>
<Setter Property="Grid.RowSpan" Value="1"/>
<Setter Property="Grid.ColumnSpan" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ExpandedCellName, Converter={StaticResource StringToVisibilityConverter}}" Value="Visible">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding ExpandedCellName}" Value="TestCell9">
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="Grid.Row" Value="0"/>
<Setter Property="Grid.Column" Value="0"/>
<Setter Property="Grid.RowSpan" Value="3"/>
<Setter Property="Grid.ColumnSpan" Value="3"/>
<Setter Property="Panel.ZIndex" Value="99"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>-->
</Grid>
</UserControl>

View File

@@ -0,0 +1,19 @@
using MainModule.ViewModels;
using System.Windows;
using System.Windows.Controls;
namespace MainModule.Views
{
/// <summary>
/// MainView.xaml 的交互逻辑
/// </summary>
public partial class MainView : UserControl
{
public MainView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,85 @@
<UserControl x:Class="MainModule.Views.ProtocolStartView"
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"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<Grid.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#F8FAFC" Offset="0"/>
<GradientStop Color="#E2E8F0" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
<materialDesign:Card Width="320" Height="380"
UniformCornerRadius="16"
Background="#FFFFFF"
materialDesign:ElevationAssist.Elevation="Dp4"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Grid Margin="30">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" HorizontalAlignment="Center" Margin="0,8,0,0">
<materialDesign:PackIcon Kind="Chip"
Width="24" Height="24"
HorizontalAlignment="Center"
Foreground="#94A3B8"/>
<TextBlock Text="{Binding TestStatus}"
FontSize="15"
FontWeight="SemiBold"
Foreground="#64748B"
Margin="0,6,0,0"
HorizontalAlignment="Center"/>
</StackPanel>
<Grid Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center">
<Ellipse Width="150" Height="150" Fill="#F1F5F9"/>
<Ellipse Width="130" Height="130" Fill="#E2E8F0"/>
<Button Command="{Binding StartProtocolCommand}"
Width="110" Height="110"
Style="{StaticResource MaterialDesignFloatingActionDarkButton}"
Background="#1E293B"
BorderBrush="#334155"
BorderThickness="2"
materialDesign:ElevationAssist.Elevation="Dp6"
materialDesign:RippleAssist.Feedback="#38BDF8">
<Button.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="55"/>
</Style>
</Button.Resources>
<materialDesign:PackIcon Kind="Play"
Width="48" Height="48"
Foreground="#38BDF8"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="4,0,0,0"/>
</Button>
</Grid>
<StackPanel Grid.Row="2" HorizontalAlignment="Center" Margin="0,0,0,8">
<TextBlock Text="READY"
FontSize="13"
FontWeight="Black"
Foreground="#10B981"
HorizontalAlignment="Center"/>
<TextBlock Text="点击按钮加载测试序列"
FontSize="12"
Foreground="#94A3B8"
Margin="0,4,0,0"
HorizontalAlignment="Center"/>
</StackPanel>
</Grid>
</materialDesign:Card>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace MainModule.Views
{
/// <summary>
/// ProtocolStartView.xaml 的交互逻辑
/// </summary>
public partial class ProtocolStartView : UserControl
{
public ProtocolStartView()
{
InitializeComponent();
}
}
}