云乡

云之幻的个人博客

0%

UWP 小书 - 简单的样式修改

本章涉及知识点:

  • 样式的添加
  • 样式的复用
  • 样式的继承

视频地址:https://www.bilibili.com/video/av53560120/

前言

前面的课程中,我们学习了一些简单的布局知识。通过布局,可以有效地组织我们呈现的信息,让软件界面井井有条。

但倘若将软件与人作比,良好的布局就像是一副整齐的骨骼,没多少人会觉得骨头架子很好看。若要美观,当然还需要漂亮的衣裳。

在软件中,所谓 “漂亮的衣裳” 指的就是样式。这里说的样式,包括了界面的背景,文本的颜色、字体、字号,控件的外观等等设计层面的东西。

听上去很繁杂,事实上也的确是。不过归根结底都会落到一个点,那就是资源。本章所谈的样式,不过是更高一级对于资源的整合罢了。

不过我们却并不从资源出发,这并不易于理解。先让我们动手修改一个按钮的样式吧,这样更有成就感。

一个蓝色的按钮

新建一个 UWP 的项目,然后在 MainPage 中新建一个 <Button>,里面随便写一点内容。

如你所见,生成了一个带有默认样式的按钮。对于我们的软件来说,大多数情况下灰色的按钮都不符合我们的主题颜色。假设我们现在以蓝色为主色调,这个按钮也要变成蓝色才行。

修改按钮的背景色,我们可以用 Background 属性。这个属性大家应该不陌生,在之前的课程中,尽管没有刻意介绍,但我们都是使用过的。

我们将 Background 的属性值设置为 Blue。如你所见,现在按钮的背景色变成蓝色了。很简单,对吗?

在这种蓝色下,黑色的文本就不太好看,我们可以尝试用白色来凸显文本的内容。

设置文本的颜色,我们一般用 Foreground 属性。这和 CSS 中的 color 属性不同。Foreground,中文称之为前景色,乍看之下与文本似乎并没有什么关系,但我可以很明确地告诉你,在实际开发中,你可以将 Foreground 视作文本颜色的代指,因为绝大多数情况下 Foreground 都是文本颜色。

我们将 Foreground 设置为 White,OK,这样一个有着自己颜色的,被我们定制过的按钮就出现了。

这是我们修改了默认样式的按钮:

1
2
<Button Background="Black"
Foreground="White" />

但就是这普通的样式修改,也有很多值得说道的地方。

样式的复用

让我来举一个情景吧。

你有一款以蓝色为主题色的软件,那么所有的主要功能按钮,比如说确认按钮,添加按钮等,都是蓝色的,而且它们都有着同样的字体,同样的字号,这很正常,对吧。这些按钮有的在主界面,有的在侧边栏,有的在对话框的底部。

很显然,这些按钮都经历了样式修改。问题在于,修改的方式是怎样的?

我们当然可以直接写在控件里面,但是这意味着每一个按钮你都要写一次,记住每一个样式的具体属性值,你比如说,字体是微软雅黑,字号是 14,背景色不是普通的 Blue,而是一个以 Hash 表示的 16 进制颜色代码。说记,按钮一多肯定记不住,那你就要来回且页面,找一个原始控件作为参照。而当你写完了所有的按钮,最后发现字号小了,需要再调整,你又要回过头一个个改……

不用试,光想想就知道这简直是噩梦。

对应这种情景,XAML 也有自己的解决办法。

其实你只要把写 C# 的经验套用过来就可以。对于 C# 代码,我们会把重复的逻辑提取出来作为一个函数加以引用。这样不光节省时间,而且对于代码的健壮性也是大有好处。

同样,对于重复的样式我们也可以提取出来,命个名,然后在控件中直接引用即可。

让我们来看看如何提取公用样式以及它的语法是怎样的。

以我们之前写的蓝色按钮为例。在编写公用样式的时候,我们可以写成下面这个样子:

1
2
3
4
<Style TargetType="Button" x:Key="BlueButton">
<Setter Property="Background" Value="Blue" />
<Setter Property="Foreground" Value="White" />
</Style>

这里我们还是拿 CSS 来比较,如果你要写一个按钮的样式,你可以写成下面这个样子:

1
2
3
4
button.blue-button{
background: blue;
color: white;
}

即便你不写前面的 button,只要写类名或者 ID 名即可,CSS 从这一点上来说要自由很多,你写了类,不管你绑定到什么元素上面都可以。

但是对于 XAML 来说就不可以了。我们之前讲过,XAML 本质上就是类,类和属性的关系不用我再多说。两个没有继承关系的类,即便其中某一个属性名和属性类型相同,他俩也是不可互相赋值的。也就是说,我们在提到 Background 的时候必须要说清楚,是谁的 Background。

这也就是 XAML 在写公共样式的时候必须声明 TargetType 的原因。如果你想要让这个样式跨控件应用,比如说又能被 Button 引用,又能被 TextBlock 引用,那么 TargetType 就要写这两个控件的基类,我们可以写 FrameworkElement,当然你还可以在往前追溯,但那没有意义。因为当你的 TargetType 确定后,Style 里的属性范围也就确定了,再往前追溯那就不是最大公约数了,你想要的属性也可能消失了。

