В чем разница между свойством зависимости и присоединенным свойством в WPF?

91

В чем разница между (настраиваемым) свойством зависимости и присоединенным свойством в WPF? Какая польза от каждого? Чем обычно отличаются реализации?

Kenwarner
источник

Ответы:

20

Аннотация

Поскольку я не нашел почти никакой документации по этому поводу, потребовалось немного покопаться в исходном коде , но вот ответ.

Существует разница между регистрацией свойства зависимости как обычного и как присоединенного свойства, кроме «философского» ( обычные свойства предназначены для использования объявляющим типом и его производными типами, присоединенные свойства предназначены для использования в качестве расширения на произвольных DependencyObject экземплярах ). «Философский», потому что, как заметил @MarqueIV в своем комментарии к ответу @ReedCopsey, обычные свойства также могут использоваться с произвольными DependencyObjectэкземплярами.

Более того, я должен не согласиться с другими ответами, в которых говорится, что присоединенное свойство является «типом свойства зависимости», потому что оно вводит в заблуждение - нет никаких «типов» свойств зависимости. Фреймворку безразлично, было ли свойство зарегистрировано как прикрепленное или нет - это даже невозможно определить (в том смысле, что эта информация не записывается, потому что это не имеет значения). Фактически, все свойства регистрируются, как если бы они были присоединенными, но в случае обычных свойств делаются некоторые дополнительные вещи, которые немного изменяют их поведение.

Отрывок кода

Чтобы избавить вас от необходимости самостоятельно просматривать исходный код, вот краткая версия того, что происходит.

При регистрации свойства без указания метаданных вызов

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

дает тот же результат, что и вызов

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

Однако при указании метаданных вызов

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

эквивалентно вызову

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

Выводы

Ключевое (и единственное) различие между обычными и присоединенными свойствами зависимостей - это метаданные по умолчанию, доступные через свойство DependencyProperty.DefaultMetadata . Об этом даже упоминается в разделе примечаний :

Для неприсоединенных свойств тип метаданных, возвращаемый этим свойством, не может быть приведен к производным типам типа PropertyMetadata , даже если свойство было изначально зарегистрировано с производным типом метаданных. Если вы хотите, чтобы изначально зарегистрированные метаданные включали исходный, возможно, производный тип метаданных, вместо этого вызовите GetMetadata (Type) , передав исходный тип регистрации в качестве параметра.

Для присоединенных свойств тип метаданных, возвращаемых этим свойством, будет соответствовать типу, указанному в исходном методе регистрации RegisterAttached .

Это хорошо видно в предоставленном коде. Маленькие подсказки также скрыты в методах регистрации, т.е. для RegisterAttachedпараметра метаданных указано имя defaultMetadata, а для Registerнего - имя typeMetadata. Для прикрепленных свойств предоставленные метаданные становятся метаданными по умолчанию. Однако в случае обычных свойств метаданные по умолчанию всегда представляют собой свежий экземпляр PropertyMetadataс только DefaultValueустановленным (либо из предоставленных метаданных, либо автоматически). Только последующий вызов OverrideMetadataфактически использует предоставленные метаданные.

Последствия

Основное практическое отличие состоит в том, что в случае обычных свойств CoerceValueCallbackи PropertyChangedCallbackприменимы только для типов, производных от типа, объявленного как тип-владелец, а для присоединенных свойств они применимы для всех типов. Например, в этом сценарии:

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

Зарегистрированный PropertyChangedCallback будет вызываться, если свойство было зарегистрировано как присоединенное свойство, но не будет вызываться, если оно было зарегистрировано как обычное свойство. То же самое и с CoerceValueCallback.

Второстепенное различие проистекает из того факта, что OverrideMetadataтребуется, чтобы поставляемый тип был производным от DependencyObject. На практике это означает , что тип владельца для обычных свойств должен быть производным от DependencyObject, тогда как для присоединенных свойств может быть в любом типе (включая статические классы, структуры, перечислений, делегат и т.д.).

Дополнение

