Propiedades de dependencia personalizadas

Propiedades de dependencia personalizadas

[ Este artículo está destinado a desarrolladores de Windows 8.x y Windows Phone 8.x que escriben aplicaciones de Windows Runtime. Si estás desarrollando para Windows 10, consulta la documentación más reciente ]

Aquí te explicamos la manera de definir e implementar tus propias propiedades de dependencia para una aplicación de Windows en tiempo de ejecución con C++, C# o Visual Basic. Enumeramos los motivos por los que los desarrolladores de aplicaciones y los creadores de componentes podrían querer crear propiedades personalizadas. También describimos los pasos para implementar una propiedad de dependencia personalizada, así como los procedimientos recomendados que pueden mejorar el rendimiento, la capacidad de uso o la versatilidad de la propiedad de dependencia.

Requisitos previos

Damos por sentado que has leído la introducción a las propiedades de dependencia y que conoces las propiedades de dependencia desde la perspectiva de un usuario de propiedades de dependencia existentes. Para seguir los ejemplos de este tema, debes conocer XAML y saber cómo escribir una aplicación básica de Windows en tiempo en ejecución con C++, C# o Visual Basic.

¿Qué es una propiedad de dependencia?

Las propiedades de dependencia son propiedades que se registran en el sistema de propiedades de Windows en tiempo de ejecución mediante una llamada al método DependencyProperty.Register, y que se identifican por un miembro identificador DependencyProperty en la clase definidora. Puedes implementar como propiedad de dependencia lo que en otras circunstancias sería una propiedad de Common Language Runtime (CLR) o de C++ para admitir estilos, enlace de datos, animaciones y valores predeterminados. Las propiedades de dependencia solo pueden ser utilizadas por tipos DependencyObject, aunque DependencyObject está en una posición bastante alta de la jerarquía de clases, por lo que la mayoría de las clases diseñadas para soporte de interfaz de usuario y de presentación pueden admitir las propiedades de dependencia. Para obtener más información acerca de las propiedades de dependencia y alguna de la terminología y las convenciones usadas para describirlas en esta documentación, consulta Introducción a las propiedades de dependencia.

Algunos ejemplos de las propiedades de dependencia en Windows en tiempo de ejecución son Control.Background, FrameworkElement.Width y TextBox.Text, entre otros muchos. Cada propiedad de dependencia expuesta por una clase tiene una propiedad public static readonly correspondiente del tipo DependencyProperty que se expone en esa misma clase y que es el identificador de la propiedad de dependencia. El nombre del identificador sigue esta convención: el nombre de la propiedad de dependencia, con la cadena "Property" agregada al final del nombre. Por ejemplo, el identificador DependencyProperty correspondiente a la propiedad Control.Background es Control.BackgroundProperty. El identificador almacena la información acerca de la propiedad de dependencia con la que se registró, y se puede usar más adelante para otras operaciones que implican a la propiedad de dependencia, como llamar a SetValue.

Contenedores de propiedades

Normalmente, las propiedades de dependencia tienen una implementación de contenedor. Sin el contenedor, la única manera de obtener o establecer las propiedades sería usar los métodos de utilidad de propiedades de dependencia GetValue y SetValue, y pasar el identificador como un parámetro. Este es un uso bastante forzado para algo que evidentemente es una propiedad. Pero con el contenedor, tu código y cualquier otro código que haga referencia a la propiedad de dependencia puede usar una sintaxis objeto-propiedad directa que resulte natural para el lenguaje que estés usando.

Si implementas tú mismo una propiedad de dependencia personalizada y quieres que sea pública y fácil de llamar, define también los contenedores de la propiedad. Los contenedores también resultan útiles para comunicar información básica sobre la propiedad de dependencia a procesos de análisis estáticos o de reflexión. Específicamente, el contenedor es el lugar donde se colocan atributos como ContentPropertyAttribute.

Cuándo implementar una propiedad como propiedad de dependencia

