Общие сведения о ресурсах XAML

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

Примечание.

Ресурсы XAML, описанные в этой статье, отличаются от ресурсов приложения, которые обычно добавляются в приложение в форме файлов, например содержимое, данные или внедренные файлы.

Использование ресурсов в XAML

В приведенном ниже примере определяется SolidColorBrush в качестве ресурса для корневого элемента страницы. Затем этот пример ссылается на ресурс и использует его для того, чтобы задать свойства нескольких дочерних элементов, в том числе Ellipse, TextBlockи Button.

<Page Name="root"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
  <Page.Resources>
    <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
    <Style TargetType="Border" x:Key="PageBackground">
      <Setter Property="Background" Value="Blue"/>
    </Style>
    <Style TargetType="TextBlock" x:Key="TitleText">
      <Setter Property="Background" Value="Blue"/>
      <Setter Property="DockPanel.Dock" Value="Top"/>
      <Setter Property="FontSize" Value="18"/>
      <Setter Property="Foreground" Value="#4E87D4"/>
      <Setter Property="FontFamily" Value="Trebuchet MS"/>
      <Setter Property="Margin" Value="0,40,10,10"/>
    </Style>
    <Style TargetType="TextBlock" x:Key="Label">
      <Setter Property="DockPanel.Dock" Value="Right"/>
      <Setter Property="FontSize" Value="8"/>
      <Setter Property="Foreground" Value="{StaticResource MyBrush}"/>
      <Setter Property="FontFamily" Value="Arial"/>
      <Setter Property="FontWeight" Value="Bold"/>
      <Setter Property="Margin" Value="0,3,10,0"/>
    </Style>
  </Page.Resources>
  <StackPanel>
    <Border Style="{StaticResource PageBackground}">
      <DockPanel>
        <TextBlock Style="{StaticResource TitleText}">Title</TextBlock>
        <TextBlock Style="{StaticResource Label}">Label</TextBlock>
        <TextBlock DockPanel.Dock="Top" HorizontalAlignment="Left" FontSize="36" Foreground="{StaticResource MyBrush}" Text="Text" Margin="20" />
        <Button DockPanel.Dock="Top" HorizontalAlignment="Left" Height="30" Background="{StaticResource MyBrush}" Margin="40">Button</Button>
        <Ellipse DockPanel.Dock="Top" HorizontalAlignment="Left" Width="100" Height="100" Fill="{StaticResource MyBrush}" Margin="40" />
      </DockPanel>
    </Border>
  </StackPanel>
</Page>


Каждый элемент уровня платформы (FrameworkElement или FrameworkContentElement) имеет свойство Resources, которое представляет собой тип ResourceDictionary, содержащий определенные ресурсы. Вы можете определить ресурсы в любом элементе, например Button. Однако наиболее часто ресурсы определяются в корневом элементе, которым в примере является Page.

Каждый ресурс в словаре ресурсов должен иметь уникальный ключ. При определении ресурсов в разметке уникальный ключ назначается с помощью директивы x:Key. Как правило, ключ является строкой. Однако можно задать его как другой тип объекта с помощью соответствующих расширений разметки. Нестроковые ключи для ресурсов используются в определенных функциональных областях WPF, в частности в стилях, ресурсах компонентов и стилях данных.

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

<Button Background="{StaticResource MyBrush}"/>
<Ellipse Fill="{StaticResource MyBrush}"/>

В предыдущем примере, когда загрузчик XAML обрабатывает значение {StaticResource MyBrush} для свойства Background в Button, логика подстановки ресурсов сначала проверяет словарь ресурсов для элемента Button. Если Button не имеет определения ключа ресурса MyBrush (в этом примере не имеет; коллекция ресурсов пуста), логика подстановки проверяет родительский элемент Button, которым является Page. При определении ресурса в корневом элементе Page все элементы в логическом дереве Page могут получить к нему доступ. Вы можете повторно использовать тот же ресурс для задания значения любого свойства, которое принимает тот же представляемый ресурсом тип. В предыдущем примере один и тот же ресурс MyBrush задает два разных свойства: свойство Background типа Button и свойство Fill типа Rectangle.

Статические и динамические ресурсы

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

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

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

  • При определении общей структуры создания ресурсов для приложения (постранично, в приложении, в свободном XAML или в сборке, содержащей только ресурсы) учитывайте следующее:

  • Функциональные возможности приложения. Выполняется ли обновление ресурсов в режиме реального времени в рамках требований вашего приложения?

  • Соответствующее поведение подстановки этого ссылочного типа ресурса.

  • Конкретное свойство или тип ресурса и собственное поведение этих типов.

Статические ресурсы

Ссылки на статические ресурсы лучше всего использовать в указанных ниже случаях.

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

  • Когда вы устанавливаете значение свойства, не являющегося DependencyObject или Freezable.

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

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

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

  • Необходимо изменить базовый ресурс для всех объектов-получателей либо поддерживать отдельные записываемые экземпляры для каждого объекта-получателя с помощью атрибута x:Shared.

Поведение подстановки статического ресурса

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

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

  2. Затем процесс подстановки обходит логическое дерево снизу вверх до родительского элемента и его словаря ресурсов. Этот процесс продолжается до тех пор, пока не будет достигнут корневой элемент.

  3. Проверяются ресурсы приложения. Ресурсы приложения — это ресурсы в словаре ресурсов, которые определены с помощью объекта Application для приложения WPF.

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

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

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

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

