Aquí está la explicación de cómo funcionan las propiedades de dependencia que siempre deseé que alguien hubiera escrito para mí. Es incompleto y muy posiblemente incorrecto, pero te ayudará a desarrollar una comprensión suficiente de ellos que podrás comprender la documentación que lees.
Las propiedades de dependencia son valores similares a propiedades que se obtienen y configuran a través de los métodos de la clase DependencyObject
. Pueden (y generalmente lo hacen) parecerse mucho a las propiedades CLR, pero no lo son. Y esto llega a la primera cosa confusa acerca de ellos. Una propiedad de dependencia en realidad está formada por un par de componentes.
He aquí un ejemplo:
Document
es una propiedad del objeto RichTextBox
. Es una propiedad CLR real. Es decir, tiene un nombre, un tipo, un captador y un colocador, al igual que cualquier otra propiedad de CLR. Pero a diferencia de las propiedades "normales", la propiedad RichTextBox
no solo obtiene y establece un valor privado dentro de la instancia. Internamente, se implementa como esto:
public FlowDocument Document
{
get { return (FlowDocument)GetValue(DocumentProperty); }
set { SetValue(DocumentProperty, value); }
}
Al configurar Document
, el valor que ha pasado en se pasa a SetValue
, junto con DocumentProperty
. ¿Y qué es que? ¿Y cómo obtiene su valor GetValue
? ¿Y por qué?
Primero, qué. Hay una propiedad estática definida en RichTextBox
llamada DocumentProperty
. Cuando esta propiedad se declara, se hace así:
public static DependencyProperty DocumentProperty = DependencyProperty.Register(
"Document",
typeof(FlowDocument),
typeof(RichTextBox));
El método Register
, en este caso, le dice al sistema de dependencia propiedad que RichTextBox
- el tipo, no es el caso - tiene ahora una propiedad de dependencia llamado Document
de tipo FlowDocument
. Este método almacena esta información ... en alguna parte. Donde, exactamente, hay un detalle de implementación que está oculto para nosotros.
Cuando el definidor de la propiedad Document
llama SetValue
, el método SetValue
observa el argumento DocumentProperty
, verifica que en realidad es una propiedad que pertenece a RichTextBox
y que value
es el tipo correcto, y luego almacena su nuevo valor ... algun lado. La documentación para DependencyObject
es tímida en este detalle de implementación, porque realmente no necesita saberlo. En mi modelo mental de cómo funciona esto, supongo que hay una propiedad de tipo Dictionary<DependencyProperty, object>
que es privada para DependencyObject
, por lo que las clases derivadas (como RichTextBox
) no pueden verla pero GetValue
y y SetValue
pueden actualizarla. Pero quién sabe, tal vez está escrito en pergamino por los monjes.
En cualquier caso, este valor ahora es lo que se denomina un "valor local", lo que significa que es un valor que es local para este RichTextBox
específico, al igual que una propiedad común.
El punto de todo esto es:
- código CLR no necesita saber que una propiedad es una propiedad de dependencia. Se ve exactamente como cualquier otra propiedad. Usted puede llamar al
GetValue
y SetValue
para obtenerlo y configurarlo, pero a menos que esté haciendo algo con el sistema de propiedad de la dependencia, probablemente no sea necesario.
- A diferencia de una propiedad normal, algo que no sea el objeto al que pertenece puede involucrarse para obtenerlo y configurarlo. (Podría hacer esto con la reflexión, concebiblemente, pero la reflexión es lenta. Buscar cosas en los diccionarios es rápido.)
- Este algo, que es el sistema de propiedad de la dependencia, se ubica esencialmente entre un objeto y sus propiedades de dependencia. Y puede hacer todo tipo de cosas.
¿Qué tipo de cosas? Bueno, veamos algunos casos de uso.
Encuadernación. Cuando se vincula a una propiedad, tiene que ser una propiedad de dependencia. Esto se debe a que el objeto Binding
no establece propiedades en el destino, sino que llama al SetValue
en el objeto de destino.
Estilos. Cuando establece la propiedad de dependencia de un objeto en un nuevo valor, SetValue
le dice al sistema de estilo que lo ha hecho. Así es como funcionan los desencadenantes: no descubren que el valor de una propiedad ha cambiado a través de la magia, el sistema de propiedad de la dependencia les dice.
Recursos dinámicos. Si escribe XAML como Background={DynamicResource MyBackground}
, puede cambiar el valor del recurso MyBackground
y se actualizará el fondo del objeto que hace referencia a él. Esto tampoco es magia; el recurso dinámico llama al SetValue
.
Animaciones. Las animaciones funcionan manipulando valores de propiedad. Esos tienen que ser propiedades de dependencia, porque la animación llama al SetValue
para obtenerlos.
Cambiar la notificación. Cuando registra una propiedad de dependencia, también puede especificar una función a la que llamará SetValue
cuando establezca el valor de la propiedad.
Herencia de valor. Cuando registra una propiedad de dependencia, puede especificar que participe en la herencia de valor de propiedad. Cuando llama al GetValue
para obtener el valor de la propiedad de dependencia de un objeto, GetValue
busca para ver si hay un valor local. Si no lo hay, atraviesa la cadena de objetos principales mirando sus valores locales para esa propiedad.
Así es como puedes configurar el FontFamily
en un Window
y mágicamente (estoy usando esa palabra mucho) cada control en la ventana usa la nueva fuente. Además, es cómo se pueden tener cientos de controles en una ventana sin que cada uno de ellos tenga una variable de miembro FontFamily
para rastrear su fuente (ya que no tienen valores locales) pero aún puede establecer FontFamily
en cualquier control (debido al diccionario de valores ocultos de seekrit que tiene cada DependencyObject
).
Ah, buena información, gracias. Ok, entonces, ¿cómo funciona la magia, que permite que un control secundario tenga acceso al DataContext de la ventana antecesor? ¿Me puede dar un enlace a una reseña de esto? – Cheeso
@ Cheeso: Busque la "herencia de valor de propiedad" aquí: http://msdn.microsoft.com/en-us/library/ms753391.aspx Hay un poco de esto en esa página ... –