Siempre que implementes una propiedad de lectura y escritura en una clase, si esta deriva de DependencyObject, tienes la opción de hacer que tu propiedad funcione como propiedad de dependencia. Algunas veces, la técnica habitual de respaldar la propiedad con un campo privado es adecuada. No siempre es necesario o apropiado definir tu propiedad personalizada como propiedad de dependencia. La elección dependerá de los escenarios que quieras que admita tu propiedad.

Podrías considerar implementarla como propiedad de dependencia cuando quieras que admita una o varias de estas características de Windows en tiempo de ejecución o de las aplicaciones de Windows en tiempo de ejecución:

  • Establecer la propiedad mediante un Style
  • Actuar como propiedad de destino válida para enlace de datos
  • Admitir valores animados mediante un Storyboard
  • Notificar el momento en que el valor anterior de la propiedad ha sido modificado por:
    • Acciones llevadas a cabo por el propio sistema de propiedades
    • El entorno
    • Acciones del usuario
    • Estilos de lectura y escritura

Lista de comprobación para definir una propiedad de dependencia

La definición de una propiedad de dependencia se puede considerar un conjunto de conceptos. Estos conceptos no son necesariamente pasos de procedimientos, porque varios de ellos pueden abordarse en una única línea de código en la implementación. Esta lista ofrece solo una introducción rápida. Explicaremos cada concepto con más detalle más adelante en este tema y mostraremos ejemplos de código en varios lenguajes.

  • (Opcional) Crear metadatos de propiedad para la propiedad de dependencia. Solo necesitas los metadatos de propiedad si quieres un comportamiento modificado por propiedades, o un valor predeterminado basado en metadatos que se pueda restablecer llamando a ClearValue.

  • Registrar el nombre de la propiedad en el sistema de la propiedad (llama a Register), especificando un tipo propietario y el tipo del valor de la propiedad. Un parámetro requerido por Register espera metadatos de propiedad. Especifica null para esto o especifica los metadatos de propiedad reales si has declarado alguno.

  • Definir un identificador DependencyProperty como un miembro de propiedad public static readonly en el tipo propietario.

  • Definir una propiedad de contenedor, siguiendo el modelo de descriptor de acceso de propiedad usado en el lenguaje que estés implementando. El nombre de la propiedad de contenedor debe coincidir con la cadena name que usaste en Register. Implementa los descriptores de acceso get y set para conectar el contenedor con la propiedad de dependencia que contiene; para ello, llama a GetValue y SetValue y pasa tu propio identificador de propiedad como un parámetro.
  • (Opcional) Colocar atributos como ContentPropertyAttribute en el contenedor.
Nota  Si estás definiendo una propiedad adjunta personalizada, normalmente omites el contenedor. En su lugar, escribes un estilo diferente de descriptor de acceso que un procesador XAML puede usar. Consulta Propiedades adjuntas personalizadas.
 

Registro de la propiedad

Para que tu propiedad sea una propiedad de dependencia, debes registrarla en un almacén de propiedades mantenido por el sistema de propiedades de Windows en tiempo de ejecución. Debes asignar a la propiedad un identificador único para usarlo como cualificador más adelante en operaciones del sistema de propiedades. Estas operaciones podrían ser internas o llamadas de tu propio código a las API del sistema de propiedades. Para registrar la propiedad, llama al método Register.

En los lenguajes de Microsoft .NET (C# y Microsoft Visual Basic), llama a Register en el cuerpo de la clase (dentro de la clase pero fuera de las definiciones de miembro). La llamada al método Register también proporciona este identificador, como valor de retorno. La llamada a Register normalmente se realiza fuera de otras definiciones de miembro porque el valor de retorno se usa para asignar y crear una propiedad public static readonly del tipo DependencyProperty como parte de tu clase. Esta propiedad se convierte en el identificador de tu propiedad de dependencia. Estos son algunos ejemplos de la llamada a Register.


public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
  "Label",
  typeof(String),
  typeof(ImageWithLabelControl),
  new PropertyMetadata(null)
);

Nota  La implementación típica es registrar la propiedad de dependencia en el cuerpo de una clase, pero también puedes registrarla en el constructor estático de la clase. Este enfoque tiene sentido si necesitas más de una línea de código para inicializar la propiedad de dependencia.
 

