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

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

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

В этом разделе предполагается, что у вас есть базовые знания о системе типа .NET и объектно-ориентированном программировании. Чтобы выполнить примеры в этом разделе, следует также иметь представление о XAML и написании простых приложений WPF. Дополнительные сведения см. в разделе Пошаговое руководство. Создание первого классического приложения WPF.

Свойства зависимостей и свойства CLR

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

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

В справочнике по SDK можно определить, что это свойство является свойством зависимостей по наличию раздела сведений о свойстве зависимостей на странице управляемой ссылки для этого свойства. В разделе сведений о свойстве зависимостей есть ссылка на идентификатор поля для этого свойства зависимостей DependencyProperty, список параметров метаданных, заданных для этого свойства, информацию по переопределению для каждого класса и другие сведения.

Свойства зависимостей поддерживают свойства CLR

Свойства зависимостей и система свойств WPF расширяют функциональные возможности свойства, предоставляя тип, который поддерживает свойство, в качестве альтернативной реализации для стандартного способа обеспечения свойства с помощью закрытого поля. Имя этого типа — DependencyProperty. Другим важным типом, который определяет систему свойств WPF, является DependencyObject. Тип DependencyObject определяет базовый класс для регистрации свойства зависимостей и использования в качестве его владельца.

Далее перечислены термины, используемые для свойств зависимостей.

  • Свойство зависимостей — свойство, поддерживаемое DependencyProperty.

  • Идентификатор свойства зависимостей. Экземпляр DependencyProperty, который получается в результате регистрации свойства зависимостей и затем сохраняется как статический член класса. Этот идентификатор используется в качестве параметра для многих API, взаимодействующих с системой свойств WPF.

  • CLR-оболочка. Фактические реализации получения и задания свойства. Эти реализации включают идентификатор свойства зависимостей, используя его в вызовах GetValue и SetValue и, таким образом, обеспечивая поддержку для свойства с помощью системы свойств WPF.

В следующем примере определяется свойство зависимостей IsSpinning и демонстрируется связь между идентификатором DependencyProperty и свойством, которое он поддерживает.

public static readonly DependencyProperty IsSpinningProperty =
    DependencyProperty.Register(
    "IsSpinning", typeof(Boolean),
    typeof(MyCode)
    );
public bool IsSpinning
{
    get { return (bool)GetValue(IsSpinningProperty); }
    set { SetValue(IsSpinningProperty, value); }
}
Public Shared ReadOnly IsSpinningProperty As DependencyProperty =
    DependencyProperty.Register("IsSpinning",
                                GetType(Boolean),
                                GetType(MyCode))

Public Property IsSpinning() As Boolean
    Get
        Return CBool(GetValue(IsSpinningProperty))
    End Get
    Set(ByVal value As Boolean)
        SetValue(IsSpinningProperty, value)
    End Set
End Property

Соглашение об именовании свойства и его поддерживающего поля DependencyProperty имеет важное значение. Имя поля всегда определяется как имя свойства с добавленным суффиксом Property. Дополнительные сведения об этом соглашении и разъяснения по нему см. в разделе Пользовательские свойства зависимостей.

Задание значений свойств

Свойства можно задать с помощью кода или XAML.

Задание значений свойств с помощью XAML

В следующем примере на XAML задается красный цвет фона кнопки. В этом примере показан случай, когда простое строковое значение атрибута на языке XAML преобразуется синтаксическим анализатором WPF XAML в тип WPF (Color посредством SolidColorBrush) в созданном коде.

<Button Background="Red" Content="Button!"/>

Язык XAML поддерживает различные синтаксические формы для задания свойств. Выбор синтаксиса для конкретного свойства зависит от типа значения, которое использует свойство, а также других факторов, например наличия преобразователя типов. Дополнительные сведения об использовании синтаксиса XAML для задания свойств см. в разделе XAML в WPF и Подробное описание синтаксиса XAML.

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

<Button Content="Button!">
  <Button.Background>
    <ImageBrush ImageSource="wavy.jpg"/>
  </Button.Background>
</Button>

Задание свойств с помощью кода

Задание значений свойств зависимостей с помощью кода обычно представляет собой обращение к реализации метода установки, предоставленного "оболочкой" CLR.

Button myButton = new Button();
myButton.Width = 200.0;
Dim myButton As New Button()
myButton.Width = 200.0

