Аннотация
Поскольку я не нашел почти никакой документации по этому вопросу, потребовалось некоторое возмущение вокруг исходного кода , но вот ответ.
Существует разница между регистрацией свойства зависимости как обычного и как присоединенного свойства, отличного от «философского» ( обычные свойства предназначены для использования объявленным типом и его производными типами, присоединенными свойствами предназначены для использования в качестве расширений для произвольных 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
, тогда как для прикрепленных свойств может быть любой тип (включая статические классы, структуры, перечисления, делегаты, и др.). * * 1092
Дополнение
Помимо предложения @ MarqueIV, я несколько раз сталкивался с мнением о том, что обычные и присоединенные свойства различаются по способу их использования в XAML . А именно, что обычные свойства требуют неявного синтаксиса имени в отличие от явного синтаксиса имени, требуемого присоединенными свойствами. Технически это не соответствует действительности , хотя на практике это обычно так. Для наглядности:
<!-- Implicit property name -->
<ns:SomeClass SomeProperty="some value" />
<!-- Explicit property name -->
<DependencyObject ns:SomeClass.SomeProperty="some value" />
В чисто XAML единственными правилами, регулирующими использование этих синтаксисов, являются следующие:
- Неявный синтаксис имени может использоваться для элемента тогда и только тогда, когда класс, который представляет этот элемент, имеет CLR свойство этого имени
- Явный синтаксис имени может использоваться для элемента тогда и только тогда, когда класс, указанный в первой части полного имени, предоставляет соответствующую статическую get / set методы (именуемые accessors ) с именами, соответствующими второй части полного имени
Удовлетворение этим условиям позволяет использовать соответствующий синтаксис независимо от того, было ли свойство резервной зависимости зарегистрировано как обычное или присоединено.
Теперь упомянутое заблуждение вызвано тем, что подавляющее большинство учебных пособий (вместе со стандартными Visual Studio фрагментами кода) инструктирует вас использовать свойство CLR для обычных свойств зависимостей, и получить / установить аксессоры для прикрепленных. Но ничто не мешает вам использовать оба одновременно, что позволяет вам использовать любой синтаксис, который вы предпочитаете.