В WPF, что, действительно, это означает быть "свойством зависимости"?
Я прочитал Обзор Свойств Зависимости Microsoft, но он действительно не впитывается для меня. Частично в той статье говорится:
Стили и шаблоны являются двумя из главных сценариев мотивации для использования свойств зависимости. Стили особенно полезны для установки свойств, которые определяют пользовательский интерфейс приложения (UI). Стили обычно определяются как ресурсы в XAML. Стили взаимодействуют с системой собственности, потому что они обычно содержат "методы set" для особых свойств, а также "триггеры", которые изменяют значение свойства на основе значения в реальном времени для другого свойства.
И затем пример кода - это:
....
Но я не получаю то, что является особенным об этом. Это просто подразумевает, это, когда я установил Style
на кнопке к данному стилю, который я на самом деле устанавливаю Background
неявно? Это - затруднение его?
Что в WPF означает "свойство зависимости"?
Чтобы быть свойством зависимости, свойство должно быть определено как DependencyProperty, статически, для класса. Система свойств зависимостей сильно отличается от стандартных свойств CLR.
Свойства зависимости обрабатываются совсем по-другому. Тип определяет свойство зависимости статически и предоставляет значение по умолчанию. Время выполнения фактически не генерирует значение для экземпляра, пока оно не понадобится. Это дает одно преимущество - свойство не существует, пока не будет запрошено для типа, поэтому вы можете иметь большое количество свойств без накладных расходов.
Это то, что заставляет работать стилизацию свойств, но также важно для обеспечения вложенных свойств, "наследования" свойств через визуальное дерево и многих других вещей, на которые опирается WPF.
Например, возьмем свойство зависимости DataContext
. Обычно вы устанавливаете свойство DataContext
зависимости для окна или UserControl. Все элементы управления внутри этого окна по умолчанию автоматически "наследуют" свойство DataContext
своего родителя, что позволяет задавать привязки данных для элементов управления. При использовании стандартного свойства CLR вам пришлось бы определить этот DataContext для каждого элемента управления в окне, чтобы привязка работала правильно.
Может быть полезно понять, какую проблему пытается решить свойство зависимости.
Если мы отложим модели привязки, анимации и события изменения в сторону, как они обсуждались в других ответах, преимуществом будет использование памяти и, следовательно, масштабируемость для размещения многих тысяч объектов WPF в окне.
Если окно содержит 1000 объектов Label
, каждый объект Label
имеет обычный Foreground
, Background
, FontFamily
, FontSize
, FontWeight
и т. Д., То обычно это потребляет память, потому что каждое свойство будет иметь частное резервное поле для хранения значения.
Большинство приложений изменят только несколько свойств, большинство из которых останутся со значениями по умолчанию. В основном очень расточительная и избыточная информация (каждый объект просто хранит в памяти одни и те же значения по умолчанию)
Вот где свойства зависимостей различаются.
// Lets register the Dependency Property with a default value of 20.5
public static readonly DependencyProperty ColumnWidthProperty =
DependencyProperty.Register("ColumnWidth", typeof(double), typeof(MyWPFControl), new UIPropertyMetadata(20.5, ColWitdhPropChanged));
public double ColumnWidth
{
get { return (double)GetValue(ColumnWidthProperty); }
set { SetValue(ColumnWidthProperty, value); }
}
Нет частного резервного поля. Когда свойство зависимости зарегистрировано, можно указать значение по умолчанию. Таким образом, в большинстве случаев возвращаемое значение из GetValue
является значением по умолчанию, которое было сохранено только один раз, чтобы охватить все экземпляры объекта Label
во всех окнах вашего приложения.
Когда свойство зависимости устанавливается с помощью SetValue
, оно сохраняет значение, отличное от значения по умолчанию, в коллекции, идентифицированной экземпляром объекта, для возврата во всех последующих вызовах GetValue
.
Таким образом, этот метод хранения будет использовать память только для свойств объектов WPF, значение которых изменилось по сравнению со значением по умолчанию. т.е. только отличия от значения по умолчанию.
Вот объяснение того, как работают свойства зависимостей, которые мне всегда хотелось, чтобы кто-нибудь написал для меня. Он неполный и, вполне возможно, неверный, но он поможет вам в достаточной степени понять их, чтобы вы смогли понять документацию, которую вы читаете.
Свойства зависимостей - это значения, подобные свойствам, которые получаются и устанавливаются с помощью методов класса DependencyObject
. Они могут (и обычно выглядят) очень похоже на свойства CLR, но это не так. И это первое, что их сбивает с толку. Свойство зависимости на самом деле состоит из нескольких компонентов.
Вот пример:
Документ
является свойством объекта RichTextBox
. Это настоящее свойство CLR. То есть у него есть имя, тип, получатель и сеттер, как и у любого другого свойства CLR. Но в отличие от «обычных» свойств, свойство RichTextBox
не просто получает и устанавливает частное значение внутри экземпляра. Внутренне это реализовано следующим образом:
public FlowDocument Document
{
get { return (FlowDocument)GetValue(DocumentProperty); }
set { SetValue(DocumentProperty, value); }
}
Когда вы устанавливаете Document
, переданное вами значение передается в SetValue
вместе с DocumentProperty
. А что то ? И как GetValue
получает свое значение? И ... почему?
Во-первых, что. В RichTextBox
определено статическое свойство с именем DocumentProperty
.Когда это свойство объявляется, это делается следующим образом:
public static DependencyProperty DocumentProperty = DependencyProperty.Register(
"Document",
typeof(FlowDocument),
typeof(RichTextBox));
В данном случае метод Register
сообщает системе свойств зависимостей, что RichTextBox
- тип, а не экземпляр - теперь имеет свойство зависимости с именем Документ
типа FlowDocument
. Этот метод хранит эту информацию ... где-нибудь. Где именно находится скрытая от нас деталь реализации.
Когда установщик для свойства Document
вызывает SetValue
, метод SetValue
просматривает аргумент DocumentProperty
, проверяет, действительно ли он свойство, которое принадлежит RichTextBox
и что значение
является правильным типом, а затем сохраняет свое новое значение ... где-то. В документации для DependencyObject
эта деталь реализации скромна, потому что вам действительно не нужно ее знать. В моей мысленной модели того, как это работает, я предполагаю, что есть свойство типа Dictionary
, которое является частным для DependencyObject
, поэтому производные классы (например, RichTextBox
) не может его видеть, но GetValue
и SetValue
могут его обновить. Но кто знает, может, это написано на пергаменте монахами.
В любом случае, это значение теперь называется «локальным значением», то есть это значение, которое является локальным для этого конкретного RichTextBox
, как и обычное свойство.
Суть всего в следующем:
GetValue
и SetValue
, чтобы получить и установить его, но если вы не делаете что-то с системой свойств зависимостей, вам, вероятно, не нужно . Что за вещи? Что ж, давайте рассмотрим несколько вариантов использования.
Переплет. Когда вы привязываетесь к свойству, это должно быть свойство зависимости. Это связано с тем, что объект Binding
на самом деле не устанавливает свойства целевого объекта, он вызывает SetValue
целевого объекта.
Стили. Когда вы устанавливаете свойство зависимостей объекта на новое значение, SetValue
сообщает системе стилей, что вы это сделали. Вот как работают триггеры: они не обнаруживают, что значение свойства изменилось с помощью магии, сообщает им система свойств зависимостей.
Динамические ресурсы. Если вы напишете XAML как Background = {DynamicResource MyBackground}
, вы можете изменить значение ресурса MyBackground
, и фон объекта, на который он ссылается, будет обновлен. Это тоже не волшебство; динамический ресурс вызывает SetValue
.
Анимация. Анимация работает, управляя значениями свойств. Это должны быть свойства зависимости, потому что анимация вызывает SetValue
, чтобы добраться до них.
Уведомление об изменении. Когда вы регистрируете свойство зависимости, вы также можете указать функцию, которую SetValue
будет вызывать при установке значения свойства.
Наследование ценностей. Когда вы регистрируете свойство зависимости, вы можете указать, что оно участвует в наследовании значения свойства. Когда вы вызываете GetValue
, чтобы получить значение свойства зависимости объекта, GetValue
проверяет, есть ли локальное значение. Если нет, он проходит вверх по цепочке родительских объектов, просматривая их локальные значения для этого свойства.
Вот как вы можете установить FontFamily
в Window
и волшебным образом (я часто использую это слово) каждый элемент управления в окне использует новый шрифт . Кроме того, вы можете иметь сотни элементов управления в окне, при этом каждый из них не имеет переменной-члена FontFamily
для отслеживания своего шрифта (поскольку у них нет локальных значений), но вы все равно можете установить FontFamily
на любом элементе управления (из-за скрытого словаря значений seekrit, который есть у каждого DependencyObject
).