Продолжим. Первую часть можно найти здесь.

Мы остановились на том, что у DependencyProperty имеются наследники CustomDependencyProperty и CustomAttachedDependencyProperty. Кроме того, существует еще один важный класс, унаследованный от DependencyProperty. Он называется CoreDependencyProperty. Такие свойства, далее мы будем называть их внутренними (core),  создаются непосредственно самим фреймворком в отличие от кастомных (custom) свойств, создаваемых разработчиками. Отмечу, что значения для внутренних (core) свойств хранятся в некотором хранилище, доступ к которому осуществляется через нативный код. Для чего так сделано точно узнать не удалось. Один из вариатов — такие контролы, как WebBrowser, являются просто обертками над нативными контролами. Поэтому для них проще иметь нативные DependencyProperty.

DependencyObject

Теперь перейдем к классу DependencyObject. Его интерфейс представлен ниже:

namespace System.Windows
{
    public abstract class DependencyObject
    {
       [EditorBrowsable(EditorBrowsableState.Advanced)]
       public Dispatcher Dispatcher { get; }
       static DependencyObject();
       [EditorBrowsable(EditorBrowsableState.Never)]
       public bool CheckAccess();
       public void SetValue(DependencyProperty dp, object value);
       public void ClearValue(DependencyProperty dp);
       public object ReadLocalValue(DependencyProperty dp);
       public object GetAnimationBaseValue(DependencyProperty dp);
       public object GetValue(DependencyProperty dp);
    }
}

Внутри DependencyObject содержит поле _valueTable. Это поле представляет собой словарь (dictionary) хранящий набор DependencyProperty и их текущих значений.

Нас будут интересовать два основных метода класса DependencyObject, а именно GetValue и SetValue. Которые нужны соответственно для получения и установки переданного в качестве аргумента значения экземпляра DependencyProperty.

Начнем с метода  GetValue.

GetValue

Во втором блоке обрабатываются кастомные (custom) свойства. Если текущее значение не найдено в словаре_valueTable, то просто возвращается значение по умолчанию.

Первый блок относится к  внутренним (core) свойствам. Здесь происходит вызов нативного метода (внутри XcpImports.GetValue), для которо аргументами являются глобальный индекс свойства (m_nKnownId) и его дескриптор.  Достучаться к коду нативной библиотеки естественно не удалось, но работать это должно примерно также как и для кастомных (custom) свойств.

Благодаря тому, что значения по  умолчанию хранятся в статических коллекциях достигается экономия памяти при использовании DependencyProperty. Т.е., к примеру, если вы бросите на форму пару десятков одинаковых кнопок, их свойства будут возвращать одни и те же значения по умолчанию. Но это, конечно, до тех пор, пока вы не измените значение свойств на что-то отличное от значения по умолчанию.

Обратимся теперь к методу SetValue. Он гораздо сложнее, поэтому его код приводить не буду. В нем сперва валидируются параметры, чтобы не позволить присвоить свойству тип отличный от того который был задан при регистрации свойства. Далее большая часть кода отвечает за приоретизацию значений свойств. Также формируется метод обратного вызова (callback), содержащий старое и новое значение. Если значение не было установлено, в качестве старого значения выступает значение по умолчанию. Для внутренних (core) свойств их  значение записывается посредством нативного кода в некоторое хранилище. В то время как для кастомных (custom)  их текущее значение попадает в _valueTable.

Замечу, что оба метода SetValue и GetValue не обращаются к коллекции _registeredProperties, о которой было рассказано в первой части. Это обращение не требуется, поскольку в методы передаются экземпляры свойсв, которые сами по себе содержат значения по умолчанию. _registeredProperties используется для получения значения свойсво по его имени. Это нужно, главный образом, для биндига (binding). Например, в выражениях вида {Binding Path=PropertyName}.

Закончить хотелось бы тем, что для сравнения я посмотрел код DependencyProperty и DependencyObject для WPF. (Обзор для WPF смотрите здесь.) Конечно WPF сложнее чем Silverlight (здесь и далее Silverlight для WP7), но все же я не ожидал такой разницы в реализации —  такое ощущение, что разработчики Silverlight в глаза не видели реализации в WPF. Неладно что-то в Дацком королестве ;-) В WPF нет никаких наследников DependencyPropertyда и сам класс является запечатанным (sealed).  Буду рад услышать ваши мнения на этот счет в комментариях.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *