Итак, для чего именно «ownerType» используется в RegisterAttached? Эта проблема не давала мне покоя в течение нескольких лет, поэтому я наконец-то поближе взглянул на код 4.6.1 в WindowsBase.
Для любого DependencyProperty
, прикрепленного или иного, к чему он в конечном итоге приводит, это то, какой тип PropertyDescriptor
WPF получает для доступа с поздней привязкой к XAML, и это не определяется до первого раза (для каждогооснование сопряжения типа / свойства) такой доступ предпринят.Эта отсрочка необходима, потому что PropertyDescriptor
инкапсулирует свойство, привязанное к определенному типу, в то время как цель прикрепленных свойств состоит в том, чтобы избежать именно этого.
К тому времени, когда происходит доступ XAML, Register(...)
против RegisterAttached(...)
Различие было потеряно для туманов времени.Как уже отмечали другие на этой странице, само «DependencyProperty» не кодирует различие между прикрепленным и не многообразием.Предполагается, что каждый DP подходит для любого использования, с учетом только того, что можно выяснить во время выполнения.
Например, приведенный ниже код .NET, кажется, полагается на , а не при поиске соответствия свойство экземпляра для типа 'tOwner' в качестве первого требования для разрешения присоединенного доступа.Чтобы подтвердить этот диагноз, он затем проверяет, предоставляет ли tOwner один из статических методов доступа.Это неопределенная проверка, поскольку она не проверяет сигнатуры метода.Эти подписи имеют значение только для доступа к XAML;все фактические цели времени выполнения для присоединенного свойства должны быть DependencyObject
с, к которым WPF обращается по возможности DependencyObject.GetValue/SetValue
, когда это возможно.(Сообщается, что VS Designer использует статические средства доступа, и ваш XAML не будет компилироваться без них)
Соответствующим кодом .NET является статическая функция DependencyPropertyDescriptor.FromProperty
, показанная здесь с моими собственными комментариями (краткое описание ниже):
internal static DependencyPropertyDescriptor FromProperty(DependencyProperty dp, Type tOwner, Type tTarget, bool _)
{
/// 1. 'tOwner' must define a true CLR property, as obtained via reflection,
/// in order to obtain a normal (i.e. non-attached) DependencyProperty
if (tOwner.GetProperty(dp.Name) != null)
{
DependencyPropertyDescriptor dpd;
var dict = descriptor_cache;
lock (dict)
if (dict.TryGetValue(dp, out dpd))
return dpd;
dpd = new DependencyPropertyDescriptor(null, dp.Name, tTarget, dp, false);
lock (dict)
dict[dp] = dpd;
/// 2. Exiting here means that, if instance properties are defined on tOwner,
/// you will *never* get the attached property descriptor. Furthermore,
/// static Get/Set accessors, if any, will be ignored in favor of those instance
/// accessors, even when calling 'RegisterAttached'
return dpd;
}
/// 3. To obtain an attached DependencyProperty, 'tOwner' must define a public,
/// static 'get' or 'set' accessor (or both).
if ((tOwner.GetMethod("Get" + dp.Name) == null) && (tOwner.GetMethod("Set" + dp.Name) == null))
return null;
/// 4. If we are able to get a descriptor for the attached property, it is a
/// DependencyObjectPropertyDescriptor. This type and DependencyPropertyDescriptor
/// both derive directly from ComponentModel.PropertyDescriptor so they share
/// no 'is-a' relation.
var dopd = DependencyObjectProvider.GetAttachedPropertyDescriptor(dp, tTarget);
/// 5. Note: If the this line returns null, FromProperty isn't called below (new C# syntax)
/// 6. FromProperty() uses the distinction between descriptor types mentioned in (4.)
/// to configure 'IsAttached' on the returned DependencyProperty, so success here is
/// the only way attached property operations can succeed.
return dopd?.FromProperty(dopd);
}
Сводка: при вызове RegisterAttached
для создания прикрепленного DependencyProperty
единственное, что используется для ownerType, - это для определения типа, который определяет соответствующие статические средства доступа Get / Set.По крайней мере, один из этих методов доступа должен существовать в 'ownerType', иначе присоединенный доступ XAML не скомпилируется или не завершится с ошибкой.Хотя RegisterAttached не дает сбоя в этом случае, и вместо этого успешно возвращает несуществующий DP.Для DP, предназначенных только для присоединенного использования, 'tOwner' не обязательно должен быть производным от DependencyObject.Вы можете использовать любой обычный класс .NET, статический или нестатический, и даже может быть структурой!