Документация по Avalonia UI
< Все темы
Печать

Решение проблем

Система стилей Avalonia представляет собой смесь подходов к стилям XAML и CSS, поэтому разработчики, знакомые только с одной из этих технологий, могут быть сбиты с толку деталями другой.

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

Селектор нацелен на несуществующий элемент управления

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

Целевое свойство переопределяется другим стилем

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

файл Styles1.axaml:

<Style Selector="TextBlock.header">
    <Style Property="Foreground" Value="Green" />
</Style>

файл Styles2.axaml:

<Style Selector="TextBlock.header">
    <Style Property="Foreground" Value="Blue" />
    <Style Property="FontSize" Value="16" />
</Style>c
<StyleInclude Source="Style1.axaml" />
<StyleInclude Source="Style2.axaml" />

Здесь первыми были применены стили из файла Styles1.axaml, поэтому приоритет имеют сеттеры из файла Styles2.axaml. Получившийся TextBlock будет иметь FontSize="16" и Foreground="Green". Такой же порядок приоритетов происходит и в файлах стилей.

Локально установленные свойства переопределяют стили

Аналогично WPF, свойства Avalonia могут иметь несколько значений, часто с разными приоритетами.

В этом примере вы можете видеть, что локальное значение (определенное непосредственно в элементе управления) имеет более высокий приоритет, чем значение стиля, поэтому текстовый блок будет иметь красный передний план:

<TextBlock Classes="header" Foreground="Red" />
...
<Style Selector="TextBlock.header">
    <Setter Property="Foreground" Value="Green" />
</Style>

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

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

Отсутствует селектор псевдокласса стиля (триггера)

Давайте представим ситуацию, в которой вы могли бы ожидать, что второй стиль переопределит предыдущий, но это не так:

<Style Selector="Border:pointerover">
    <Setter Property="Background" Value="Blue" />
</Style>
<Style Selector="Border">
    <Setter Property="Background" Value="Red" />
</Style>
...
<Border Width="100" Height="100" Margin="100" />

В этом примере кода граница обычно имеет красный фон и синий, когда указатель находится над ней. Это связано с тем, что, как и в случае с CSS, приоритет имеют более конкретные селекторы. Это проблема, когда вы хотите переопределить стили по умолчанию для любого состояния (наведение указателя, нажатие или другое) одним стилем. Для этого вам также понадобятся новые стили для этих состояний.

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

Селектор с псевдоклассом не переопределяет значение по умолчанию

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

<Style Selector="Button">
    <Setter Property="Background" Value="Red" />
</Style>
<Style Selector="Button:poinverover">
    <Setter Property="Background" Value="Blue" />
</Style>

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

Причина скрыта в шаблоне Button. Вы можете найти шаблоны по умолчанию в исходном коде Avalonia (старая тема по умолчанию Avalonia.Themes.Default/Button.xaml на данный момент недоступна,  и новая тема Fluent), но для удобства здесь мы упростили шаблон из темы Fluent:

<Style Selector="Button">
    <Setter Property="Background" Value="{DynamicResource ButtonBackground}"/>
    <Setter Property="Template">
        <ControlTemplate>
            <ContentPresenter Name="PART_ContentPresenter"
                              Background="{TemplateBinding Background}"
                              Content="{TemplateBinding Content}"/>
        </ControlTemplate>
    </Setter>
</Style>
<Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
    <Setter Property="Background" Value="{DynamicResource ButtonBackgroundPointerOver}" />
</Style>

Фактический фон визуализируется ContentPresenter, который по умолчанию привязан к свойству кнопки Background. Однако в состоянии указателя селектор напрямую применяет фон к ContentPresenter (Button:pointerover/template/ContentPresenter#PART_ContentPresenter). Вот почему наш установщик был проигнорирован в предыдущем примере кода. Исправленный код также должен быть нацелен непосредственно на ContentPresenter:

<!-- Here #PART_ContentPresenter name selector is not necessary, but was added to have more specific style -->
<Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
    <Setter Property="Background" Value="Blue" />
</Style>

Вы можете наблюдать такое поведение для всех элементов управления в темах по умолчанию (как старой Default, так и новой Fluent), а не только Button. И не только для фона, но и для других свойств, зависящих от состояния.

 

Почему стили по умолчанию изменяют свойство ContentPresenter Background напрямую, а не свойство Button.Background?

Это связано с тем, что если бы пользователь установил локальное значение для кнопки, оно переопределило бы все стили и сделало бы кнопку всегда одного цвета. Для получения более подробной информации см. этот восстановленный PR.

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

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

Типичный пример — CommandProperty. Он определяется как DirectProperty и никогда не будет работать должным образом. В будущем попытка стилизовать прямое свойство приведет к ошибке времени компиляции, см. #6837.

Оглавление