1

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();
    }
}
pastillman
  • 1,104
  • 2
  • 16
  • 27

2 Answers2

2

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 call Dispose on the child ViewModel.

If your main ViewModel has unmanaged resources that need to be cleaned up, then it should implementIDisposable and the bootstrapper that creates that should take responsibility for cleaning them up.

Another good reference is Caliburn.Micro

satnhak
  • 9,407
  • 5
  • 63
  • 81
  • 1
    Well, I probably should have mentioned this in the question, but the unmanaged resources are View resources (DirectX resources). So the ViewModel is graph data and the View uses DirectX to render the ViewModel Data. The View could implement IDisposable, but its unclear when to call Dispose on that View, as mentioned in my question there doesn't appear to be a reliable event. If you could post some code for how this would work I'd appreciate it. You say that "the parent would remove the child viewModel", but the parent isn't involved in when closing a window using the close button – pastillman Apr 21 '14 at 17:21
  • View resources are a bit different. I think that the right place in this case is the Finalizer; if all you care about is that memory gets released at some point then this should do. The Finalizer should get called when memory needs to be released; the timing is arbitrary, but that might not matter. Unless you know you have a memory problem that requires a more complex solution don't over engineer it. – satnhak Apr 22 '14 at 08:59
1

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.

dev hedgehog
  • 8,698
  • 3
  • 28
  • 55
  • But the ControlWithUnmanagedResources is a child element of the window, so its not the windows Job to cleanup after it. ControlWithUnmanagedResources is self contained, it shouldn't need to know what hiearchy its in. – pastillman Apr 21 '14 at 11:04
  • Window shall subscribe to closing event and then inside handler it should call a method from control to clean controls resources. What unamanaged resources do you have anyway? – dev hedgehog Apr 21 '14 at 18:49
  • Whilst that might work, its seems like a horrible hack. The control is self contained and it should be able to perform the clean up itself without users having to know to call some cleanup method on it. There must be a way of the control being able to detect when it is no longer referenced and should perform cleanup – pastillman Apr 21 '14 at 21:00
  • It all depends on resources you wish to release. Maybe you should explain me more what resources are you talking about. Control does know when its no longer in use and when its about to collected by GC. WPF is all about weak events and weak instances. Control knows when its no longer in visual tree or when its no longer visible or when its no longer enabled or whatever you need. If the window gets closed and thrown away the controls within will also get collected so maybe you do not need to free your resources since everything is gonna be destryed anyway. What resources are we talking about? – dev hedgehog Apr 21 '14 at 21:22