diff --git a/TestingModule/ViewModels/StepsManagerViewModel.cs b/TestingModule/ViewModels/StepsManagerViewModel.cs index 3d3ae41..046c820 100644 --- a/TestingModule/ViewModels/StepsManagerViewModel.cs +++ b/TestingModule/ViewModels/StepsManagerViewModel.cs @@ -4,6 +4,8 @@ using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; using System.Linq; using System.Reflection; using System.Text; @@ -79,6 +81,10 @@ namespace TestingModule.ViewModels private readonly GlobalInfo _globalInfo; private List tmpCopyList = new List(); + // 追踪当前订阅的集合实例,用于在集合被整体替换时重新订阅 + private ObservableCollection _trackedStepCollection; + private ObservableCollection _trackedErrorStepCollection; + #endregion #region 命令 @@ -101,8 +107,8 @@ namespace TestingModule.ViewModels DeleteStepCommand = new DelegateCommand(DeleteStep); TabSelectionChangedCommand = new DelegateCommand(TabSelectionChanged); SelectionChangedCommand = new DelegateCommand(SelectionChanged); - Program.StepCollection.CollectionChanged += StepCollection_CollectionChanged; - Program.ErrorStepCollection.CollectionChanged += StepCollection_CollectionChanged; + SubscribeStepCollections(); + Program.PropertyChanged += Program_PropertyChanged; Admin = _globalInfo.IsAdmin; _eventAggregator.GetEvent().Subscribe(() => { @@ -206,13 +212,67 @@ namespace TestingModule.ViewModels #endregion #region 辅助方法 - private void StepCollection_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + + /// + /// 订阅当前 Program 的 StepCollection 和 ErrorStepCollection 的 CollectionChanged 事件。 + /// + private void SubscribeStepCollections() + { + _trackedStepCollection = Program.StepCollection; + _trackedErrorStepCollection = Program.ErrorStepCollection; + + if (_trackedStepCollection != null) + _trackedStepCollection.CollectionChanged += StepCollection_CollectionChanged; + if (_trackedErrorStepCollection != null) + _trackedErrorStepCollection.CollectionChanged += StepCollection_CollectionChanged; + } + + /// + /// 取消订阅当前追踪的集合事件。 + /// + private void UnsubscribeStepCollections() + { + if (_trackedStepCollection != null) + { + _trackedStepCollection.CollectionChanged -= StepCollection_CollectionChanged; + _trackedStepCollection = null; + } + if (_trackedErrorStepCollection != null) + { + _trackedErrorStepCollection.CollectionChanged -= StepCollection_CollectionChanged; + _trackedErrorStepCollection = null; + } + } + + /// + /// 当 Program 的 StepCollection / ErrorStepCollection 被整体替换时,自动重新订阅并刷新序号。 + /// + private void Program_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(ProgramVM.StepCollection) || + e.PropertyName == nameof(ProgramVM.ErrorStepCollection)) + { + UnsubscribeStepCollections(); + SubscribeStepCollections(); + + // 集合被整体替换后,对新集合重新编号 + Application.Current?.Dispatcher.BeginInvoke(new Action(() => + { + for (int i = 0; i < Program.StepCollection.Count; i++) + Program.StepCollection[i].Index = i + 1; + for (int i = 0; i < Program.ErrorStepCollection.Count; i++) + Program.ErrorStepCollection[i].Index = i + 1; + }), System.Windows.Threading.DispatcherPriority.Background); + } + } + + private void StepCollection_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { var collection = sender as ObservableCollection; // Add/Move/Remove 都会触发,这里判断具体情形 - if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add || - e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Move|| - e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove) + if (e.Action == NotifyCollectionChangedAction.Add || + e.Action == NotifyCollectionChangedAction.Move|| + e.Action == NotifyCollectionChangedAction.Remove) { // 如果需要等 UI 更新完再处理,可也用 Dispatcher 延迟一小段时间 Application.Current?.Dispatcher.BeginInvoke(new Action(() => @@ -238,31 +298,27 @@ namespace TestingModule.ViewModels { try { - // 1. 【核心修复】必须取消订阅 CollectionChanged 事件,否则 VM 永远无法被释放 + // 1. 取消订阅 CollectionChanged 事件(从追踪的实例上取消,而非可能已被替换的当前实例) + UnsubscribeStepCollections(); + + // 2. 取消订阅 Program.PropertyChanged if (Program != null) { - if (Program.StepCollection != null) - { - Program.StepCollection.CollectionChanged -= StepCollection_CollectionChanged; - } - if (Program.ErrorStepCollection != null) - { - Program.ErrorStepCollection.CollectionChanged -= StepCollection_CollectionChanged; - } + Program.PropertyChanged -= Program_PropertyChanged; } - // 2. 【核心修复】必须显式退订 Prism 全局事件(AlarmEvent) + // 3. 【核心修复】必须显式退订 Prism 全局事件(AlarmEvent) // 注意:因为订阅时使用的是匿名 Lambda,最安全稳妥的退订方式是把整个事件上的当前 VM 订阅者全部注销 _eventAggregator?.GetEvent()?.Unsubscribe(null); - // 3. 清空临时缓存集合与 UI 绑定列表,避免悬挂指针 + // 4. 清空临时缓存集合与 UI 绑定列表,避免悬挂指针 tmpCopyList?.Clear(); tmpCopyList = null!; SelectedItems?.Clear(); SelectedItems = null!; - // 4. 清除选中项状态引用 + // 5. 清除选中项状态引用 SelectedStep = null; if (_ScopedContext != null) {