Поделиться через


Общие сведения о свойствах зависимостей

В этом разделе описывается система свойств зависимостей, доступная при написании приложения среды выполнения Windows с определениями XAML для пользовательского интерфейса.

Что такое свойство зависимости?

Свойство зависимости — это специализированный тип свойства. В частности, это свойство, в котором значение свойства отслеживается и зависит от выделенной системы свойств, которая является частью среды выполнения Windows.

Для поддержки свойства зависимостей объект, определяющий свойство, должен быть DependencyObject (другими словами, класс, имеющий базовый класс DependencyObject где-то в его наследовании). Многие типы, используемые для определений пользовательского интерфейса для приложения UWP с XAML, будут подклассом DependencyObject и будут поддерживать свойства зависимостей. Однако любой тип, поступающий из пространства имен среды выполнения Windows, у которых нет xaml в его имени, не будет поддерживать свойства зависимостей; свойства таких типов — это обычные свойства, которые не будут иметь поведения зависимостей системы свойств.

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

  • Внешние входные данные, такие как предпочтения пользователя
  • Механизмы определения свойств точно-в-срок, такие как привязка данных, анимации и сториборды
  • Шаблоны для многократного использования, такие как ресурсы и стили
  • Значения, известные через взаимосвязи "родитель-дочерний" с другими элементами в дереве объектов

Свойство зависимостей представляет или поддерживает определенную функцию модели программирования для определения приложения среды выполнения Windows с помощью XAML для пользовательского интерфейса. К этим функциям относятся:

  • Привязка данных
  • Стили
  • Раскадровки анимаций
  • Поведение PropertyChanged; свойство зависимости можно реализовать для предоставления обратных вызовов, которые могут распространять изменения на другие свойства зависимости.
  • Использование значения по умолчанию, полученного из метаданных свойства
  • Общая утилита системы свойств, такая как ClearValue и поиск метаданных

Свойства зависимостей и свойства среды выполнения Windows

Свойства зависимостей расширяют базовые функциональные возможности свойств среды выполнения Windows, предоставляя глобальное внутреннее хранилище свойств, которое поддерживает все свойства зависимостей в приложении во время выполнения. Это альтернатива стандартному шаблону резервного копирования свойства с частным полем, частным в классе определения свойств. Это внутреннее хранилище свойств можно рассматривать как набор идентификаторов свойств и значений, которые существуют для любого конкретного объекта (если это DependencyObject). Вместо определения по имени каждое свойство в хранилище определяется экземпляром DependencyProperty . Однако система свойств в основном скрывает эти сведения о реализации: обычно можно получить доступ к свойствам зависимостей с помощью простого имени (имя программного свойства на используемом языке кода или имя атрибута при написании XAML).

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

Ниже приведена сумма терминологии, используемой в документации при обсуждении свойств зависимостей:

Срок Description
Свойство зависимости Свойство, которое существует в идентификаторе DependencyProperty (см. ниже). Обычно этот идентификатор доступен как статический элемент производного класса DependencyObject .
Идентификатор свойства зависимостей Константное значение для идентификации свойства обычно является общедоступным и доступным только для чтения.
Оболочка свойств Функциональные реализации get и set для свойства в среде выполнения Windows. Или языковая проекция исходного определения. Реализация оболочки свойства доступа get вызывает GetValue, передав соответствующий идентификатор зависимого свойства.

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

В следующем примере определяется настраиваемое свойство зависимостей, определенное для C#, и показано отношение идентификатора свойства зависимостей к оболочке свойства.

public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
  "Label",
  typeof(string),
  typeof(ImageWithLabelControl),
  new PropertyMetadata(null)
);


public string Label
{
    get { return (string)GetValue(LabelProperty); }
    set { SetValue(LabelProperty, value); }
}

Замечание

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

Приоритет значения свойства зависимости

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

Например, стили и шаблоны предназначены для общего начального пункта для установления значений свойств и, таким образом, внешнего вида элемента управления. Но в определенном экземпляре элемента управления может потребоваться изменить его значение в отличие от общего шаблонного значения, например, задать этому элементу управления другой цвет фона или использовать другую текстовую строку в качестве содержимого. Система свойств среды выполнения Windows считает локальные значения более высоким приоритетом, чем значения, предоставляемые стилями и шаблонами. Это позволяет использовать специфичные для приложения значения, чтобы перезаписать шаблоны, и сделать элементы управления более полезными в пользовательском интерфейсе вашего приложения.

