I'm trying to find a good way of cleaning up unmanaged resources that my custom controls may generate. The scenario is in which the parent window opens a child window that has a custom control with unmanaged resources (see code below). These resources need to be cleaned up when the CustomControl is no longer in use, i.e when the tree it is within is unloaded (i.e the child window closes), or it is removed from a tree (i.e it itself is unloaded)
Method 1 : Unloaded event This gets triggered when you close a child window manually, but not if you close the parent window (which then automatically closes the children)
Method 2 : OnVisualChildrenChanged This doesn't get called when the child window is closed manually or automatically by the parent, and is only of use if the CustomControl is moved to a different parent element.
Method 3 : Dispatcher.ShutdownStarted This isn't really much help as the user may have opened/closed several child windows before they finish with the app, and having that memory only cleaned up at the end isn't good enough.
Method 4 : Have the CustomControl subscribe to ChildWindow.Closing This isn't good enough either, .. the control shouldn't have to know that it is in a window.
Method 5 : Finalizer Suffers from same issue as Method 3, .. it could be a while before its called
public class CustomControlWithManagedResources : Control
{
~CustomControlWithManagedResources()
{
Console.WriteLine("~CustomControlWithManagedResources");
}
public CustomControlWithManagedResources()
{
Unloaded += CustomControl_Unloaded;
Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;
}
void Dispatcher_ShutdownStarted(object sender, EventArgs e)
{
Console.WriteLine("ShutdownStarted");
}
void CustomControl_Unloaded(object sender, RoutedEventArgs e)
{
Console.WriteLine("Unloaded");
}
protected override void OnVisualParentChanged(DependencyObject oldParent)
{
base.OnVisualParentChanged(oldParent);
if(oldParent != null)
Console.WriteLine("OnVisualParentChanged");
}
}
public class ChildWindow : Window
{
public ChildWindow()
{
Content = new CustomControlWithManagedResources();
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
{
base.OnMouseDoubleClick(e);
new ChildWindow() { Owner = this }.Show();
}
}
The right way to do this in a WPF application is to use the MVVM pattern and to remove all logic and dependencies from your Views (controls) and into ViewModels.
Your parent ViewModel would create a child ViewModel that implemented
IDisposable
and then when it removed the child ViewModel it would callDispose
on the child ViewModel.If your main ViewModel has unmanaged resources that need to be cleaned up, then it should implement
IDisposable
and the bootstrapper that creates that should take responsibility for cleaning them up.Another good reference is Caliburn.Micro
You seem to be asking for Closing event.
Take a look at this:
http://msdn.microsoft.com/en-us/library/system.windows.window_events(v=vs.110).aspx
When a window closes, it raises two events: Closing and Closed.
While a window can be explicitly closed through mechanisms provided in the non-client and client areas, a window can also be implicitly closed as a result of behavior in other parts of the application or Windows, including the following:
A user logs off or shuts down Windows.
A window's owner closes (see Owner).
The main application window is closed and ShutdownMode is OnMainWindowClose.
Shutdown is called.
In all those scenarions Closing and Close events will be called.
Do not subscribe your control to Window.Closing event. Let the Window do the job.