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

Обновлен: Ноябрь 2007

В данном обзоре объясняется, как использовать ресурсы WPF для упрощения повторного использования часто определяемых объектов и значений. Этот обзор посвящен использованию ресурсов в XAML. Можно также создать и получить доступ к ресурсам с помощью кода или попеременно кодом и Язык XAML (Extensible Application Markup Language). Дополнительные сведения см. в разделе Ресурсы и код.

В этом разделе содержатся следующие подразделы.

  • Использование ресурсов в XAML
  • Статические и динамические ресурсы
  • Стили, DataTemplates и неявные ключи
  • Связанные разделы

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

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

<Page Name="root"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://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), определенные ресурсом. Можно определить ресурсы в любом элементе. Однако наиболее часто ресурсы определяются в корневом элементе, которым в примере является Page.

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

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

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

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

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

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

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

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

  • Общая схема создания ресурсов для приложения (постранично, в приложении, в свободном XAML, в сборке только ресурсов).

  • Является ли обновление ресурсов в режиме реального времени частью требований приложения.

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

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

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

Ссылки на статические ресурсы лучше всего использовать в следующих случаях:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Динамические ресурсы лучше всего подходят для следующих случаев:

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

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

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

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

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

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

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

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

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

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

    • Если элемент определяет свойство Style, проверяется словарь Resources в Style.

    • Если элемент определяет свойство Template, проверяется словарь Resources в FrameworkTemplate.

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

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

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

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

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

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

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

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

Ограничения

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

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

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

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

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

Стили для элементов управления могут и быть созданы, и ссылаться с помощью неявного ключа. Стили темы, определяющие внешний вид элемента управления по умолчанию, полагаются на этот неявный ключ. Неявный ключ с точки зрения запроса представляет 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(Button). В разметке можно указать TargetType непосредственно как имя типа (или можно опционально использовать {x:Type...}, чтобы вернуть Type.

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

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

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

См. также

Задачи

Практическое руководство. Определение и создание ссылки на ресурс

Основные понятия

Оптимизация производительности: ресурсы приложения

Ресурсы и код

Общие сведения об управлении приложением

Ссылки

Расширение разметки x:Type

ResourceDictionary

Расширение разметки StaticResource

Расширение разметки DynamicResource