En C++, tienes opciones para dividir la implementación entre el encabezado y el archivo de código. La división típica es declarar el propio identificador como propiedad public static en el encabezado, con una implementación de get pero sin set. La implementación de get hace referencia a un campo privado, que es una instancia de DependencyProperty sin inicializar. También puedes declarar los contenedores y las implementaciones de get y set del contenedor. En este caso, el encabezado incluye una implementación mínima. Si el contenedor necesita atribución de Windows en tiempo de ejecución, inclúyela también en el encabezado. Pon la llamada de Register en el archivo de código, dentro de la función auxiliar que solo se ejecuta cuando la aplicación se inicializa por primera vez. Usa el valor devuelto de Register para rellenar los identificadores estáticos pero sin inicializar que has declarado en el encabezado, que estableciste inicialmente en nullptr, en el ámbito raíz del archivo de implementación.


//.h file
//using namespace Windows::UI::Xaml::Controls;
//using namespace Windows::UI::Xaml::Interop;
//using namespace Windows::UI::Xaml;
//using namespace Platform;

public ref class ImageWithLabelControl sealed : public Control
{  
private:
    static DependencyProperty^ _LabelProperty;
...
public:
    static void RegisterDependencyProperties(); 
    static property DependencyProperty^ LabelProperty
    {
        DependencyProperty^ get() {return _LabelProperty;}
    }
...
};



//.cpp file
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml.Interop;

DependencyProperty^ ImageWithLabelControl::_LabelProperty = nullptr;

// This function is called from the App constructor in App.xaml.cpp 
// to register the properties
void ImageWithLabelControl::RegisterDependencyProperties() 
{ 
    if (_LabelProperty == nullptr) 
    { 
        _LabelProperty = DependencyProperty::Register(
          "Label", Platform::String::typeid, ImageWithLabelControl::typeid, nullptr); 
    } 
}

Nota  Para el código C++, el motivo de tener un campo privado y una propiedad pública de solo lectura que muestra la DependencyProperty es que, de esta manera, otros llamadores que usan tu propiedad de dependencia también pueden usar las API de utilidad del sistema de propiedades que requieran que el identificador sea público. Si haces que el identificador sea privado, los usuarios no podrán usar estas API de utilidad. Algunos ejemplos de estas API y escenarios son GetValue o SetValue a elegir, ClearValue, GetAnimationBaseValue, SetBinding y Setter.Property. No puedes usar un campo público para esto porque las reglas de compilación de Windows en tiempo de ejecución no admiten miembros de datos públicos que usan tipos de referencia como DependencyProperty.
 

Convenciones de nomenclatura para propiedades de dependencia

Existen convenciones de nomenclatura para las propiedades de dependencia; síguelas siempre salvo circunstancias excepcionales. La propiedad de dependencia en sí tiene un nombre básico ("Label" en el ejemplo anterior) que se proporciona como el primer parámetro de Register. El nombre debe ser único en cada tipo de registro, y este requisito también se aplica a todos los miembros heredados. Las propiedades de dependencia heredadas mediante tipos base ya se consideran parte del tipo de registro; los nombres de las propiedades heredadas no se pueden volver a registrar.

Precaución  Aunque el nombre que proporciones aquí puede ser cualquier identificador de cadena que sea válido en la programación del lenguaje que uses, normalmente también querrás poder establecer tu propiedad de dependencia en XAML. Para poder establecerse en XAML, el nombre de propiedad que elijas debe ser un nombre XAML válido. Para obtener más información, consulta Introducción a XAML.
 

Cuando crees la propiedad de identificador, combina el nombre de la propiedad tal y como lo registraste con el sufijo "Property" ("LabelProperty", por ejemplo). Esta propiedad es el identificador de la propiedad de dependencia y se usa como entrada para las llamadas a SetValue y GetValue que hagas en tus propios contenedores de propiedad. También puede usarlo el sistema de propiedades y, posiblemente, los procesadores XAML.

Implementación del contenedor

