Хорошо, Stackers, я потратил пару часов на этот вопрос и хочу знать, есть ли у кого-нибудь окончательный ответ.
Несмотря на все исследования, которые я провел, я не могу найти НИКАКОЙ разницы между .Register
и .RegisterAttached
в Silverlight. Теперь, прежде чем вы бросите пистолет и скажете мне, что .RegisterAttached
используется для присоединения DP к другому классу, попробуйте реализовать свойство Attached Dependency с помощью DependencyProperty.Register()
. Я не нашел ни единой разницы, поэтому я не понимаю, в чем разница.
Кроме того, в моем конкретном случае я пытаюсь расширить функциональность класса Grid и хочу дать это некоторые дополнительные свойства. Таким образом, я попытался перечислить передачу как typeof(Grid)
, так и typeof(FluidLayoutManager)
(реализующий класс) в качестве параметра ownerType, и это также, похоже, не имеет большого значения ... (я считаю, что это имеет значение, когда я передаю два настраиваемых класса из то же пространство имен. Однако при передаче определенного Microsoft класса по сравнению с пользовательским классом он всегда отображается в XAML как DP пользовательского класса.)
Любые разъяснения по этой теме были бы весьма признательны, поскольку я Я сижу здесь, почесывая затылок, и гадаю, есть ли вообще какая-то разница, или Microsoft просто снова меня лажает.
Разница между прикрепленными и неприсоединенными свойствами зависимостей в Silverlight
Ответы (5)
Учитывая обсуждения, текущие в комментариях, я постараюсь сделать это на простом английском языке:
Основное различие между Attached Dependency Properties и Dependency Properties (и, следовательно, между .Register и .RegisterAttached) заключается в том, что RegisterAttached позволяет присвоить значение любому объекту зависимости, тогда как Register позволяет присоединить его только к классу, переданному как параметр ownerType.
Как упоминает Харис Хасан (глубоко в ветке комментариев), ваш пример использует единственный разрешенный тип (например, CustomControl) и не показывает, что присоединенная версия может быть назначена ЛЮБОМУ объекту зависимости.
например вы можете сделать это с помощью своего Attached Dependency Property (но не простого DP):
<Grid local:AttacherClass.ADP1="1" x:Name="LayoutRoot" Background="White">
</Grid>
Лучшая справочная информация по ADP, которую я могу найти, это: http://msdn.microsoft.com/en-us/library/ms749011.aspx
Мы использовали ADP в качестве основы системы локализации, чтобы переводы можно было паразитировать на объектах во время загрузки, а не использовать ужасно длинные привязки. Невозможно сделать это с DP
Обновлять:
Я также хотел бы уточнить, что родительское ограничение применяется к использованию атрибута на основе XAML. Исходя из кода, родительское ограничение, по-видимому, не применяется.
SetDP/ADP()
и GetDP/ADP()
для обоих, но вызовы SetValue(DP/ADP, value)
и GetValue(DP/ADP)
- нет. 2: В конструкторе XAML Intellisense будет использовать подписи методов SetDP/ADP()
и GetDP/ADP()
, чтобы определить, отображается ли свойство зависимости или свойство присоединенной зависимости. Таким образом, он будет отображать недопустимые свойства зависимостей, если параметры SetDP/ADP()
принимают DependencyObject
вместо более конкретного класса.
- person Melodatron; 27.07.2011
.RegisterAttached
, вообще что-нибудь означает в Silverlight?
- person Melodatron; 27.07.2011
Неверно полагать, что «RegisterAttached позволяет присвоить значение любому объекту зависимости, тогда как Register позволяет присоединить его только к классу, переданному как параметр ownerType». Вот прекрасно работающий пример прикрепленной собственности, зарегистрированной в Register:
class FooPropertyDeclaringType
{
public static readonly DependencyProperty FooProperty =
DependencyProperty.Register("Foo", typeof(int), typeof(FooPropertyDeclaringType));
}
class SomeUnrelatedType : DependencyObject { }
class Program
{
static void Main()
{
var obj = new SomeUnrelatedType();
obj.SetValue(FooPropertyDeclaringType.FooProperty, 10);
Debug.Assert(10 == (int)obj.GetValue(FooPropertyDeclaringType.FooProperty));
}
}
Reflector показывает, что единственное различие между Register и RegisterAttached состоит в том, что Register выбрасывает большую часть предоставленных метаданных и сохраняет их только для экземпляров класса регистрации (через OverrideMetadata). Это означает, что такие атрибуты, как наследование и различные уведомления об обновлении, обычно указываемые в метаданных, не работают для свойств, зарегистрированных с помощью Register и прикрепленных к объектам других типов (кроме типа регистрации). Так что на самом деле Register - это урезанная версия RegisterAttached. Вероятно, это было сделано из соображений производительности.
В примере, на который ссылается Харис Хасан в комментарии к его ответу, если вы измените RegisterAttached на Register, кнопки перестают двигаться (поскольку свойство больше не предоставляет метаданные AffectsParentArrange для типа Button), но они, тем не менее, перерисовываются в своих новых местах при изменении размера окна. Но если вы добавите те же метаданные к типу Button после вызова InitializeComponent ():
RadialPanel.AngleProperty.OverrideMetadata(
typeof(Button),
new FrameworkPropertyMetadata(
0.0, FrameworkPropertyMetadataOptions.AffectsParentArrange));
затем все снова работает, как если бы был вызван RegisterAttached.
Они могут не сильно отличаться с точки зрения реализации, но они отличаются в действиях, т.е. они разные в том, что они делают и для чего используются.
Simple Register
используется для простых свойств зависимостей, которые вы обычно используете для привязок и проверок, поэтому они являются обычными свойствами CLR с некоторой дополнительной магией, которая помогает в WPF
RegisterAttached
обычно используется там, где вы хотите предоставить свойство, к которому можно получить доступ и установить в дочернем классе, например DockPanel
, где дочерние элементы управления сообщают родителю, где они хотят быть размещены, используя Dock.Left
или Dock.Right
. Таким образом, они представляют собой своего рода специальные свойства зависимостей, к которым можно получить доступ в дочерних элементах управления (что не относится к простым свойствам Register
), и они (в случае DockPanel
) помогают родительскому элементу управления отображать дочерние элементы.
Короче говоря, можно сказать, что Register
используется для регистрации dependency properties
, которые используются в том же классе, в то время как RegisterAttached
используется для регистрации специальных свойств зависимостей, называемых attached properties
, и они используются и доступны для классов, отличных от того, который его определил.
Это хорошее объяснение прикрепленных свойств и того, чего нельзя достичь. через простой DP
Attached Property
в своем примере в неправильном контексте. Вы используете Attached Property
, где даже простые dependency property
будут работать. Как я уже сказал, Attached Property
- это dependency property
с дополнительными функциями, поэтому вы можете получить к нему доступ таким же образом. Но в вашем примере вы не используете реальную мощность Attached Properties
. Вы не получаете к нему доступ из другого класса, это главное, что отличает его от простого dependency property
.
- person Haris Hasan; 27.07.2011
.RegisterAttached
на .Register
, и он больше не работал правильно. Основное различие заключалось в том, что положение кнопок не обновлялось при нажатии, что, как я полагаю, связано с this.InternalChildren
и / или FrameworkPropertyMetadata
- ни то, ни другое не существует в SL!
- person Melodatron; 27.07.2011
Если вы зарегистрируетесь в RegisterAttached, он станет глобальным как свойство в хранилище любого DependencyObject, то есть вы можете SetValue для любого объекта зависимости
Если вы используете Register при вызове Get / Setvalue, будет выполняться проверка того, что вызов является объектом, который может быть приведен к типу регистрации.
Примером свойства, которое ведет себя как RegisterAttached, является Grid.Row и Grid.Column.
IncompatibleControl.SetValue()
я могу устанавливать и извлекать значение Attached Dependency в C #. Однако DP выдает ошибку и не работает в XAML.
- person Melodatron; 27.07.2011
Так что же именно «ownerType» используется в RegisterAttached? Эта проблема не давала мне покоя в течение нескольких лет, поэтому я, наконец, внимательно рассмотрел код 4.6.1 в WindowsBase.
Для любого DependencyProperty
, прикрепленного или нет, в конечном итоге все сводится к тому, какой тип PropertyDescriptor
WPF получает для доступа к XAML с поздней привязкой, и это не определяется до первого раза (на основе пары тип / свойство) такого доступа предпринимается попытка. Эта отсрочка необходима, потому что PropertyDescriptor
инкапсулирует свойство, привязанное к определенному типу, тогда как цель прикрепленных свойств состоит в том, чтобы избежать именно этого.
К тому времени, когда происходит доступ XAML, различие между Register(...)
и RegisterAttached(...)
теряется в тумане (времени выполнения). Как другие отмечали на этой странице, «DependencyProperty» сам по себе не кодирует различие между присоединенным и неизменным. Предполагается, что каждый DP имеет право на любое использование, в зависимости только от того, что может быть выяснено во время выполнения.
Например, приведенный ниже код .NET, похоже, полагается на то, что не найдено подходящее свойство экземпляра для типа tOwner в качестве первого требования для разрешения присоединенного доступа. Чтобы подтвердить этот диагноз, он затем проверяет, предоставляет ли tOwner один из статических методов доступа. Это расплывчатая проверка, поскольку она не проверяет сигнатуры методов. Эти подписи имеют значение только для доступа XAML; все фактические целевые объекты времени выполнения для присоединенного свойства должны быть DependencyObject
s, к которым 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, статический или нестатический, и на самом деле может даже быть структурой!