Приоритет значения свойства зависимостей (WPF .NET)

Работа системы свойств Windows Presentation Foundation (WPF) влияет на значение свойства зависимостей. В этой статье объясняется, как приоритет различных входных данных на основе свойств в системе свойств WPF определяет эффективное значение свойства зависимости.

Важно!

Документация по рабочему столу для .NET 7 и .NET 6 находится в стадии разработки.

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

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

Система свойств WPF

Система свойств WPF использует различные факторы для определения значения свойств зависимостей, таких как проверка свойств в режиме реального времени, поздняя привязка и уведомления об изменении свойств для связанных свойств. Хотя порядок и логика, используемая для определения значений свойств зависимостей, сложна, обучение может помочь избежать ненужных параметров свойств, а также выяснить, почему попытка задать свойство зависимостей не привело к ожидаемому значению.

Свойства зависимостей, заданные в нескольких местах

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

<StackPanel>
    <StackPanel.Resources>
        <ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
            <Border Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" 
                    BorderBrush="{TemplateBinding BorderBrush}">
                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
            </Border>
        </ControlTemplate>
    </StackPanel.Resources>

    <Button Template="{StaticResource ButtonTemplate}" Background="Red">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Setter Property="Background" Value="Blue"/>
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="Yellow" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
        Which color do you expect?
    </Button>
</StackPanel>

В примере Background свойство локально задано Red. Однако неявный стиль, объявленный в область кнопки, пытается задать Background для свойства значение Blue. При нажатии мыши на кнопку триггер в неявном стиле пытается задать Background для свойства значение Yellow. За исключением приведения и анимации, локально заданное значение свойства имеет наивысший приоритет, поэтому кнопка будет красной, даже при наведении мыши. Но при удалении локально заданного значения из кнопки он получит его Background значение из стиля. В стиле триггеры имеют приоритет, поэтому кнопка будет желтой на мыши и синей в противном случае. Пример заменяет значение по умолчанию кнопки, так как шаблон по умолчанию ControlTemplate имеет жестко закодированное значение мыши Background .

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

Следующий список является окончательным порядком приоритета, который система свойств использует при назначении значений среды выполнения свойствам зависимостей. Сначала указаны элементы с наивысшим приоритетом.

  1. Приведение системы свойств. Дополнительные сведения о приведение см. в разделе "Приведение и анимация".

  2. Активные анимации или анимации с поведением Hold. Чтобы иметь практический эффект, значение анимации должно иметь приоритет над базовым (неаниматизованным) значением, даже если базовое значение было задано локально. Дополнительные сведения см. в разделе "Приведение и анимация".

  3. Локальные значения. Можно задать локальное значение через свойство "оболочка", которое равнозначно настройке атрибута или элемента свойства в XAML, или вызовом SetValue API с помощью свойства определенного экземпляра. Локальное значение, заданное с помощью привязки или ресурса, будет иметь тот же приоритет, что и значение, которое устанавливается напрямую.

  4. Значения свойств шаблона TemplatedParent. Элемент имеет элемент, TemplatedParent созданный шаблоном (ControlTemplate или DataTemplate). Дополнительные сведения см. в разделе TemplatedParent. В шаблоне, указанном в соответствии с TemplatedParentпорядком приоритета, является:

    1. Триггеры.

    2. Наборы свойств, обычно через атрибуты XAML.

  5. Неявные стили. Применяется только к свойству Style. Значение Style — это любой ресурс стиля со TargetType значением, соответствующим типу элемента. Ресурс стиля должен существовать в пределах страницы или приложения. Поиск неявного ресурса стиля не распространяется на ресурсы стилей в темах.

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

  7. Триггеры шаблонов. Триггер шаблона — это триггер из непосредственно примененного шаблона или из шаблона в стиле. Стиль должен существовать в пределах страницы или приложения.

  8. Значения набора стилей. Значение набора стилей — это значение, применяемое Setter в стиле. Стиль должен существовать в пределах страницы или приложения.

  9. Стили по умолчанию, также известные как стили тем. Дополнительные сведения см. в статьях по умолчанию (Тема). В стиле по умолчанию порядок приоритета:

    1. Активные триггеры.

    2. Сеттеры.

  10. Наследование. Некоторые свойства зависимостей дочернего элемента наследуют их значение от родительского элемента. Поэтому может не потребоваться задать значения свойств для каждого элемента в приложении. Дополнительные сведения см. в разделе "Наследование значений свойств".

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

