Entendiendo las propiedades de dependencia en Silverligth

He decidido escribir este post, como ayuda para la cápsula de Silverligt relacionada con la implementación de Content Controls, en donde se requiere definir para el Content Control una propiedad de este tipo.

Haber, las propiedades de dependencia, aunque sin de pronto saberlo, las has usado cada vez que le asignas su valor a una propiedad de las principales de los controles que hemos visto: TextBox, CheckBox, Button o TextBlock. Ello porque las propiedades principales de los controles que mayormente usamos, todas permiten en un momento determinado la aplicación de: Animaciones, Estilos o Enlace a datos, como se verá en otras cápsulas.

Las propiedades de dependencia son entonces propiedades que permiten extender la funcionalidad de la propia propiedad. Es decir, tomemos como ejemplo, la propiedad Text del control TextBlock. Con esta propiedad, podemos definir el texto que muestra el TextBlock, allí usamos la funcionalidad normal de la propiedad Text. Sin embargo, como veremos en cápsulas sucesivas, se puede utilizar la propiedad Text, para mostrar la información proveniente de una fuente de datos, es decir, podemos enlazar la propiedad a una fuente de datos con una sintaxis dada:

<TextBlock Text="{Binding Team.TeamName}"/>

En este caso, la propiedad Text se enlaza a una fuente de datos, para tomar el nombre de un equipo.

Por ello, la propiedad Text es una propiedad de dependencia, porque, en un momento dado, permite extender su funcionalidad básica que es definir el texto para el TextBlock y, en este caso particular, enlazarse previamente a una fuente de datos para tomar la información.

Los objetos o controles de Silverligt son objetos DependencyObject y deben serlo porque la mayoría de ellos permiten la animación, enlazado a datos o la aplicación de estilos de al menos una propiedad y para permitir dichas acciones sobre la propiedad éstas deben ser propiedades de dependencia que solo se pueden definir en objetos de dependencia.

Para que un objeto permita en una propiedad de dependencia, el enlazado a datos, debe también heredar de FrameworkElement.

En el caso del TextBlock, podemos enlazar la propiedad Text a una fuente de datos y como muestra la imagen, vemos, en su jerarquía de clase, que hereda de FrameworkElement:

JerarquiaHerenciaTextBlock

También, vemos como es un DependencyObject.

En Silverligth, nosotros podemos definir nuestras propias propiedades de dependencia para proporcionarles a los Content Controles, User Controls o demás tipos de controles que implementemos, propiedades que permitan el enlazado de datos, el establecimiento de animaciones o la aplicación de estilos cuando sea requerido.

Las propiedades de dependencia se definen, en código, de manera similar a las propiedades convencionales clásicas que se respaldaban en un campo privado. A continuación, el comparativo:

Propiedad clásica convencional

Propiedad de dependencia

private bool isSpinning;

 

 

 

 

 

public bool IsSpinning

{

get { return isSpinning;}

set { isSpinning = value;}

}

public static readonly DependencyProperty IsSpinningProperty =

DependencyProperty.Register(

"IsSpinning", typeof(Boolean),

typeof(SilverlightExampleClass), null

);

 

public bool IsSpinning

{

get { return (bool)GetValue(IsSpinningProperty); }

set { SetValue(IsSpinningProperty, value); }

}

Es decir, en lugar del campo privado se usa el tipo DependencyProperty estático público y de solo lectura, que por convención lleva el nombre de la propiedad de dependencia seguido del término Property (<NombrePropiedad>Property. Ejemplo:IsSpinningProperty);  y para obtener y asignar el valor de la propiedad se deben usar los métodos GetValue y SetValue.

También notar como la propiedad de dependencia se debe registrar utilizando el método DependencyProperty.Register.

Que recibe como parámetros:

  • El nombre de la propiedad de dependencia a registrar
  • Su tipo
  • El tipo de la clase donde se registra la propiedad de dependencia
  • Y, por último, de manera opcional, información de metadata de la propiedad de dependencia.

