Вступление
Когда вы начинаете разрабатывать приложения на WPF, то вскором времени столкнётесь с DependencyProperties. Они очень похоже на обычные .NET свойства, но их концепция гораздо мощнее и сложнее.
Главное отличие в том, что значения обычных .NET свойств можно читать прямо из закрытых членов вашего класса, в то время как значение DependencyProperties может динамически вызываться при вызове GetValue(), которые наследуется от DependencyObject.
Когда вы устанавливаете значение DepebdencyProperties, оно не хранится в поле вашего объекта, но хранится в словаре ваших ключей и значений, предоставленных DependencyObject базового класса. Ключ - это мя свойства, а значение - значение, которые вы хотите установить.
Преимущества Dependency Properties: Снижение памяти. Наследование значений. Уведомления об изменениях. Value resolution strategy
Каждый раз, когда вы получаете доступ к dependency property, оно внутренне устанавливает значение, преимущественно от высокого к низкому. Оно проверяет, если локальное значение доступно, если нет, то проверяется пользовательский триггер. В конце концов, значение по умочланию всегда доступно.
Каждый элемент управления WPF регистрирует набор DependencyProperties к статическому классу DependencyProperty. Каждый из них состоит из ключа - который должен быть уникальным для каждого типа - и метаданных, которые содержат обратные вызовы и значение по умолчанию.
Все виды, которые хотят использовать DependencyProperties, должны быть производными от DependencyObject. Этот базовый класс (BaseClass) определяет ключ, словарь значений, содержащий локальные значения DependencyProperty.
При доступе к DependencyPropertyон, внутренне вызывается GetValue (DependencyProperty) для доступа к значению. Если локальный значение доступно, читается его непосредственно из словаря. Если значение не устанавливается, то значение ищется в логическом дереве и поулчает наследуемого значения. Если не найдено значение, принимается значение по умолчанию, определенное в метаданных свойства. Эта последовательность являестя упрощённой, но показывает основную концепцию.
Как создать DependencyProperty
Чтобы создать DependencyProperty, надо добавить статическое поле типа DepdencyProperty и вызвать DependencyProperty.Register () для создания экземпляра свойства зависимостей. Название DependendyProperty должны всегда заканчиваться на "....Property". Это соглашение об именовании в WPF.
Важно: Не добавляйте логику для этих свойств, поскольку они вызываются только при установке свойства из кода. Если вы установите свойство от XAML, метод SetValue () вызывется напрямую.
Если вы используете Visual Studio, вы можете ввести propdp и нажать 2 раза вкладку создать свойство зависимостей.
// Dependency Property
public static readonly DependencyProperty CurrentTimeProperty = DependencyProperty.Register( "CurrentTime", typeof(DateTime), typeof(MyClockControl), new FrameworkPropertyMetadata(DateTime.Now));
// .NET Property wrapper
public DateTime CurrentTime{ get { return (DateTime)GetValue(CurrentTimeProperty); } set { SetValue(CurrentTimeProperty, value); }}
Изменение уведомления является статическим методом, который вызывается каждый раз, когда изменяется значение TimeProperty. Новое значение передается в EventArgs, объект, на котором значение изменилось передается в качестве источника.
private static void OnCurrentTimePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e){ MyClockControl control = source as MyClockControl; DateTime time = (DateTime)e.NewValue; // Put some update logic here...}
Принудительность обратного вызова позволяет регулировать значение. Хорошим примером является индикатор со значением ниже наименьшего или выше максимального. В этом случае мы можем принудить значение в пределах разрешенных границ. В следующем примере мы ограничиваем время, чтобы быть в прошлом.
private static object OnCoerceTimeProperty( DependencyObject sender, object data ) { if ((DateTime)data > DateTime.Now ) { data = DateTime.Now; } return data; }
ReadOnly DependencyProperty
Некоторые DependencyProperty элементов управления WPF являются только для чтения. Они часто используются, чтобы сообщить о состояния элемента управления, как IsMouseOver, например. Не имеет смысла обеспечивать сеттер для этого значения.
Может быть, вы спросите себя, почему бы просто не использовать обычный. NET Property? Одной из важных причин является то, что вы не можете установить триггеры нормально для .NET свойств.
СозданиеReadOnly DependencyProperty похоже на создание обычного DependencyProperty. Вместо вызова DependencyProperty.Register () вы вызываете DependencyProperty.RegisterReadonly ().
Это возвращает вам DependencyPropertyKey. Этот ключ должен храниться в закрытых или защищенных статических только для чтения полях вашего класса. Ключ дает вам доступ для установки значения из вашего класса и возможность использовать его как обычные DependencyProperty.
// Register the private key to set the value private static readonly DependencyPropertyKey IsMouseOverPropertyKey = DependencyProperty.RegisterReadOnly("IsMouseOver", typeof(bool), typeof(MyClass), new FrameworkPropertyMetadata(false));
// Register the public property to get the value public static readonly DependencyProperty IsMouseoverProperty = IsMouseOverPropertyKey.DependencyProperty;
// .NET Property wrapper
public int IsMouseOver{ get { return (bool)GetValue(IsMouseoverProperty); } private set { SetValue(IsMouseOverPropertyKey, value); }}
Вложенные свойства (Attached Property)
Вложенные свойства представляют собой особый вид DependencyProperties. Они позволяют придавать значение на объект, который ничего об этом значении не знает.
Хорошим примером для этого понятия являются панели слоёв. Каждый панель должна иметь разные свойства, чтобы выровнять его дочерние элементы. Canvas нуждается в Topу и Left, DockPanel необходим Dock., и т.д. Так вы можете написать свою собственную панель слоёв, список бесконечен. Итак, вы видите, что это невозможно - иметь все эти свойства на всех элементов управления WPF.
Решение - вложенные свойства. Они определяются контролем, который нуждается данные из другого элемента управления в определенном контексте. Например элементом, который выравнивается с помощью родительского макета панели.
Чтобы установить значение вложенного свойства, добавте атрибут в XAML с префиксом элемента, который обеспечивает вложенное свойство. Чтобы установить на Canvas.Top и Canvas.Left для Buton, вы пишете следующим образом:
<Canvas>
<Button Canvas.Top="20" Canvas.Left="20" Content="Click me!"/> </Canvas>
public static readonly DependencyProperty TopProperty = DependencyProperty.RegisterAttached("Top", typeof(double), typeof(Canvas), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.Inherits)); public static void SetTop(UIElement element, double value){ element.SetValue(TopProperty, value);} public static double GetTop(UIElement element){ return (double)element.GetValue(TopProperty);}
Как очистить локальное значение
button1.ClearValue( Button.ContentProperty );
|