目錄
介紹
在 Avalonia UI 中有幾種概念 Theme
Style
ControlTheme
,從WPF轉過來的時候對於 ControlTheme 跟 Theme 的區別是什麼呢? 爲什麼Style跟我們的WPF的Style的效果不太一樣? Trigger 也沒了?
首選需要說的是, Theme
、Style
、ControlTheme
都是繼承自 IStyle
也就是說他們都是 樣式(Style)
, 但是他們之間有一些差異.
這裏介紹一下我個人如何理解這三種 IStyle
Theme
暫且理解爲全局主題(Global Theme)
Style
暫且理解爲局部主題(Local Theme)
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 Theme
與 Local Theme
是一樣的, 都是放置在 Styles
屬性下. 問題的關鍵是:
-
Styles
下的Theme
與Resource
下的ControlTheme
有什麼區別? -
Styles
跟ControlTheme
同樣可以重寫Template
, 那我要選哪個來重寫Template
?
問題分析
在下文我將 Styles
下的Theme
或Style
稱爲 Styles
, 將Resources
下的ControlTheme
成爲ControlTheme
, 方便大家理解.
問題1 區別
按我個人的理解來看,這是屬於兩種UI設計模式
.
-
Styles
類似於CSS
樣式表操作,針對在應用範圍內的所有選擇的元素的Style
都將被應用. -
ControlTheme
是類似與WPF
的 Style, 除了默認ControlTheme
, 其他ControlTheme
都需要指定Key,相對獨立。
問題2 重寫Template
用 Styles
還是 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
對於 特定 Control
的ControlTheme
之間是彼此獨立的。
<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
屬性即可.
最佳實踐
- 開發UI框架時最好使用
ControlTheme
定義控件Template
, 這也是官方推薦的。 - 利用控件可以應用多個
Styles
的優點,標準化一些常用樣式, 供控件的擴展UI使用 - 利用
Styles
應用優先級比ControlTheme
高的優點,- 標準業務裏邊的多顏色主題使用
Styles
- 全局控制應用的主題
- 標準業務裏邊的多顏色主題使用
- 不要寫默認的全局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
, 它將所有作用域範圍內的Styles
的Setters
都放在一起應用到控件上. 這也就是爲什麼原始的Style
如果新的Style
不需要也要設置相同屬性進行覆蓋
優先級
從應用樣式的角度 Style > ControlTheme 即 Default Style
(或者設置了Classes的控件) 的 Setter
都將覆蓋 ControlTheme
的 Setter
從定義控件的角度 ControlTheme > Style 即 定義一類新Template(類似WPF的 ControlTemplate) 優先使用ControlTheme, 並且利用 Style 控制上層顏色方案
樣式來源
ControlTheme 將遍歷 可視樹 (Visual Tree)
, 這種方式也與WPF類似
Style 類似於將所有作用域範圍內的所有 Setter
合併並收集起來, 應用到控件
實例上.
多Styles
單ControlTheme
控件實例可以引用多個Styles
, 引用方式爲 Classes="H1 Blue"
控件實例只可以引用一個ControlTheme
, 引用方式爲 Theme="{StaticResources XXXXTheme}"
參考文檔
===
知識共享許可協議 本作品採用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名 0xJins
(包含此鏈接),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請 與我聯繫 。