2009-10-11 18 views
20

Soy relativamente nuevo en WPF, y algunas cosas son bastante extrañas para mí. Por un lado, a diferencia de Windows Forms, la jerarquía de control de WPF no admite IDisposable. En Windows Forms, si un control de usuario usaba recursos administrados, era muy fácil limpiar los recursos anulando el método Dispose que implementó cada control.Limpieza adecuada de los controles de usuario de WPF

En WPF, la historia no es tan simple. Lo he buscado durante varias horas y encontré dos temas básicos:

El primer tema es que Microsoft establece claramente que WPF no implementa IDisposable porque los controles WPF no tienen recursos no administrados. Si bien eso puede ser cierto, parecen haber pasado por alto el hecho de que las extensiones de usuario a su jerarquía de clases WPF pueden usar recursos administrados (directa o indirectamente a través de un modelo). Al no implementar IDisposable, Microsoft ha eliminado efectivamente el único mecanismo garantizado mediante el cual se pueden limpiar los recursos no administrados utilizados por un control o ventana personalizada de WPF.

En segundo lugar, encontré algunas referencias a Dispatcher.ShutdownStarted. Intenté utilizar el evento ShutdownStarted, pero parece que no se dispara para cada control. Tengo un montón de UserControl de WPF que he implementado un controlador para ShutdownStarted, y nunca se llama. No estoy seguro si solo funciona para Windows, o tal vez para la clase de la aplicación WPF. Sin embargo, no está disparando correctamente, y estoy filtrando objetos abiertos de PerformanceCounter cada vez que se cierra la aplicación.

¿Existe una mejor alternativa para limpiar los recursos no administrados que el evento Dispatcher.ShutdownStarted? ¿Hay algún truco para implementar IDisposable de modo que se llame a Dispose? Preferiría mucho evitar usando un finalizador si es posible.

Respuesta

12

Me temo que Dispatcher.ShutdownStarted realmente parece ser el único mecanismo que proporciona WPF para deshacerse de recursos en UserControls. (Ver un muy similar question pregunté hace un tiempo).

Otra forma de abordar el problema es mover todos sus recursos desechables (si es posible) fuera del código subyacente y en clases separadas (como ViewModel cuando se utiliza el patrón MVVM). Luego, en un nivel superior, puede manejar el cierre de su ventana principal y notificar a todos los Modelos de Vista a través de una clase de Messenger.

Me sorprende que no obtenga el evento Dispatcher.ShutdownStarted. ¿Están sus UserControls conectados a la ventana de nivel superior en ese momento?

+4

+1 para mover recursos desechables fuera del código subyacente. Uno de los puntos clave de aprendizaje para WPF es minimizar el código subyacente para aprovechar la fortaleza y la expresividad de la arquitectura de enlace de datos. Es algo doloroso de aprender (la curva de aprendizaje es más como escalar un acantilado), pero gratificante cuando "obtienes" el modo de pensamiento WPF. –

+0

Todos los recursos desechables están realmente en ViewModel's, que a su vez son IDisposables. Estoy realmente confundido acerca de por qué el evento Dispatcher.ShutdownStarted no se activa. El control de contador de rendimiento (y su ViewModel asociado) están realmente conectados al gráfico de WPF, ya que está incrustado en un en un . – jrista

+1

@Greg D: Generalmente obtengo el modelo WPF. Empecé a usar MVVM tan pronto como comprendí los conceptos básicos de WPF, y mi CodeBehind está casi desnudo (simplemente el constructor predeterminado y su llamada a InitializeComponent). La capacidad de compsabilidad y de enlace de datos de WPF es asombrosa, y nunca más volveré a los formularios de Windows si puedo. – jrista

9

La interfaz IDisposable tiene (casi) ningún significado bajo WPF, porque el mecanismo es diferente de Winforms. En WPF, debes tener en cuenta el árbol visual y lógico: eso es fundamental.
Por lo tanto, cualquier objeto visual generalmente vive como hijo de algún otro objeto. La base del mecanismo de construcción de WPF es unir el objeto visual jerárquicamente, luego separarlo y destruirlo cuando no sean útiles.

Creo que puede verificar el método OnVisualParentChanged expuesto desde UIElement: este método se llama cuando se adjunta un objeto visual y cuando se desconecta. Ese podría ser el lugar correcto para deshacerse de los objetos no administrados (sockets, archivos, etc.).

+0

Gracias por el consejo sobre OnVisualParentChanged. Jugaré con eso y veré si ayuda a resolver mi problema. – jrista

0

Mientras que otros le han dado información realmente útil acerca de este problema, hay un poco de información que puede no tener que explicará mucho acerca de por qué no hay IDisposable. Básicamente, WPF (y Silverlight) hace un uso intensivo de WeakReferences: esto le permite hacer referencia a un objeto que el GC aún puede recopilar.

+0

Gracias por la información Pete. Tengo curiosidad si tienes algunos enlaces que expliquen esto con más detalle. Tengo curiosidad por saber cuán pesado uso de WeakReferences no causa problemas. Pueden ser una herramienta poderosa en situaciones de nicho ... pero no puedo imaginar cómo se usan en WPF. – jrista

5

que estaba buscando para esto también y después de probar diferentes opciones que he implementado la solución de Venezia

protected override void OnVisualParentChanged(DependencyObject oldParent) 
    { 
     if (oldParent != null) 
     { 
      MyOwnDisposeMethod(); //Release all resources here 
     } 

     base.OnVisualParentChanged(oldParent); 
    } 

me di cuenta de que cuando la llamada matriz Children.Clear() Método y artículos ya habían añadido a los niños, DependencyObject tenía un valor. Pero cuando el padre agregó un artículo (Children.Add(CustomControl)) y los niños estaban vacíos, DependencyObject era nulo.

+0

Lo cambié a if (Parent == null) de modo que si muevo el control a un contenedor diferente, no se autodestruirá – Sean

Cuestiones relacionadas