TemplatedParent

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

Свойство Style

Тот же порядок приоритета применяется ко всем свойствам зависимостей, кроме Style свойства. Свойство Style уникально в том, что оно не может быть стилем. Принудительное или анимирование Style свойства не рекомендуется (и Style анимация свойства потребует пользовательского класса анимации). В результате не все элементы приоритета применяются. Существует только три способа задания Style свойства:

  • Явный стиль. Свойство Style элемента задано напрямую. Значение Style свойства действует так, как будто это локальное значение и имеет то же приоритет, что и элемент 3 в списке приоритетов. В большинстве сценариев явные стили не определяются встроенными и вместо этого явно ссылаются как на ресурс, например Style="{StaticResource myResourceKey}".

  • Неявный стиль. Style Свойство элемента не задано напрямую. Вместо этого стиль применяется, если он существует на определенном уровне в пределах страницы или приложения, и имеет ключ ресурса, соответствующий типу элемента, к которому применяется стиль, например <Style TargetType="x:Type Button">. Тип должен совпадать точно, например <Style TargetType="x:Type Button"> , не будет применяться к MyButton типу, даже если MyButton он является производным от Button. Значение Style свойства имеет тот же приоритет, что и элемент 5 в списке приоритетов. Неявное значение стиля можно обнаружить путем вызова DependencyPropertyHelper.GetValueSource метода, передачи Style свойства и проверка в ImplicitStyleReference результатах.

  • Стиль по умолчанию, также известный как стиль темы. Style Свойство элемента не задано напрямую. Вместо этого она исходит от оценки темы среды выполнения подсистемой презентаций WPF. Перед средой выполнения Style значение свойства равно null. Значение Style свойства имеет тот же приоритет, что и элемент 9 в списке приоритетов.

Стили по умолчанию (тема)

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

Это ControlTemplate важный элемент в стиле по умолчанию для элемента управления. ControlTemplate — это значение набора для свойства стиля Template . Если стили по умолчанию не содержали шаблон, элемент управления без пользовательского шаблона в составе пользовательского стиля не будет визуального вида. Не только шаблон определяет внешний вид элемента управления, он также определяет соединения между свойствами в визуальном дереве шаблона и соответствующим классом элемента управления. Каждый элемент управления предоставляет набор свойств, которые могут повлиять на внешний вид элемента управления без замены шаблона. Например, рассмотрим визуальный вид Thumb элемента управления по умолчанию, который является компонентом ScrollBar .

Элемент Thumb управления имеет определенные настраиваемые свойства. Шаблон Thumb элемента управления по умолчанию создает базовую структуру или визуальное дерево с несколькими вложенными Border компонентами для создания выпущенного вида. В шаблоне свойства, которые предназначены для настройки Thumb классом, предоставляются с помощью TemplateBinding. Шаблон по умолчанию для Thumb элемента управления имеет различные свойства границы, которые совместно используют привязку шаблона с такими свойствами, как Background или BorderThickness. Но если значения свойств или визуальных договоренностей жестко закодируются в шаблоне или привязаны к значениям, поступающим непосредственно из темы, можно изменить только эти значения, заменив весь шаблон. Как правило, если свойство поступает из шаблонного родительского элемента и не предоставляется объектом TemplateBinding, то значение свойства нельзя изменить по стилям, так как для него нет удобного способа его назначения. Но это свойство по-прежнему может влиять на наследование значений свойств в примененном шаблоне или по умолчанию.