Список приоритета свойств зависимостей

Ниже приведен окончательный порядок использования системой свойств при назначении значения времени выполнения для свойства зависимостей. Наивысший приоритет указывается первым. Вы найдете более подробные объяснения только после этого списка.

  1. Анимированные значения: Активные анимации, анимации визуального состояния или анимации с поведением HoldEnd. Чтобы иметь практический эффект, анимация, применяемая к свойству, должна иметь приоритет над базовым (неанимируемым) значением, даже если это значение было задано локально.
  2. Локальное значение: Локальное значение может быть задано с помощью удобства оболочки свойств, которая также соответствует настройке в качестве атрибута или элемента свойства в XAML, или вызовом метода SetValue с помощью свойства определенного экземпляра. Если задать локальное значение с помощью привязки или статического ресурса, каждое действие выполняется в приоритете, как если задано локальное значение, а привязки или ссылки на ресурсы удаляются, если задано новое локальное значение.
  3. Шаблонные свойства: Элемент имеет их, если он был создан в рамках шаблона (из ControlTemplate или DataTemplate).
  4. Установщики стилей: Значения из сеттера в стилях из ресурсов страницы или приложения.
  5. Значение по умолчанию: Свойство зависимостей может иметь значение по умолчанию в рамках его метаданных.

Свойства шаблонов

Шаблонные свойства в качестве элемента приоритета не применяются к любому свойству элемента, объявляемого непосредственно в разметке страницы XAML. Концепция шаблонного свойства существует только для объектов, созданных при применении шаблона XAML к элементу пользовательского интерфейса, и таким образом определяет их визуальные элементы.

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

Замечание

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

Привязки и приоритет

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

Не только привязки работают с тем же приоритетом, что и локальное значение, они действительно являются локальным значением, где привязка является заполнителем для отложенного значения. Если для значения свойства установлена привязка, но во время выполнения вы задаете для него локальное значение, это полностью заменяет привязку. Аналогичным образом, если вы вызываете SetBinding чтобы задать привязку, которая существует только во время выполнения, вы заменяете любое локальное значение, которое вы могли применить в XAML или ранее выполненный код.

Раскадровка анимации и исходное значение

Анимация, выполненная по раскадровке, работает на принципе базового значения. Базовое значение — это значение, определяемое системой свойств в соответствии с его приоритетами, но без учета последнего шага поиска анимаций. Например, базовое значение может поступать из шаблона элемента управления или может быть задано локальное значение для экземпляра элемента управления. В любом случае применение анимации перезаписывает это базовое значение и применяет анимированное значение до тех пор, пока анимация продолжает выполняться.

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

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

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

Для получения дополнительной информации см. анимации с раскадровками.

Значения по умолчанию

Определение значения по умолчанию для свойства зависимостей со значением PropertyMetadata подробно описано в разделе свойств настраиваемых зависимостей .

Свойства зависимостей по-прежнему имеют значения по умолчанию, даже если эти значения по умолчанию не были явно определены в метаданных этого свойства. Если они не были изменены метаданными, значения по умолчанию для свойств зависимостей среды выполнения Windows обычно являются одним из следующих:

  • Свойство, использующее объект времени выполнения или базовый тип объекта ( ссылочный тип), имеет значение null по умолчанию. Например, DataContext имеет значение NULL , пока не будет намеренно задано или не наследуется.
  • Свойство, использующее базовое значение, например числа или логическое значение ( тип значения), использует ожидаемое значение по умолчанию для этого значения. Например, 0 для целых чисел и чисел с плавающей запятой, false для булевого значения.
  • Свойство, использующее структуру среды выполнения Windows, имеет значение по умолчанию, полученное путем вызова неявного конструктора по умолчанию этой структуры. Этот конструктор использует значения по умолчанию для каждого из основных полей значения структуры. Например, значение по умолчанию для Point инициализируется с X и Y равными 0.
  • Свойство, использующее перечисление, имеет значение по умолчанию первого определенного элемента в этом перечислении. Проверьте ссылку на определенные перечисления, чтобы узнать, что такое значение по умолчанию.
  • Свойство, использующее строку (System.String для .NET, Platform::String для C++/CX), имеет значение по умолчанию пустой строки ("").
  • Свойства коллекции обычно не реализуются как свойства зависимостей, по причинам, рассмотренным далее в этом разделе. Но если вы реализуете свойство пользовательской коллекции и хотите, чтобы оно было свойством зависимости, обязательно избегайте непреднамеренного одиночного объекта, как описано в конце настраиваемых свойств зависимости.

