云乡

云之幻的个人博客

0%

UWP 小书 - 样式的作用域以及资源

本章涉及知识点:

  • 样式的作用域
  • 资源是什么
  • 主题的切换

视频地址:BiliBili

前言

上节课,我们讲述了基础的样式修改,以及公共样式的建立。

在上节课里,我要求大家将建立的 Style 放在 <Page.Resources> 标签中,为什么要放在这个标签里,我并未详加阐述。本节课,我们就要从这里入手,带你了解样式的作用域。

样式的作用域

样式,说穿了就是部分属性的集合。这个集合我们称之为样式。

写在控件内的,比如在 <Button> 标签内写 FontFamily 属性,这种即为行内样式。它只更改所属控件的样式。我们说它的作用域仅限于所属控件本身。

而如我们上节课做的,将样式抽离出来,放在 <Page.Resources> 中,所有引用该样式的控件都能应用样式。我们说它的作用域限于当前页面内的所有引用控件。

引用控件好说,为什么作用域仅限于当前页面呢?

注意我们放样式的地方:

Page.Resources

页面的资源。

这里你就应该明白了。将样式作为资源放到当前页面的资源列表中,可不就是只有当前页面才能引用吗?

当你新建了 B 页面,再想引用 A 页面定义的资源,那就引用不到了。

这就出现问题了。

如果我有一个按钮的样式,这个样式我打算应用到所有按钮上,是跨页面的。难道我每新建一个页面就要再写一次按钮样式吗?

不必。

那怎么办?

扩大作用域

应用是一个框,框里面有许多的页面,按照功能进行切换。那么页面的上一个层级是什么?

应用。

换句话说,我们只要将样式定义在应用这个层级上,那么凡是该应用下的页面都可以获取到,这一点是毫无疑问的。

问题是,怎么定义在应用层级上呢?


新建应用的时候,VS 提供了两个 xaml 文件,一个叫做 MainPage.xaml,另一个叫 App.xaml。MainPage 是我们的主页,而 App 则代表了我们的应用。

让我们打开 App.xaml。

现在的 App.xaml 中除了命名空间别无一物。不要紧,就像我们在 Page 中做的那样,先新建一个 Resources 标签,然后将我们 Page 中的 Style 移过来即可:

1
2
3
4
5
6
<Application.Resources>
<Style x:Key="BasicButton" TargetType="Button">
<Setter Property="FontSize" Value="15"/>
<!-- 输入其它样式属性 -->
</Style>
</Application.Resources>

现在试一试,不管在哪个页面,所有的 Button 控件现在都可以引用这个样式了。

这就是我们所谈的作用域。

我们可以发现,样式的作用域很简单,层级也很少。

1
控件 -- 页面 -- 应用

就这么简单。

谈谈资源

不知你是否有留意,我们存放样式的标签为何叫 Resources。

Resources 是什么?是资源。

那么什么是资源呢?

大而化之,资源表示应用内的一切可引用的数据,比如图片、数据模板、样式文件、字体、颜色等等。

这些资源不是都放在 Resources 标签内的。比如 Assets 文件夹,这个文件夹存在的目的是为了放静态文件资源,比如图片,新建应用时,应用的 Logo 就放在这个文件夹里面。再比如我们常常看到一个应用,支持多语言,这些文本,也是资源,并不放在 Resources 标签中,这个我们后面也会讲到。

那么放在 Resources 标签里的有什么呢?

常见的,比如我们的样式,还有 DataTemplate,我们称其为数据模板,这个后面会讲到。还有 ControlTemplate,控件的模板,这个我们下节课会说明。

总的来说,资源是多种多样的。而且资源有一个特点,那就是事先就会被定义。

也就是说,在你引用这个资源之前,这个资源必须存在。

所以我们会用 StaticResource 作为关键词去引用我们定义好的资源。

但如果你之前有过预习,会发现除了 StaticResource 之外,还有一种资源的引用方式,叫做 ThemeResource,这又是什么呢?

主题资源

谈到 ThemeResource,就不得不提一下 UWP 的主题系统了。

从 Windows 10 的主题设置就可以看出来,Win10 主要有两种主题,LightDark。除此之外还有一种高对比度主题,这里我们略过不表。

