Avalonia UI 中 Styles 与 ControlTheme 的区别

目录

介绍

在 Avalonia UI 中有几种概念 Theme Style ControlTheme,从WPF转过来的时候对于 ControlTheme 跟 Theme 的区别是什么呢? 为什么Style跟我们的WPF的Style的效果不太一样? Trigger 也没了?

首选需要说的是, ThemeStyleControlTheme 都是继承自 IStyle 也就是说他们都是 样式(Style), 但是他们之间有一些差异.

这里介绍一下我个人如何理解这三种 IStyle

  1. Theme 暂且理解为 全局主题(Global Theme)
  2. Style 暂且理解为 局部主题(Local Theme)
  3. ControlTheme 暂且理解为 控件样式 (Control Style, 类似WPF中定义控件Style以及Template)

使用方式

全局主题 (Global Theme)

// App.xaml
<Application xmlns="https://github.com/avaloniaui"
             RequestedThemeVariant="Default">
             <!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
    <Application.Styles>
        <FluentTheme />     // 这里的Theme 其实也是Style
    </Application.Styles>
</Application>

局部主题 (Local Theme)

如果将 Style 方式在 Window或者UserControl或者Control下即为局部主题

<Window>
    <Window.Styles>
        <!-- Common button properties -->
        <Style Selector="Button">
            <Setter Property="Margin" Value="10" />
            <Setter Property="MinWidth" Value="200" />
            <Setter Property="Height" Value="50" />
			<Setter Property="HorizontalContentAlignment" Value="Right" />
			<Setter Property="VerticalContentAlignment" Value="Bottom" />
			<Style Selector="^:pointerover /template/ ContentPresenter">
				<Setter Property="Background" Value="Green" />
			</Style>
        </Style>
    </Window.Styles>

    // ...
</Window>

控件主题 (ControlTheme)

注意: 这里的 ControlTheme 是放置在 Window.Resource

<Window.Resource>
    <ControlTheme x:Key="{x:Type Button}" TargetType="Button">
        <Setter Property="Background" Value="#C3C3C3" />
        <Setter Property="FontFamily" Value="Arial" />
        <Setter Property="FontSize" Value="14" />
        <Setter Property="Height" Value="100" />
        <Setter Property="Template">
            <ControlTemplate>
                // ... 
            </ControlTemplate>
        </Setter>
        <Style Selector="^:pointerover">
            <Setter Property="Background" Value="red" />
        </Style>
    </ControlTheme>
</Window.Resource>

问题描述

从使用方式上看 Global ThemeLocal Theme 是一样的, 都是放置在 Styles 属性下. 问题的关键是:

  1. Styles 下的 ThemeResource下的ControlTheme有什么区别?

  2. StylesControlTheme 同样可以重写 Template, 那我要选哪个来重写 Template?

问题分析

在下文我将 Styles下的ThemeStyle称为 Styles, 将Resources下的ControlTheme成为ControlTheme, 方便大家理解.

问题1 区别

按我个人的理解来看,这是属于两种UI设计模式.

  • Styles 类似于 CSS 样式表操作,针对在应用范围内的所有选择的元素的 Style 都将被应用.

  • ControlTheme 是类似与 WPF 的 Style, 除了默认ControlTheme, 其他ControlTheme都需要指定Key,相对独立。

问题2 重写TemplateStyles 还是 ControlTheme?

两种模式都可以写, ControlTheme 是从 v11 版本引入的. 主要是为了解决 Styles 之间的隔离性. 如:

 <Style Selector="Button">   // Default Style
    <Setter Property="HorizontalContentAlignment" Value="Right" />
    <Setter Property="VerticalContentAlignment" Value="Bottom" />
 </Style>
 // 两个Button的Style有关联关系
 <Style Selector="Button.NewStyle"> // 对 Default Style 的修改都有可能影响其他Style
    <Setter Property="HorizontalContentAlignment" Value="Left" /> 
    // <Setter Property="VerticalAlignment" Value="Bottom" />    // 来此Default Style
 </Style>

试想这样一个场景, 如果我在代码中引入了第三方控件库,它重写了系统默认控件的样式, 这个时候我们又有自己的样式, 如

// 第三方库
 <Style Selector="Button"> 
    <Setter Property="VerticalContentAlignment" Value="Center" />
    <Setter Property="HorizontalContentAlignment" Value="Center" />
    <Setter Property="MinWidth" Value="100" />
 </Style>

// 我们自己的 
 <Style Selector="Button">   
    <Setter Property="HorizontalContentAlignment" Value="Left" />
    <Setter Property="VerticalContentAlignment" Value="Bottom" />
    <Setter Property="Width" Value="50" />
 </Style>

