Разница между вложенными и не прикрепленными свойствами зависимостей в Silverlight - PullRequest
13 голосов
/ 27 июля 2011

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

Ответы [ 5 ]

9 голосов
/ 27 июля 2011

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

Основное различие между вложенными зависимыми свойствами и зависимыми свойствами (и, следовательно, между .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 в качестве основы для системы локализации, так что трансляции можно было паразитировать на объектах во время загрузки, а не использовать ужасно длинные привязки. Не могу сделать это с ДП

Обновление:

Я также хотел бы уточнить, что родительское ограничение применяется к использованию атрибута на основе XAML. Из кода родительское ограничение явно не распространяется.

6 голосов
/ 18 сентября 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));
    }
}

Отражатель показывает, что единственное различие между Register и RegisterAttached состоит в том, что Register выбрасывает большую часть предоставленных метаданных и сохраняет его только дляэкземпляры регистрации класса (через OverrideMetadata).Это означает, что такие атрибуты, как Inherits и различные уведомления об обновлениях, обычно указываемые в метаданных, не работают со свойствами, зарегистрированными в Register и прикрепленными к объектам других типов (кроме типа регистрации).Таким образом, Register на самом деле является урезанной версией RegisterAttached.Вероятно, это было сделано из соображений производительности.

В примере , связанном Харисом Хасаном в комментариях к его ответу, если вы измените значение RegisterAttached на Register, кнопки перестанут двигаться (посколькусвойство больше не предоставляет метаданные AffectsParentArrange для типа Button), но, тем не менее, они перерисовываются в новых местах при изменении размера окна.Но если вы добавите те же метаданные к типу Button после вызова InitializeComponent ():

RadialPanel.AngleProperty.OverrideMetadata(
    typeof(Button), 
    new FrameworkPropertyMetadata(
        0.0, FrameworkPropertyMetadataOptions.AffectsParentArrange));

, то все снова будет работать так, как если бы был вызван RegisterAttached.

5 голосов
/ 27 июля 2011

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

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

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

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

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

2 голосов
/ 27 июля 2011

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

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

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

1 голос
/ 05 июня 2016

Итак, для чего именно «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, статический или нестатический, и даже может быть структурой!

...