UWP小书 - 简单的样式修改
:::tip
本章涉及知识点:
- 样式的添加
- 样式的复用
- 样式的继承
:::
视频地址:https://www.bilibili.com/video/av53560120/
前言
前面的课程中,我们学习了一些简单的布局知识。通过布局,可以有效地组织我们呈现的信息,让软件界面井井有条。
但倘若将软件与人作比,良好的布局就像是一副整齐的骨骼,没多少人会觉得骨头架子很好看。若要美观,当然还需要漂亮的衣裳。
在软件中,所谓“漂亮的衣裳”指的就是样式。这里说的样式,包括了界面的背景,文本的颜色、字体、字号,控件的外观等等设计层面的东西。
听上去很繁杂,事实上也的确是。不过归根结底都会落到一个点,那就是资源。本章所谈的样式,不过是更高一级对于资源的整合罢了。
不过我们却并不从资源出发,这并不易于理解。先让我们动手修改一个按钮的样式吧,这样更有成就感。
一个蓝色的按钮
新建一个UWP的项目,然后在 MainPage
中新建一个\
如你所见,生成了一个带有默认样式的按钮。对于我们的软件来说,大多数情况下灰色的按钮都不符合我们的主题颜色。假设我们现在以蓝色为主色调,这个按钮也要变成蓝色才行。
修改按钮的背景色,我们可以用Background
属性。这个属性大家应该不陌生,在之前的课程中,尽管没有刻意介绍,但我们都是使用过的。
我们将Background
的属性值设置为Blue
。如你所见,现在按钮的背景色变成蓝色了。很简单,对吗?
在这种蓝色下,黑色的文本就不太好看,我们可以尝试用白色来凸显文本的内容。
设置文本的颜色,我们一般用Foreground
属性。这和CSS中的color
属性不同。Foreground
,中文称之为前景色,乍看之下与文本似乎并没有什么关系,但我可以很明确地告诉你,在实际开发中,你可以将Foreground
视作文本颜色的代指,因为绝大多数情况下Foreground
都是文本颜色。
我们将Foreground
设置为White
,OK,这样一个有着自己颜色的,被我们定制过的按钮就出现了。
这是我们修改了默认样式的按钮:
1 | <Button Background="Black" |
但就是这普通的样式修改,也有很多值得说道的地方。
样式的复用
让我来举一个情景吧。
你有一款以蓝色为主题色的软件,那么所有的主要功能按钮,比如说确认按钮,添加按钮等,都是蓝色的,而且它们都有着同样的字体,同样的字号,这很正常,对吧。这些按钮有的在主界面,有的在侧边栏,有的在对话框的底部。
很显然,这些按钮都经历了样式修改。问题在于,修改的方式是怎样的?
我们当然可以直接写在控件里面,但是这意味着每一个按钮你都要写一次,记住每一个样式的具体属性值,你比如说,字体是微软雅黑,字号是14,背景色不是普通的Blue
,而是一个以Hash表示的16进制颜色代码。说记,按钮一多肯定记不住,那你就要来回且页面,找一个原始控件作为参照。而当你写完了所有的按钮,最后发现字号小了,需要再调整,你又要回过头一个个改……
不用试,光想想就知道这简直是噩梦。
对应这种情景,XAML也有自己的解决办法。
其实你只要把写C#的经验套用过来就可以。对于C#代码,我们会把重复的逻辑提取出来作为一个函数加以引用。这样不光节省时间,而且对于代码的健壮性也是大有好处。
同样,对于重复的样式我们也可以提取出来,命个名,然后在控件中直接引用即可。
让我们来看看如何提取公用样式以及它的语法是怎样的。
以我们之前写的蓝色按钮为例。在编写公用样式的时候,我们可以写成下面这个样子:
1 | <Style TargetType="Button" x:Key="BlueButton"> |
这里我们还是拿CSS来比较,如果你要写一个按钮的样式,你可以写成下面这个样子:
1 | button.blue-button{ |
即便你不写前面的button
,只要写类名或者ID名即可,CSS从这一点上来说要自由很多,你写了类,不管你绑定到什么元素上面都可以。
但是对于XAML来说就不可以了。我们之前讲过,XAML本质上就是类,类和属性的关系不用我再多说。两个没有继承关系的类,即便其中某一个属性名和属性类型相同,他俩也是不可互相赋值的。也就是说,我们在提到Background的时候必须要说清楚,是谁的Background。
这也就是XAML在写公共样式的时候必须声明TargetType
的原因。如果你想要让这个样式跨控件应用,比如说又能被Button引用,又能被TextBlock引用,那么TargetType
就要写这两个控件的基类,我们可以写FrameworkElement
,当然你还可以在往前追溯,但那没有意义。因为当你的TargetType确定后,Style里的属性范围也就确定了,再往前追溯那就不是最大公约数了,你想要的属性也可能消失了。
当我们敲定了TargetType
之后,我们要给这个公共样式命名,这样才能去引用,这和我们给控件起名是一样的道理。
之后,我们在Style中写Setter
,一个Setter表示一个属性,通过Property
标识属性名,通过Value
标识属性值。
这样一个个把你想要的属性写下来,就算完成了一个公共样式的创建。
从这里可以看出,在样式的编写上,XAML其实比CSS要繁琐一些。如果你是从前端过来的,你可能有些不适应,但是我告诉你这是值得的,一切为了类型安全嘛。
接下来的问题就是,这个公共样式放在哪呢?
在\
1 | <Page.Resource> |
这时候,删除按钮中的样式属性,添加一个对公共样式的引用:
1 | <Button Style="{StaticResource BlueButton}" /> |
没问题,一切正常,这说明样式已经被应用到按钮上了。这样在新建多个按钮控件时,就可以通过引用的方式来设置样式,而且想要修改样式,只用修改公共样式的定义就可以了。这样无疑大大简化了我们的工作。
样式的继承
让我们丰富一下我们的应用情景。在一个应用之中,总会有一些危险操作,比如说删除,比如说退出。这些也都是按钮,它们和主要按钮在样式上的区别也仅仅只是颜色不同,比如我们用红色来标识危险按钮。
现在我们有两条路。
- 样式覆盖。
我们先引用主要按钮的样式,然后在它的后面重写Background
属性,这样后写的属性会把公共样式中的属性覆盖掉,我们就实现了保留按钮大部分外观的情况下,只改变颜色的目的。
- 写新的公共样式
如果你之前认真听我讲了的话,我相信你不会选择第一种方式,因为这样确实弊端颇多。那么摆在面前的就是另外一条路了,写一个新的RedButton
的公共样式。
但是这样感觉很亏。
为什么呢?
因为除了背景色,这俩按钮的其它属性都是一样的,它们有着相同的字体,相同的字号,相同的文本颜色,甚至其它更多的样式属性。仅仅为了改一个背景色,就要把所有的属性再写一遍,这实在太浪费了。
还记得之前我们为什么要写公共样式吗?就是为了把重复的样式提取出来。
那么已经是公共样式的样式还能再提取吗?换句话说,控件可以引用样式,那么样式本身可以引用别的样式吗?
可以。
怎么做?
通过继承。
既然我们的两个按钮除了背景色,其余属性全部相同,那么我们就把所有相同的属性提取出来,做一个单独的样式:
1 | <Style TargetType="Button" x:Key="BasicButton"> |
:::tip
这里为了简单起见,只写了Foreground
一种属性,事实上还可以包括字体、字号等其它自定义属性。
:::
之后,我们通过BasedOn
属性来让我们的两个按钮样式继承BasicButton
样式。
1 | <Style TargetType="Button" BasedOn="{StaticResource BasicButton}" x:Key="BlueButton"> |
现在尝试新建两个按钮,然后分别引用 BlueButton
和 RedButton
看看效果吧!
小结
创建公用样式目的何在?
相信通过刚才的举例你已经很清楚了。公用样式存在的目的主要有两个:
- 提高代码可维护性,着重体现在代码修改上。
- 增强UI的一致性。
但是我们这样创建的Style是非常基础的,之前的例子,看上去很简单,但实际上,通过这种方式定制的样式只是表面功夫。
我们知道,按钮的外观是会随着状态变化而变化的,在普通状态下是一个样,在鼠标滑过的时候是一个样,在点击的时候又是一个样,甚至在按钮禁用的时候还有一种样式。普普通通地设置一个背景色,一个前景色,只会改变按钮在普通状态下的外观,对于其它状态的样式并没有任何帮助。
如何更改控件在不同状态下的样式,这涉及到控件模板,但这不是我们这节课的内容。这里只是为了让你了解,这种普通样式的局限性。
但你要说它就无用武之地了吗?也不尽然。
事实上还有许多无状态的控件。比如TextBlock
,比如ProgressRing
等等。在你以后学习自定义控件时,这种样式也可以帮到你。
这是后话了,只有在自己开发的时候你才会有更深刻的理解。