Метаданные свойства зависимостей

Система свойств Windows Presentation Foundation (WPF) включает систему отчетности по метаданным, которая выходит за пределы стандартной отчетности о свойстве, отражая общие характеристики среды CLR. Метаданные для свойства зависимости также можно уникально назначить для отдельного класса, который определяет свойство зависимости, можно изменить, когда свойство зависимости добавляется в другой класс, и можно переопределить, в частности, всеми производными классами, наследующими свойство зависимости от определяющего базового класса.

Необходимые компоненты

В этом разделе предполагается, что вы понимаете свойства зависимостей с точки зрения потребителя существующих свойств зависимостей в классах WPF и ознакомились с обзором свойств зависимостей. Чтобы выполнить примеры в этом разделе, следует также иметь представление о XAML и написании простых приложений WPF.

Как используются метаданные свойства зависимости

Метаданные свойств зависимости существуют в виде объекта, которому можно направлять запросы, чтобы проанализировать свойство зависимости. Система свойств также часто осуществляет доступ к этим метаданным, обрабатывая то или иное свойство зависимости. Объект метаданных для свойства зависимости может содержать следующие сведения.

  • Значение по умолчанию для свойства зависимости, если никакое другое значение для свойства зависимости не может быть определено локальным значением, стилем, наследованием и т. д. Подробное описание того, как значения по умолчанию применяются системой свойств при определении приоритета присвоения значений для свойств зависимостей, см. в разделе Приоритет значения свойства зависимости.

  • Ссылки на реализации обратных вызовов, которые влияют на поведение приведения или уведомления об изменении для каждого типа владельца. Обратите внимание, что эти обратные вызовы часто определяются закрытым уровнем доступа, поэтому получить реальные ссылки из метаданных, как правило, невозможно, если только эти ссылки не находятся внутри области, к которой у вас имеется доступ. Дополнительные сведения об обратных вызовах свойств зависимости см. в разделе Обратные вызовы свойств зависимости и проверка.

  • Если рассматриваемое свойство зависимости является свойством уровня среды WPF, метаданные могут содержать характеристики свойства зависимости уровня среды WPF, сообщающие информацию о службах и их состоянии, например модуле макетов уровня среды WPF и логику наследования свойств. Дополнительные сведения об этом аспекте метаданных свойства зависимости см. в разделе Метаданные свойств среды.

API метаданных

Тип, сообщающий больше всего сведений метаданных, используемых системой свойств, — это класс PropertyMetadata. Экземпляры метаданных указываются при необходимости, когда свойства зависимости регистрируются в системе свойств; их можно повторно указать для дополнительных типов, добавляющихся в качестве владельцев или переопределяющих метаданные, которые они наследуют от определения свойства зависимости базового класса. (Для случаев, когда регистрация свойства не задает метаданные, создается класс PropertyMetadata со значениями по умолчанию для этого класса.) Зарегистрированные метаданные возвращаются в качестве класса PropertyMetadata при вызове разнообразных перегрузок GetMetadata, которые получают метаданные от свойства зависимости в экземпляре DependencyObject.

Затем происходит наследование от класса PropertyMetadata, чтобы предоставить более конкретные метаданные для подразделений архитектуры, таких как классы уровня среды WPF. UIPropertyMetadata добавляет отчетность по анимации, а FrameworkPropertyMetadata предоставляет свойства уровня среды WPF, упомянутые в предыдущем разделе. При регистрации свойств зависимости их можно зарегистрировать с производными классами PropertyMetadata. При проверке метаданных базовый тип PropertyMetadata можно привести к производным классам, чтобы можно было проверить более конкретные свойства.

Примечание.

Характеристики свойства, которые могут быть указаны в классе FrameworkPropertyMetadata, иногда называют в этой документации "флагами". При создании новых экземпляров метаданных для использования в регистрациях свойств зависимости или переопределениях метаданных, эти значения указываются с использованием флагового перечисления FrameworkPropertyMetadataOptions, после чего можно указать потенциально сцепленные значения перечисления конструктору FrameworkPropertyMetadata. Но после построения эти характеристики параметра представляются в FrameworkPropertyMetadata как ряд логических свойств, а не как созданные значения перечисления. Логические свойства позволяют проверить каждое условие, а не требуют применения маски к значению флагового перечисления для получения интересующей вас информации. Конструктор использует сцепленные значения FrameworkPropertyMetadataOptions, чтобы сократить длину подписи конструктора, в то время как фактически построенные метаданные предоставляют дискретные свойства, чтобы сделать запрос метаданных более понятным.

