Qual é a diferença entre uma propriedade de dependência e uma propriedade anexada no WPF?

91

Qual é a diferença entre uma propriedade de dependência (personalizada) e uma propriedade anexada no WPF? Quais são os usos de cada um? Como as implementações geralmente diferem?

Kenwarner
fonte

Respostas:

20

Resumo

Como eu encontrei pouca ou nenhuma documentação sobre o assunto, demorei um pouco a vasculhar o código-fonte , mas aqui está uma resposta.

Há uma diferença entre registrar uma propriedade de dependência como regular e como uma propriedade anexada, diferente de uma "filosófica" ( propriedades regulares devem ser usadas pelo tipo declarante e seus tipos derivados, propriedades anexadas devem ser usadas como extensões em DependencyObject instâncias arbitrárias ). "Filosófico", porque, como @MarqueIV notou em seu comentário à resposta de @ReedCopsey, propriedades regulares também podem ser usadas com DependencyObjectinstâncias arbitrárias .

Além disso, tenho que discordar de outras respostas afirmando que a propriedade anexada é "tipo de propriedade de dependência", porque é enganosa - não há nenhum "tipo" de propriedade de dependência. O framework não se preocupa se o imóvel foi cadastrado como anexo ou não - nem é possível determinar (no sentido de que essa informação não fica registrada, por ser irrelevante). Na verdade, todas as propriedades são registradas como se fossem propriedades anexadas, mas no caso das propriedades regulares algumas coisas adicionais são feitas que modificam ligeiramente seu comportamento.

Trecho de código

Para evitar o trabalho de examinar o código-fonte sozinho, aqui está uma versão resumida do que acontece.

Ao registrar uma propriedade sem metadados especificados, chamando

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

produz exatamente o mesmo resultado que chamar

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

No entanto, ao especificar metadados, chamando

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

é equivalente a chamar

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
    });

Conclusões

A principal (e única) diferença entre as propriedades de dependência regulares e anexadas são os metadados padrão disponíveis por meio da propriedade DependencyProperty.DefaultMetadata . Isso é até mencionado na seção Comentários :

Para propriedades não anexadas, o tipo de metadados retornado por esta propriedade não pode ser convertido em tipos derivados do tipo PropertyMetadata , mesmo se a propriedade foi registrada originalmente com um tipo de metadados derivado. Se você quiser os metadados registrados originalmente, incluindo seu tipo de metadados possivelmente derivado original, chame GetMetadata (Type) , passando o tipo de registro original como um parâmetro.

Para propriedades anexadas, o tipo dos metadados retornados por esta propriedade corresponderá ao tipo fornecido no método de registro RegisterAttached original .

Isso é claramente visível no código fornecido. Pequenas dicas também estão escondidas nos métodos de registro, ou seja, para RegisterAttachedo parâmetro de metadados é nomeado defaultMetadata, enquanto para Registerele é nomeado typeMetadata. Para propriedades anexadas, os metadados fornecidos tornam-se os metadados padrão. No caso de propriedades regulares, no entanto, os metadados padrão são sempre uma nova instância de PropertyMetadatacom apenas DefaultValuedefinido (dos metadados fornecidos ou automaticamente). Apenas a chamada subsequente para OverrideMetadatarealmente usa os metadados fornecidos.

Consequências

A principal diferença prática é que no caso de propriedades regulares, CoerceValueCallbacke PropertyChangedCallbacksão aplicáveis apenas para tipos derivados do tipo declarado como o tipo de proprietário, e para propriedades anexadas eles são aplicáveis ​​para todos os tipos. Por exemplo, neste cenário:

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

o registrado PropertyChangedCallback será chamado se o imóvel foi registrado como imóvel anexado, mas não será chamado se estiver registrado como imóvel regular. O mesmo vale para CoerceValueCallback.

Uma diferença secundária decorre do fato de que OverrideMetadataexige que o tipo fornecido derive DependencyObject. Na prática, isso significa que o tipo de proprietário para propriedades regulares deve derivar de DependencyObject, enquanto para propriedades anexadas em pode ser qualquer tipo (incluindo classes estáticas, estruturas, enums, delegados, etc.).

