2009-02-02 23 views
99

He creado un control de usuario WPF personalizado que está destinado a ser utilizado por un tercero. Mi control tiene un miembro privado que es desechable, y me gustaría asegurar que siempre se llame a su método de eliminación una vez que se cierre la ventana/aplicación que lo contiene. Sin embargo, UserControl no es desechable. Intenté implementar la interfaz IDisposable y suscribirme al evento descargado, pero ninguno se llama cuando la aplicación anfitriona se cierra. Si es posible, no quiero depender de los consumidores de mi control que recuerden llamar a un método específico de Disposición.Desecho Controles de usuario WPF

public partial class MyWpfControl : UserControl 
{ 
    SomeDisposableObject x; 

    // where does this code go? 
    void Somewhere() 
    { 
     if (x != null) 
     { 
      x.Dispose(); 
      x = null; 
     } 

    } 
} 

La única solución que he encontrado hasta ahora es suscribirse al evento ShutdownStarted del despachador. ¿Es este un enfoque razonable?

this.Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted; 
+0

¿Qué pasa con el caso sin carga del control de usuario? – akjoshi

+2

@akjoshi: MSDN dice que: El evento descargado no se puede subir en absoluto.Y también podría activarse más de una vez, es cuando el usuario cambia de tema. – Dudu

+0

Aunque podría implementar la interfaz IDisposable en su control de usuario, no hay garantía de que su tercero llamará al método de eliminación de su implementación de patrón de eliminación. Si se aferra a los recursos nativos (por ejemplo, una secuencia de archivos), debe considerar usar un finalizador. – Philippe

Respuesta

51

Interesante blog aquí:

http://geekswithblogs.net/cskardon/archive/2008/06/23/dispose-of-a-wpf-usercontrol-ish.aspx

Menciona la suscripción de Dispatcher_ShutDownStarted a disponer de sus recursos.

+0

Bueno, esperaba que hubiera una manera más limpia que esto, pero parece que por ahora es lo mejor para hacerlo. –

+28

¿Pero qué sucede si el UserControl muere antes de que la aplicación fallezca? El Dispatcher solo se avergonzará cuando la aplicación lo haga, ¿verdad? –

+0

Totalmente de acuerdo, pero no entiendo por qué el OP necesita deshacerse de los controles. Suena ... extraño –

-3

Un UserControl tiene un Destructor, ¿por qué no lo usa?

~MyWpfControl() 
    { 
     // Dispose of any Disposable items here 
    } 
+0

Esto no parece funcionar. Acabo de probar ese enfoque y nunca se llama. – JasonD

+8

Eso no es un destructor, es un finalizador. Siempre implementa un finalizador y se deshace como un par, de lo contrario corre el riesgo de tener pérdidas. –

+1

Y, en el finalizador solo debe limpiar objetos no administrados pero no objetos administrados, porque los finalizadores se ejecutan en un orden no especificado en subprocesos de GC, por lo que los objetos gestionados pueden finalizarse antes y su Dispose() puede tener afinidad por subprocesos. – Dudu

10

Tiene que tener cuidado con el destructor. Esto se invocará en el hilo del Finalizador de GC. En algunos casos, los recursos que su liberación pueden no gustar se liberan en un hilo diferente del que se crearon.

+1

Gracias por esta advertencia. este era mi caso exactamente! _Application: devenv.exe Versión de Framework: v4.0.30319 Descripción: El proceso finalizó debido a una excepción no controlada. Excepción Info: System.InvalidOperationException Pila: en MyControl.Finalize() _ mi solución era mover código de finalizador en ShutdownStarted – itsho

4

Mi escenario es un poco diferente, pero la intención es la misma Me gustaría saber cuándo se cierra/cierra la ventana principal que aloja mi control de usuario. La vista (es decir, mi usercontrol) debe invocar a los presentadores en closeView para ejecutar algunas funciones y realizar limpieza (Bueno, estamos implementando un patrón MVP en una aplicación PRISM de WPF).

Acabo de figurar que en el evento Loaded del usercontrol, puedo conectar mi método ParentWindowClosing al evento Parent Closing de Windows. ¡De esta manera, mi Usercontrol puede estar al tanto cuando la ventana principal se cierra y actúa en consecuencia!

26

Dispatcher.ShutdownStarted evento se activa solo al final de la aplicación. Vale la pena llamar a la lógica de eliminación justo cuando el control deja de usarse. En particular, libera recursos cuando el control se usa muchas veces durante el tiempo de ejecución de la aplicación. Entonces ioWint es preferible. Aquí está el código:

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 
    }; 
} 
+0

En una tienda de aplicaciones de Windows, no existe GetWindow(). –

+0

Bravo, la mejor respuesta. – MDDDC

+4

Cœur: En una aplicación de Windows Store, no está usando WPF –

8

utilizo el siguiente comportamiento interactividad para proporcionar un evento de descarga a UserControls WPF. Puede incluir el comportamiento en UserControls XAML. Para que pueda tener la funcionalidad sin colocar la lógica en cada UserControl.

declaración XAML:

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

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

manejador CodeBehind:

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

Código de Comportamiento:

/// <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); 
    } 
} 
+3

Me gustaría levantar una bandera aquí ... ¿qué pasa si algo más cancela el cierre de la ventana (tal vez suscrito después de su control por lo que 'e.Cancel' sigue siendo falsa cuando llegue a tu delegado 'WindowClosingHandler') ?. Su control estaría "descargado" y la ventana aún se abrió. Definitivamente haré esto en el evento 'Closed', no en el de' Closing'. – Jcl

+0

Oh sí, me perdí este punto. Gracias. –

Cuestiones relacionadas