¿Cuál es la diferencia entre una propiedad de dependencia y una propiedad adjunta en WPF?


91

¿Cuál es la diferencia entre una propiedad de dependencia (personalizada) y una propiedad adjunta en WPF? ¿Cuáles son los usos de cada uno? ¿En qué se diferencian normalmente las implementaciones?

Respuestas:


20

Resumen

Dado que encontré poca o ninguna documentación sobre el tema, me costó buscar en el código fuente , pero aquí hay una respuesta.

Existe una diferencia entre registrar una propiedad de dependencia como una propiedad regular y como una propiedad adjunta, que no sea una "filosófica" ( las propiedades regulares están destinadas a ser utilizadas por el tipo declarante y sus tipos derivados, las propiedades adjuntas están destinadas a ser utilizadas como extensiones en DependencyObject instancias arbitrarias ). "Filosófico", porque, como @MarqueIV notó en su comentario a la respuesta de @ ReedCopsey, las propiedades regulares también se pueden usar con DependencyObjectinstancias arbitrarias .

Además, no estoy de acuerdo con otras respuestas que afirman que la propiedad adjunta es un "tipo de propiedad de dependencia", porque es engañoso: no hay ningún "tipo" de propiedades de dependencia. Al marco no le importa si la propiedad se registró como adjunta o no, ni siquiera es posible determinarlo (en el sentido de que esta información no se registra, porque es irrelevante). De hecho, todas las propiedades se registran como si fueran propiedades adjuntas, pero en el caso de las regulares se realizan algunas cosas adicionales que modifican ligeramente su comportamiento.

Extracto del código

Para evitarle la molestia de revisar el código fuente usted mismo, aquí hay una versión resumida de lo que sucede.

Al registrar una propiedad sin metadatos especificados, llamar

DependencyProperty.Register(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass))

produce exactamente el mismo resultado que llamar

DependencyProperty.RegisterAttached(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass))

Sin embargo, al especificar metadatos, llamar

DependencyProperty.Register(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass),
    typeMetadata: new FrameworkPropertyMetadata
    {
        CoerceValueCallback = CoerceCallback,
        DefaultValue = "default value",
        PropertyChangedCallback = ChangedCallback
    });

es equivalente a llamar

var property = DependencyProperty.RegisterAttached(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass),
    defaultMetadata: new PropertyMetadata
    {
        DefaultValue = "default value",
    });
property.OverrideMetadata(
    forType: typeof(MyClass),
    typeMetadata: new FrameworkPropertyMetadata
    {
        CoerceValueCallback = CoerceCallback,
        DefaultValue = "default value",
        PropertyChangedCallback = ChangedCallback
    });

Conclusiones

La diferencia clave (y única) entre las propiedades de dependencia regulares y adjuntas son los metadatos predeterminados disponibles a través de la propiedad DependencyProperty.DefaultMetadata . Esto incluso se menciona en la sección Comentarios :

En el caso de las propiedades no adjuntas, el tipo de metadatos devuelto por esta propiedad no se puede convertir en tipos derivados del tipo PropertyMetadata , incluso si la propiedad se registró originalmente con un tipo de metadatos derivados. Si desea que los metadatos registrados originalmente incluyan su tipo de metadatos posiblemente derivado original, llame a GetMetadata (Type) en su lugar, pasando el tipo de registro original como parámetro.

Para las propiedades adjuntas, el tipo de metadatos devueltos por esta propiedad coincidirá con el tipo dado en el método de registro RegisterAttached original .

Esto es claramente visible en el código proporcionado. También se ocultan pequeñas pistas en los métodos de registro, es decir, para RegisterAttachedel parámetro de metadatos se nombra defaultMetadata, mientras que para Registerél se nombra typeMetadata. Para las propiedades adjuntas, los metadatos proporcionados se convierten en los metadatos predeterminados. Sin embargo, en el caso de propiedades normales, los metadatos predeterminados son siempre una instancia nueva de PropertyMetadatacon solo un DefaultValueconjunto (ya sea de los metadatos proporcionados o automáticamente). Solo la llamada posterior a OverrideMetadatautiliza realmente los metadatos proporcionados.

Consecuencias

La principal diferencia práctica es que, en el caso de propiedades regulares, CoerceValueCallbacky PropertyChangedCallbackson aplicables solo para tipos derivados del tipo declarado como tipo propietario, y para propiedades adjuntas son aplicables para todos los tipos. Por ejemplo, en este escenario:

var d = new DependencyObject();
d.SetValue(SomeClass.SomeProperty, "some value");

el registrado PropertyChangedCallback se llamará si la propiedad se registró como una propiedad adjunta, pero no se llamará si se registró como una propiedad regular. Lo mismo ocurre con CoerceValueCallback.