当我们敲定了 TargetType 之后,我们要给这个公共样式命名,这样才能去引用,这和我们给控件起名是一样的道理。

之后,我们在 Style 中写 Setter,一个 Setter 表示一个属性,通过 Property 标识属性名,通过 Value 标识属性值。

这样一个个把你想要的属性写下来,就算完成了一个公共样式的创建。

从这里可以看出,在样式的编写上,XAML 其实比 CSS 要繁琐一些。如果你是从前端过来的,你可能有些不适应,但是我告诉你这是值得的,一切为了类型安全嘛。

接下来的问题就是,这个公共样式放在哪呢?

在 <Page> 内部,<Grid > 的上方,新建一个附加属性标签 < Page.Resource>,在这里面把我们的公共样式放进来。

1
2
3
4
5
6
<Page.Resource>
<Style TargetType="Button" x:Key="BlueButton">
<Setter Property="Background" Value="Blue" />
<Setter Property="Foreground" Value="White" />
</Style>
</Page.Resource>

这时候,删除按钮中的样式属性,添加一个对公共样式的引用:

1
<Button Style="{StaticResource BlueButton}" />

没问题,一切正常,这说明样式已经被应用到按钮上了。这样在新建多个按钮控件时,就可以通过引用的方式来设置样式,而且想要修改样式,只用修改公共样式的定义就可以了。这样无疑大大简化了我们的工作。

样式的继承

让我们丰富一下我们的应用情景。在一个应用之中,总会有一些危险操作,比如说删除,比如说退出。这些也都是按钮,它们和主要按钮在样式上的区别也仅仅只是颜色不同,比如我们用红色来标识危险按钮。

现在我们有两条路。

  1. 样式覆盖。

我们先引用主要按钮的样式,然后在它的后面重写 Background 属性,这样后写的属性会把公共样式中的属性覆盖掉,我们就实现了保留按钮大部分外观的情况下,只改变颜色的目的。

  1. 写新的公共样式

如果你之前认真听我讲了的话,我相信你不会选择第一种方式,因为这样确实弊端颇多。那么摆在面前的就是另外一条路了,写一个新的 RedButton 的公共样式。

但是这样感觉很亏。

为什么呢?

因为除了背景色,这俩按钮的其它属性都是一样的,它们有着相同的字体,相同的字号,相同的文本颜色,甚至其它更多的样式属性。仅仅为了改一个背景色,就要把所有的属性再写一遍,这实在太浪费了。

还记得之前我们为什么要写公共样式吗?就是为了把重复的样式提取出来。

那么已经是公共样式的样式还能再提取吗?换句话说,控件可以引用样式,那么样式本身可以引用别的样式吗?

可以。

怎么做?

通过继承。

既然我们的两个按钮除了背景色,其余属性全部相同,那么我们就把所有相同的属性提取出来,做一个单独的样式:

1
2
3
<Style TargetType="Button" x:Key="BasicButton">
<Setter Property="Foreground" Value="White" />
</Style>

这里为了简单起见,只写了 Foreground 一种属性,事实上还可以包括字体、字号等其它自定义属性。

之后,我们通过 BasedOn 属性来让我们的两个按钮样式继承 BasicButton 样式。

1
2
3
4
5
6
7
<Style TargetType="Button" BasedOn="{StaticResource BasicButton}" x:Key="BlueButton">
<Setter Property="Background" Value="Blue" />
</Style>

<Style TargetType="Button" BasedOn="{StaticResource BasicButton}" x:Key="RedButton">
<Setter Property="Background" Value="Red" />
</Style>

现在尝试新建两个按钮,然后分别引用 BlueButtonRedButton 看看效果吧!


小结

创建公用样式目的何在?

相信通过刚才的举例你已经很清楚了。公用样式存在的目的主要有两个:

  1. 提高代码可维护性,着重体现在代码修改上。
  2. 增强 UI 的一致性。

但是我们这样创建的 Style 是非常基础的,之前的例子,看上去很简单,但实际上,通过这种方式定制的样式只是表面功夫。

我们知道,按钮的外观是会随着状态变化而变化的,在普通状态下是一个样,在鼠标滑过的时候是一个样,在点击的时候又是一个样,甚至在按钮禁用的时候还有一种样式。普普通通地设置一个背景色,一个前景色,只会改变按钮在普通状态下的外观,对于其它状态的样式并没有任何帮助。

如何更改控件在不同状态下的样式,这涉及到控件模板,但这不是我们这节课的内容。这里只是为了让你了解,这种普通样式的局限性。

但你要说它就无用武之地了吗?也不尽然。

事实上还有许多无状态的控件。比如 TextBlock,比如 ProgressRing 等等。在你以后学习自定义控件时,这种样式也可以帮到你。

这是后话了,只有在自己开发的时候你才会有更深刻的理解。