Стилизация и использование шаблонов

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

Другая функция модели стилизации WPF состоит в разделении представления и логики. Это означает, что дизайнеры могут создавать внешний вид приложения, используя только XAML, в то же самое время, когда разработчики работают над логикой программы, используя C# или Visual Basic.

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

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

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

  • Пример стилизации и использования шаблонов
  • Основы стиля
  • Шаблоны данных
  • Шаблоны элементов управления
  • Триггеры
  • Совместно используемые ресурсы и темы
  • Связанные разделы

Пример стилизации и использования шаблонов

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

Стилизированный ListView

Это простое демонстрационное фотоизображение использует стилизацию и шаблоны для создания визуально привлекательного взаимодействия с пользователем. В этом фотоизображении имеется два элемента TextBlock и элемент управления ListBox, привязанный к списку изображений. Полный пример см. на странице Знакомство с примером "Styling and Templating Sample" (страница может отображаться на английском языке).

Основы стиля

Style — это удобный способ для применения набора значений свойств к нескольким элементам. В качестве примера рассмотрим следующие элементы TextBlock и их внешний вид по умолчанию:

<TextBlock>My Pictures</TextBlock>
<TextBlock>Check out my new pictures!</TextBlock>

Снимок экрана примера стилизации

Внешний вид по умолчанию можно изменить путем задания свойств, таких как FontSize и FontFamily, непосредственно для каждого элемента TextBlock. Однако, если элементы TextBlock должны совместно использовать некоторые свойства, можно создать Style в разделе Resources файла XAML, как показано ниже:

<Window.Resources>


...


<!--A Style that affects all TextBlocks-->
<Style TargetType="TextBlock">
  <Setter Property="HorizontalAlignment" Value="Center" />
  <Setter Property="FontFamily" Value="Comic Sans MS"/>
  <Setter Property="FontSize" Value="14"/>
</Style>


...


</Window.Resources>

При установке TargetType стиля в тип TextBlock, стиль будет применен ко всем элементам TextBlock в окне.

Теперь элементы TextBlock выглядят следующим образом:

Снимок экрана примера стилизации

Расширяемые стили

Предположим, что два элемента TextBlock должны совместно использовать некоторые свойства, например FontFamily и центрированное HorizontalAlignment, но текст "My Pictures" также должен иметь дополнительные свойства. Это можно сделать путем создания нового стиля на основе первого стиля, как показано ниже:

<Window.Resources>


...


<!--A Style that extends the previous TextBlock Style-->
<!--This is a "named style" with an x:Key of TitleText-->
<Style BasedOn="{StaticResource {x:Type TextBlock}}"
       TargetType="TextBlock"
       x:Key="TitleText">
  <Setter Property="FontSize" Value="26"/>
  <Setter Property="Foreground">
  <Setter.Value>
      <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
        <LinearGradientBrush.GradientStops>
          <GradientStop Offset="0.0" Color="#90DDDD" />
          <GradientStop Offset="1.0" Color="#5BFFFF" />
        </LinearGradientBrush.GradientStops>
      </LinearGradientBrush>
    </Setter.Value>
  </Setter>
</Style>


...


</Window.Resources>

Следует отметить, что предыдущему стилю присваивается x:Key. Чтобы применить стиль, можно установить свойство Style TextBlock в значение x:Key, как показано ниже:

<TextBlock Style="{StaticResource TitleText}" Name="textblock1">My Pictures</TextBlock>
<TextBlock>Check out my new pictures!</TextBlock>

Этот стиль TextBlock теперь имеет значение HorizontalAlignment, равное Center, значение FontFamily, равное Comic Sans MS, значение FontSize, равное 26, и значение Foreground, устанавливаемое в LinearGradientBrush, как показано в примере. Следует отметить, что значение FontSize базового стиля переопределяется. Если имеется несколько параметров Setter того же свойства в Style, то объявленный последним Setter получает более высокий приоритет.

Ниже показано, как теперь выглядят элементы TextBlock:

Стилизированный TextBlocks

Этот стиль TitleText расширяет стиль, созданный для типа TextBlock. Можно также расширить стиль, имеющий x:Key, используя значение x:Key. В качестве примера см. пример, приведенный для свойства BasedOn.

Отношение свойства TargetType и атрибута x:Key

Как показано в первом примере, установка свойства TargetType в TextBlock без присвоения стиля x:Key, применит стиль ко всем элементам TextBlock. В этом случае для x:Key неявным образом устанавливается значение {x:Type TextBlock}. Это означает, что при явном задании значения x:Key, отличного от {x:Type TextBlock}, Style не применится ко всем элементам TextBlock автоматически. Вместо этого следует явно применить стиль (с помощью значения x:Key) к элементам TextBlock. Если стиль находится в разделе ресурсов, а свойство TargetType в стиле не задается, то необходимо указать x:Key.

