Ответ 1
Моя ментальная модель того, как работают свойства зависимостей:
Любой класс DependencyObject
реализует два специальных свойства. Одно, статическое свойство класса, является словарем объектов DependencyProperty
. Каждый экземпляр класса может заглянуть внутрь этого словаря, чтобы найти метаинформацию о каждом DependencyProperty
- имени свойства, его типе, любых обратных вызовах, которые нужно вызывать при их получении и установке, о том, как он участвует в наследовании свойств и т.д. Когда вы регистрируете свойство зависимостей, вы добавляете запись в этот словарь.
Другим свойством является свойство экземпляра: это словарь с ключом DependencyProperty
, который содержит локальное значение каждого DependencyProperty
, если он был установлен.
Методы SetValue
и GetValue
, которые вы реализуете в установщике и получателе свойства CLR, - это в основном ленивая оценка стероидов. Вместо того, чтобы хранить и извлекать значение свойства в поле поддержки, они сохраняют и извлекают значение свойства в словаре значений.
Магия свойств зависимостей заключается в том, что делают GetValue
и SetValue
.
GetValue
просматривает значение для свойства в словаре значений объектов. Если он не находит его, он вызывает GetValue
в родительском элементе, чтобы получить то, что родительский элемент считает значением. Например, при создании TextBox
в Window
все, что смотрит на TextBox
FontFamily
, на самом деле вызывает GetValue
. Если вы явно не задали шрифт, в его словаре для этого свойства нет записи. Поэтому GetValue
запрашивает родительский элемент для значения. Родительский элемент может иметь или не иметь FontFamily
set; если нет, то его вызов GetValue
возвращает значение из его родителя. И так далее, пока не будет достигнут объект Window
, и будет найдено фактическое значение FontFamily
.
Если вы установите FontFamily
на TextBox
, SetValue
сохранит значение в словаре значений. В следующий раз что-нибудь нужно получить значение FontFamily
для этого TextBox
, GetValue
находит значение в словаре и возвращает его, поэтому ему не нужно запрашивать родительский элемент.
Если вы устанавливаете FontFamily
в Window
, SetValue
не только обновляет значение в словарях Window
value, он запускает событие изменения свойства, которое все зависит от свойства. (Вот почему они называются свойствами зависимостей, помните.) И если предмет, зависящий от свойства, сам по себе является свойством зависимости, он запускает свои собственные события изменения свойств. Именно так, изменение FontFamily
на Window
изменяет шрифт для каждого элемента управления в окне, а также предлагает WPF повторно отобразить измененные элементы управления.
Прикрепленные свойства работают с использованием такого же подхода. Любой объект, который может иметь прикрепленные свойства, имеет словарь, в котором хранятся значения прикрепленных свойств. Когда вы устанавливаете Grid.Column
на CheckBox
в XAML, вы просто добавляете запись в этот словарь CheckBox
. Когда Grid
должен знать, в каком столбце находится CheckBox
, он отображает значение из этого словаря. Когда вы устанавливаете Grid.IsSharedSizeScope
на True
на объект, этот словарь объектов будет содержать новое свойство - словарь, который содержит ширины/высоты для каждого SharedSizeKey
.
Я должен подчеркнуть, что это моя ментальная модель. Я не сел с Reflector и посмотрел на фактическую реализацию Register
, GetValue
и SetValue
, чтобы выяснить, как они на самом деле работают. Возможно, я ошибаюсь в деталях. Но это модель, которая точно предсказывает, как ведет себя этот материал, поэтому он достаточно хорош.
Концепция хранения значений свойств в словарях довольно странна для программистов на С#. Тем не менее, это старая шляпа для программистов на Python. В Python все свойства класса - все объекты, по сути, хранятся в словарях, и поэтому вы можете получить их значение либо через аксессуры свойств, либо просто просмотрев их. Свойства зависимостей и приложенные свойства - это еще один способ, которым .NET, украв все, что было у Java, которое стоило воровать, теперь грабит Python. (Или из того места, где Python грабил их.) Изучение Python сделало меня намного лучшим программистом на С#; Я рекомендую его любому разработчику С#, который еще не сделал этого.