From c7f8a00635173916e754b964cca252c39000b0e7 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 22 May 2026 07:28:48 -0400 Subject: [PATCH] fix(client-ui): resolve Medium code-review finding (Client.UI-008) Implement IDisposable on MainWindowViewModel to detach ConnectionStateChanged, call Teardown() on the subscription/alarm VMs, and dispose _service so the OPC UA session and SDK resources are released. Call Dispose() from MainWindow.OnClosing alongside the existing SaveSettings() call. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../ViewModels/MainWindowViewModel.cs | 17 ++++++++++++++++- .../Views/MainWindow.axaml.cs | 3 +++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/MainWindowViewModel.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/MainWindowViewModel.cs index f73b39c..d1766a5 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/MainWindowViewModel.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/ViewModels/MainWindowViewModel.cs @@ -10,7 +10,7 @@ namespace ZB.MOM.WW.OtOpcUa.Client.UI.ViewModels; /// /// Main window ViewModel coordinating all panels. /// -public partial class MainWindowViewModel : ObservableObject +public partial class MainWindowViewModel : ObservableObject, IDisposable { private readonly IUiDispatcher _dispatcher; private readonly IOpcUaClientServiceFactory _factory; @@ -411,6 +411,21 @@ public partial class MainWindowViewModel : ObservableObject }); } + /// + /// Detaches the connection-state handler and disposes the OPC UA client service, releasing + /// the session, certificate validator, and any background reconnect resources. + /// + public void Dispose() + { + if (_service != null) + { + _service.ConnectionStateChanged -= OnConnectionStateChanged; + Subscriptions?.Teardown(); + Alarms?.Teardown(); + _service.Dispose(); + } + } + private static string[]? ParseFailoverUrls(string? csv) { if (string.IsNullOrWhiteSpace(csv)) diff --git a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Views/MainWindow.axaml.cs b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Views/MainWindow.axaml.cs index 108f908..7178ab5 100644 --- a/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Views/MainWindow.axaml.cs +++ b/src/Client/ZB.MOM.WW.OtOpcUa.Client.UI/Views/MainWindow.axaml.cs @@ -140,7 +140,10 @@ public partial class MainWindow : Window protected override void OnClosing(WindowClosingEventArgs e) { if (DataContext is MainWindowViewModel vm) + { vm.SaveSettings(); + vm.Dispose(); + } base.OnClosing(e); } }