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


Обзор ресурсов 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 задает два разных свойства: BackgroundButtonи FillRectangle.

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

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

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

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

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

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

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

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

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

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

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

  • Вы задаете значение свойства, которое не находится в DependencyObject или Freezable.

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

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

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

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

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

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

  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 почти всегда определяется как запись в словаре ресурсов, так как стили изначально предназначены для повторного использования. Для получения дополнительной информации о стилях см. Стилизация и шаблоны.

Стили элементов управления можно как создавать с неявным ключом, так и ссылаться на него. Стили темы, определяющие внешний вид элемента управления по умолчанию, полагаются на этот неявный ключ. С точки зрения отправки запроса, неявный ключ — это 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> в любом месте страницы, а стиль, определенный TargetTypeButton будет применяться к этой кнопке. Если вы хотите, вы можете явно задать стиль тем же значением типа, что и TargetType для ясности в разметке, но это необязательно.

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

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

См. также