Помимо значения по умолчанию для x:Key, свойство TargetType указывает тип, к которому применяются свойства установщика. Если TargetType не указывается, необходимо уточнить свойства в объектах Setter, дополнив их именем класса при помощи синтаксиса Property="ClassName.Property". Например, вместо установки Property="FontSize" необходимо установить Property в "TextBlock.FontSize" или "Control.FontSize".

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

Стили и ресурсы

Стиль может использоваться для любого элемента, производного от FrameworkElement или FrameworkContentElement. Наиболее распространенный способ для объявления стилей состоит в их объявлении в качестве ресурса в разделе Resources файла XAML, как показано в предыдущих примерах. Так как стили являются ресурсами, они подчиняются тем же правилам области, которые применяются ко всем ресурсам; место объявления стиля влияет на место его применения. Например, если объявить стиль в корневом элементе файла XAML определения приложения, этот стиль можно будет использовать в любом месте приложения. Если создается приложение переходов, а стиль объявляется в одном из файлов приложения XAML, стиль может использоваться только в этом XAML файле. Дополнительные сведения о правилах области для ресурсов см. в разделе Общие сведения о ресурсах.

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

Установка стилей программным путем

Для присвоения именованного стиля к элементу программными средствами получите стиль из коллекции ресурсов и присвойте его свойству элемента Style. Следует отметить, что элементы в коллекции ресурсов имеют тип Object. Таким образом, необходимо привести извлеченный стиль к типу Style перед назначением его свойству Style. Например, чтобы задать определенный стиль TitleText на TextBlock с именем textblock1, выполните следующие действия.

textblock1.Style = CType(Me.Resources("TitleText"), Style)
textblock1.Style = (Style)(this.Resources["TitleText"]);

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

Можно создать объект, который выбирает стиль для применения на основе пользовательской логики. См. пример, приведенный для класса StyleSelector.

Привязки, динамические ресурсы и обработчики событий

Обратите внимание,что свойство Setter.Value можно использовать для указания Привязка расширения разметки или Расширение разметки DynamicResource. Для получения дополнительных сведений см. примеры, приведенные для свойства Setter.Value.

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

Шаблоны данных

В этом примере приложения присутствует элемент управления ListBox, который связан со списком фотографий:

<ListBox ItemsSource="{Binding Source={StaticResource MyPhotos}}"
         Background="Silver" Width="600" Margin="10" SelectedIndex="0"/>

В настоящее время этот ListBox выглядит так:

ListBox до применения шаблона

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

В приложении образца каждый пользовательский объект Photo имеет свойство Source типа строки, которая указывает путь к файлу изображения. В настоящее время объекты фотографий отображаются как пути к файлам.

Чтобы отобразить фотографии как изображения, создается DataTemplate в качестве ресурса:

<Window.Resources>


...


<!--DataTemplate to display Photos as images
    instead of text strings of Paths-->
<DataTemplate DataType="{x:Type local:Photo}">
  <Border Margin="3">
    <Image Source="{Binding Source}"/>
  </Border>
</DataTemplate>


...


</Window.Resources>

Обратите внимание, что свойство DataType очень похоже на свойство TargetType из Style. Если шаблон DataTemplate расположен в разделе ресурсов, то при указании свойства DataType для типа и отсутствии присвоения ему x:Key шаблон DataTemplate будет применяться при каждом отображении этого типа. Пользователь всегда может присвоить DataTemplate x:Key и затем задать его как StaticResource для свойств, которые принимают типы DataTemplate, такие как свойство ItemTemplate или свойство ContentTemplate.

DataTemplate в приведенном выше примере определяет то, что существовующий объект Photo должен отображаться как Image внутри Border. С этим DataTemplate приложение теперь выглядит следующим образом:

Фото

Модель шаблонов данных предоставляет другие возможности. Например, если данные коллекции, которая содержит другие коллекции, отображаются с помощью типа HeaderedItemsControl (например Menu или TreeView), то существует HierarchicalDataTemplate. Другой возможностью шаблонов данных является DataTemplateSelector, который позволяет выбрать DataTemplate для использования на основе пользовательской логики. Дополнительные сведения содержатся в разделе Общие сведения о шаблонах данных, который предоставляет более подробное рассмотрение различных функций шаблонов данных.

Шаблоны элементов управления

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

Триггеры