Функциональные возможности свойств, предоставляемые свойством зависимостей

Привязка данных

Свойство зависимости может иметь значение, заданное путем применения привязки данных. Привязка данных использует синтаксис расширения разметки {Binding} в XAML, расширение разметки {x:Bind} или класс привязки в коде. Для свойства, связанного с данными, определение конечного значения откладывается до времени выполнения программы. В то время значение получается из источника данных. Роль, которую играет здесь система свойств зависимостей, заключается в обеспечении поведения заполнителя для таких операций, как загрузка XAML, когда значение еще не известно; затем, во время выполнения, это значение предоставляется за счет взаимодействия с движком привязки данных среды выполнения Windows.

В следующем примере задается значение Text для элемента TextBlock с помощью привязки в XAML. Привязка использует наследуемый контекст данных и источник данных объекта. (Ни один из них не показан в сокращенном примере; более полный пример, показывающий контекст и источник, см. в подробной статье о привязке данных.)

<Canvas>
  <TextBlock Text="{Binding Team.TeamName}"/>
</Canvas>

Можно также установить привязки с помощью кода, а не XAML. См. раздел SetBinding.

Замечание

Привязки подобного типа рассматриваются как локальное значение для целей приоритета значения свойства зависимости. Если задать другое локальное значение для свойства, которое изначально содержало значение привязки , вы полностью перезаписываете привязку, а не только значение времени выполнения привязки. {x:Bind} Привязки реализуются с помощью созданного кода, который задает локальное значение для свойства. Если задать локальное значение для свойства, использующее {x:Bind}, это значение будет заменено при следующем вычислении привязки, например при наблюдении за изменением свойства в исходном объекте.

Источники привязки, целевые объекты привязки, роль FrameworkElement

Чтобы быть источником привязки, свойство не должно быть свойством зависимостей; Обычно любое свойство можно использовать в качестве источника привязки, хотя это зависит от языка программирования, и каждый из них имеет определенные пограничные варианты. Тем не менее, чтобы быть целевым объектом расширения разметки {Binding} или Binding, это свойство должно быть свойством зависимостей. {x:Bind} не имеет этого требования, так как он использует созданный код для применения значений привязки.

Если вы создаете привязку в коде, обратите внимание, что API SetBinding определен только для FrameworkElement. Однако вместо этого можно создать определение привязки с помощью BindingOperations и таким образом ссылаться на любое свойство DependencyObject .

Для кода или XAML помните, что DataContext является свойством FrameworkElement . Используя форму наследования свойств между родительским и дочерним элементами (как правило, установленную в разметке XAML), система привязки может разрешить DataContext, который существует в родительском элементе. Это наследование может оценить, даже если дочерний объект (который имеет целевое свойство) не является FrameworkElement и поэтому не содержит собственного значения DataContext . Однако родительский элемент, наследуемый, должен быть FrameworkElement для задания и хранения DataContext. Кроме того, необходимо определить привязку таким образом, что она может функционировать с значением NULL для DataContext.

Подключение привязки — это не единственное, что необходимо для большинства сценариев привязки данных. Чтобы односторонняя или двусторонняя привязка была эффективной, исходное свойство должно поддерживать уведомления об изменениях, которые распространяются в систему привязки и таким образом целевой объект. Для пользовательских источников привязки это означает, что свойство должно быть свойством зависимостей, или объект должен поддерживать INotifyPropertyChanged. Коллекции должны поддерживать INotifyCollectionChanged. Некоторые классы поддерживают эти интерфейсы в их реализации, чтобы они были полезны в качестве базовых классов для сценариев привязки данных; Пример такого класса — ObservableCollection<T>. Дополнительные сведения о привязке данных и о том, как привязка данных связана с системой свойств, см. в подробной статье о привязке данных.

Замечание

