From 5cac253cb8e15d218635207a028180ad930fc707 Mon Sep 17 00:00:00 2001 From: hsc Date: Thu, 11 Jun 2026 15:45:29 +0800 Subject: [PATCH] =?UTF-8?q?DIspose=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ADP/ViewModels/ShellViewModel.cs | 4 -- .../ViewModels/AutomatedTestingViewModel.cs | 16 ++++++- SettingModule/ViewModels/SettingViewModel.cs | 15 +++++- .../ViewModels/CommandTreeViewModel.cs | 24 +++++++++- TestingModule/ViewModels/LogAreaViewModel.cs | 31 ++++++++++-- .../ViewModels/ParametersManagerViewModel.cs | 22 ++++++++- .../ViewModels/SingleStepEditViewModel.cs | 22 ++++++++- .../ViewModels/StepsManagerViewModel.cs | 47 ++++++++++++++++++- 8 files changed, 164 insertions(+), 17 deletions(-) diff --git a/ADP/ViewModels/ShellViewModel.cs b/ADP/ViewModels/ShellViewModel.cs index 7792b69..1d1e396 100644 --- a/ADP/ViewModels/ShellViewModel.cs +++ b/ADP/ViewModels/ShellViewModel.cs @@ -678,10 +678,6 @@ namespace ADP.ViewModels } } - _globalInfo.ContextDic.Remove(targetRegionName); - _globalInfo.StepRunningDic.Remove(targetRegionName); - _globalInfo.ScopeDic.Remove(targetRegionName); - var parameters = new NavigationParameters { { "Name", targetRegionName } }; _regionManager.RequestNavigate(targetRegionName, "ProtocolStartView", parameters); _eventAggregator.GetEvent().Publish(""); diff --git a/MainModule/ViewModels/AutomatedTestingViewModel.cs b/MainModule/ViewModels/AutomatedTestingViewModel.cs index aa12dd7..b444a70 100644 --- a/MainModule/ViewModels/AutomatedTestingViewModel.cs +++ b/MainModule/ViewModels/AutomatedTestingViewModel.cs @@ -79,20 +79,34 @@ namespace MainModule.ViewModels public void Dispose() { + // 1. 如果 TestStatus 为空,说明还没走到 OnNavigatedTo 赋值,直接释放 scope 即可 if (string.IsNullOrEmpty(TestStatus)) { _scope?.Dispose(); return; } + try { + // 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}] 全局引用失败: {ex.Message}"); + Logger.LoggerHelper.ErrorWithNotify($"卸载机台 [{TestStatus}] 全局引用或资源失败: {ex.Message}"); } finally { diff --git a/SettingModule/ViewModels/SettingViewModel.cs b/SettingModule/ViewModels/SettingViewModel.cs index 2b30092..c980c24 100644 --- a/SettingModule/ViewModels/SettingViewModel.cs +++ b/SettingModule/ViewModels/SettingViewModel.cs @@ -84,7 +84,19 @@ namespace SettingModule.ViewModels public void Dispose() { - _scope?.Dispose(); + try + { + if (DeviceList != null) + { + DeviceList = null!; + } + SelectedDevice = null; + _scopedContext = null!; + } + catch (Exception ex) + { + Logger.LoggerHelper.ErrorWithNotify($"释放配置管理组件(SettingViewModel)资源失败: {ex.Message}"); + } } #region 命令 @@ -191,5 +203,6 @@ namespace SettingModule.ViewModels } } #endregion + } } \ No newline at end of file diff --git a/TestingModule/ViewModels/CommandTreeViewModel.cs b/TestingModule/ViewModels/CommandTreeViewModel.cs index e6ecf03..b39b7e5 100644 --- a/TestingModule/ViewModels/CommandTreeViewModel.cs +++ b/TestingModule/ViewModels/CommandTreeViewModel.cs @@ -24,7 +24,7 @@ using NLog; namespace TestingModule.ViewModels { - public class CommandTreeViewModel:NavigateViewModelBase + public class CommandTreeViewModel:NavigateViewModelBase,IDisposable { #region 属性 private string _SearchText; @@ -108,7 +108,25 @@ namespace TestingModule.ViewModels ReloadCommand = new DelegateCommand(Reload); } - + public void Dispose() + { + try + { + _ScopedContext = null!; + InstructionTree?.Clear(); + SubPrograms?.Clear(); + TreeNodeMap?.Clear(); + XmlDocumentCache?.Clear(); + InstructionTree = null!; + SubPrograms = null!; + TreeNodeMap = null!; + XmlDocumentCache = null!; + } + catch (Exception ex) + { + LoggerHelper.Error($"清理指令树缓存失败: {ex.Message}"); + } + } #region 委托命令 private void Reload() { @@ -584,6 +602,8 @@ namespace TestingModule.ViewModels } } + + #endregion } diff --git a/TestingModule/ViewModels/LogAreaViewModel.cs b/TestingModule/ViewModels/LogAreaViewModel.cs index 4f867f9..49af0fb 100644 --- a/TestingModule/ViewModels/LogAreaViewModel.cs +++ b/TestingModule/ViewModels/LogAreaViewModel.cs @@ -11,12 +11,11 @@ using UIShare.ViewModelBase; namespace TestingModule.ViewModels { - public class LogAreaViewModel : NavigateViewModelBase + public class LogAreaViewModel : NavigateViewModelBase, IDisposable { // 日志集合 private ObservableCollection _logs = new(); - public ObservableCollection Logs { get => _logs; @@ -27,18 +26,42 @@ namespace TestingModule.ViewModels public LogAreaViewModel(IContainerProvider containerProvider) : base(containerProvider) { ClearLogCommand = new DelegateCommand(ClearLog); + + // 2. 保持原有逻辑,但请确保在 Dispose 中对其进行清理 LoggerHelper.Progress = new System.Progress<(string message, string color, int depth)>( log => { + // 增加防御性代码:防止进入销毁流程时异步回调引发空引用异常 + if (Logs == null) return; + var brush = (Brush)new BrushConverter().ConvertFromString(log.color); Logs.Add(new LogItem(log.message, brush, log.depth)); }); } - private void ClearLog() { - Logs.Clear(); + Logs?.Clear(); + } + + /// + /// 完善后的资源释放方法 + /// + public void Dispose() + { + try + { + LoggerHelper.Progress = null!; + if (Logs != null) + { + Logs.Clear(); + Logs = null!; + } + } + catch (Exception ex) + { + Logger.LoggerHelper.ErrorWithNotify($"释放日志组件(LogAreaViewModel)资源失败: {ex.Message}"); + } } } diff --git a/TestingModule/ViewModels/ParametersManagerViewModel.cs b/TestingModule/ViewModels/ParametersManagerViewModel.cs index 4c3adad..dbb14ba 100644 --- a/TestingModule/ViewModels/ParametersManagerViewModel.cs +++ b/TestingModule/ViewModels/ParametersManagerViewModel.cs @@ -12,7 +12,7 @@ using Prism.Events; namespace TestingModule.ViewModels { - public class ParametersManagerViewModel:NavigateViewModelBase + public class ParametersManagerViewModel:NavigateViewModelBase, IDisposable { #region 属性 //private ObservableCollection _DeviceList; @@ -160,6 +160,24 @@ namespace TestingModule.ViewModels #endregion - + public void Dispose() + { + try + { + DeviceInfoVM?.Clear(); + DeviceInfoVM = null!; + SelectedParameter = null!; + SelectedDevice = null!; + if (_ScopedContext != null) + { + _ScopedContext.SelectedParameter = null; + _ScopedContext = null!; + } + } + catch (Exception ex) + { + Logger.LoggerHelper.Error($"释放参数管理组件(ParametersManagerViewModel)资源失败: {ex.Message}"); + } + } } } diff --git a/TestingModule/ViewModels/SingleStepEditViewModel.cs b/TestingModule/ViewModels/SingleStepEditViewModel.cs index 7b8d835..afdaa7b 100644 --- a/TestingModule/ViewModels/SingleStepEditViewModel.cs +++ b/TestingModule/ViewModels/SingleStepEditViewModel.cs @@ -18,7 +18,7 @@ using UIShare.GlobalVariable; namespace TestingModule.ViewModels { - public class SingleStepEditViewModel:NavigateViewModelBase + public class SingleStepEditViewModel:NavigateViewModelBase,IDisposable { #region 属性 private Guid _ID; @@ -196,6 +196,26 @@ namespace TestingModule.ViewModels SelectedStep = new StepVM(_ScopedContext.SelectedStep); } #endregion + public void Dispose() + { + try + { + // 1. 【核心修复】必须严格退订所有全局 Prism 事件 + _eventAggregator?.GetEvent()?.Unsubscribe(EditSingleStep); + _eventAggregator?.GetEvent()?.Unsubscribe(DisposeSelectedStep); + _eventAggregator?.GetEvent()?.Unsubscribe(ParamsChanged); + + // 2. 清空当前正在编辑的步骤副本,断开前台绑定,防止 UI 视图树悬挂 + SelectedStep = null!; + + // 3. 断开对工位隔离上下文的强引用 + _ScopedContext = null!; + } + catch (Exception ex) + { + LoggerHelper.Error($"释放单步编辑组件(SingleStepEditViewModel)资源失败: {ex.Message}"); + } + } } } diff --git a/TestingModule/ViewModels/StepsManagerViewModel.cs b/TestingModule/ViewModels/StepsManagerViewModel.cs index 8c7e3dd..3d3ae41 100644 --- a/TestingModule/ViewModels/StepsManagerViewModel.cs +++ b/TestingModule/ViewModels/StepsManagerViewModel.cs @@ -17,7 +17,7 @@ using Prism.Events; namespace TestingModule.ViewModels { - public class StepsManagerViewModel:NavigateViewModelBase + public class StepsManagerViewModel:NavigateViewModelBase, IDisposable { #region 属性 private string _Title; @@ -231,7 +231,50 @@ namespace TestingModule.ViewModels } } + + #endregion - + public void Dispose() + { + try + { + // 1. 【核心修复】必须取消订阅 CollectionChanged 事件,否则 VM 永远无法被释放 + if (Program != null) + { + if (Program.StepCollection != null) + { + Program.StepCollection.CollectionChanged -= StepCollection_CollectionChanged; + } + if (Program.ErrorStepCollection != null) + { + Program.ErrorStepCollection.CollectionChanged -= StepCollection_CollectionChanged; + } + } + + // 2. 【核心修复】必须显式退订 Prism 全局事件(AlarmEvent) + // 注意:因为订阅时使用的是匿名 Lambda,最安全稳妥的退订方式是把整个事件上的当前 VM 订阅者全部注销 + _eventAggregator?.GetEvent()?.Unsubscribe(null); + + // 3. 清空临时缓存集合与 UI 绑定列表,避免悬挂指针 + tmpCopyList?.Clear(); + tmpCopyList = null!; + + SelectedItems?.Clear(); + SelectedItems = null!; + + // 4. 清除选中项状态引用 + SelectedStep = null; + if (_ScopedContext != null) + { + _ScopedContext.SelectedStep = null; + _ScopedContext = null!; // 断开上下文引用 + } + } + catch (Exception ex) + { + Logger.LoggerHelper.Error($"释放步骤管理组件(StepsManagerViewModel)资源失败: {ex.Message}"); + } + } + } }