Триггер задает свойства или начинает действия, такие как анимация, при изменении значения свойства или при возникновении события. Style, ControlTemplate и DataTemplate имеют свойство Triggers, которое может содержать набор триггеров. Существуют разные типы триггеров.

Триггеры свойств

Триггер Trigger, который задает значения свойств или запускает действия на основе на значения свойства, называется триггером свойства.

Чтобы продемонстрировать использование триггеров свойств, можно сделать каждый элемент ListBoxItem частично прозрачным, если он не является выбранным. Следующий стиль устанавливает значение Opacity элемента ListBoxItem равным 0.5. Когда свойство IsSelected равно true, Opacity устанавливается в 1.0.

<Style TargetType="ListBoxItem">
  <Setter Property="Opacity" Value="0.5" />
  <Setter Property="MaxHeight" Value="75" />
  <Style.Triggers>
    <Trigger Property="IsSelected" Value="True">
        <Setter Property="Opacity" Value="1.0" />
    </Trigger>


...


  </Style.Triggers>
</Style>

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

Следует отметить, что свойство MaxHeight элемента ListBoxItem устанавливается в значение 75. На следующем рисунке третий элемент является выбранным:

Стилизированный ListView

EventTrigger и раскадровки

Другим типом триггера является EventTrigger, запускающий набор действий в зависимости от наличия события. Например, следующие объекты EventTrigger указывают на то, что при вхождении указателя мыши в ListBoxItem свойство MaxHeight изменяется до значения 90 за период в 0.2 секунды. Когда указатель мыши перемещается из элемента, свойство возвращается к исходному значению за 1 секунду. Обратите внимание, что значение To не обязательно указывать для анимации MouseLeave. Это происходит потому, что анимация имеет возможность следить за исходным значением.

        <EventTrigger RoutedEvent="Mouse.MouseEnter">
          <EventTrigger.Actions>
            <BeginStoryboard>
              <Storyboard>
                <DoubleAnimation
                  Duration="0:0:0.2"
                  Storyboard.TargetProperty="MaxHeight"
                  To="90"  />
              </Storyboard>
            </BeginStoryboard>
          </EventTrigger.Actions>
        </EventTrigger>
        <EventTrigger RoutedEvent="Mouse.MouseLeave">
          <EventTrigger.Actions>
            <BeginStoryboard>
              <Storyboard>
                <DoubleAnimation
                  Duration="0:0:1"
                  Storyboard.TargetProperty="MaxHeight"  />
              </Storyboard>
            </BeginStoryboard>
          </EventTrigger.Actions>
        </EventTrigger>

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

На следующем рисунке мышь указывает на третий элемент:

Снимок экрана примера стилизации

MultiTrigger, DataTrigger и MultiDataTrigger

Кроме Trigger и EventTrigger существуют и другие типы триггеров. Триггеры MultiTrigger позволяют задавать значения свойств на основе нескольких условий. DataTrigger и MultiDataTrigger используются в том случае, когда свойство условия связано с данными.

Совместно используемые ресурсы и темы

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

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

Ресурсы темы Windows Presentation Foundation (WPF) хранятся в словарях внедренных ресурсов. Эти словари ресурсов должны быть внедрены в ту же подписанную сборку, что и сам код, или в параллельную пописанную сборку. В случае с PresentationFramework.dll (сборки, которая содержит элементы управления Windows Presentation Foundation (WPF)) ресурсы тем находятся в сериях параллельных сборок.

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

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

Для совместного использования набора ресурсов, включая стили и шаблоны, всеми приложениями можно создать файл XAML и определить ResourceDictionary. Например, можно посмотреть на следующий рисунок, на котором показана часть примера "Styling with ControlTemplates".

Примеры шаблона элемента управления

Если взглянуть на файлы XAML в этом примере, можно заметить, что для всех файлов справедливо следующее:

<ResourceDictionary.MergedDictionaries>
  <ResourceDictionary Source="Shared.xaml" />
</ResourceDictionary.MergedDictionaries>

Общий доступ к shared.xaml определяет ResourceDictionary, содержащий набор стилей и ресурсов кисти, которая позволяет элементам управления в образце иметь согласованный внешний вид.

Дополнительные сведения см. в разделе Объединенные словари ресурсов.

При создании темы для пользовательского элемента управления см. раздел "Внешняя библиотека элементов управления" в теме Общие сведения о разработке управления.

См. также

Задачи

Поиск элемента, созданного шаблоном ControlTemplate

Практическое руководство. Поиск элементов, созданных с использованием шаблона DataTemplate

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

URI типа "pack" в WPF