Разница между прикрепленными и неприсоединенными свойствами зависимостей в Silverlight

Хорошо, Stackers, я потратил пару часов на этот вопрос и хочу знать, есть ли у кого-нибудь окончательный ответ.
Несмотря на все исследования, которые я провел, я не могу найти НИКАКОЙ разницы между .Register и .RegisterAttached в Silverlight. Теперь, прежде чем вы бросите пистолет и скажете мне, что .RegisterAttached используется для присоединения DP к другому классу, попробуйте реализовать свойство Attached Dependency с помощью DependencyProperty.Register(). Я не нашел ни единой разницы, поэтому я не понимаю, в чем разница.
Кроме того, в моем конкретном случае я пытаюсь расширить функциональность класса Grid и хочу дать это некоторые дополнительные свойства. Таким образом, я попытался перечислить передачу как typeof(Grid), так и typeof(FluidLayoutManager) (реализующий класс) в качестве параметра ownerType, и это также, похоже, не имеет большого значения ... (я считаю, что это имеет значение, когда я передаю два настраиваемых класса из то же пространство имен. Однако при передаче определенного Microsoft класса по сравнению с пользовательским классом он всегда отображается в XAML как DP пользовательского класса.)
Любые разъяснения по этой теме были бы весьма признательны, поскольку я Я сижу здесь, почесывая затылок, и гадаю, есть ли вообще какая-то разница, или Microsoft просто снова меня лажает.


person Melodatron    schedule 27.07.2011    source источник


Ответы (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. Исходя из кода, родительское ограничение, по-видимому, не применяется.

person Gone Coding    schedule 27.07.2011
comment
Дополнительная информация XAML 1: 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
comment
+1 за этот комментарий ... теперь мне просто нужно перевести это на чистый английский, и мы закончили :) - person Gone Coding; 27.07.2011
comment
Итак, мой следующий вопрос: есть ли другие различия между ними, и действительно ли параметр ownerType, переданный в .RegisterAttached, вообще что-нибудь означает в Silverlight? - person Melodatron; 27.07.2011
comment
@Melodatron: Хороший вопрос. Кажется, я кое-что помню о прикрепленных свойствах, связанных с родительским элементом определенного типа (и это называется типом владельца), но я не понимаю, как это на самом деле где-либо используется для ограничения Это. На самом деле похоже, что это больше намекает на отношения, чем на их принуждение. - person Gone Coding; 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.

person srgstm    schedule 18.09.2011
comment
Чтобы прояснить, упомянутое мной родительское ограничение применяется к использованию атрибутов в XAML, а не в коде. - person Gone Coding; 20.02.2014

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

Simple Register используется для простых свойств зависимостей, которые вы обычно используете для привязок и проверок, поэтому они являются обычными свойствами CLR с некоторой дополнительной магией, которая помогает в WPF

RegisterAttached обычно используется там, где вы хотите предоставить свойство, к которому можно получить доступ и установить в дочернем классе, например DockPanel, где дочерние элементы управления сообщают родителю, где они хотят быть размещены, используя Dock.Left или Dock.Right. Таким образом, они представляют собой своего рода специальные свойства зависимостей, к которым можно получить доступ в дочерних элементах управления (что не относится к простым свойствам Register), и они (в случае DockPanel) помогают родительскому элементу управления отображать дочерние элементы.

Короче говоря, можно сказать, что Register используется для регистрации dependency properties, которые используются в том же классе, в то время как RegisterAttached используется для регистрации специальных свойств зависимостей, называемых attached properties, и они используются и доступны для классов, отличных от того, который его определил.

Это хорошее объяснение прикрепленных свойств и того, чего нельзя достичь. через простой DP

person Haris Hasan    schedule 27.07.2011
comment
Еще одно примечание (которое может пригодиться): прикрепленные установщики свойств вызываются при анализе Xaml, содержащего свойство. Установщики зависимостей вызываются только вами (в остальное время доступ к свойствам осуществляется исключительно SetValue и GetValue. - person Gone Coding; 27.07.2011
comment
@Haris: Пожалуйста, примите во внимание изменение моего вопроса. Я понимаю, что Register / RegisterAttached обычно используется для ... но это не имеет значения с точки зрения реализации или функции, и это вопрос, на который я хочу получить ответ. Кроме того, как вы можете видеть в примере, нет абсолютно никаких проблем с доступом к зарегистрированному DP из другого класса. - person Melodatron; 27.07.2011
comment
@Melodatron: проблема в том, что вы пытаетесь использовать Attached Property в своем примере в неправильном контексте. Вы используете Attached Property, где даже простые dependency property будут работать. Как я уже сказал, Attached Property - это dependency property с дополнительными функциями, поэтому вы можете получить к нему доступ таким же образом. Но в вашем примере вы не используете реальную мощность Attached Properties. Вы не получаете к нему доступ из другого класса, это главное, что отличает его от простого dependency property. - person Haris Hasan; 27.07.2011
comment
@Haris: Я принимаю то, что вы говорите, что ADP - это расширенные DP, однако то, что я говорю, заключается в том, что на данный момент для меня эти дополнительные функции являются полной загадкой для меня. Если бы вы могли обрисовать и / или привести пример того, что могут делать ADP, а DP не могут, я был бы очень признателен. - person Melodatron; 27.07.2011
comment
Сейчас у меня нет времени приводить пример, но в Интернете их полно. См. blogs.msdn.com/b/bencon/archive Например, /2006/07/24/677520.aspx. Вы не можете сделать это с помощью обычного DP, и это то место, где ADP действительно пригодится и покажет свою силу. - person Haris Hasan; 27.07.2011
comment
Еще один красивый и простой пример: silverlightshow.net/items/ - person Haris Hasan; 27.07.2011
comment
@Haris: Хотя первый пример показался мне интересным, он заставил меня понять, что мне нужно прояснить то, что я упомянул только в тегах, а не в описании вопроса. Этот вопрос специально направлен на платформу Silverlight, а не на платформу WPF. Кроме того, я изменил .RegisterAttached на .Register, и он больше не работал правильно. Основное различие заключалось в том, что положение кнопок не обновлялось при нажатии, что, как я полагаю, связано с this.InternalChildren и / или FrameworkPropertyMetadata - ни то, ни другое не существует в SL! - person Melodatron; 27.07.2011
comment
@Haris: Хорошо, пока я начинаю видеть разницу здесь и разницу там и ценю вашу помощь, я не согласен с тем, что пример silverlightshow является хорошим примером. Вся информация в нем теряется среди объяснений. Как я уже сказал, я действительно хочу знать, в чем разница между ними. - person Melodatron; 27.07.2011

Если вы зарегистрируетесь в RegisterAttached, он станет глобальным как свойство в хранилище любого DependencyObject, то есть вы можете SetValue для любого объекта зависимости

Если вы используете Register при вызове Get / Setvalue, будет выполняться проверка того, что вызов является объектом, который может быть приведен к типу регистрации.

Примером свойства, которое ведет себя как RegisterAttached, является Grid.Row и Grid.Column.

person jaywayco    schedule 27.07.2011
comment
Хорошо, хотя я мог бы крикнуть за то, что прочитал Пример свойства, которое ведет себя как 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; все фактические целевые объекты времени выполнения для присоединенного свойства должны быть DependencyObjects, к которым 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, статический или нестатический, и на самом деле может даже быть структурой!

person Glenn Slayden    schedule 05.06.2016