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包含此鏈接),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請 與我聯繫

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章