В частности, если нужно, чтобы свойство было целью операции привязки данных или анимации, если оно обязано уведомлять о своем изменении, если свойство должно быть в состоянии работать в качестве установщика в стиле WPF или получать свои значения от родительского элемента, то возможностей обычного свойства CLR окажется UserControl
, нужно выработать в себе привычку при построении специальных элементов управления всегда определять свойства зависимости.
Исследование существующего свойства зависимости
Прежде чем вы научитесь создавать специальные свойства зависимости, давайте рассмотрим внутреннюю реализацию свойства Height
класса FrameworkElement
. Ниже приведен соответствующий код (с комментариями):
// FrameworkElement "является" DependencyObject.
public class FrameworkElement : UIElement, IFrameworkInputElement,
IInputElement, ISupportInitialize, IHaveResources, IQueryAmbient
{
...
// Статическое поле только для чтения типа DependencyProperty.
public static readonly DependencyProperty HeightProperty;
// Поле DependencyProperty часто регистрируется
// в статическом конструкторе класса.
static FrameworkElement
{
...
HeightProperty = DependencyProperty.Register(
"Height",
typeof(double),
typeof(FrameworkElement),
new FrameworkPropertyMetadata((double) 1.0 / (double) 0.0,
FrameworkPropertyMetadataOptions.AffectsMeasure,
new PropertyChangedCallback(FrameworkElement.OnTransformDirty)),
new ValidateValueCallback(FrameworkElement.IsWidthHeightValid));
}
// Оболочка CLR, реализованная с использованием
// унаследованных методов GetValue/SetValue.
public double Height
{
get { return (double) base.GetValue(HeightProperty); }
set { base.SetValue(HeightProperty, value); }
}
}
Как видите, по сравнению с обычными свойствами CLR свойства зависимости требуют немалого объема дополнительного кода. В реальности зависимость может оказаться даже еще более сложной, чем показано здесь (к счастью, многие реализации проще свойства Height
).
В первую очередь вспомните, что если в классе необходимо определить свойство зависимости, то он должен иметь в своей цепочке наследования DependencyObject
, т.к. именно этот класс определяет методы GetValue
и SetValue
, применяемые в оболочке CLR. Из-за того, что класс FrameworkElement
"является" DependencyObject
, указанное требование удовлетворено.
Далее вспомните, что сущность, где действительно хранится значение свойства (значение double
в случае Height
), представляется как открытое, статическое, допускающее только чтение поле типа DependencyProperty
. По соглашению имя этого свойства должно всегда формироваться из имени связанной оболочки CLR с добавлением суффикса Property
:
public static readonly DependencyProperty HeightProperty;
Учитывая, что свойства зависимости объявляются как статические поля, они обычно создаются (и регистрируются) внутри статического конструктора класса. Объект DependencyProperty
создается посредством вызова статического метода DependencyProperty.Register
. Данный метод имеет множество перегруженных версий, но в случае свойства Height
он вызывается следующим образом:
HeightProperty = DependencyProperty.Register(
"Height",
typeof(double),
typeof(FrameworkElement),
new FrameworkPropertyMetadata((double)0.0,
FrameworkPropertyMetadataOptions.AffectsMeasure,
Бьерн Страуструп , Бьёрн Страуструп , Валерий Федорович Альмухаметов , Ирина Сергеевна Козлова
Программирование, программы, базы данных / Базы данных / Программирование / Учебная и научная литература / Образование и наука / Книги по IT