Tu contenedor de propiedad debe llamar a GetValue en la implementación de get, y a SetValue en la implementación de set.

Precaución  Salvo en circunstancias excepcionales, las implementaciones del contenedor solo deben realizar las operaciones GetValue y SetValue. De lo contrario, el comportamiento cuando la propiedad se establece mediante XAML será diferente de cuando se establece mediante código. Por motivos de eficacia, el analizador XAML omite los contenedores al establecer las propiedades de dependencia; siempre que es posible, utiliza el Registro de propiedades de dependencia.
 

public String Label
{
    get { return (String)GetValue(LabelProperty); }
    set { SetValue(LabelProperty, value); }
}

Metadatos de propiedad para una propiedad de dependencia personalizada

Cuando se asignan metadatos de propiedad a una propiedad de dependencia, se aplican los mismos metadatos a dicha propiedad en todas las instancias del tipo propietario de la propiedad o sus subclases. En los metadatos de propiedad, puedes especificar dos comportamientos:

  • Un valor predeterminado que el sistema de propiedades asigna a todas las clases de la propiedad.
  • Un método de devolución de llamada estático que se invoca automáticamente en el sistema de propiedades siempre que se detecta un valor de propiedad.

Llamada al Registro con metadatos de propiedad

En los anteriores ejemplos de llamada a DependencyProperty.Register, pasamos un valor nulo para el parámetro propertyMetadata. Para habilitar una propiedad de dependencia para que proporcione un valor predeterminado o use una devolución de llamada modificada por propiedades, deberás definir una instancia de PropertyMetadata que proporcione una de estas funcionalidades o las dos.

Normalmente deberás proporcionar un PropertyMetadata como instancia creada en línea, dentro de los parámetros de DependencyProperty.Register.

Nota  Si vas a definir una implementación de CreateDefaultValueCallback, deberás usar el método de utilidad PropertyMetadata.Create en vez de llamar a un constructor PropertyMetadata para definir la instancia de PropertyMetadata.
 

Este siguiente ejemplo modificar los ejemplos mostrados anteriormente DependencyProperty.Register al hacer referencia a una instancia de PropertyMetadata con un valor de PropertyChangedCallback. La implementación de la devolución de llamada "OnLabelChanged" se mostrará más adelante en esta sección.


public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
  "Label",
  typeof(String),
  typeof(ImageWithLabelControl),
  new PropertyMetadata(null,new PropertyChangedCallback(OnLabelChanged))
);

Valor predeterminado

Puedes especificar un valor predeterminado para una propiedad de dependencia de tal forma que la propiedad siempre devuelva un valor predeterminado particular cuando esté sin establecer. Este valor puede ser distinto que el valor predeterminado inherente del tipo de esa propiedad.

Si no se especifica un valor predeterminado, el valor predeterminado de una propiedad de dependencia es null para un tipo de referencia, o el valor predeterminado del tipo para un tipo de valor o tipo primitivo del lenguaje (por ejemplo, 0 para un entero o una cadena vacía para una cadena). El principal motivo para establecer un valor predeterminado es que este valor se restablece al llamar a ClearValue en la propiedad. Quizás sea más conveniente establecer un valor predeterminado para cada propiedad que establecer valores predeterminados en los constructores, especialmente para los tipos de valor. Sin embargo, para los tipos de referencia, asegúrate de que al establecer un valor predeterminado no se crea un patrón de singleton no intencionado. Para obtener más información, consulta los Procedimientos recomendados más adelante en este tema

Nota  No realices el registro con un valor predeterminado de UnsetValue. Esto confundirá a los usuarios de la propiedad y tendrá consecuencias imprevistas en el sistema de propiedades.
 

CreateDefaultValueCallback

En algunos escenarios, definirás propiedades de dependencias para objetos que se usan en más de un subproceso de interfaz de usuario. Este podría ser el caso si vas a definir un objeto de datos que usen varias aplicaciones, o un control que uses en más de una aplicación. Puedes habilitar el intercambio del objeto entre distintos subprocesos de interfaz de usuario proporcionando una implementación de CreateDefaultValueCallback en vez de una instancia de valor predeterminado, que está vinculada al subproceso que registró la propiedad. Básicamente, una CreateDefaultValueCallback define una fábrica de valores predeterminados. El valor devuelto por CreateDefaultValueCallback siempre está asociado al actual subproceso de interfaz de usuario CreateDefaultValueCallback que usa el objeto.

Para definir metadatos que especifiquen una CreateDefaultValueCallback, deberás llamar a PropertyMetadata.Create para volver a una instancia de metadatos; los constructores PropertyMetadata no tienen una firma que incluya un parámetro CreateDefaultValueCallback.

El patrón de implementación típico de una CreateDefaultValueCallback consiste en crear una nueva clase DependencyObject, establecer el valor de propiedad específico de cada propiedad del DependencyObject en el valor predeterminado previsto y devolver la nueva clase como una referencia de Object mediante el valor devuelto del método CreateDefaultValueCallback.

Método de devolución de llamada modificado por propiedades

Puedes definir un método de devolución de llamada modificado por propiedades para definir las interacciones de tu propiedad con las demás propiedades de dependencia, o para establecer una propiedad o un estado interno del objeto siempre que la propiedad cambie. Si se invoca tu devolución de llamada, el sistema de propiedades ha determinado que hay un cambio efectivo en el valor de propiedad. Como el método de devolución de llamada es estático, el parámetro d de la devolución de llamada es importante porque te dice qué instancia de la clase ha notificado el cambio. Una implementación típica usa la propiedad NewValue de los datos del evento y realiza algún procesamiento en ese valor, normalmente realizando otros cambios en el objeto pasado como d. Otras respuestas al cambio de una propiedad son rechazar el valor notificado por NewValue, para restablecer OldValue, o aplicar a NewValue un valor restringido mediante programación.

El siguiente ejemplo muestra una implementación de PropertyChangedCallback. Implementa el método al que se hizo referencia en los ejemplos de Register anteriores, como parte de los argumentos de construcción de PropertyMetadata. En el escenario al que se dirige esta devolución de llamada, la clase también tiene una propiedad calculada de solo lectura denominada "HasLabelValue" (no se muestra la implementación). Siempre que se vuelve a evaluar la propiedad "Label", se invoca a este método de devolución de llamada y la devolución de llamada permite que el valor calculado dependiente permanezca sincronizado con los cambios de la propiedad de dependencia.


private static void OnLabelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    ImageWithLabelControl iwlc = d as ImageWithLabelControl; //null checks omitted
    String s = e.NewValue as String; //null checks omitted
    if (s == String.Empty)
    {
        iwlc.HasLabelValue = false;
    } else {
        iwlc.HasLabelValue = true;
    }
}

Comportamiento modificado por propiedades para estructuras y enumeraciones

Si una DependencyProperty es una enumeración o una estructura, la devolución de llamada podría invocarse incluso si los valores internos de la estructura o el valor de la enumeración no cambiaron. Esto es distinto de un primitivo del sistema, como una cadena donde solo se invoca si el cambia el valor. Esto es un efecto secundario de las operaciones de conversiones boxing y unboxing en estos valores que se realizan internamente. Si tienes un método PropertyChangedCallback para una propiedad en la que el valor es una enumeración o una estructura, deberás compara los valores OldValue y NewValue difundiendo tú mismo los valores y usando los operadores de comparación sobrecargados disponibles para los valores ahora difundidos. O, si ninguno de estos operadores está disponible (lo que podría ocurrir en caso de una estructura personalizada), es posible que tengas que comparar los valores individuales. Normalmente no tendrías que hacer nada si el resultado es que los valores no han cambiado.



private static void OnVisibilityValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    if ((Visibility)e.NewValue != (Visibility)e.OldValue)
    {
        //value really changed, invoke your changed logic here
    } // else this was invoked because of boxing, do nothing
}

Procedimiento recomendado

Toma las consideraciones siguientes como procedimientos recomendados cuando definas tu propiedad de dependencia personalizada.

DependencyObject y subprocesos

Todas las instancias de DependencyObject deben crearse en el subproceso de interfaz de usuario asociado a la clase Window actual mostrada por una aplicación de Windows en tiempo de ejecución. Aunque cada DependencyObject debe crearse en el subproceso de interfaz de usuario, se puede tener acceso a los objetos mediante una referencia de distribuidor desde otros subprocesos llamando a Dispatcher.

Los aspectos de subprocesos de DependencyObject son relevantes porque, por lo general, significa que solo el código que se ejecuta en el subproceso de interfaz de usuario puede cambiar o incluso leer el valor de una propiedad de dependencias. Los problemas de subprocesos normalmente se pueden evitar en código típico de interfaz de usuario que haga un uso correcto de los patrones async y de los subprocesos de trabajo en segundo plano. Normalmente solo te encontrarás con problemas de subprocesos relacionados con DependencyObject si vas a definir tipos de DependencyObject e intentas usarlos para los orígenes de datos u otros escenarios donde un DependencyObject no es necesariamente lo adecuado.

Evitar singleton no intencionados

Se puede producir un singleton no intencionado si declaras una propiedad de dependencia que toma un tipo de referencia y llamas a un constructor para este tipo de referencia como parte del código que establece tu PropertyMetadata. Lo que sucede es que todos los usos de la propiedad de dependencia comparten tan solo una instancia de PropertyMetadata y, por consiguiente, intentan compartir el único tipo de referencia que has construido. Después, las subpropiedades de ese tipo de valor que estableces mediante la propiedad de dependencia se propagan a los demás objetos de maneras que, probablemente, no pretendías.

Puedes usar constructores de clases para establecer los valores iniciales de una propiedad de dependencia de tipo de referencia si deseas un valor no null, pero ten en cuenta que este debe considerarse un valor local en términos de precedencia de las propiedades de dependencia. Sería más apropiado usar una plantilla para este propósito, si tu clase admite plantillas. Otra manera de evitar un patrón de singleton y seguir proporcionando un valor predeterminado útil, es exponer una propiedad estática en el tipo de referencia que proporcione valores predeterminados apropiados para dicha clase.

Propiedades de dependencia de tipo de colección

Las propiedades de dependencia de tipo de colección presentan algunas dificultades de implementación adicionales que debes tener en cuenta.

Las propiedades de dependencia de tipo de colección son relativamente raras en la API de Windows en tiempo de ejecución. En la mayoría de los casos, puedes usar colecciones en las que los elementos sean una subclase DependencyObject, pero la propiedad de colección en sí se implementa como una propiedad convencional de CLR o C++. El motivo es que las colecciones no son necesariamente adecuadas para algunos escenarios habituales en los que hay implicadas propiedades de dependencia. Por ejemplo:

  • Normalmente no animas una colección.
  • No sueles rellenar previamente los elementos de una colección con estilos o una plantilla.
  • Aunque el enlace a colecciones es un escenario importante, una colección no necesita ser una propiedad de dependencia para ser un origen de enlace. En el caso de los destinos de enlace, es más habitual usar subclases de ItemsControl o DataTemplate para admitir elementos de colección o para usar patrones de modelo de vista. Para obtener más información sobre el enlace hacia o desde colecciones, consulta Enlace de datos con XAML.
  • Las notificaciones de los cambios en una colección se controlan mejor mediante interfaces como INotifyPropertyChanged o INotifyCollectionChanged, o derivando el tipo de colección de ObservableCollection.

En cualquier caso, existen escenarios para propiedades de dependencia de tipo de colección. Las siguientes tres secciones proporcionan una orientación sobre cómo implementar una propiedad de dependencia de tipo de colección.

Inicialización de la colección

Al crear una propiedad de dependencia, puedes establecer un valor predeterminado mediante metadatos de propiedad de dependencia. Ten cuidado de no usar una colección estática de singleton como valor predeterminado. En su lugar, debes establecer deliberadamente el valor de la colección en una colección única (instancia) como parte de la lógica del constructor de la clase propietaria de la propiedad de colección.

Notificación de cambios