Помимо предложения @ MarqueIV, в нескольких случаях я встречал мнения о том, что обычные и прикрепленные свойства отличаются тем, как они могут использоваться в XAML . А именно, эти обычные свойства требуют неявного синтаксиса имени в отличие от явного синтаксиса имени, требуемого присоединенными свойствами. Технически это не так , хотя на практике это обычно так. Для ясности:

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

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

В чистом XAML единственными правилами, регулирующими использование этих синтаксисов, являются следующие:

  • Неявный синтаксис имени может использоваться для элемента тогда и только тогда, когда класс, который представляет этот элемент, имеет свойство CLR с этим именем.
  • Явный синтаксис имени может использоваться для элемента тогда и только тогда, когда класс, указанный в первой части полного имени, предоставляет соответствующие статические методы получения / установки (называемые аксессорами ) с именами, соответствующими второй части полного имени.

Удовлетворение этих условий позволяет использовать соответствующий синтаксис независимо от того, было ли свойство зависимостей поддержки зарегистрировано как обычное или прикрепленное.

Упомянутое заблуждение вызвано тем фактом, что подавляющее большинство руководств (вместе со стандартными фрагментами кода Visual Studio ) инструктируют вас использовать свойство CLR для обычных свойств зависимостей и получать / устанавливать аксессоры для прикрепленных. Но ничто не мешает вам использовать оба варианта одновременно, позволяя использовать любой синтаксис, который вы предпочитаете.

Grx70
источник
71

Присоединенные свойства - это тип свойства зависимости. Разница в том, как они используются.

С присоединенным свойством свойство определяется в классе, который не является тем же классом, для которого оно используется. Обычно это используется для верстки. Хорошими примерами являются Panel.ZIndex или Grid.Row - вы применяете это к элементу управления (например, Button), но на самом деле он определен в Panel или Grid. Свойство «прикреплено» к экземпляру кнопки.

Это позволяет контейнеру, например, создавать свойства, которые можно использовать в любом UIelement.

Что касается различий в реализации - в основном это просто вопрос использования Register и RegisterAttached при определении свойства.

Рид Копси
источник
10
Но в чем именно разница ?! Из того, что я видел, вы можете прикрепить неприсоединяемое свойство к другому с помощью кода (хотя я думаю, что это заблокировано в XAML). Возможно, в этом разница?
Марк А. Донохо
5

Присоединенные свойства в основном предназначены для элементов контейнера. Например, если у вас есть сетка и у вас есть grid.row, теперь это считается присоединенным свойством элемента сетки. Также вы можете использовать это свойство в texbox, button и т. Д., Чтобы установить его разместить в сетке.

Свойство зависимости похоже на то, что свойство в основном принадлежит какому-то другому классу и используется в другом классе. например: например, у вас есть прямоугольник, высота и ширина - это обычные свойства прямоугольника, но left и top - это свойство зависимости, поскольку оно принадлежит классу Canvass.

Светлана
источник
-1

Присоединенные свойства - это особый вид DependencyProperties. Они позволяют вам прикрепить значение к объекту, который ничего не знает об этом значении. Хорошим примером этой концепции являются панели макета. Каждой панели макета нужны разные данные для выравнивания дочерних элементов. Canvas нуждается в Top и Left, DockPanel нуждается в Dock и т. Д. Поскольку вы можете написать свою собственную панель макета, список бесконечен. Итак, вы видите, что невозможно иметь все эти свойства во всех элементах управления WPF. К решению прилагаются свойства. Они определяются элементом управления, которому требуются данные из другого элемента управления в определенном контексте. Например, элемент, выровненный по родительской панели макета.

Мукеш
источник
-1

Я думаю, вы можете определить присоединенное свойство в самом классе или вы можете определить его в другом классе. Мы всегда могли использовать присоединенное свойство для расширения стандартных элементов управления Microsoft. Но свойство зависимости вы определяете в собственном настраиваемом элементе управления. Например, вы можете унаследовать свой элемент управления от стандартного элемента управления и определить свойство зависимости в своем собственном элементе управления и использовать его. Это эквивалентно определению присоединенного свойства и использованию этого присоединенного свойства в стандартном элементе управления.

spspli
источник