Динамические ресурсы лучше подходят в нижеуказанных случаях.

  • Когда значение ресурса, в том числе системных ресурсов или ресурсов, которые в противном случае устанавливаются пользователем, зависит от условий, которые не известны до времени выполнения. Например, вы можете создать значения метода задания, которые ссылаются на системные свойства, как представлено SystemColors, SystemFonts или SystemParameters. Эти значения являются действительно динамическими, так как они в конечном счете берутся из среды выполнения пользователя и операционной системы. Кроме того, возможны темы уровня приложения, которые могут изменяться, когда при доступе к ресурсу уровня страницы также необходимо отследить изменения.

  • При создании стилей темы или ссылки на них для пользовательского элемента управления.

  • Когда необходимо настроить содержимое ResourceDictionary во время существования приложения.

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

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

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

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

Поведение подстановки динамического ресурса

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

  1. Процесс подстановки ищет запрошенный ключ в словаре ресурсов, определенном элементом, который устанавливает это свойство:

  2. Процесс подстановки обходит логическое дерево снизу вверх до родительского элемента и его словаря ресурсов. Этот процесс продолжается до тех пор, пока не будет достигнут корневой элемент.

  3. Проверяются ресурсы приложения. Ресурсы приложения — это ресурсы в словаре ресурсов, которые определены с помощью объекта Application для приложения WPF.

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

  5. Проверяются системные ресурсы.

Поведение исключения (если таковое имеется) варьируется.

  • Если ресурс был запрошен с помощью вызова FindResource и не найден, возникает исключение.

  • Если ресурс был запрошен с помощью вызова TryFindResource и не найден, исключение не возникает, а возвращаемое значение равно null. Если задаваемое свойство не принимает null, то по-прежнему возможен вызов исключения более глубокого уровня в зависимости от задания отдельного свойства.

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

Ограничения

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

  • Задаваемое свойство должно быть свойством FrameworkElement или FrameworkContentElement. Это свойство должно подкрепляться с помощью DependencyProperty.

  • Ссылка указывает на значение в StyleSetter.

  • Задаваемое свойство должно быть свойством Freezable, которое предоставляется как значение свойства FrameworkElement или FrameworkContentElement или как значение Setter.

Так как задаваемое свойство должно быть свойством DependencyProperty или Freezable, большинство изменений свойств может распространяться на пользовательский интерфейс, так как изменение свойства (измененное значение динамического ресурса) подтверждается системой свойств. Большая часть элементов управления содержит логику, которая принудительно создаст макет элемента управления, если DependencyProperty изменяется и это свойство может повлиять на макет. Однако не все свойства, имеющие расширение разметки DynamicResource в качестве значения, гарантированно предоставляют обновления в пользовательском интерфейсе в режиме реального времени. Функциональные возможности по-прежнему могут отличаться в зависимости от свойства, а также типа, которому принадлежит свойство, или даже логической структуры приложения.

Стили, DataTemplates и неявные ключи

Хотя все элементы в ResourceDictionary должны иметь ключ, это не значит, что все ресурсы должны иметь явный атрибут x:Key. Некоторые типы объектов поддерживают неявный ключ, если он определен как ресурс, где значение ключа связано со значением другого свойства. Этот тип ключа называется неявным ключом, в то время как атрибут x:Key является явным ключом. Любой неявный ключ можно перезаписать, указав явный ключ.

Одним очень важным сценарием для ресурсов является определение Style. На самом деле Style почти всегда определяется как запись в словаре ресурсов, так как стили наследственным образом предназначены для повторного использования. Дополнительные сведения о стилях см. в статье Стили и шаблоны в WPF.

С помощью неявного ключа можно создавать стили для элементов управления либо ссылаться на них. Стили темы, определяющие внешний вид элемента управления по умолчанию, используют этот неявный ключ. Неявный ключ с точки зрения запроса представляет тип Type самого элемента управления. С точки зрения определения ресурсов неявным ключом является TargetType стиля. Таким образом, если вы создаете темы для пользовательских элементов управления, создавая стили, которые взаимодействуют с существующими стилями темы, нет необходимости задавать директиву x:Key для этого стиля Style. А если требуется использовать стили из тем, то вообще не нужно задавать стиль. Например, следующее определение стиля работает, даже если ресурс Style не имеет ключа:

<Style TargetType="Button">
  <Setter Property="Background">
    <Setter.Value>
      <LinearGradientBrush>
        <GradientStop Offset="0.0" Color="AliceBlue"/>
        <GradientStop Offset="1.0" Color="Salmon"/>           
      </LinearGradientBrush>
    </Setter.Value>
  </Setter>  
  <Setter Property="FontSize" Value="18"/>
</Style>

У этого стиля на самом деле есть ключ: неявный ключ typeof(System.Windows.Controls.Button). В разметке можно непосредственно указать свойство TargetType в качестве имени типа (при необходимости можно использовать директиву {x:Type...}, чтобы вернуть Type).

В механизмах стилей темы по умолчанию, используемых в WPF, этот стиль будет применен как стиль среды выполнения для Button на странице, даже если элемент Button не пытается задать свое свойство Style или конкретную ссылку на ресурс в качестве стиля. Стиль, определенный на странице, находится в последовательности подстановки раньше, чем стиль словаря темы, и использует тот же ключ, что и стиль словаря темы. Вы можете просто указать <Button>Hello</Button> в любом месте на странице, и стиль, определенный с помощью Button для TargetType, будет применен к этой кнопке. Если требуется, можно по-прежнему явно задать ключ стиля с тем же значением типа, что и для TargetType, для ясности в разметке, но это необязательно.

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

DataTemplate также имеет неявный ключ. Неявным ключом для DataTemplate является значение свойства DataType. DataType также можно задать как имя типа вместо явного использования {x:Type...}. Дополнительные сведения см. в статье Общие сведения о шаблонах данных.

См. также