引用自官网: 如果你想要修改控件的特定实例的样式Styles,唯一的选项是应用一个新的Styles为控件实例,并希望它能够重写原始的Styles中的设置过的所有属性.

以上的场景 Button 的宽度是多少? 答 100. 这个对于来自WPF的小朋友就感觉就难受了, 第三方库加了个MinWidth,我又没继承,难道我还要在自己的样式中自己给MinWidth或者重新整个样式吗? 也就是Avalonia UI官方说的一旦一个Style被应用到一个控件上,没有办法移除它

坑就来了啊,如果第三方库更新了Styles加了个属性咋办? 我也要跟着加?

使用 ControlTheme

所以 V11 版本后引入了 ControlTheme 对于 特定 ControlControlTheme之间是彼此独立的。

<ControlTheme x:Key="A" TargetType="Button">
   <Setter Property="VerticalContentAlignment" Value="Center" />
   <Setter Property="HorizontalContentAlignment" Value="Center" />
   <Setter Property="MinWidth" Value="100" />
</ControlTheme>
// 两个Button的ControlTheme相互独立
<ControlTheme x:Key="B" TargetType="Button">
   <Setter Property="HorizontalContentAlignment" Value="Left" />
   <Setter Property="VerticalContentAlignment" Value="Bottom" />
   <Setter Property="Width" Value="50" />
</ControlTheme>

问: Button的宽度是多少? 答 50. 这不就跟我们大WPF一样了吗? 如果我还要继承第三方库写的样式咋办? 加上 Baseon 属性即可.

最佳实践

  1. 开发UI框架时最好使用 ControlTheme 定义控件 Template, 这也是官方推荐的。
  2. 利用控件可以应用多个Styles的优点,标准化一些常用样式, 供控件的扩展UI使用
  3. 利用 Styles 应用优先级比 ControlTheme 高的优点,
    1. 标准业务里边的多颜色主题使用Styles
    2. 全局控制应用的主题
  4. 不要写默认的全局Style<Style Selector="Button">, 否则所有Button都将受到影响, 官方全局样式里边的已经都替换成ControlTheme了, 有兴趣参考下面链接。

总结

Style & ControlTheme 的特性

独立性

ControlTheme 彼此之间是独立的
Style 彼此是相互覆盖的

如:

<ControlTheme x:Key="A" TargetType="Button">
   //...
</ControlTheme>
// 两个Button的ControlTheme相互独立
<ControlTheme x:Key="B" TargetType="Button">
   //...
</ControlTheme>
<Style Selector="Button">   // Default Style
   <Setter Property="HorizontalContentAlignment" Value="Right" />
   <Setter Property="VerticalContentAlignment" Value="Bottom" />
</Style>
// 两个Button的Style有关联关系
<Style Selector="Button.NewStyle"> // 对 Default Style 的修改都有可能影响其他Style
   <Setter Property="HorizontalContentAlignment" Value="Left" /> 
   // <Setter Property="VerticalAlignment" Value="Bottom" />    // 来此Default Style
</Style>

继承性

ControlTheme 由于ControlTheme之间相互独立,但是其支持 BaseOn 类似 WPF 的<Style BaseOn="{StaticResource BaseStyle}"
Style 参考上一点独立性, 新增的Style都将继承Default Style

如:

<ControlTheme x:Key="A" TargetType="Button">
   //...
</ControlTheme>
// 两个Button的ControlTheme相互独立
<ControlTheme x:Key="B" TargetType="Button" BaseOn="{StaticResource A}">
   //...
</ControlTheme>

Styles 类似 CSS, 它将所有作用域范围内的StylesSetters都放在一起应用到控件上. 这也就是为什么原始的Style如果新的Style不需要也要设置相同属性进行覆盖

优先级

从应用样式的角度 Style > ControlTheme 即 Default Style(或者设置了Classes的控件) 的 Setter 都将覆盖 ControlThemeSetter

从定义控件的角度 ControlTheme > Style 即 定义一类新Template(类似WPF的 ControlTemplate) 优先使用ControlTheme, 并且利用 Style 控制上层颜色方案

样式来源

ControlTheme 将遍历 可视树 (Visual Tree), 这种方式也与WPF类似

Style 类似于将所有作用域范围内的所有 Setter 合并并收集起来, 应用到控件实例上.

StylesControlTheme

控件实例可以引用多个Styles, 引用方式为 Classes="H1 Blue"

控件实例只可以引用一个ControlTheme, 引用方式为 Theme="{StaticResources XXXXTheme}"

参考文档

Control Themes

Styles

Button Themes

===

CC BY-NC-SA 4.0

知识共享许可协议 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 0xJins包含此链接),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系

posted @ 2024-04-25 15:31  JinsYang  阅读(8)  评论(0编辑  收藏  举报