Когда следует переопределять метаданные, а когда — создавать производный класс

В системном свойстве WPF имеются установленные возможности для изменения некоторых характеристик свойств зависимости без повторного создания с нуля. Это достигается путем создания другого экземпляра метаданных свойства для свойства зависимости в том виде, в котором оно существует в определенном типе. Обратите внимание, что большинство существующих свойств зависимости не являются виртуальными свойствами, поэтому строго говоря, их повторное создание в унаследованных классах возможно только путем затемнения существующего члена.

Если невозможно реализовать нужный вам сценарий для свойства зависимости в типе путем изменения характеристик существующих свойств зависимости, возможно, потребуется создать производный класс, а затем объявить в нем пользовательское свойство зависимости. Пользовательское свойство зависимости ведет себя идентично свойствам зависимости, определенным API WPF. Дополнительные сведения о пользовательских свойствах зависимости см. в разделе Пользовательские свойства зависимости.

Единственной важной характеристикой свойства зависимости, которую невозможно переопределить, является его тип значения. Если вы наследуете свойство зависимости, которое имеет примерное поведение, нужное вам, но вам нужен другой тип свойства, потребуется создать пользовательское свойство зависимости и, возможно, связать два свойства друг с другом через преобразование типов или другую реализацию пользовательского класса. Кроме того, невозможно заменить существующий класс ValidateValueCallback, так как этот обратный вызов существует в поле регистрации и не входит в метаданные.

Сценарии для изменения существующих метаданных

При работе с метаданными существующего свойства зависимости одним из распространенных сценариев для изменения метаданных свойства зависимости является изменение значения по умолчанию. Изменение или добавление обратных вызовов системы свойств является более сложным сценарием. Это целесообразно, если в вашей реализации производного класса существуют различные взаимосвязи между свойствами зависимостей. Одно из условий наличия модели программирования, которая поддерживает и код, и декларативное использование, заключается в том, что свойства должны обеспечить возможность настройки их значений в любом порядке. Следовательно, все зависимые свойства необходимо настраивать в режиме JIT без контекста; при этом невозможно использовать какую-либо информацию о порядке настройки (которую можно найти, например, в конструкторе). Дополнительные сведения об этом аспекте системы свойств см. в разделе Проверка и обратные вызовы свойства зависимостей. Обратите внимание, что обратные вызовы проверки не являются частью метаданных; они являются частью идентификатора свойства зависимости. Следовательно, обратные вызовы проверки невозможно изменить путем переопределения метаданных.

В некоторых случаях целесообразно изменить параметры метаданных свойств уровня среды WPF для существующих свойств зависимости. Эти параметры связывают некоторые известные условия свойств уровня среды WPF с другими процессами среды WPF, такими как система макетов. Настройка параметров, как правило, выполняется только при регистрации нового свойства зависимости; кроме того, можно изменить метаданные свойства уровня среды WPF в составе вызова OverrideMetadata или AddOwner. Дополнительные сведения и рекомендации по использованию конкретных значений см. в разделе Метаданные свойств среды. Дополнительные сведения о том, как следует настраивать эти свойства для только что зарегистрированного свойства зависимости, см. в разделе Пользовательские свойства зависимости.

Переопределение метаданных

Основной целью переопределения метаданных является то, что у вас есть возможность изменить различные производные от метаданных характеристики, которые применяются к свойству зависимости в том виде, в котором оно существует в вашем типе. Причины этого рассматриваются более подробно в разделе Метаданные. Дополнительные сведения, включая примеры кода, см. в разделе Переопределение метаданных для свойства зависимости.

Метаданные свойства можно предоставить для свойства зависимости во время вызова регистрации (Register). Но во многих случаях целесообразно предоставить метаданные определенного типа для вашего класса, когда он наследует соответствующее свойство зависимости. Это можно сделать, вызвав метод OverrideMetadata. Рассмотрим пример из API WPF: класс FrameworkElement является типом, который первым регистрирует свойство зависимости Focusable. Но класс Control переопределяет метаданные, чтобы свойство зависимости могло предоставить собственное исходное значение по умолчанию, изменив его с false на true; кроме того, класс повторно использует исходную реализацию Focusable и другими способами.