Перечисленные здесь типы поддерживают источники данных Microsoft .NET. Источники данных C++/CX используют различные интерфейсы для уведомления об изменении или наблюдаемого поведения, см. подробные сведения о привязке данных.

Стили и шаблоны

Стили и шаблоны — это два сценария для свойств, определенных как свойства зависимостей. Стили полезны для задания свойств, определяющих пользовательский интерфейс приложения. Стили определяются как ресурсы в XAML либо как запись в коллекции ресурсов , либо в отдельных файлах XAML, таких как словари ресурсов темы. Стили взаимодействуют с системой свойств, так как они содержат методы задания свойств. Самым важным свойством здесь является свойство Control.Template элемента Управления: оно определяет большую часть визуального вида и визуального состояния элемента управления. Дополнительные сведения о стилях и некоторый пример XAML, определяющий стиль и использующий установщики, см. в разделе стилизация элементов управления.

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

Раскадровки анимаций

Вы можете анимировать значение свойства зависимости с помощью анимации. Анимированные раскадровки в Windows Runtime — это не просто визуальные украшения. Более полезно рассматривать анимацию как метод компьютера состояния, который может задавать значения отдельных свойств или всех свойств и визуальных элементов элемента управления, а также изменять эти значения с течением времени.

Для анимации целевое свойство анимации должно быть свойством зависимостей. Кроме того, чтобы быть анимированным, тип значения целевого свойства должен поддерживаться одним из существующих типов анимации, производных от временной шкалы. Значения цвета, Double и Point можно анимировать путем интерполяции или с использованием техники ключевых кадров. Большинство других значений можно анимировать с помощью дискретных ключевых кадров объектов .

Если анимация применяется и выполняется, анимированное значение имеет более высокий приоритет, чем любое другое значение (например, локальное значение), которым в противном случае обладает свойство. Анимации также имеют необязательное поведение HoldEnd , которое может привести к применению анимации к значениям свойств, даже если анимация визуально останавливается.

Принцип конечного автомата реализуется использованием анимированных сценариев в рамках модели состояний VisualStateManager для элементов управления. Дополнительные сведения об анимациях с раскадровкой см. в разделе Анимации с раскадровкой. Дополнительные сведения о VisualStateManager и определении визуальных состояний для элементов управления см. в разделах "Анимации раскадровками для визуальных состояний" или "Шаблоны элементов управления".

Поведение при изменении свойства

Поведение, связанное с изменением свойства, является основой термина "свойство зависимости". Сохранение допустимых значений для свойства, когда другое свойство может повлиять на значение первого свойства, является сложной проблемой разработки во многих платформах. В системе свойств среды выполнения Windows каждое свойство зависимостей может указывать обратный вызов, который вызывается всякий раз, когда его значение свойства изменяется. Этот обратный вызов можно использовать для уведомления или изменения связанных значений свойств, как правило, синхронно. У многих существующих свойств зависимостей есть поведение, связанное с изменением свойств. Вы также можете добавить аналогичное поведение обратного вызова в настраиваемые свойства зависимостей и реализовать собственные обратные вызовы для изменения свойства. Пример см. в свойствах настраиваемых зависимостей .

В Windows 10 представлен метод RegisterPropertyChangedCallback . Это позволяет коду приложения регистрировать уведомления об изменениях при изменении указанного свойства зависимостей в экземпляре DependencyObject.

Значение по умолчанию и ClearValue

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

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

DependencyObject и потоки

Все экземпляры DependencyObject должны быть созданы в потоке пользовательского интерфейса, связанном с текущим окном , которое отображается приложением среды выполнения Windows. Хотя каждый объект DependencyObject должен быть создан в основном потоке пользовательского интерфейса, доступ к этим объектам из других потоков возможен, если использовать ссылку диспетчера и обратиться к свойству DispatcherQueue. Затем можно вызвать такие методы, как TryEnqueue и выполнить код в правилах ограничений потоков в потоке пользовательского интерфейса.

Замечание

Для приложений UWP перейдите к свойству Диспетчера . Затем можно вызвать такие методы, как RunAsync в объекте CoreDispatcher , и выполнить код в правилах ограничений потоков в потоке пользовательского интерфейса. Дополнительные сведения о различиях между UWP и WinUI для пакета SDK для приложений Windows см. в разделе "Миграция функций потоков".

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

Концептуальный материал