Disposing WPF User Controls

109,169

Solution 1

Interesting blog post here: Dispose of a WPF UserControl (ish)

It mentions subscribing to Dispatcher.ShutdownStarted to dispose of your resources.

Solution 2

Dispatcher.ShutdownStarted event is fired only at the end of application. It's worth to call the disposing logic just when control gets out of use. In particular it frees resources when control is used many times during application runtime. So ioWint's solution is preferable. Here's the code:

public MyWpfControl()
{
     InitializeComponent();
     Loaded += (s, e) => { // only at this point the control is ready
         Window.GetWindow(this) // get the parent window
               .Closing += (s1, e1) => Somewhere(); //disposing logic here
     };
}

Solution 3

You have to be careful using the destructor. This will get called on the GC Finalizer thread. In some cases the resources that your freeing may not like being released on a different thread from the one they were created on.

Solution 4

I use the following Interactivity Behavior to provide an unloading event to WPF UserControls. You can include the behavior in the UserControls XAML. So you can have the functionality without placing the logic it in every single UserControl.

XAML declaration:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

<i:Interaction.Behaviors>
    <behaviors:UserControlSupportsUnloadingEventBehavior UserControlClosing="UserControlClosingHandler" />
</i:Interaction.Behaviors>

CodeBehind handler:

private void UserControlClosingHandler(object sender, EventArgs e)
{
    // to unloading stuff here
}

Behavior Code:

/// <summary>
/// This behavior raises an event when the containing window of a <see cref="UserControl"/> is closing.
/// </summary>
public class UserControlSupportsUnloadingEventBehavior : System.Windows.Interactivity.Behavior<UserControl>
{
    protected override void OnAttached()
    {
        AssociatedObject.Loaded += UserControlLoadedHandler;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.Loaded -= UserControlLoadedHandler;
        var window = Window.GetWindow(AssociatedObject);
        if (window != null)
            window.Closing -= WindowClosingHandler;
    }

    /// <summary>
    /// Registers to the containing windows Closing event when the UserControl is loaded.
    /// </summary>
    private void UserControlLoadedHandler(object sender, RoutedEventArgs e)
    {
        var window = Window.GetWindow(AssociatedObject);
        if (window == null)
            throw new Exception(
                "The UserControl {0} is not contained within a Window. The UserControlSupportsUnloadingEventBehavior cannot be used."
                    .FormatWith(AssociatedObject.GetType().Name));

        window.Closing += WindowClosingHandler;
    }

    /// <summary>
    /// The containing window is closing, raise the UserControlClosing event.
    /// </summary>
    private void WindowClosingHandler(object sender, CancelEventArgs e)
    {
        OnUserControlClosing();
    }

    /// <summary>
    /// This event will be raised when the containing window of the associated <see cref="UserControl"/> is closing.
    /// </summary>
    public event EventHandler UserControlClosing;

    protected virtual void OnUserControlClosing()
    {
        var handler = UserControlClosing;
        if (handler != null) 
            handler(this, EventArgs.Empty);
    }
}

Solution 5

My scenario is little different, but the intent is same i would like to know when the parent window hosting my user control is closing/closed as The view(i.e my usercontrol) should invoke the presenters oncloseView to execute some functionality and perform clean up. ( well we are implementing a MVP pattern on a WPF PRISM application).

I just figured that in the Loaded event of the usercontrol, i can hook up my ParentWindowClosing method to the Parent windows Closing event. This way my Usercontrol can be aware when the Parent window is being closed and act accordingly!

Share:
109,169
Mark Heath
Author by

Mark Heath

I'm the creator of NAudio, an open source audio library for .NET. I'm interested in any ways to improve the quality of my code, and teaching others the things I learn along the way. I'm also the author of several Pluralsight courses.

Updated on July 01, 2021

Comments

  • Mark Heath
    Mark Heath almost 3 years

    I have created a custom WPF user control which is intended to be used by a third party. My control has a private member which is disposable, and I would like to ensure that its dispose method will always get called once the containing window/application is closed. However, UserControl is not disposable.

    I tried implementing the IDisposable interface and subscribing to the Unloaded event but neither get called when the host application closes. MSDN says that the Unloaded event may not be raised at all. And it might also be triggered more than once, that is when user changes theme.

    If at all possible, I don't want to rely on consumers of my control remembering to call a specific Dispose method.

     public partial class MyWpfControl : UserControl
     {
         SomeDisposableObject x;
    
         // where does this code go?
         void Somewhere() 
         {
             if (x != null)
             {
                 x.Dispose();
                 x = null;
             }
    
         }
     }
    

    The only solution I have found so far is to subscribe to the Dispatcher's ShutdownStarted event. Is this a reasonable approach?

    this.Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;
    
  • Mark Heath
    Mark Heath over 14 years
    well I was hoping there would be a cleaner way than this, but it looks like for now this is the best to do it.
  • Robert Jeppesen
    Robert Jeppesen over 14 years
    But what if the UserControl dies before the app dies? The Dispatcher will only shyt down when the app does, right?
  • Ray Booysen
    Ray Booysen over 11 years
    Completely agree, but I don't understand why the OP needs to dispose of controls. Sounds... odd
  • Neutrino
    Neutrino about 11 years
    Because many controls reuse COM components or other unmanaged resources that were not coded with a view to being left hanging around indefinitely, or finalized on a thread pool thread, and expect/require deterministic deallocation.
  • Cœur
    Cœur almost 11 years
    In a Windows Store App, GetWindow() doesn't exist.
  • Cœur
    Cœur almost 11 years
    In a Windows Store App, ShutdownStarted doesn't exist.
  • itsho
    itsho over 10 years
    Thank you for this warning. this was my case exactly! Application: devenv.exe Framework Version: v4.0.30319 Description: The process was terminated due to an unhandled exception. Exception Info: System.InvalidOperationException Stack: at MyControl.Finalize() my solution was to move code from finalizer into ShutdownStarted
  • Jcl
    Jcl over 9 years
    I'd raise a flag here... what if anything else cancels the window closing (maybe subscribed after your control so e.Cancel is still false when it reaches your WindowClosingHandler delegate)?. Your control would be "unloaded" and the window still opened. I'd definitely do this on the Closed event, not on the Closing one.
  • Nic
    Nic over 8 years
    Bravo, greatest answer.
  • DanW
    DanW over 8 years
    Or you need to dereference event handlers, or you need to stop threads started in that control, ...
  • Alan Baljeu
    Alan Baljeu about 8 years
    Cœur: In a Windows Store App, you aren't using WPF
  • L.Trabacchin
    L.Trabacchin over 7 years
    what if there are more windows involved and the main one never get closed? Or your control is hosted in page which get loaded/unloaded multiple times ? see: stackoverflow.com/a/14074116/1345207
  • LOST
    LOST about 6 years
    The window might not be closing very often. If the control is part of a list item, many will be created/destroyed until its parent window may close.
  • Byrel Mitchell
    Byrel Mitchell almost 3 years
    Note that Closing doesn't always fire when the window closes. Its only called in cases where it makes sense to potentially cancel that close. This is particularly relevant for subwindows; if the window is closing because its Owner window is closing, it will fire Closed but not Closing.