Получение значения свойства также является фактически вызовом реализации метода получения, имеющегося в "оболочке":

double whatWidth;
whatWidth = myButton.Width;
Dim whatWidth As Double
whatWidth = myButton.Width

API GetValue и SetValue системы свойств также можно вызывать напрямую. Обычно в этом нет необходимости при использовании существующих свойств (оболочки более удобны и дают лучшее предоставление свойства для средств разработчика), но в некоторых сценариях удобнее использовать непосредственный вызов API.

Свойства можно задать на языке XAML, а затем использовать их в коде, с помощью кода программной части. Дополнительные сведения см. в разделе Код программной части и XAML в WPF.

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

Свойство зависимостей предоставляет дополнительные функциональные возможности в сравнении с теми, которые предоставляются свойством, поддерживаемым полем. Часто такая функция представляет или поддерживает одну из следующих возможностей:

Ресурсы

Значение свойства зависимостей можно задать ссылкой на ресурс. Ресурсы обычно указываются как значение свойства Resources корневого элемента страницы или приложения. (Эти расположения делают возможным наиболее удобный доступ к ресурсу.) В следующем примере показано, как определить ресурс SolidColorBrush.

<DockPanel.Resources>
  <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
</DockPanel.Resources>

После определения ресурса можно ссылаться на него и использовать для предоставления значения свойства:

<Button Background="{DynamicResource MyBrush}" Content="I am gold" />

Этот конкретный ресурс называется Расширение разметки DynamicResource (в WPF XAML можно использовать статическую или динамическую ссылку на ресурс). Чтобы использовать динамическую ссылку на ресурс, необходимо задать значение для свойства зависимостей. Это явное использование динамической ссылки на ресурс, разрешенное системой свойств WPF. Дополнительные сведения см. в разделе Ресурсы XAML.

Примечание.

Ресурсы считаются локальными значениями. Это означает, что если задается другое локальное значение, ссылка на ресурс удаляется. Дополнительные сведения см. в разделе Приоритет значений свойств зависимостей.

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

Свойства зависимостей могут ссылаться на значения через привязки данных. Привязка данных использует специальный синтаксис расширения разметки на языке XAML или объект Binding в коде. Благодаря функции привязки данных определение окончательного значения свойства откладывается до времени выполнения. Затем значение извлекается из источника данных.

В следующем примере свойство Content задается для Button с помощью привязки, объявленной в XAML. Привязка использует наследуемый контекст данных и источник данных XmlDataProvider (не показан). Привязка сама определяет нужный источник с помощью свойства XPath в источнике данных.

<Button Content="{Binding XPath=Team/@TeamName}"/>

Примечание.

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

Для свойств зависимостей (или класса DependencyObject) не предусмотрена собственная поддержка INotifyPropertyChanged для формирования уведомлений об изменениях в исходном значении свойства DependencyObject для операций привязки данных. Дополнительные сведения о создании свойств для использования в привязках данных, которые могут передавать сообщения об изменениях в целевой объект привязки данных, см. в разделе Общие сведения о привязках данных.

Стили

Существует два основных направления применения свойств зависимостей — стили и шаблоны. Стили особенно полезны для настройки свойств, определяющих пользовательский интерфейс приложения. Обычно стили определяются в XAML в качестве ресурсов. Стили взаимодействуют с системой свойств, так как они обычно содержат код для задания значения конкретного свойства, а также "правила срабатывания", которые определяют изменение значения свойства в зависимости от значения другого свойства в режиме реального времени.

В следующем примере создается простой стиль, который будет определяться внутри словаря Resources (не показан). Затем этот стиль применяется непосредственно к свойству Style для Button. Метод задания в стиле задает для свойства Background элемента управления Button значение "зеленый".

<Style x:Key="GreenButtonStyle">
  <Setter Property="Control.Background" Value="Green"/>
</Style>
<Button Style="{StaticResource GreenButtonStyle}">I am green!</Button>

Более подробную информацию см. в разделе Стилизация и использование шаблонов.

Анимации

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

В следующем примере Background анимируется для свойства Button (с технической точки зрения Background анимируется с помощью синтаксиса элемента свойства для указания пустого SolidColorBrush в качестве Background, тогда свойство Color этого элемента SolidColorBrush — это свойство, которое непосредственно анимируется).

