Содержание
Для начала пару слов, чтобы освежить память.
Dependency Property (Свойство зависимости) — это основа любого Silverlight/WPF/WP7 приложения. Их можно рассматривать как обычные свойства, но они обладают дополнительными возможностями, позволяющими работать с анимацией, привязкой данных, стилями. Также они эффективно потребляют память и поддерживают уведомления об изменениях.
Одной из основной черт Dependecy Property является приоретизация. Т.е. при присвоении им значения будет учтено несколько факторов. Наивысший приоритет будет иметь анимация, самый низкий – значения по умолчанию. Более подробно можно посмотреть на MSDN. В принципе, запоминать последовательность приоритетов не очень нужно — она и так интуитивно понятна.
Для того чтобы в классе можно было реализовывать Dependency Property необходимо, чтобы класс наследовался от DependencyObject либо его наследников, таких например как UIElement, Geometry, FrameworkTemplate, Style и других.
Напоминаю, как выглядит типична реализация Dependency Property:
public class MyStateControl : ButtonBase { public MyStateControl() : base() { } public Boolean State { get { return (Boolean)this.GetValue(StateProperty); } set { this.SetValue(StateProperty, value); } } public static readonly DependencyProperty StateProperty = DependencyProperty.Register("State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false)); }
Реализация
Давайте посмотрим как DependencyProperty работают внутри. Первое, что я попытался сделать для этого, это использовать Reflector. К сожалению, в сборках для WP7 он не отобразил реализацию методов. Оказалось, что сборки, которые лежат в c:Program Files (x86)Reference AssembliesMicrosoftFrameworkSilverlightv4.0ProfileWindowsPhone71 просто используются Visual Studi’ей для подсветки синтаксиса! В конце концов, натолкнулся на вот эту статью и закачал себе архив с дампом. Внутри дампа лежит не обфусцированая сборка GAC_System.Windows_v2_0_5_0_cneutral_1.dll, содержащая пространство имен System.Windows, в котором определены нужные нам объекты DependencyProperty и DependencyObject.
DependencyProperty
Итак, обратимся классу DependencyProperty:
namespace System.Windows { public class DependencyProperty { public static readonly object UnsetValue; static DependencyProperty(); public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata); public static DependencyProperty RegisterAttached(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata); public PropertyMetadata GetMetadata(Type forType); } }
Нас интересуют методы Register и RegisterAttached. Оба они вызывают внутреннюю реализацию Register со следующей сигнатурой
internal static DependencyProperty Register(bool fIsAttachedDP, string name, Type propertyType, Type ownerType, PropertyMetadata propertyMetadata, bool readOnly)
В случае Register первым параметром (fIsAttachedDP) является false, а в случае RegisterAttached – true. Также передается имя свойства (name), его тип (propertyType), тип владельца (ownerType), флаг указывающий является ли свойство свойством только для чтения (readOnly) и экземпляр класса PropertyMetadata.
Сам по себе класс PropertyMetadata безумно простой. Он всего-навсего содержит два поля – значение по умолчание (defaultValue), и делегат, который вызывается, когда свойство меняет значение (propertyChangedCallback).
public class PropertyMetadata { public object DefaultValue { get; private set; } internal PropertyChangedCallback PropertyChangedCallback { get; private set; } }
Есть еще три конструктора, но они опущены для экономии места, поскольку интереса не представляют.
Взглянем на метод Register поподробнее
Его код можно условно разделить на три блока.
В первом в зависимости от переданного параметра fIsAttachedDP создается экземпляр класса CustomDependencyProperty либо CustomAttachedDependencyProperty, причем CustomAttachedDependencyProperty является наследником CustomDependencyProperty. В свою очередь CustomDependencyProperty наследуется непосредственно от DependencyProperty. Иерархия классов следующая:
Во втором блоке происходит простое копирование данных. Причем класс PropertyMetadata нигде дальше не используется — его поля просто-напросто копируются в DependencyProperty.
Наибольший интерес представляет собой третий блок, в котором вызывается метод RememberRegisteredPropery, ниже его реализация:
_registeredProperties – это статическое поле, представляющее собой словарь, ключом которого является тип владельца (ownerType), а значением еще один словарь, в котором ключ – имя регистрируемого свойства(name), а значение – непосредственно экземпляр этого свойства (dp). Изобразить это, например, можно вот так:
Еще раз обращаю ваше внимание на то, что _registeredPropertiesявляется статическим (помечено как static) полем.
В следующем блоке формируется глобальный индекс свойства – m_KnownId. Заметчу, что m_KnownId – это поле класса DependencyProperty типа uint, к тожу же оно internal, то есть доступно другим классам в сборке.
К _registeredPropertiesи m_KnownId мы еще вернемся при рассмотрении DependencyObject. Но об этом, и о том за счет чего происходит экономия памяти при использовании DependencyProperty читайте во второй части.
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: