Пользовательские свойства зависимостей
В этом разделе описываются основания для создания настраиваемых свойств зависимостей для приложений Windows Presentation Foundation (WPF), а также этапы и некоторые варианты реализации, которые могут повысить производительность, удобство использования и универсальность свойств.
Предварительные требования
Предполагается, что вы имеете представление о свойствах зависимостей с точки зрения потребителя существующих свойств зависимостей в классах WPF и ознакомились с разделом Общие сведения о свойствах зависимостей. Чтобы выполнить примеры в этом разделе, следует также иметь представление о XAML и написании простых приложений WPF.
Что такое свойство зависимостей?
Реализовать поведение, аналогичное свойству CLR, для поддержки стилизации, привязки данных, наследования, анимации и значений по умолчанию можно путем реализации его как свойства зависимостей. Свойства зависимостей являются свойствами, которые зарегистрированы в WPF системы свойств путем вызова Register метод (или RegisterReadOnly), и снабжаются DependencyProperty поле идентификатора. Свойства зависимостей могут использоваться только DependencyObject типов, но DependencyObject находится достаточно высоко в WPF иерархии классов, поэтому большинство классов, доступных в WPF могут поддерживать свойства зависимостей. Дополнительные сведения о свойствах зависимостей и некоторых терминах и соглашениях, используемых для их описания в этом SDK, см. в разделе Общие сведения о свойствах зависимостей.
Примеры свойств зависимостей
Примеры свойств зависимостей, реализуемых в WPF классы включают Background свойство, Width свойство и Text свойство среди многих других. Каждое свойство зависимости, предоставленное классом имеет соответствующий открытое статическое поле типа DependencyProperty на том же классе. Это идентификатор для свойства зависимостей. Идентификатор именуется по следующему соглашению: имя свойства зависимостей, за которым следует строка Property
. Например, соответствующий DependencyProperty поле идентификатора для Background свойство BackgroundProperty. Идентификатор хранит сведения о свойстве зависимостей, как она была зарегистрирована, а затем идентификатор позже используется для других операций, в которых участвует свойство зависимостей, например, вызов SetValue.
Как упоминалось в разделе Общие сведения о свойствах зависимостей, все свойства зависимостей в WPF (за исключением наиболее присоединенных свойств) также являются свойствами CLR из-за реализации "оболочки". Таким образом, из кода можно получать или задавать свойства зависимостей путем вызова методов доступа CLR, определяющих оболочки таким же образом, как и для других свойств CLR. Как потребитель созданных свойств зависимостей, вы обычно не используете DependencyObject методы GetValue и SetValue, которые являются точки подключения к базовой системе свойств. Вместо этого существующая реализация CLR уже будет вызван свойства GetValue и SetValue в get
и set
реализации оболочки свойства, соответствующим образом используя поле идентификатора . При собственной реализации настраиваемого свойства зависимостей вы будете определять оболочку аналогичным образом.
Почему требуется реализовывать свойство зависимостей?
При реализации свойства в классе, до тех пор, пока ваш класс является производным от DependencyObject, вы можете снабдить свое свойство с DependencyProperty идентификатор и, следовательно, чтобы сделать его свойства зависимостей. Назначение свойства свойством зависимостей не всегда требуется и не всегда уместно и зависит от конкретной ситуации. В некоторых случаях достаточно просто снабдить свойство закрытым полем. Тем не менее реализовать свойство как свойство зависимостей потребуется, если свойство должно поддерживать одну или несколько из следующих возможностей WPF.
Требуется, чтобы свойство было задаваемым в стиле. Более подробную информацию см. в разделе Стилизация и использование шаблонов.
Требуется, чтобы свойство поддерживало привязку данных. Дополнительные сведения о свойствах зависимостей привязки данных см. в разделе Привязка свойств двух элементов управления.
Требуется, чтобы свойство было задаваемым с помощью динамической ссылки ресурса. Дополнительные сведения см. в разделе Ресурсы XAML.
Требуется настроить автоматическое наследование значения свойства из родительского элемента в дереве элементов. В этом случае регистрация RegisterAttached метод, даже при создании оболочки свойства для CLR доступа. Дополнительные сведения см. в разделе Наследование значения свойства.
Требуется, чтобы свойство поддерживало анимацию. Более подробную информацию см. в разделе Общие сведения об эффектах анимации.
Требуется, чтобы система свойств сообщала об изменении предыдущего значения свойства действиями, выполняемыми системой свойств, окружением или пользователем, или путем чтения и использования стилей. Используя метаданные свойства, свойство может указывать метод обратного вызова, который будет вызываться каждый раз, когда система свойств определит, что значение свойства было однозначно изменено. Связанным понятием является приведение значения свойства. Дополнительные сведения см. в разделе Проверка и обратные вызовы свойства зависимостей.
Требуется использовать установленные соглашения о метаданных, которые также используются процессами WPF, такими как передача сведений о том, нужно ли системе макета перестраивать визуальные компоненты элемента при изменении значения свойства. Или требуется возможность использовать переопределения метаданных таким образом, чтобы производные классы могли изменять основанные на метаданных характеристики, такие как значение по умолчанию.
Требуется, чтобы свойства пользовательского элемента управления для получения конструктора WPF для Visual Studio поддерживают, например свойства редактирование окна. Дополнительные сведения см. в разделе Общие сведения о разработке элементов управления.
Рекомендуется рассмотреть возможность реализации сценариев путем переопределения метаданных существующего свойства зависимостей, вместо реализации совершенно нового свойства. Практическая польза переопределения метаданных зависит от сценария и того, насколько этот сценарий схож с реализацией существующих свойств зависимостей и классов WPF. Дополнительные сведения о переопределении метаданных для существующих свойств см. в разделе Метаданные свойств зависимостей.
Контрольный список определения свойства зависимостей
Определение свойства зависимостей состоит из четырех различных понятий. Эти понятия не обязательно являются строгими этапами процедуры, так как некоторые из них в итоге объединяются в одну строку кода в реализации.
(Необязательно.) Создайте метаданные для свойства зависимостей.
Зарегистрируйте имя свойства в системе свойств, указав тип владельца и тип значения свойства. Также укажите метаданные свойства, если они используются.
Определение DependencyProperty идентификатор, что
public
static
readonly
поля в типе владельца.Определите свойство-"оболочку" CLR, имя которого соответствует имени свойства зависимостей. Реализуйте методы доступа
get
иset
свойства-"оболочки" CLR для подключения к свойству зависимостей, которое оно поддерживает.
Регистрация свойства в системе свойств
Чтобы назначить свойство свойством зависимостей, необходимо зарегистрировать это свойство в таблице, обслуживаемой системой свойств, и предоставить ему уникальный идентификатор, используемый в качестве квалификатора для последующих операций системы свойств. Эти операции могут быть внутренними операциями или вашим собственным кодом, вызывающим систему свойств API. Чтобы зарегистрировать свойство, вызовите Register метод в теле класса (внутри класса, но вне определения любого из членов). Поле идентификатора также предоставляется Register вызова метода, в качестве возвращаемого значения. Причина, Register вызов выполняется за пределами другого элемента определения обусловлено это возвращаемое значение используется для назначения и создания public
static
readonly
поле типа DependencyProperty как часть класса. Это поле становится идентификатором для вашего свойства зависимостей.
public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
"AquariumGraphic",
typeof(Uri),
typeof(AquariumObject),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnUriChanged)
)
);
Соглашения об именовании свойств зависимостей
Существуют установленные соглашения об именовании, касающиеся свойств зависимостей, которым необходимо следовать, если ситуация не является исключительной.
Само свойство зависимости будет иметь базовое имя, «AquariumGraphic» пример, в котором задается как первый параметр Register. Это имя должно быть уникальным в пределах каждого регистрирующего типа. Считается, что свойства зависимостей, наследуемые через базовые типы, уже являются частью регистрирующего типа: имена наследуемых свойств нельзя зарегистрировать повторно. Однако существует способ добавления класса как владельца свойства зависимостей даже в том случае, если это свойство зависимостей не наследуется. Дополнительные сведения см. в разделе Метаданные свойств зависимостей.
При создании поля идентификатора назовите это поле по зарегистрированному имени свойства, добавив суффикс Property
. Это поле является идентификатором для свойства зависимостей, и он будет использоваться позже в качестве входных данных для SetValue и GetValue разрешить вызовы, которые будут выполняться в оболочках, при других кода доступ к свойству через ваш код доступе любого внешнего кода вы , системой свойств и, вероятно, XAML процессоров.
Note
Определение свойства зависимостей в теле класса является обычной реализацией, однако его также можно определить в статическом конструкторе класса. Этот подход может оказаться целесообразным, если для инициализации свойства зависимостей требуется несколько строк кода.
Реализация "оболочки"
Реализация оболочки должна вызывать GetValue в get
реализации, и SetValue в set
(исходный вызов регистрации и поле показанная здесь реализация слишком для ясности).
В все, кроме исключительных обстоятельствах реализации оболочки должны выполнять только GetValue и SetValue действия, соответственно. Причина этого обсуждается в разделе Загрузка кода XAML и свойства зависимостей.
Все существующие открытые свойства зависимостей, предоставляемые для классов WPF, используют эту простую модель реализации оболочки. Основные сложности обработки свойств зависимостей определяются наследуемым поведением системы свойств или реализацией через другие понятия, такие как приведение или обратные вызовы изменения свойства через метаданные свойства.
public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
"AquariumGraphic",
typeof(Uri),
typeof(AquariumObject),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnUriChanged)
)
);
public Uri AquariumGraphic
{
get { return (Uri)GetValue(AquariumGraphicProperty); }
set { SetValue(AquariumGraphicProperty, value); }
}
Опять же, по соглашению, имя свойства-оболочки должно быть совпадает с именем, выбранным и заданным в качестве первого параметра Register вызов, который зарегистрировал свойство. Если свойство не следует соглашению, это не обязательно ведет к его неработоспособности, однако будет наблюдаться несколько серьезных проблем.
Не будут работать определенные аспекты стилей и шаблонов.
Большинство инструментов и конструкторов полагается на соглашения об именовании для правильной сериализации XAML, а также для предоставления помощи разработчику на уровне свойств.
Текущая реализация загрузчика WPF XAML полностью обходит оболочки и основывается на соглашении об именовании при обработке значений атрибутов. Дополнительные сведения см. в разделе Загрузка кода XAML и свойства зависимостей.
Метаданные свойства для нового свойства зависимостей
При регистрации свойства зависимостей через систему свойств создается объект метаданных, который хранит характеристики свойства. Многие из этих характеристик имеют значения по умолчанию, заданные в случае, если данное свойство зарегистрировано с помощью простых сигнатур Register. Другие сигнатуры Register позволяют указывать метаданные, требуемые при регистрации свойства. Как правило, метаданные, задаваемые для свойства зависимостей, предполагают предоставление значений по умолчанию, применяемых для новых экземпляров, которые используют свойство.
При создании свойства зависимостей, которое существует в производном классе FrameworkElement, можно использовать более специализированный класс метаданных FrameworkPropertyMetadata вместо базового PropertyMetadata класса. Конструктор FrameworkPropertyMetadata класс имеет несколько сигнатур, в котором можно указать различные характеристики метаданных в сочетании. Если вы хотите указать только значение по умолчанию, используйте сигнатуру, которая принимает один параметр типа Object. Передайте этот параметр объекта как значение по умолчанию определенного типа, для свойства (предоставленное значение по умолчанию должен быть типом, указанным как propertyType
параметр в Register вызова).
Для FrameworkPropertyMetadata, можно также указать флаги параметров метаданных для свойства. Эти флаги преобразуются в дискретные свойства в метаданных свойства после регистрации и используются для передачи определенных условных объектов в другие процессы, например в обработчик макетов.
Задание соответствующих флагов метаданных
Если свойство (или изменения в его значении) влияет на UI , и в частности затрагивает как система макета размер или отрисовку элемента на странице, задайте одно или несколько из следующих флагов: AffectsMeasure, AffectsArrange, AffectsRender.
AffectsMeasure Указывает, что изменение этого свойства требует изменения для UI подготовки отчетов, где содержащего его объекта может потребовать больше или меньше места в родительском элементе. Например, этот флаг следует задать для свойства "Ширина".
AffectsArrange Указывает, что изменение этого свойства требует изменения для UI отрисовки, обычно не требует изменения в выделенном пространстве, однако означает, что изменилось размещение в пространстве. Например, этот флаг следует задать для свойства "Выравнивание".
AffectsRender Указывает, что произошло некоторое другое изменение, не повлияет на макет и измерение, но требуют повторной визуализации. Примером будет свойство, изменяющее цвет существующего элемента, например "Фон".
Эти флаги часто используются в качестве протокола в метаданных для собственных реализаций переопределения системы свойств или обратных вызовов макета. Например, возможно, OnPropertyChanged обратного вызова, который будет вызывать InvalidateArrange Если любое свойство экземпляра сообщает об изменении значения и имеет AffectsArrange как
true
в своих метаданных.
Некоторые свойства могут влиять на характеристики отрисовки содержащего их родительского элемента указанными выше способами и помимо изменений в обязательном размере, упомянутых выше. Например, MinOrphanLines свойства, используемого в модели потокового документа, где изменения этого свойства можно изменять общую отрисовку документа нефиксированного формата, содержащего абзац. Используйте AffectsParentArrange или AffectsParentMeasure для определения аналогичных случаев в ваших собственных свойствах.
По умолчанию свойства зависимостей поддерживают привязку данных. Вы можете специально отключить привязку данных для случаев, когда отсутствует практический смысл в привязке данных или когда быстродействие в привязке данных для больших объектов становится проблемой.
По умолчанию, привязка данных Mode для значений по умолчанию свойства зависимостей для OneWay. Можно всегда изменить привязки на TwoWay каждого экземпляра привязки; Дополнительные сведения см. в разделе Указание направления привязки. Но как автор свойства зависимостей, можно использовать свойство TwoWay режима привязки по умолчанию. Примером существующего свойства зависимости является MenuItem.IsSubmenuOpen; сценарий для этого свойства является то, что IsSubmenuOpen параметр логики и компоновки MenuItem взаимодействовать со стилями темы по умолчанию. IsSubmenuOpen Свойство логики изначально использует привязку данных для поддержания состояния свойства в соответствии с другими свойствами состояния и вызовы методов. Другим примером свойства, которое привязывает TwoWay по умолчанию является TextBox.Text.
Можно также включить наследование свойств в пользовательское свойство зависимости, задав Inherits флаг. Наследование свойств удобно для ситуации, когда родительские и дочерние элементы имеют общее свойство и для дочерних элементов имеет смысл задать то же значение свойства, что и в родительском элементе. Примером наследуемого свойства является DataContext, который используется для операций привязки, чтобы включить важный сценарий «основной-подробности» для представления данных. Благодаря DataContext наследуемые, дочерние элементы наследуют контекст данных также. Благодаря наследованию значения свойства вы можете указать контекст данных в корне приложения или страницы и вам не потребуется уточнять его для привязок во всех возможных дочерних элементах. DataContext также представляет собой хороший пример, чтобы продемонстрировать, что наследование переопределяет значение по умолчанию, но может всегда быть локально установлено для любого конкретного дочернего элемента. Дополнительные сведения см. в разделе использование шаблона «основной-подробности» с иерархическими данными. Наследование значения свойства может влиять на производительность, и таким образом, его следует использовать с осторожностью. Дополнительные сведения см. в разделе Наследование значения свойства.
Задайте Journal флаг, указывающий, если свойство зависимостей должны обнаруживаться или использоваться службами ведения журнала переходов. Пример — SelectedIndex свойство; любой элемент, выбранный в выделенной области элемента управления должны сохраняться при навигации по истории ведения журнала.
Свойства зависимости "только для чтения"
Можно определить свойство зависимости, которое доступно только для чтения. Однако ситуации для такого использования немного отличаются, как и процедура регистрации свойства в системе свойств и предоставление идентификатора. Дополнительные сведения см. в разделе Свойства зависимостей "только для чтения".
Свойства зависимостей типа коллекция
Свойства зависимостей типа коллекции имеют некоторые дополнительные проблемы при реализации, которые необходимо учитывать. Подробности см. в разделе Свойства зависимостей типа коллекции.
Замечания по безопасности свойств зависимостей
Свойства зависимостей должны объявляться как открытые свойства. Поля идентификаторов свойств зависимостей должны объявляться как открытые статические поля. Даже при попытке объявить другие уровни доступа (например, защищенный) к свойству зависимостей всегда можно обращаться через идентификатор в сочетании с API системы свойств. Даже защищенное поле идентификатора потенциально доступно из-за метаданных reporting или определения значения API , которые являются частью системы свойств, таких как LocalValueEnumerator. Дополнительные сведения см. в разделе Безопасность свойств зависимостей.
Свойства зависимостей и конструкторы класса
Есть общий принцип в программировании управляемого кода (он часто принудительно применяется средствами анализа кода, такими как FxCop), подразумевающий, что конструкторы класса не должны вызывать виртуальные методы. Этот принцип обусловлен тем, что конструкторы могут быть вызваны в качестве базовой инициализации конструктора производного класса, а ввод виртуального метода через конструктор может произойти в состоянии неполной инициализации конструируемого экземпляра объекта. При наследовании от любого класса, который уже является производным от DependencyObject, следует иметь в виду, что сама система свойств вызывает и предоставляет виртуальные методы. Эти виртуальные методы являются частью служб системы свойств WPF. Переопределение методов позволяет производным классам участвовать в определении значения. Чтобы избежать потенциальных проблем при инициализации среды выполнения, не задавайте значения свойств зависимостей в конструкторах классов (если только вы не следуете конкретному шаблону конструктора). Подробнее см. в разделе Шаблоны безопасного конструктора для DependencyObjects.