<Button>I am animated
  <Button.Background>
    <SolidColorBrush x:Name="AnimBrush"/>
  </Button.Background>
  <Button.Triggers>
    <EventTrigger RoutedEvent="Button.Loaded">
      <BeginStoryboard>
        <Storyboard>
          <ColorAnimation
            Storyboard.TargetName="AnimBrush" 
            Storyboard.TargetProperty="(SolidColorBrush.Color)"
            From="Red" To="Green" Duration="0:0:5" 
            AutoReverse="True" RepeatBehavior="Forever" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Button.Triggers>
</Button>

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

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

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

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

public class SpinnerControl : ItemsControl
{
    static SpinnerControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(SpinnerControl),
            new FrameworkPropertyMetadata(typeof(SpinnerControl))
        );
    }
}
Public Class SpinnerControl
    Inherits ItemsControl
    Shared Sub New()
        DefaultStyleKeyProperty.OverrideMetadata(GetType(SpinnerControl), New FrameworkPropertyMetadata(GetType(SpinnerControl)))
    End Sub
End Class

Дополнительные сведения о переопределении или получении метаданных свойства см. в разделе Метаданные свойств зависимостей.

Наследование значения свойства

Элемент может наследовать значение свойства зависимостей от своего родительского элемента в дереве объектов.

Примечание.

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

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

<StackPanel Canvas.Top="50" DataContext="{Binding Source={StaticResource XmlTeamsSource}}">
  <Button Content="{Binding XPath=Team/@TeamName}"/>
</StackPanel>

Дополнительные сведения см. в разделе Наследование значения свойства.

Интеграция конструктора WPF

Пользовательский элемент управления со свойствами, которые реализуются как свойства зависимостей, получает соответствующую поддержку Конструктора WPF для Visual Studio. Один из примеров этого — возможность редактирования прямых и присоединенных свойств зависимостей с помощью окна Свойства. Дополнительные сведения см. в разделе Общие сведения о разработке элемента управления.

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

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

Рассмотрим следующий пример. В примере представлен стиль, применяемый ко всем кнопкам и их свойствам Background, однако также указывается одна кнопка с локально заданным значением Background.

Примечание.

В документации по SDK при описании свойств зависимостей используется термин "локальное значение" или иногда "локально заданное значение". Локально заданное значение — это значение свойства, которое задается непосредственно для экземпляра объекта в коде или как атрибут элемента на языке XAML.

Фактически для первой кнопки свойство задано дважды, но применяется только одно значение, то, у которого наивысший приоритет. Локально заданное значение имеет наивысший приоритет (исключение — выполняющаяся анимация, но анимаций нет в этом примере), поэтому вместо значения цвета фона первой кнопки, определенного стилем, используется локально заданное значение. Для второй кнопки не задано локальное значение (или другое значение с более высоким приоритетом, чем у стиля), поэтому цвет фона для этой кнопки определяется стилем.

<StackPanel>
  <StackPanel.Resources>
    <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
     <Setter Property="Background" Value="Red"/>
    </Style>
  </StackPanel.Resources>
  <Button Background="Green">I am NOT red!</Button>
  <Button>I am styled red</Button>
</StackPanel>

Для чего нужны приоритеты свойств зависимостей?

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

Примечание.

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

Дополнительные сведения о свойствах зависимостей

  • Присоединенное свойство — это тип свойства, поддерживающий специализированный синтаксис на языке XAML. Присоединенные свойства часто не имеют однозначного соответствия со свойствами среды CLR и не обязательно являются свойствами зависимостей. Обычным назначением присоединенного свойства является разрешение дочерним элементам передавать значения свойств родительским элементам, даже если это свойство не включено в список элементов класса обоих элементов — родительского и дочернего. Как правило, требуется разрешить дочерним элементам уведомлять родительский элемент о том, как они должны быть представлены в пользовательском интерфейсе (пример см. в разделе Dock или Left). Дополнительные сведения см. в разделе Общие сведения о вложенных свойствах зависимостей.

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

  • Рассматривайте свойства зависимостей как открытые свойства, доступные или по крайней мере видимые любому вызывающему объекту, имеющему доступ к экземпляру. Дополнительные сведения см. в разделе Безопасность свойств зависимостей.

См. также