我们可以通俗地说一个是白昼主题,一个是黑夜主题。这里的主题不光会影响到系统,也会影响 UWP 应用。

不客气地说,作为一个 UWP 应用,适配这两种主题不是一个可选项,而是必选项。

为什么这么说?我们来看一个例子:

我显式设置 MainPage 的 Background 为白色,新建了一个 TextBlock,只设置内容,不设置颜色,则其颜色为默认。

1c1eeef3-9359-4c4b-bcdf-44131a5aa75f.png

在 Light 主题下,没问题,工作得很正常。

f13e7d64-2736-4be1-8ec1-343b690db7a8.png

但是在 Dark 主题下,凉凉了,什么都看不见了。

是 TextBlock 消失了吗?

不是。

是 TextBlock 的前景色变成白色了,也就相当于隐形了。

为什么 TextBlock 的颜色会改变呢?

因为默认情况下,TextBlock 的前景色是使用 ThemeResource 作为关键词的。

这个时候,ThemeResource 的作用也就呼之欲出了。那就是根据主题的不同,切换不同的资源

如何建立主题资源

前面我们说过,做一个 UWP 应用,就必须要对两种主题进行适配。那么我们如何建立两种主题所需要的资源呢?

这里我们就来简单地做一个按钮。这个按钮有一个特点,在 Light 主题中,它是红色的,在 Dark 主题中,是蓝色的。

如何做?

打开 App.xaml,将之前写的 Style 代码删除,替换为下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonBackground" Color="Red"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="ButtonBackground" Color="Blue"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>

我们来解释一下这段代码。

在 Resource 标签中,我们首先建立一个资源字典 (Resource Dictionary) 。Resource 标签像个书架,那这个资源字典就是一本书。

在资源字典里面新建一个附属标签,叫主题字典 (Theme Dictionaries) 。这个主题字典就是我们需要的东西,这里存放和主题相关的资源。

有了容器还不够,我们还需要分类,我要标明哪些资源是 Light 主题的,哪些是 Dark 主题的。

于是我们又建立了两个资源字典,分别给了一个键,标识其是 Light 还是 Dark。

到了这一步,我们已经完成了准备工作,建了两个容器,分别是 Light 和 Dark,软件会识别主题名字,并针对性地应用资源。

这就意味着一件事,资源必须同名。

我们最终在控件里引用的时候,只是引用一个名字,那就要求这个名字在 Light 中有,在 Dark 中也有,所以,资源必须同名。

我们新建了两个纯色颜色笔刷的资源。这个颜色笔刷你可能是第一次接触到,不要慌,后面我们还会讲,毕竟颜色在设计里是占了很大比重的。

这里我们把它当作一个普通的样式来看待,命名,设置颜色,很简单,不要纠结。

至此,我们就完成了针对不同主题的资源定制。

回到我们的 MainPage.xaml。

定义一个 <Button> 如下:

1
<Button Background="{ThemeResource ButtonBackground}" Content="Click Me"/>

注意这里的 Background 我们使用了 ThemeResource 进行定义,想要让资源随主题而变化,则必须使用 ThemeResource

现在。运行应用,在设置里切换主题看看吧,一切工作正常?恭喜你,完成了本节课的全部内容。

小结

这节课,讲了一些资源定制的事情。包括作用域以及主题资源。

值得一提的是,在课程教授中,我们直接将样式写在了标签里面。看到这句话你可能不太明白,样式不写在标签里面还能写在哪?

在实际的应用开发中,我们会创建很多的资源。这些资源如果全部写在 App.xaml 标签中,那么整个 App.xaml 会非常的臃肿。这个时候,我们往往是通过创建资源字典文件的方式,将这些资源放在不同的文件之中,然后在 App.xaml 里通过路径去对其进行引用。

但原理不变。

如何创建资源字典文件,如何通过路径去引用。这里面也有门道,但你可以自己去查资料。这里暂且不表。

思考

  1. StaicResource 只引用一种资源,ThemeResource 则可以对主题资源进行选择。那么在 ThemeResource 中定义的资源可以被 StaticResource 引用吗?如果可以,那么引用的是哪个资源?主题变化时,资源会有什么变化?
  2. 原本没有定义为主题资源的资源(比如上节课我们建立的 Style),却被 ThemeResource 引用,会是什么结果?