¿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:
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 DependencyObject
instancias 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.
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
});
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 RegisterAttached
el 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 PropertyMetadata
con solo un DefaultValue
conjunto (ya sea de los metadatos proporcionados o automáticamente). Solo la llamada posterior a OverrideMetadata
utiliza realmente los metadatos proporcionados.
La principal diferencia práctica es que, en el caso de propiedades regulares, CoerceValueCallback
y PropertyChangedCallback
son 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 OverrideMetadata
requiere 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.).
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:
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.
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.
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.
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.
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.