En resumen, entonces, tenemos que las propiedades de dependencia permiten extender la funcionalidad de la propiedad para permitir: Animaciones, enlace a datos o aplicación de estilos. Sin embargo, las propiedades de dependencia presentan otra característica importante y es la de proporcionar devoluciones de llamada que puedan propagar sus cambios a otras propiedades. Es decir, las devoluciones de llamada se pueden utilizar para notificar o cambiar los valores de las propiedades relacionadas, de un modo sincrónico en general.

Para usar las devoluciones de llamada, las definimos en la información de metadata del método de registro. Por ejemplo:

public static readonly DependencyProperty HeaderContentProperty =
    DependencyProperty.Register("HeaderContent", typeof(object), typeof(MiContentControl),
    new PropertyMetadata(new TextBlock() { Text="Usar un TextBlock"}, new PropertyChangedCallback(OnHeaderContentChanged)));
 
static void OnHeaderContentChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    MiContentControl miContentControl = (MiContentControl)sender;
 
    if (!(e.NewValue is TextBlock))
    {
        miContentControl.ClearValue(HeaderContentProperty);
    }
}
 
public object HeaderContent { 
    get { return (object)GetValue(HeaderContentProperty); } 
    set { SetValue(HeaderContentProperty, value); } 
}



Notar como, en este caso, definimos valor para la información de Metadata, en el método de registro:


new PropertyMetadata(new TextBlock() { Text="Usar un TextBlock"}, new PropertyChangedCallback(OnHeaderContentChanged))


El objeto PropertyMetadata recibe dos parámetros:



  • El valor por defecto de la propiedad. Que para el caso es:

        new TextBlock() { Text="Usar un TextBlock"}



  • La definición de la devolución de la llamada. Y corresponde a:

         new PropertyChangedCallback(OnHeaderContentChanged)


Es en la implementación de la devolución de llamada donde se puede hacer la modificación de las propiedades relacionadas con base al valor de la propiedad de dependencia.


En el ejemplo, la implementación de la devolución de la llamada corresponde a:




static void OnHeaderContentChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    MiContentControl miContentControl = (MiContentControl)sender;
 
    if (!(e.NewValue is TextBlock))
    {        
 
       //Se asigna de nuevo como valor de la propiedad el definido por defecto
        miContentControl.ClearValue(HeaderContentProperty);
    }
}


Donde se define lo que ocurre cuando se modifica el encabezado de un Content Control.

Por defecto (resaltado de azul), se define que su valor será un TextBlock con el texto: Usar un TextBlock y en la implementación de la devolución de llamada, se controla que si el valor de la propiedad, luego de modificado por el usuario (NewValue), no es un TextBlock; se le asigna su valor por defecto. Esto se logra al llamar el método ClearValue pasándole como parámetro la propiedad de dependencia.

De esta forma, se usa la devolución de llamada, para controlar que solo se pueda usar un TextBlock como encabezado.

El código precedente, entonces, también ilustra otras características que poseen las propiedades de dependencia que son: la posibilidad de definirles un valor por defecto y la posibilidad de restablecer ese valor por defecto, en cualquier momento, por medio del método ClearValue que recibe como parámetro la propiedad de dependencia.

Finalmente, como también es ilustrado, en la implementación de la devolución de la llamada podemos acceder a la información de los distintos valores de la propiedad de dependencia para la toma de decisiones:

NewValue: Valor después de realizado el cambio del valor de la propiedad de dependencia.

OldValue: Valor de la propiedad antes de realizado el cambio.

Resumen, en mapa conceptual:

ResumenPropiedadDependencia

Comentarios

Entradas populares de este blog

Visual Studio 2012 Backup and Restore bases de datos

Configuración de expresiones en Quartz

Hacer que Windows XP luzca como Windows 7