При переопределении метаданных различные характеристики метаданных объединяются или заменяют друг друга.

  • PropertyChangedCallback объединяется. При добавлении нового обратного вызова PropertyChangedCallback этот обратный вызов хранится в метаданных. Если не указать PropertyChangedCallback в переопределении, значение PropertyChangedCallback переходит в качестве ссылки из ближайшего родительского объекта, указывающего этот метод обратного вызова в метаданных.

  • Фактическое поведение системы свойств для PropertyChangedCallback заключается в следующем: реализации для всех владельцев метаданных в иерархии сохраняются и добавляются в таблицы в определенном порядке выполнения системой свойств. Обратные вызовы наиболее глубоких производных классов вызываются первыми.

  • DefaultValue заменяется. Если не указать DefaultValue в переопределении, значение DefaultValue берется из ближайшего родительского элемента, задававшего его в метаданных.

  • Реализации CoerceValueCallback заменяются. При добавлении нового обратного вызова CoerceValueCallback этот обратный вызов хранится в метаданных. Если не указать CoerceValueCallback в переопределении, значение CoerceValueCallback переходит в качестве ссылки из ближайшего родительского объекта, указывающего этот метод обратного вызова в метаданных.

  • Поведение системы свойств заключается в том, что вызывается только метод обратного вызова CoerceValueCallback в непосредственных метаданных. Никакие ссылки на другие реализации CoerceValueCallback в иерархии не сохраняются.

Это поведение реализуется Merge и может быть перезаписано в производных классах метаданных.

Переопределение метаданных присоединенного свойства

В WPF вложенные свойства реализованы как свойства зависимости. Это означает, что они также имеют метаданные свойств, которые могут быть переопределены отдельными классами. При определении области действия вложенного свойства в WPF не следует забывать, что любой объект DependencyObject может задать для них вложенное свойство. Следовательно, любой производный класс DependencyObject может переопределить метаданные для любого вложенного свойства, так как оно может быть задано в экземпляре класса. Можно переопределить значения по умолчанию, обратные вызовы или свойства отчетности о характеристиках уровня среды WPF. Если вложенное свойство задано в экземпляре класса, действуют характеристики переопределенных метаданных свойства. Например, можно переопределить значение по умолчанию, чтобы переопределенное значение передавалось как значение вложенного свойства в экземплярах класса всякий раз, когда свойство не задано никаким другим способом.

Примечание.

Свойство Inherits не относится к вложенным свойствам.

Добавление класса в качестве владельца существующего свойства зависимости

Класс может добавить себя в качестве владельца свойства зависимости, которое уже было зарегистрировано, с помощью метода AddOwner. Это позволяет классу использовать свойство зависимости, первоначально зарегистрированное для другого типа. Добавляемый класс, как правило, не является производным классом типа, который первым зарегистрировал это свойство зависимости в качестве владельца. Фактически это позволяет классу и его производным классам "наследовать" реализацию свойства зависимости, даже если исходный класс-владелец и добавляемый класс не находятся в одной действительной иерархии классов. Кроме того, добавляемый класс (и все его наследуемые классы) могут затем предоставить метаданные определенного типа для исходного свойства зависимости.

Помимо добавления себя в качестве владельца с использованием служебных методов системы свойств, добавляемый класс должен объявить в себе дополнительные открытые элементы, чтобы сделать свойство зависимости полноценным участником в системе свойств, которое доступно и коду, и разметке. Класс, который добавляет существующее свойство зависимости, имеет те же обязанности по предоставлению объектной модели для свойства зависимости, что и класс, определяющий новое пользовательское свойство зависимости. Первый такой предоставляемый элемент — поле идентификатора свойства зависимости. Это поле должно представлять собой поле public static readonly типа DependencyProperty, который назначается возвращаемому значению вызова AddOwner. Вторым определяемым элементом является свойство "программы-оболочки" среды CLR. Благодаря оболочке управлять свойством зависимости в коде становится гораздо удобнее (вызывать метод SetValue каждый раз не нужно, достаточно сделать соответствующий вызов однократно в самой программе-оболочке). Оболочка реализуется так же, как если бы регистрировалось пользовательское свойство зависимости. Дополнительные сведения о реализации свойства зависимости см. в разделе Пользовательские свойства взаимозависимости и Добавление типа владельца для свойства зависимостей.

AddOwner и вложенные свойства

Можно вызвать AddOwner для свойства зависимости, которое определено классом-владельцем как вложенное свойство. Обычно так делают, чтобы предоставить ранее вложенное свойство в качестве невложенного свойства зависимости. Затем вы предоставляете возвращаемое значение AddOwner в качестве поля public static readonly, чтобы его можно было использовать как идентификатор свойства зависимости, и определяете подходящие свойства программы-оболочки, чтобы свойство отображалось в таблице элементов и поддерживало использование невложенных свойств в вашем классе.

См. также