Стили по умолчанию указывают TargetType в их определениях. Оценка темы среды выполнения соответствует TargetType стилю по умолчанию свойству DefaultStyleKey элемента управления. В отличие от этого, поведение подстановки для неявных стилей использует фактический тип элемента управления. Значение DefaultStyleKey наследуется производными классами, поэтому производные элементы, которые в противном случае не имеют связанного стиля, получают внешний вид визуального элемента по умолчанию. Например, если вы наследуетеMyButton, ButtonMyButton наследует шаблон Buttonпо умолчанию. Производные классы могут переопределить значение DefaultStyleKey по умолчанию в метаданных свойства зависимостей. Таким образом, если требуется другое визуальное представление MyButton, можно переопределить метаданные свойства зависимостей для DefaultStyleKeyMyButtonвключено, а затем определить соответствующий стиль по умолчанию, включая шаблон, который будет упакован с помощью MyButton элемента управления. Дополнительные сведения см. в разделе "Обзор разработки элементов управления".

Динамический ресурс

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

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

Хотя динамические ссылки на ресурсы и привязки имеют приоритет расположения, в котором они заданы, значение отложено. Одним из последствий этого является то, что при установке динамического ресурса или привязки к локальному значению любое изменение локального значения полностью заменяет динамический ресурс или привязку. Даже при вызове ClearValue метода для очистки локально заданного значения динамический ресурс или привязка не будет восстановлена. На самом деле, если вы вызываете ClearValue свойство с динамическим ресурсом или привязкой (без локального значения литералов), динамический ресурс или привязка будут удалены.

SetCurrentValue

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

Приведение и анимация

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

Если анимация не указывает FromTo и значения свойств для определенных действий, или если анимация намеренно отменить изменения базовому значению при завершении, базовое значение может повлиять на анимированное значение. Чтобы увидеть это на практике, запустите пример приложения "Целевые значения ". В примере для высоты прямоугольника попробуйте задать начальные локальные значения, отличающиеся от любого From значения. Примеры анимаций начинаются сразу с помощью From значения вместо базового значения. Указав Stop в качестве FillBehaviorзначения анимации, при завершении анимация сбрасывает значение свойства в базовое значение. Обычный приоритет используется для определения базового значения после окончания анимации.

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

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

Поведение триггера

Элементы управления часто определяют поведение триггеров в рамках их стиля по умолчанию. Установка локальных свойств для элементов управления может потенциально конфликтовать с этими триггерами, предотвращая реагирование триггеров (визуально или поведенческим образом) на события, управляемые пользователем. Обычное использование триггера свойства заключается в управлении свойствами состояния, такими как IsSelected или IsEnabled. Например, по умолчанию при отключении Button триггер стиля темы (IsEnabled is false) задает Foreground значение, которое будет Button отображаться серым. Если вы задали локальное Foreground значение, то более высокий приоритет локального свойства переопределит значение стиля Foreground темы, даже если значение Button отключено. При задании значений свойств, которые переопределяют поведение триггеров уровня темы для элемента управления, не следует не мешать предполагаемому пользовательскому интерфейсу для этого элемента управления.

ClearValue

Метод ClearValue очищает любое локально примененное значение свойства зависимостей для элемента. Но вызов ClearValue не гарантирует, что значение по умолчанию, установленное в метаданных во время регистрации свойств, является новым эффективным значением. Все остальные участники списка приоритетов по-прежнему активны, и удаляется только локально заданное значение. Например, при вызове ClearValue свойства, имеющего стиль темы, значение стиля темы будет применяться в качестве нового значения, а не по умолчанию на основе метаданных. Если вы хотите задать значение свойства для значения зарегистрированных метаданных по умолчанию, получите значение метаданных по умолчанию, запросив метаданные свойства зависимостей, а затем локально задайте значение свойства вызовом SetValue.

См. также