From f6457b190965e2e5f0c1c8200d0c92d5941c38ef Mon Sep 17 00:00:00 2001 From: Markus Ewald Date: Thu, 19 Jun 2025 14:02:02 +0200 Subject: [PATCH] View model disposal is now done via a lambda method with captured variables rather than by tagging the view --- ReadMe.md | 5 ++- Source/Views/ViewControl.cs | 7 ++++ Source/WindowManager.cs | 66 +++++++++++++++++-------------------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 34abdac..7cd8cce 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -57,9 +57,8 @@ static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - using(var windowManager = new WindowManager()) { - Application.Run(windowManager.OpenRoot()); - } + var windowManager = new WindowManager(); + Application.Run(windowManager.OpenRoot()); } ``` diff --git a/Source/Views/ViewControl.cs b/Source/Views/ViewControl.cs index b5b72aa..329f673 100644 --- a/Source/Views/ViewControl.cs +++ b/Source/Views/ViewControl.cs @@ -21,6 +21,10 @@ using System; using System.ComponentModel; using System.Windows.Forms; +#if NET6_0_OR_GREATER +using System.Runtime.Versioning; +#endif + using Nuclex.Support; namespace Nuclex.Windows.Forms.Views { @@ -28,6 +32,9 @@ namespace Nuclex.Windows.Forms.Views { /// /// Base class for MVVM user controls that act as views connected to a view model /// +#if NET6_0_OR_GREATER + [SupportedOSPlatform("windows")] +#endif public class ViewControl : UserControl, IView { /// Initializes a new view control diff --git a/Source/WindowManager.cs b/Source/WindowManager.cs index d73d0ff..4d0fea8 100644 --- a/Source/WindowManager.cs +++ b/Source/WindowManager.cs @@ -29,6 +29,7 @@ using System.Runtime.Versioning; using Nuclex.Support; using Nuclex.Windows.Forms.AutoBinding; using Nuclex.Windows.Forms.Views; +using static System.Windows.Forms.VisualStyles.VisualStyleElement; namespace Nuclex.Windows.Forms { @@ -125,11 +126,10 @@ namespace Nuclex.Windows.Forms { window.Closed += this.rootWindowClosedDelegate; // If we either created the view model or the user explicitly asked us to - // dispose his view model, tag the window so that we know to dispose it - // when we're done (but still allow the user to change his mind) + // dispose his view model, prepare the window so that it will dispose its + // view model when the window is done. if((viewModel == null) || disposeOnClose) { - window.Tag = "DisposeViewModelOnClose"; // TODO: Wrap SetProp() instead? - //window.SetValue(DisposeViewModelOnCloseProperty, true); + setupViewModelDisposal(window); } window.Show(); @@ -158,11 +158,10 @@ namespace Nuclex.Windows.Forms { try { // If we either created the view model or the user explicitly asked us to - // dispose his view model, tag the window so that we know to dispose it - // when we're done (but still allow the user to change his mind) + // dispose his view model, prepare the window so that it will dispose its + // view model when the window is done. if((viewModel == null) || disposeOnClose) { - window.Tag = "DisposeViewModelOnClose"; // TODO: Wrap SetProp() instead? - //window.SetValue(DisposeViewModelOnCloseProperty, true); + setupViewModelDisposal(window); } DialogResult result = window.ShowDialog(this.activeWindow); @@ -178,14 +177,6 @@ namespace Nuclex.Windows.Forms { window.Activated -= this.rootWindowActivatedDelegate; ActiveWindow = window.Owner; - if(shouldDisposeViewModelOnClose(window)) { - IView windowAsView = window as IView; - if(windowAsView != null) { - object viewModelAsObject = windowAsView.DataContext; - windowAsView.DataContext = null; - disposeIfDisposable(viewModelAsObject); - } - } disposeIfDisposable(window); } } @@ -331,6 +322,10 @@ namespace Nuclex.Windows.Forms { return Activator.CreateInstance(type); } + protected virtual object CreateServiceScope() { + return null; + } + /// Called when one of the application's root windows is closed /// Window that has been closed /// Not used @@ -339,20 +334,11 @@ namespace Nuclex.Windows.Forms { closedWindow.Closed -= this.rootWindowClosedDelegate; closedWindow.Activated -= this.rootWindowActivatedDelegate; - // If the view model was created just for this view or if the user asked us - // to dispose of his view model, do so now. - if(shouldDisposeViewModelOnClose(closedWindow)) { - IView windowAsView = closedWindow as IView; - if(windowAsView != null) { - object viewModelAsObject = windowAsView.DataContext; - windowAsView.DataContext = null; - disposeIfDisposable(viewModelAsObject); - } - } - lock(this) { ActiveWindow = null; } + + //disposeIfDisposable(closedWindow); } /// Called when one of the application's root windows is activated @@ -402,16 +388,24 @@ namespace Nuclex.Windows.Forms { } } - /// Determines if the view owns the view model - /// View that will be checked for ownership - /// True if the view owns the view model - private static bool shouldDisposeViewModelOnClose(Control view) { - string tagAsString = view.Tag as string; - if(tagAsString != null) { - return tagAsString.Contains("DisposeViewModelOnClose"); - } else { - return false; + /// Attaches a view model disposer to a control + /// + /// Control whose view model will be disposed when it is itself disposed + /// + private void setupViewModelDisposal(Control control) { + IView controlAsView = control as IView; + if(controlAsView != null) { + IDisposable disposableViewModel = controlAsView.DataContext as IDisposable; + if(disposableViewModel != null) { + control.Disposed += delegate(object sender, EventArgs arguments) { + disposableViewModel.Dispose(); + controlAsView.DataContext = null; + }; + } } + + //window.Tag = "DisposeViewModelOnClose"; // TODO: Wrap SetProp() instead? + //window.SetValue(DisposeViewModelOnCloseProperty, true); } /// Filters a list of types to contain only those in a specific namespace