Mi modelo mental de cómo funcionan las propiedades de dependencia:
Cualquier DependencyObject
clase implementa dos propiedades especiales. Uno, una propiedad estática de la clase, es un diccionario de objetos DependencyProperty
. Cada instancia de la clase puede buscar dentro de ese diccionario la metainformación sobre cada DependencyProperty
, el nombre de la propiedad, su tipo, las devoluciones de llamada a las que se debe llamar cuando se obtiene y configura, cómo participa en la herencia de la propiedad, etc. Cuando registra una propiedad de dependencia, está agregando una entrada a este diccionario.
La otra propiedad es una propiedad de instancia: se trata de un diccionario, introducido por DependencyProperty
, que contiene el valor local de cada DependencyProperty
, si se ha establecido.
Los métodos SetValue
y GetValue
que implementa en el setter y getter de la propiedad CLR son básicamente una evaluación perezosa con esteroides. En lugar de almacenar y recuperar el valor de la propiedad en un campo de respaldo, almacenan y recuperan el valor de la propiedad en el diccionario de valores.
La magia de las propiedades de dependencia está en lo que GetValue
y realmente hacen.
GetValue
busca el valor de la propiedad en el diccionario de valores del objeto. Si no lo encuentra, llama al GetValue
en el elemento padre, para obtener lo que el elemento padre cree que es el valor. Por ejemplo, cuando crea un TextBox
en un Window
, cualquier cosa que mire el TextBox
FontFamily
está llamando al GetValue
. A menos que haya configurado explícitamente la fuente, no hay ninguna entrada en su diccionario para esa propiedad. Entonces GetValue
pregunta al elemento padre por el valor.El elemento padre puede o no tener FontFamily
establecer; si no, es llame al GetValue
para devolver el valor desde su padre. Y así sucesivamente, hasta que se alcanza el objeto Window
y se encuentra el valor real FontFamily
.
Si configura FontFamily
en el TextBox
, SetValue
almacena el valor en el diccionario de valores. La próxima vez que algo necesite obtener el valor de FontFamily
para ese TextBox
, GetValue
encuentra el valor en el diccionario y lo devuelve, por lo que no necesita preguntar al elemento padre.
Si establece FontFamily
en el Window
, SetValue
no sólo actualiza el valor en el diccionario de valor Window
's, se dispara un evento de cambio de propiedad de que todo depende de la propiedad oye. (Es por eso que se llaman propiedades de dependencia, recuerde). Y si lo que depende de la propiedad es en sí mismo una propiedad de dependencia, activa su propia propiedad: cambia los eventos. Así es como cambiando el FontFamily
en el Window
se cambia la fuente de cada control en la ventana y también se le pide a WPF que vuelva a procesar los controles que han cambiado.
Las propiedades adjuntas funcionan con el mismo tipo de enfoque. Cualquier objeto que pueda tener propiedades adjuntas tiene un diccionario en el que están almacenados los valores de las propiedades adjuntas. Cuando configura Grid.Column
en un CheckBox
en XAML, solo está agregando una entrada al diccionario CheckBox
. Cuando el Grid
necesita saber en qué columna está el CheckBox
, se ve el valor de ese diccionario. Cuando establece Grid.IsSharedSizeScope
en True
en un objeto, el diccionario de ese objeto contendrá una nueva propiedad, un diccionario que contiene anchuras/alturas para cada SharedSizeKey
.
Debo enfatizar que este es mi modelo mental. No me senté con Reflector y miré la implementación real de Register
, GetValue
y SetValue
para ver cómo funcionan realmente. Puedo estar equivocado sobre los detalles. Pero es un modelo que predice con precisión cómo se comporta esto, por lo que es lo suficientemente bueno.
El concepto de almacenar valores de propiedad en diccionarios es bastante extraño para los programadores de C#. Aunque es un viejo sombrero para los programadores de Python. En Python, todas las propiedades de clase -todos los objetos, de hecho- se almacenan en diccionarios, de modo que puede obtener su valor a través de los descriptores de propiedades o simplemente buscándolos. Las propiedades de dependencia y las propiedades adjuntas son solo otra forma en que .NET, después de haber robado todo lo que Java tenía que valía la pena robar, ahora está saqueando Python. (O de donde sea que Python los haya saqueado). Aprender Python me ha convertido en un programador de C# mucho mejor; Lo recomiendo a cualquier desarrollador de C# que aún no lo haya hecho.
Robert, muchísimas gracias por esa maravillosa explicación. Ahora podía imaginarme cómo funcionan. No pude entender las propiedades adjuntas correctamente. He aquí lo que entendí: la casilla de verificación tiene un diccionario codificado por una propiedad estática. Ese diccionario puede tener valores codificados para todas las propiedades que tiene el CheckBox. Pero también puede contener otros valores codificados por las propiedades estáticas (Grid.Column) de otros objetos (Grid). Cuando el Grid quiere saber sobre su ubicación, busca una entrada introducida por Grid .Column y Grid.Row y lo utiliza. Así que podemos agregar una entrada para Grid.Column incluso cuando no está dentro de una Grid. –
Robert, ¿estoy en lo cierto? –
Me parece correcto, aunque "Ese diccionario puede tener valores codificados para todas las propiedades que el CheckBox tiene" debería decir "propiedades de dependencia". También debo señalar que cuando escribí la publicación anterior, estaba bajo el error de que las propiedades adjuntas eran un mecanismo como las propiedades de dependencia; de hecho, las propiedades adjuntas * son * propiedades de dependencia. –