Suplemento

Além da sugestão de @MarqueIV, em várias ocasiões eu me deparei com opiniões de que as propriedades regulares e anexadas diferem na maneira como podem ser usadas em XAML . Ou seja, as propriedades regulares requerem sintaxe de nome implícita em oposição à sintaxe de nome explícita exigida pelas propriedades anexadas. Isso não é tecnicamente verdade , embora na prática geralmente seja o caso. Para maior clareza:

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

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

No XAML puro , as únicas regras que regem o uso dessas sintaxes são as seguintes:

  • A sintaxe de nome implícita pode ser usada em um elemento se e somente se a classe que este elemento representa tiver uma propriedade CLR com esse nome
  • A sintaxe de nome explícito pode ser usada em um elemento se e somente se a classe especificada pela primeira parte do nome completo expõe métodos get / set estáticos apropriados (referidos como acessadores ) com nomes que correspondem à segunda parte do nome completo

Satisfazer essas condições permite que você use a sintaxe correspondente, independentemente de a propriedade de dependência de apoio ter sido registrada como regular ou anexada.

Agora, o equívoco mencionado é causado pelo fato de que a grande maioria dos tutoriais (junto com os trechos de código do Visual Studio ) instruem você a usar a propriedade CLR para propriedades de dependência regulares e obter / definir acessores para as anexadas. Mas não há nada que o impeça de usar os dois ao mesmo tempo, permitindo que você use a sintaxe de sua preferência.

Grx70
fonte
71

As propriedades anexadas são um tipo de propriedade de dependência. A diferença está em como eles são usados.

Com uma propriedade anexada, a propriedade é definida em uma classe que não é a mesma classe para a qual está sendo usada. Isso geralmente é usado para layout. Bons exemplos são Panel.ZIndex ou Grid.Row - você aplica isso a um controle (isto é: Botão), mas na verdade é definido em Panel ou Grid. A propriedade é "anexada" à instância do botão.

Isso permite que um contêiner, por exemplo, crie propriedades que podem ser usadas em qualquer UIelement.

Quanto às diferenças de implementação - é basicamente apenas uma questão de usar Register vs. RegisterAttached ao definir a propriedade.

Reed Copsey
fonte
10
Mas qual é exatamente a diferença ?! Pelo que vi, você pode anexar uma propriedade não anexável a outra via código (acho que isso está bloqueado em XAML). Talvez seja essa a diferença?
Mark A. Donohoe
5

As propriedades anexadas são basicamente destinadas aos elementos do contêiner. coloque na grade.

A propriedade de dependência é como se a propriedade pertencesse basicamente a alguma outra classe e fosse usada em outra classe. Ex: como se você tivesse um retângulo aqui, altura e largura são propriedades regulares do retângulo, mas left e top são a propriedade de dependência, pois pertencem à classe Canvass.

shweta
fonte
-1

As propriedades anexadas são um tipo especial de DependencyProperties. Eles permitem que você atribua um valor a um objeto que não sabe nada sobre esse valor. Um bom exemplo desse conceito são os painéis de layout. Cada painel de layout precisa de dados diferentes para alinhar seus elementos filho. O Canvas precisa de Top e Left, o DockPanel precisa de Dock, etc. Já que você pode escrever seu próprio painel de layout, a lista é infinita. Como você pode ver, não é possível ter todas essas propriedades em todos os controles WPF. A solução são propriedades anexadas. Eles são definidos pelo controle que precisa dos dados de outro controle em um contexto específico. Por exemplo, um elemento alinhado por um painel de layout pai.

Mukesh
fonte
-1

Acho que você pode definir a propriedade anexada na própria classe ou pode defini-la em outra classe. Sempre poderíamos usar a propriedade anexada para estender os controles padrão da Microsoft. Mas a propriedade de dependência, você a define em seu próprio controle personalizado. por exemplo, você pode herdar seu controle de um controle padrão e definir uma propriedade de dependência em seu próprio controle e usá-la. Isso é equivalente a definir uma propriedade anexada e usar esta propriedade anexada no controle padrão.

spspli
fonte