From 99ac3f2b8dc777e21fef17eae3f52754f1c867ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chsc=E2=80=9D?= <“huangsucan@kaiyili-lab.com”> Date: Fri, 7 Aug 2026 14:43:57 +0800 Subject: [PATCH] =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=A8=8B=E5=BA=8F=E5=AD=90?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACP/ViewModels/ShellViewModel.cs | 17 ++- ACP/Views/ShellView.xaml | 10 +- .../ViewModels/CommandTreeViewModel.cs | 32 +++- .../ViewModels/StepsManagerViewModel.cs | 142 +++++++++--------- TestingModule/Views/StepsManager.xaml | 40 +++-- .../GlobalVariable/ProgramNavigationState.cs | 89 +++++++++++ UIShare/GlobalVariable/ScopedContext.cs | 64 ++------ UIShare/GlobalVariable/StepRunning.cs | 13 +- UIShare/PubEvent/SubProgramNavigateEvent.cs | 3 + 9 files changed, 261 insertions(+), 149 deletions(-) create mode 100644 UIShare/GlobalVariable/ProgramNavigationState.cs diff --git a/ACP/ViewModels/ShellViewModel.cs b/ACP/ViewModels/ShellViewModel.cs index af76648..a49621e 100644 --- a/ACP/ViewModels/ShellViewModel.cs +++ b/ACP/ViewModels/ShellViewModel.cs @@ -46,11 +46,18 @@ namespace ACP.ViewModels #region 子程序导航 - /// - /// 当前台架是否可以返回上一级 - /// - public bool CanGoBack => CurrentContext?.CanGoBack ?? false; + public bool CanGoBack => + (CurrentContext?.MainNav.CanGoBack ?? false) || + (CurrentContext?.ErrorNav.CanGoBack ?? false); + private void OnGoBack() + { + var ctx = CurrentContext; + if (ctx == null) return; + // 工具栏按钮同时重置两个 Tab 的导航 + ctx.MainNav.Reset(ctx.Program, "主程序"); + ctx.ErrorNav.Reset(ctx.Program, "错误程序"); + } #endregion @@ -167,6 +174,7 @@ namespace ACP.ViewModels public ICommand GetFileStringCommand { get; set; } public ICommand SilenceBuzzerCommand { get; set; } public ICommand SettingChannelCommand { get; set; } + public ICommand GoBackCommand { get; set; } #endregion public ShellViewModel(IContainerProvider containerProvider) @@ -218,6 +226,7 @@ namespace ACP.ViewModels SilenceBuzzerCommand = new AsyncDelegateCommand(SilenceBuzzer); SettingChannelCommand = new AsyncDelegateCommand(SilenceBuzzer); SettingChannelCommand = new DelegateCommand(SettingChannel); + GoBackCommand = new DelegateCommand(OnGoBack); _globalInfo.ContextDic.Add("default", new ScopedContext()); _eventAggregator.GetEvent().Subscribe(() => diff --git a/ACP/Views/ShellView.xaml b/ACP/Views/ShellView.xaml index 39a0696..3b66b38 100644 --- a/ACP/Views/ShellView.xaml +++ b/ACP/Views/ShellView.xaml @@ -245,7 +245,15 @@ Command="{Binding GetFileStringCommand}"> - + + + + + + = 0; i--) + if (_ScopedContext.SelectedStepList == "主程序") { - if (Program.StepCollection[i].StepType == "循环开始") + for (int i = Program.StepCollection.Count - 1; i >= 0; i--) { - bool isMatched = Program.StepCollection.Any(s => s.StepType == "循环结束" && s.LoopStartStepId == Program.StepCollection[i].ID); - - if (!isMatched) + if (Program.StepCollection[i].StepType == "循环开始") { - lastUnmatchedLoopStart = Program.StepCollection[i]; - break; + bool isMatched = Program.StepCollection.Any(s => s.StepType == "循环结束" && s.LoopStartStepId == Program.StepCollection[i].ID); + + if (!isMatched) + { + lastUnmatchedLoopStart = Program.StepCollection[i]; + break; + } } } } + else + { + for (int i = Program.ErrorStepCollection.Count - 1; i >= 0; i--) + { + if (Program.ErrorStepCollection[i].StepType == "循环开始") + { + bool isMatched = Program.ErrorStepCollection.Any(s => s.StepType == "循环结束" && s.LoopStartStepId == Program.StepCollection[i].ID); + if (!isMatched) + { + lastUnmatchedLoopStart = Program.ErrorStepCollection[i]; + break; + } + } + } + } var newStep = new StepVM { Name = "循环结束", diff --git a/TestingModule/ViewModels/StepsManagerViewModel.cs b/TestingModule/ViewModels/StepsManagerViewModel.cs index 4b048ff..d2853c4 100644 --- a/TestingModule/ViewModels/StepsManagerViewModel.cs +++ b/TestingModule/ViewModels/StepsManagerViewModel.cs @@ -99,39 +99,24 @@ namespace TestingModule.ViewModels public ICommand GoBackCommand { get; set; } #endregion - #region 子程序导航 + #region 子程序导航(主程序 / 错误程序各自独立) + + // --- 主程序 Tab 导航属性 --- + public ProgramVM MainCurrentProgram => _ScopedContext.MainNav.CurrentProgram; + public string MainBreadcrumbPath => _ScopedContext.MainNav.BreadcrumbPath; + public bool MainCanGoBack => _ScopedContext.MainNav.CanGoBack; + public ObservableCollection MainDisplaySteps => _ScopedContext.MainNav.DisplaySteps; + + // --- 错误程序 Tab 导航属性 --- + public ProgramVM ErrorCurrentProgram => _ScopedContext.ErrorNav.CurrentProgram; + public string ErrorBreadcrumbPath => _ScopedContext.ErrorNav.BreadcrumbPath; + public bool ErrorCanGoBack => _ScopedContext.ErrorNav.CanGoBack; + public ObservableCollection ErrorDisplaySteps => _ScopedContext.ErrorNav.DisplaySteps; /// - /// 当前显示的程序(绑定到 DataGrid 的 ItemsSource 根)。 - /// 委托到 ScopedContext.CurrentProgram,保证 ShellViewModel 也能通过 ScopedContext 访问。 + /// 当前激活的导航状态(根据选中的 Tab 决定) /// - 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; + private ProgramNavigationState ActiveNav => _ScopedContext.ActiveNav; #endregion @@ -153,19 +138,36 @@ namespace TestingModule.ViewModels Program.PropertyChanged += Program_PropertyChanged; Admin = _globalInfo.IsAdmin; - // 初始化 CurrentProgram 为根程序 - _ScopedContext.CurrentProgram = Program; - _ScopedContext.BreadcrumbPath = "主程序"; + // 初始化两套导航状态 + _ScopedContext.MainNav.Initialize(Program, "主程序"); + _ScopedContext.ErrorNav.Initialize(Program, "错误程序"); - // 订阅 ScopedContext 属性变化,外部(ShellViewModel)修改时转发通知给 UI - _ScopedContext.PropertyChanged += (s, e) => + // 订阅主程序导航状态变化 → 转发给 UI + _ScopedContext.MainNav.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)); + if (e.PropertyName == nameof(ProgramNavigationState.CurrentProgram)) + { + RaisePropertyChanged(nameof(MainCurrentProgram)); + RaisePropertyChanged(nameof(MainDisplaySteps)); + } + else if (e.PropertyName == nameof(ProgramNavigationState.BreadcrumbPath)) + RaisePropertyChanged(nameof(MainBreadcrumbPath)); + else if (e.PropertyName == nameof(ProgramNavigationState.CanGoBack)) + RaisePropertyChanged(nameof(MainCanGoBack)); + }; + + // 订阅错误程序导航状态变化 → 转发给 UI + _ScopedContext.ErrorNav.PropertyChanged += (s, e) => + { + if (e.PropertyName == nameof(ProgramNavigationState.CurrentProgram)) + { + RaisePropertyChanged(nameof(ErrorCurrentProgram)); + RaisePropertyChanged(nameof(ErrorDisplaySteps)); + } + else if (e.PropertyName == nameof(ProgramNavigationState.BreadcrumbPath)) + RaisePropertyChanged(nameof(ErrorBreadcrumbPath)); + else if (e.PropertyName == nameof(ProgramNavigationState.CanGoBack)) + RaisePropertyChanged(nameof(ErrorCanGoBack)); }; // 订阅子程序导航事件(运行时由 StepRunning 发布) @@ -272,45 +274,38 @@ namespace TestingModule.ViewModels #region 子程序导航方法 /// - /// 编辑模式:右键打开子程序 + /// 编辑模式:右键打开子程序(操作当前激活的 Tab 的导航状态) /// private void OpenSubProgram() { if (SelectedStep == null || SelectedStep.StepType != "子程序" || SelectedStep.SubProgram == null) return; - // 压栈:保存当前程序 - _ScopedContext.SubProgramNavigationStack.Push(CurrentProgram); - _ScopedContext.UpdateCanGoBack(); - // 更新面包屑 + var nav = ActiveNav; + nav.NavigationStack.Push(nav.CurrentProgram); + nav.UpdateCanGoBack(); var stepName = SelectedStep.Name ?? "子程序"; - BreadcrumbPath = $"{BreadcrumbPath} > {stepName}"; - // 切换到子程序 - CurrentProgram = SelectedStep.SubProgram; - RaisePropertyChanged(nameof(CanGoBack)); + nav.BreadcrumbPath = $"{nav.BreadcrumbPath} > {stepName}"; + nav.CurrentProgram = SelectedStep.SubProgram; } /// - /// 返回上一级程序 + /// 返回上一级程序(操作当前激活的 Tab 的导航状态) /// private void GoBack() { - if (_ScopedContext.SubProgramNavigationStack.Count == 0) + var nav = ActiveNav; + if (nav.NavigationStack.Count == 0) return; - // 出栈:恢复上一级程序 - var parentProgram = _ScopedContext.SubProgramNavigationStack.Pop(); - _ScopedContext.UpdateCanGoBack(); - CurrentProgram = parentProgram; + var parentProgram = nav.NavigationStack.Pop(); + nav.UpdateCanGoBack(); + nav.CurrentProgram = parentProgram; - // 更新面包屑:去掉最后一段 - var parts = BreadcrumbPath.Split(" > "); - if (parts.Length > 1) - BreadcrumbPath = string.Join(" > ", parts.Take(parts.Length - 1)); - else - BreadcrumbPath = "主程序"; - - RaisePropertyChanged(nameof(CanGoBack)); + var parts = nav.BreadcrumbPath.Split(" > "); + nav.BreadcrumbPath = parts.Length > 1 + ? string.Join(" > ", parts.Take(parts.Length - 1)) + : nav == _ScopedContext.MainNav ? "主程序" : "错误程序"; } /// @@ -318,18 +313,25 @@ namespace TestingModule.ViewModels /// private void OnSubProgramNavigate(SubProgramNavigatePayload payload) { + var nav = payload.IsErrorProgram ? _ScopedContext.ErrorNav : _ScopedContext.MainNav; if (payload.Action == NavigateAction.Enter && payload.SubProgram != null) { - _ScopedContext.SubProgramNavigationStack.Push(CurrentProgram); - _ScopedContext.UpdateCanGoBack(); - CurrentProgram = payload.SubProgram; + nav.NavigationStack.Push(nav.CurrentProgram); + nav.UpdateCanGoBack(); + nav.CurrentProgram = payload.SubProgram; var stepName = payload.StepName ?? "子程序"; - BreadcrumbPath = $"{BreadcrumbPath} > {stepName}"; - RaisePropertyChanged(nameof(CanGoBack)); + nav.BreadcrumbPath = $"{nav.BreadcrumbPath} > {stepName}"; } else if (payload.Action == NavigateAction.Exit) { - GoBack(); + if (nav.NavigationStack.Count == 0) return; + var parentProgram = nav.NavigationStack.Pop(); + nav.UpdateCanGoBack(); + nav.CurrentProgram = parentProgram; + var parts = nav.BreadcrumbPath.Split(" > "); + nav.BreadcrumbPath = parts.Length > 1 + ? string.Join(" > ", parts.Take(parts.Length - 1)) + : nav == _ScopedContext.MainNav ? "主程序" : "错误程序"; } } diff --git a/TestingModule/Views/StepsManager.xaml b/TestingModule/Views/StepsManager.xaml index dcb8b75..5672702 100644 --- a/TestingModule/Views/StepsManager.xaml +++ b/TestingModule/Views/StepsManager.xaml @@ -17,23 +17,23 @@ - - - - -