Definir la colección como una propiedad de dependencia no proporciona automáticamente notificaciones para los elementos de la colección cuando el sistema de propiedades invoca el método de devolución de llamadas "PropertyChanged". Si deseas notificaciones para colecciones o elementos de colección,— por ejemplo, un escenario de enlace de datos, — implementa la interfaz INotifyPropertyChanged o INotifyCollectionChanged. Para obtener más información, consulta Enlace de datos con XAML.

Consideraciones de seguridad de las propiedades de dependencia

Declara las propiedades de dependencia como propiedades públicas. Declara los identificadores de las propiedades de dependencia como miembros de solo lectura, estáticos y públicos. Aunque intentes declarar otros niveles de acceso permitidos por un lenguaje (como protected), una propiedad de dependencia siempre es accesible mediante el identificador en combinación con las API del sistema de propiedades. Declarar el identificador de la propiedad de dependencia como interno o privado no funcionará porque el sistema de propiedades no podría funcionar correctamente.

En realidad, las propiedades de contenedor se utilizan por conveniencia. Los mecanismos de seguridad que se aplican a los contenedores se pueden omitir con llamadas a GetValue o SetValue. Por lo tanto, haz que las propiedades de contenedor sean públicas; de lo contrario, solo conseguirás que la propiedad sea más difícil de usar para los usuarios legítimos sin proporcionar a cambio ninguna ventaja en cuanto a seguridad.

Windows en tiempo de ejecución no ofrece ninguna manera de registrar una propiedad de dependencia personalizada como de solo lectura.

Propiedades de dependencia y constructores de clases

Existe un principio general por el que los constructores de clases no deben llamar a métodos virtuales. El motivo es que se puede llamar a los constructores para realizar la inicialización de base de un constructor de clases derivado, y la entrada en el método virtual a través del constructor podría ocurrir cuando la instancia del objeto que se está construyendo aún no se ha inicializado por completo. Al derivar de una clase que ya deriva de DependencyObject, recuerda que el propio sistema de propiedades llama y expone los métodos virtuales internamente como parte de sus servicios. Para evitar posibles problemas con la inicialización en tiempo de ejecución, establece los valores de las propiedades de dependencia en los constructores de clases.

Registro de las propiedades de dependencia de las aplicaciones C++/CX

La implementación para registrar una propiedad en C++/CX es más complicada que en C#C#, tanto debido a la separación en encabezado y archivo de implementación, como debido a la inicialización en el ámbito raíz del archivo de implementación como una práctica errónea. (Las extensiones de componente Visual C++ (C++/CX) ponen el código del inicializador estático desde el ámbito raíz directamente en DllMain, mientras que los compiladores C# asignan los inicializadores estáticos a clases y, por lo tanto, evitan problemas de bloqueo de carga de DllMain). En este caso, la mejor práctica consiste en declarar una función auxiliar que se encargue de todo el registro de la propiedad de dependencia de una clase, una función por clase. Luego, por cada clase personalizada que la aplicación consuma, tendrás que hacer referencia a la función de registro del auxiliar que todas las clases personalizadas que quieres utilizar exponen. Llama una vez a cada función de registro del auxiliar como parte del Application constructor (App::App()), antes de InitializeComponent. Ese constructor solo se ejecuta cuando se hace realmente referencia a la aplicación por primera vez. No se volverá a ejecutar si, por ejemplo, se reanuda una aplicación suspendida. Además, tal como se ha visto en el ejemplo de registro de C++ anterior, la comprobación de nullptr en torno a cada llamada de Register es importante: garantiza que ningún llamador de la función pueda registrar dos veces la propiedad. Una segunda llamada de registro probablemente bloquearía la aplicación sin esa comprobación, ya que el nombre de la propiedad sería un duplicado. Puedes ver este patrón de implementación en el ejemplo de controles personalizados y de usuario XAML si buscas en el código la versión C++/CX del ejemplo.

Temas relacionados

DependencyObject
DependencyProperty.Register
Introducción a las propiedades de dependencia
Muestra de controles de usuario y personalizados de XAML

 

 

Mostrar:
© 2017 Microsoft