Una diferencia secundaria se deriva del hecho de que OverrideMetadatarequiere que el tipo suministrado se derive de DependencyObject. En la práctica, significa que el tipo propietario de las propiedades regulares debe derivar de DependencyObject, mientras que las propiedades adjuntas pueden ser de cualquier tipo (incluidas clases estáticas, estructuras, enumeraciones, delegados, etc.).

Suplemento

Además de la sugerencia de @ MarqueIV, en varias ocasiones me he encontrado con opiniones de que las propiedades regulares y adjuntas difieren en la forma en que se pueden usar en XAML . Es decir, que las propiedades regulares requieren una sintaxis de nombre implícita en contraposición a la sintaxis de nombre explícita requerida por las propiedades adjuntas. Esto no es técnicamente cierto , aunque en la práctica suele ser así. Para mayor claridad:

<!-- Implicit property name -->
<ns:SomeClass SomeProperty="some value" /> 

<!-- Explicit property name -->
<DependencyObject ns:SomeClass.SomeProperty="some value" />

En XAML puro , las únicas reglas que rigen el uso de estas sintaxis son las siguientes:

  • La sintaxis de nombre implícita se puede usar en un elemento si y solo si la clase que representa este elemento tiene una propiedad CLR de ese nombre
  • La sintaxis de nombre explícita se puede usar en un elemento si y solo si la clase especificada por la primera parte del nombre completo expone los métodos get / set estáticos apropiados (denominados descriptores de acceso ) con nombres que coinciden con la segunda parte del nombre completo

El cumplimiento de estas condiciones le permite utilizar la sintaxis correspondiente independientemente de si la propiedad de dependencia de respaldo se registró como regular o adjunta.

Ahora, el error mencionado se debe al hecho de que la gran mayoría de los tutoriales (junto con los fragmentos de código de Visual Studio ) le indican que use la propiedad CLR para las propiedades de dependencia regulares y obtenga / establezca los accesos para las adjuntas. Pero no hay nada que le impida usar ambos al mismo tiempo, lo que le permite usar la sintaxis que prefiera.


71

Las propiedades adjuntas son un tipo de propiedad de dependencia. La diferencia está en cómo se usan.

Con una propiedad adjunta, la propiedad se define en una clase que no es la misma clase para la que se usa. Suele utilizarse para el diseño. Buenos ejemplos son Panel.ZIndex o Grid.Row: aplica esto a un control (es decir: Botón), pero en realidad está definido en Panel o Grid. La propiedad está "adjunta" a la instancia del botón.

Esto permite que un contenedor, por ejemplo, cree propiedades que se pueden usar en cualquier UIelement.

En cuanto a las diferencias de implementación, básicamente se trata de usar Register vs. RegisterAttached cuando define la propiedad.


10
¡¿Pero cuál es exactamente la diferencia ?! Por lo que he visto, puede adjuntar una propiedad no adjuntable a otra a través de código (aunque creo que esto está bloqueado en XAML). ¿Quizás esa es la diferencia?
Mark A. Donohoe

5

Las propiedades adjuntas están destinadas básicamente a los elementos del contenedor, como si tiene una cuadrícula y tiene una cuadrícula, ahora esto se considera una propiedad adjunta de un elemento de cuadrícula. También puede usar esta propiedad en texbox, button, etc. colocar en la cuadrícula.

La propiedad de dependencia es como la propiedad básicamente pertenece a otra clase y se usa en otra clase. Por ejemplo: como si tuviera un rectángulo aquí, la altura y el ancho son propiedades regulares del rectángulo, pero la izquierda y la parte superior son la propiedad de dependencia ya que pertenece a la clase Canvass.


-1

Las propiedades adjuntas son un tipo especial de DependencyProperties. Le permiten adjuntar un valor a un objeto que no sabe nada sobre este valor. Un buen ejemplo de este concepto son los paneles de diseño. Cada panel de diseño necesita datos diferentes para alinear sus elementos secundarios. El Canvas necesita Top e Left, The DockPanel necesita Dock, etc. Como puede escribir su propio panel de diseño, la lista es infinita. Como puede ver, no es posible tener todas esas propiedades en todos los controles de WPF. La solución son propiedades adjuntas. Están definidos por el control que necesita los datos de otro control en un contexto específico. Por ejemplo, un elemento que está alineado con un panel de diseño principal.


-1

Creo que puede definir la propiedad adjunta en la propia clase o puede definirla en otra clase. Siempre podríamos usar la propiedad adjunta para extender los controles estándar de Microsoft. Pero la propiedad de dependencia, la define en su propio control personalizado. Por ejemplo, puede heredar su control de un control estándar y definir una propiedad de dependencia en su propio control y utilizarla. Esto es equivalente a definir una propiedad adjunta y usar esta propiedad adjunta en el control estándar.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.