Limpieza adecuada de los controles de usuario de WPF
user-controls dispose (4)
Estaba buscando esto también y después de probar diferentes opciones implementé 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 un padre llama al método Children.Clear()
y ya tenía elementos agregados a Children, DependencyObject tenía un valor. Pero cuando el padre agregó un elemento ( Children.Add(CustomControl)
) y los hijos estaban vacíos, DependencyObject era nulo.
Soy relativamente nuevo en WPF, y algunas cosas con él son bastante extrañas para mí. Por un lado, a diferencia de los formularios de Windows, la jerarquía de control de WPF no admite IDisposable. En los formularios de Windows, si un control de usuario usaba algún recurso administrado, era muy fácil limpiar los recursos al anular el método Dispose que implementaba cada control.
En WPF, la historia no es tan simple. Lo he buscado durante varias horas y he encontrado dos temas básicos:
El primer tema es que Microsoft declara claramente que WPF no implementa IDisposable porque los controles de WPF no tienen recursos no administrados. Si bien eso puede ser cierto, parece que han pasado por alto el hecho de que las extensiones de usuario a su jerarquía de clases de WPF pueden usar recursos administrados (directa o indirectamente a través de un modelo). Al no implementar IDisposible, Microsoft ha eliminado de manera efectiva el único mecanismo garantizado mediante el cual los recursos no administrados utilizados por un control o ventana de WPF personalizado se pueden limpiar.
En segundo lugar, encontré algunas referencias a Dispatcher.ShutdownStarted. He intentado usar el evento ShutdownStarted, pero no parece activarse para todos los controles. Tengo un montón de WPF UserControl que he implementado un controlador para ShutdownStarted, y nunca se llama. No estoy seguro de si solo funciona para Windows, o tal vez la clase de aplicación WPF. Sin embargo, no se está activando correctamente, y estoy perdiendo 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 el uso de un finalizador si es posible.
La interfaz IDisposable no tiene (casi) ningún significado en WPF, porque el mecanismo es diferente de Winforms. En WPF, debes tener en cuenta el árbol visual y lógico: eso es fundamental.
Entonces, 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 sea útil.
Creo que puede comprobar el método OnVisualParentChanged
expuesto desde el UIElement
: este método se llama cuando se adjunta un objeto visual y cuando se separa. Ese podría ser el lugar adecuado para disponer los objetos no administrados (sockets, archivos, etc.).
Me temo que Dispatcher.ShutdownStarted realmente parece ser el único mecanismo que proporciona WPF para la eliminación de recursos en UserControls. (Vea una pregunta muy similar que hice hace un tiempo).
Otra forma de abordar el problema es mover todos sus recursos desechables (si es posible) fuera del código detrás y en clases separadas (como ViewModel cuando se usa el patrón MVVM). Luego, en un nivel superior, podría manejar el cierre de su ventana principal y notificar a todos los ViewModels a través de una clase de Messenger.
Me sorprende que no reciba el evento Dispatcher.ShutdownStarted. ¿Están sus UserControls conectados a la ventana de nivel superior en ese momento?
Mientras que otros le han brindado información realmente útil sobre este problema, hay un poco de información que tal vez no tenga, lo cual explicará mucho sobre por qué no hay IDisposable. Básicamente, WPF (y Silverlight) hace un uso intensivo de WeakReferences, lo que le permite hacer referencia a un objeto que el GC aún puede recopilar.