Table of Contents

Свои темы

В этом руководстве игра получает собственный вид: палитру, которая перекрашивает встроенные кнопки и списки, новый шаблон для одного элемента и, когда этого мало, целую тему. Тема — это файл, поэтому художник может менять её, не трогая код.

Вам понадобится игра с подключённым IcyUI (Начало работы) и представление о стилях и шаблонах из страниц Ресурсы, стили и темы и Шаблоны элементов и визуальные состояния.

1. Наложите файл темы на встроенную тему

ExtendDefaultTheme загружает файл словаря ресурсов и накладывает его на встроенную тему. Всё, чего в файле нет, сохраняет встроенный вид:

// Layer the game's palette over the built-in theme.
public static StrideIcyHost Extend(IGame game) =>
    game.UseIcyUI(builder => builder.ConfigureTheme().ExtendDefaultTheme("Themes/MyGame.xml"));

Путь отсчитывается от папки вывода игры, как у любого файла разметки: добавьте Themes/MyGame.xml в проект с Copy to Output Directory = «Copy if newer». Отсутствующий или некорректный файл останавливает игру при запуске с сообщением, в котором назван этот файл.

2. Перекрасьте, сохранив шаблоны и состояния

Вот весь файл целиком:

<!-- Themes/MyGame.xml: a game palette, layered over the built-in theme with ExtendDefaultTheme. -->
<ResourceDictionary>
  <!-- Recolor. An implicit style here builds on the built-in style for its type: the button keeps its template and its
       focus state, and changes only what is set. The built-in hover and press states set their own colors, so they
       are repeated here in teal; a state group with the same name replaces the built-in one. -->
  <Style TargetType="Button" Background="#FF1F6F6B" BorderBrush="#FF5FD0C0">
    <Style.StateGroups>
      <VisualStateGroup Name="CommonStates">
        <VisualState Name="Hovered" State="Hovered" Background="#FF2A8A85"/>
        <VisualState Name="Pressed" State="Pressed" Background="#FF174F4C"/>
      </VisualStateGroup>
    </Style.StateGroups>
  </Style>

  <!-- A visual state group with the same name as one of the built-in style's replaces it, so this repeats the states
       to keep: list rows hover and select in teal, and keep the keyboard cursor. -->
  <Style TargetType="SelectorItem">
    <Style.StateGroups>
      <VisualStateGroup Name="CommonStates">
        <VisualState Name="Hovered" State="Hovered" Background="#FF2A4B49"/>
        <VisualState Name="Highlighted" State="Highlighted" Background="#FF2F6A66"/>
        <VisualState Name="HighlightedHovered" State="Highlighted,Hovered" Background="#FF2F6A66"/>
        <VisualState Name="Selected" State="Selected" Background="#FF1F6F6B"/>
        <VisualState Name="SelectedHovered" State="Selected,Hovered" Background="#FF1F6F6B"/>
      </VisualStateGroup>
    </Style.StateGroups>
  </Style>

  <!-- A new template for one control. ReplacesTheme drops the built-in style entirely, so this style provides everything:
       the template, the colors and every state the control needs. A later entry can use the ones before it. -->
  <ControlTemplate x:Key="GameCheckBoxTemplate" TargetType="CheckBox">
    <StackPanel Orientation="Horizontal">
      <Border Width="20" Height="20" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="2" Margin="0,0,10,0"/>
      <ContentPresenter Content="{TemplateBinding Content}" VerticalAlignment="Center"/>
    </StackPanel>
  </ControlTemplate>

  <Style TargetType="CheckBox" ReplacesTheme="True" Template="{StaticResource GameCheckBoxTemplate}"
         Background="#FF26262C" BorderBrush="#FF5FD0C0">
    <Style.StateGroups>
      <VisualStateGroup Name="CheckStates">
        <VisualState Name="Checked" State="Checked" Background="#FF5FD0C0"/>
      </VisualStateGroup>
    </Style.StateGroups>
  </Style>
</ResourceDictionary>

Первый стиль — самый частый случай. <Style TargetType="Button" Background="..."/> в файле строится на встроенном стиле кнопки: сохраняет шаблон и состояние фокуса и меняет только заданные цвета. Встроенные состояния наведения и нажатия задают собственные цвета, поэтому файл повторяет их бирюзовыми (шаг 3); без этого кнопка при наведении или нажатии возвращалась бы к встроенному серому. По тому же правилу стиль в ресурсах страницы работает поверх темы (Тема под стилями), здесь оно применено к файлу темы.

3. Замените группу визуальных состояний

Стиль, объявляющий группу состояний с тем же именем, что и во встроенном стиле, заменяет эту группу, поэтому состояния, которые нужно сохранить, придётся повторить. Строки списка в файле заменяют CommonStates, чтобы подсвечиваться при наведении и выделении бирюзовым, и сохраняют состояние курсора клавиатуры (Highlighted).

4. Дайте одному элементу новый шаблон

Флажок в файле не перекрашивает встроенный, а заменяет его: у него собственный шаблон — квадрат без галочки — и свои цвета и состояния. ReplacesTheme="True" велит IcyUI полностью отбросить встроенный стиль для этого типа, так что стиль сам даёт всё, что нужно элементу: шаблон, цвета и состояния. У этого флажка нет вида при наведении и фокусе, потому что файл их не объявляет; добавьте их так, как это делает тема по умолчанию.

Запись в файле темы может использовать объявленные перед ней: стиль берёт GameCheckBoxTemplate по ключу, поэтому объявляйте шаблон первым.

Примечание

Лучший образец того, как выглядят шаблон и его состояния, — файл встроенной темы: DefaultTheme.xml.

5. Замените тему целиком

Чтобы начать с нуля, UseThemeFile делает файл всей темой вместо наложения:

// Or replace the built-in theme entirely with your own file.
public static StrideIcyHost Replace(IGame game) =>
    game.UseIcyUI(builder => builder.ConfigureTheme().UseThemeFile("Themes/Dark.xml"));

Скопируйте DefaultTheme.xml (ссылка выше) в проект как Themes/Dark.xml и отредактируйте. С UseThemeFile у элемента, которого в файле нет, нет никакого вида: ни шаблона, ни цветов. Если нужно изменить лишь часть, лучше используйте ExtendDefaultTheme.

Что вы построили

Файл темы, который перекрашивает встроенные кнопки и списки, заменяет флажок шаблоном вашей разработки и оставляет все остальные элементы как были, а также способ заменить встроенную тему целиком.

Примечание

Тема выбирается при запуске: Canvas берёт тему при создании, поэтому смена темы во время работы игры не меняет вид уже созданных Canvas. Записи с ключом (x:Key) в файле темы также не видны для {StaticResource} на ваших страницах: он ищет только по загружаемой странице; для темы используйте неявные стили, а стили с ключом держите в Resources страницы.

Дальше