子程序使用优化

This commit is contained in:
“hsc”
2026-08-07 13:46:11 +08:00
parent 57db6776b8
commit 80a2cbd80b
7 changed files with 306 additions and 15 deletions
@@ -16,6 +16,7 @@ using System.Xml;
using UIShare.GlobalVariable;
using UIShare.ViewModelBase;
using Prism.Events;
using Prism.Commands;
namespace TestingModule.ViewModels
{
@@ -94,6 +95,44 @@ namespace TestingModule.ViewModels
public ICommand DeleteStepCommand { get;set; }
public ICommand TabSelectionChangedCommand { get;set; }
public ICommand SelectionChangedCommand { get;set; }
public ICommand OpenSubProgramCommand { get; set; }
public ICommand GoBackCommand { get; set; }
#endregion
#region
/// <summary>
/// 当前显示的程序(绑定到 DataGrid 的 ItemsSource 根)。
/// 委托到 ScopedContext.CurrentProgram,保证 ShellViewModel 也能通过 ScopedContext 访问。
/// </summary>
public ProgramVM CurrentProgram
{
get => _ScopedContext.CurrentProgram;
set
{
if (_ScopedContext.CurrentProgram != value)
{
_ScopedContext.CurrentProgram = value;
RaisePropertyChanged();
}
}
}
public string BreadcrumbPath
{
get => _ScopedContext.BreadcrumbPath;
set
{
if (_ScopedContext.BreadcrumbPath != value)
{
_ScopedContext.BreadcrumbPath = value;
RaisePropertyChanged();
}
}
}
public bool CanGoBack => _ScopedContext.CanGoBack;
#endregion
public StepsManagerViewModel(IContainerProvider containerProvider, ScopedContext scopedContext, SystemConfig systemConfig, GlobalInfo globalInfo) : base(containerProvider)
@@ -108,9 +147,31 @@ namespace TestingModule.ViewModels
DeleteStepCommand = new DelegateCommand(DeleteStep);
TabSelectionChangedCommand = new DelegateCommand<string>(TabSelectionChanged);
SelectionChangedCommand = new DelegateCommand<object>(SelectionChanged);
OpenSubProgramCommand = new DelegateCommand(OpenSubProgram);
GoBackCommand = new DelegateCommand(GoBack);
SubscribeStepCollections();
Program.PropertyChanged += Program_PropertyChanged;
Admin = _globalInfo.IsAdmin;
// 初始化 CurrentProgram 为根程序
_ScopedContext.CurrentProgram = Program;
_ScopedContext.BreadcrumbPath = "主程序";
// 订阅 ScopedContext 属性变化,外部(ShellViewModel)修改时转发通知给 UI
_ScopedContext.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(ScopedContext.CurrentProgram))
RaisePropertyChanged(nameof(CurrentProgram));
else if (e.PropertyName == nameof(ScopedContext.BreadcrumbPath))
RaisePropertyChanged(nameof(BreadcrumbPath));
else if (e.PropertyName == nameof(ScopedContext.CanGoBack))
RaisePropertyChanged(nameof(CanGoBack));
};
// 订阅子程序导航事件(运行时由 StepRunning 发布)
_eventAggregator.GetEvent<SubProgramNavigateEvent>()
.Subscribe(OnSubProgramNavigate, ThreadOption.UIThread, false,
payload => payload.Scope == _systemConfig.Title);
}
private void SelectionChanged(object parameter)
@@ -208,6 +269,72 @@ namespace TestingModule.ViewModels
}
#endregion
#region
/// <summary>
/// 编辑模式:右键打开子程序
/// </summary>
private void OpenSubProgram()
{
if (SelectedStep == null || SelectedStep.StepType != "子程序" || SelectedStep.SubProgram == null)
return;
// 压栈:保存当前程序
_ScopedContext.SubProgramNavigationStack.Push(CurrentProgram);
_ScopedContext.UpdateCanGoBack();
// 更新面包屑
var stepName = SelectedStep.Name ?? "子程序";
BreadcrumbPath = $"{BreadcrumbPath} > {stepName}";
// 切换到子程序
CurrentProgram = SelectedStep.SubProgram;
RaisePropertyChanged(nameof(CanGoBack));
}
/// <summary>
/// 返回上一级程序
/// </summary>
private void GoBack()
{
if (_ScopedContext.SubProgramNavigationStack.Count == 0)
return;
// 出栈:恢复上一级程序
var parentProgram = _ScopedContext.SubProgramNavigationStack.Pop();
_ScopedContext.UpdateCanGoBack();
CurrentProgram = parentProgram;
// 更新面包屑:去掉最后一段
var parts = BreadcrumbPath.Split(" > ");
if (parts.Length > 1)
BreadcrumbPath = string.Join(" > ", parts.Take(parts.Length - 1));
else
BreadcrumbPath = "主程序";
RaisePropertyChanged(nameof(CanGoBack));
}
/// <summary>
/// 运行时:处理 StepRunning 发布的子程序导航事件
/// </summary>
private void OnSubProgramNavigate(SubProgramNavigatePayload payload)
{
if (payload.Action == NavigateAction.Enter && payload.SubProgram != null)
{
_ScopedContext.SubProgramNavigationStack.Push(CurrentProgram);
_ScopedContext.UpdateCanGoBack();
CurrentProgram = payload.SubProgram;
var stepName = payload.StepName ?? "子程序";
BreadcrumbPath = $"{BreadcrumbPath} > {stepName}";
RaisePropertyChanged(nameof(CanGoBack));
}
else if (payload.Action == NavigateAction.Exit)
{
GoBack();
}
}
#endregion
#region
/// <summary>
@@ -304,9 +431,9 @@ namespace TestingModule.ViewModels
Program.PropertyChanged -= Program_PropertyChanged;
}
// 3. 【核心修复】必须显式退订 Prism 全局事件AlarmEvent
// 注意:因为订阅时使用的是匿名 Lambda,最安全稳妥的退订方式是把整个事件上的当前 VM 订阅者全部注销
// 3. 【核心修复】必须显式退订 Prism 全局事件
_eventAggregator?.GetEvent<AlarmEvent>()?.Unsubscribe(null);
_eventAggregator?.GetEvent<SubProgramNavigateEvent>()?.Unsubscribe(null);
// 4. 清空临时缓存集合与 UI 绑定列表,避免悬挂指针
tmpCopyList?.Clear();
+24 -11
View File
@@ -8,11 +8,26 @@
xmlns:behaviors="clr-namespace:UIShare.Behaviors;assembly=UIShare"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:prism="http://prismlibrary.com/"
xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</UserControl.Resources>
<Grid>
<GroupBox Header="{Binding Title}">
<DockPanel>
<!-- 面包屑导航栏 -->
<Border DockPanel.Dock="Top" Background="#F0F0F0" Padding="6,3" Visibility="{Binding CanGoBack, Converter={StaticResource BooleanToVisibilityConverter}}">
<DockPanel>
<Button DockPanel.Dock="Right" Content="↩ 返回上一级"
Command="{Binding GoBackCommand}"
Style="{StaticResource MaterialDesignFlatButton}"
FontSize="12" Padding="8,2" Margin="8,0,0,0"/>
<TextBlock Text="{Binding BreadcrumbPath}" VerticalAlignment="Center" FontSize="12" Foreground="#555"/>
</DockPanel>
</Border>
<TabControl SelectedIndex="{Binding SelectedTabIndex, Mode=TwoWay}">
<i:Interaction.Behaviors>
<behaviors:TabControlSelectionChangedBehavior Command="{Binding TabSelectionChangedCommand}"
@@ -29,7 +44,7 @@
Background="Transparent"
CanUserAddRows="False"
CanUserSortColumns="False"
ItemsSource="{Binding Program.StepCollection}"
ItemsSource="{Binding CurrentProgram.StepCollection}"
SelectedItem="{Binding SelectedStep, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
SelectionMode="Extended"
SelectionUnit="FullRow">
@@ -120,6 +135,9 @@
<MenuItem Header="删除"
Foreground="Red"
Command="{Binding DeleteStepCommand}" />
<Separator/>
<MenuItem Header="打开子程序"
Command="{Binding OpenSubProgramCommand}" />
</ContextMenu>
</DataGrid.ContextMenu>
@@ -135,7 +153,7 @@
Background="Transparent"
CanUserAddRows="False"
CanUserSortColumns="False"
ItemsSource="{Binding Program.ErrorStepCollection}"
ItemsSource="{Binding CurrentProgram.ErrorStepCollection}"
SelectedItem="{Binding SelectedStep, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
SelectionMode="Extended"
SelectionUnit="FullRow">
@@ -226,21 +244,16 @@
<MenuItem Header="删除"
Foreground="Red"
Command="{Binding DeleteStepCommand}" />
<Separator/>
<MenuItem Header="打开子程序"
Command="{Binding OpenSubProgramCommand}" />
</ContextMenu>
</DataGrid.ContextMenu>
<!-- 键盘 删除键绑定 Delete 命令 -->
<!--<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewKeyDown">
<i:InvokeCommandAction Command="{Binding DeleteStepCommand}"
CommandParameter="{Binding SelectedStep}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>-->
</DataGrid>
</TabItem>
</TabControl>
</DockPanel>
</GroupBox>
</Grid>