【跟Z学开发】1.8 仿造一个标题栏(4)

zcxsythenew UID.911687
2017-08-05 发表

本帖最后由 zcxsythenew 于 2017-8-5 22:54 编辑

专题一 开发一个媒体播放应用
课题8 仿造一个标题栏(4)

语言:XAML、C#*选学

本节包含:
资源字典的XAML用法
主题资源字典(包括:丙烯酸Acrylic效果)
合并资源字典

本节内容较难,建议先看完本区置顶的UWP视频的第1至80集,然后再浏览此教程。
提供源代码与安装包下载。地址:
***链接停止解析***
在文件夹1.8内。

[backcolor=red]请从电脑的网页版浏览此教程。[/backcolor][page]专题一 开发一个媒体播放应用
课题8 仿造一个标题栏(4)

将Style放入资源字典

如果你还没有忘记的话,上节课我们写过这样的Button:
[mw_shl_code=xml,true] <Button x:Name="PlayButton" Grid.Column="1" Height="48">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Content" Value=""/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="FontFamily" Value="Segoe MDL2 Assets"/>
<Setter Property="Width" Value="48"/>
</Style>
</Button.Style>
</Button>[/mw_shl_code]
这里的Style太长,我们可以把它放在Resources里面。
有很多的控件支持Resources。例如,Button控件就支持Resources。
[mw_shl_code=xml,true] <Button x:Name="PlayButton" Grid.Column="1" Height="48">
<Button.Resources>
<Style TargetType="Button">
<Setter Property="Content" Value=""/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="FontFamily" Value="Segoe MDL2 Assets"/>
<Setter Property="Width" Value="48"/>
</Style>
</Button.Resources>
</Button>[/mw_shl_code]
放在Resources里面的东西大都要指定x:Key,以便使用。
[mw_shl_code=xml,true] <Button x:Name="PlayButton" Grid.Column="1" Height="48">
<Button.Resources>
<Style x:Key="PlayButtonStyle" TargetType="Button">
<!--......-->
</Style>
</Button.Resources>
</Button>[/mw_shl_code]
然后,在Button里面,就可以用StaticResource标记扩展来用这个Style了。
[mw_shl_code=xml,true]<Button x:Name="PlayButton" Grid.Column="1" Height="48" Style="{StaticResource PlayButtonStyle}">
<!--......-->
</Button>
[/mw_shl_code]
但是,你肯定觉得不对劲。怎么用了Resources,代码更繁琐了呢?!
我们可以把这个Style放在其它的Resources里面。但要注意的是,这个Style的可用性取决于你放的位置。比如,我们刚刚把Style放在这个按钮的资源字典里面,那么这个Style只有这个按钮可用。如果我们放到Grid里面,只有这个Grid和它里面的Children可用。如果放到Page里面,只有这个Page和它的Content可用。
以上仅仅局限于MainPage.xaml。其实,还可以放到App.xaml里面,这样整个App都可用。
[mw_shl_code=xml,true]<Application
x:Class="MusicSampleforWfun.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MusicSampleforWfun"
RequestedTheme="Light">
<Application.Resources>
<Style x:Key="PlayButtonStyle" TargetType="Button">
<Setter Property="Content" Value=""/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="FontFamily" Value="Segoe MDL2 Assets"/>
<Setter Property="Width" Value="48"/>
</Style>
</Application.Resources>
</Application>[/mw_shl_code]
如果你看过置顶的UWP视频,你应该对XAML的特点(简化代码)有所了解。以上的代码和以下代码目前是等价的:
[mw_shl_code=xml,true]<Application
x:Class="MusicSampleforWfun.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MusicSampleforWfun"
RequestedTheme="Light">
<Application.Resources>
<ResourceDictionary>
<Style x:Key="PlayButtonStyle" TargetType="Button">
<Setter Property="Content" Value=""/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="FontFamily" Value="Segoe MDL2 Assets"/>
<Setter Property="Width" Value="48"/>
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>[/mw_shl_code]
这里的代码和上面的没有什么差别,只不过是加上了ResourceDictionary。但是,很快我们就只能用后一种了,因为我们马上就要讲到主题资源字典。[page]专题一 开发一个媒体播放应用
课题8 仿造一个标题栏(4)

利用资源字典添加丙烯酸(Acrylic)效果*选学

丙烯酸这个正规中文翻译好不习惯……我还是用回音译“亚克力”吧……

除了Style之外,Brush等几乎任何东西都能放进ResourceDicrtionary中。AcrylicBrush是Brush中的一种。

我们在ResourcesDictionary里面,加上:
[mw_shl_code=xml,true] <AcrylicBrush x:Key="MyBackgroundAcrylicBrush"
BackgroundSource="HostBackdrop"
FallbackColor="Black"
TintColor="Black"
TintOpacity="0"/>[/mw_shl_code]
如果你在这个时候尝试运行这个程序,果断报错。提示:
[mw_shl_code=vbnet,true]'XamlCompiler error WMC0151: Type 'Windows.UI.Xaml.Media.AcrylicBrush' is defined under contract 'Windows.Foundation.UniversalApiContract' version '5.0.0.0', but the contract version for the targeted min version is '4.0.0.0'![/mw_shl_code]
这是怎么回事呢?在我们的“前言和准备工作”里边,提到目标版本选择Insider Preview,但最低版本不要选择Insider Preview,否则在手机上因为版本太低无法运行。(万恶的feature2通道。)
那么怎么解决呢?稍后,我们会用到“合并资源字典”。现在我们暂时忽略这个错误,回到亚克力效果中。

FallbackColor表示当亚克力效果不可用(窗口未处于激活状态)的时候,用何种颜色代替。TintColor表示覆盖在亚克力效果上的颜色,TintOpacity表示该颜色的不透明度(0表示完全透明,1表示不透明)。BackgroundSource有两个选项:Backdrop表示应用内的背景(类似Windows地图),HostBackdrop表示应用外的背景(类似Windows计算器)。

在MainPage.xaml中,将{ThemeResource ApplicationPageBackgroundThemeBrush}替换为{StaticResource MyBackgroundAcrylicBrush}即可把背景换成亚克力效果。

但是,Windows是史上最个性化的系统(改编自Windows Phone 8.1广告词),Windows允许用户指定暗色、亮色模式和主题色。那么如何才能让亚克力效果的颜色(TintColor和FallbackColor)符合用户的系统设置呢?

利用微软预置的资源

首先,如果你没有在App.xaml里面把RequestedTheme="Light"去掉,现在把它去掉。
微软已经写好了很多个Style(这些Style里面包括我们下节课要学的Template)。

在输入ThemeResource之后,按空格键,稍等片刻,可以看到已经内置的其它Style。

***附件停止解析***

注:输入StaticResource之后,你也可以找到它们。但是ThemeResource和StaticResource有所不同。请参阅:***链接停止解析***

聪明的你可能已经想到要用哪一种Style用来做标题栏的按钮了。(想不到?一个个试啊!)

*以下内容选学
当目标版本选择了Insider Preview的时候,可能无法弹出上面的框。你可以再开启一个Visual Studio,并新建一个以15063为目标版本的Windows通用应用项目,专门用来看上面的这个框。
*以上内容选学

使用主题资源字典*选学

但是,光用到内置的ThemeResource好像不太“自由”(你知道某国最喜欢谈论自由了)。那么,有什么更加自由的方法呢?
答案是肯定的。内置的ThemeResource没有的东西,我们可以自己做一个出来。
[mw_shl_code=xml,true]<ResourceDictionary>
<!--...原有的内容放在此处...-->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Dark">

</ResourceDictionary>
<ResourceDictionary x:Name="Light">

</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>[/mw_shl_code]
如上面的代码所示,用到了ResourceDictionary.ThemeDictionaries。
其中,x:Name的值并不能任意选,必须是“Dark”、“Light”、“Default”(统一Dark和Light)、“HighContrast”(高对比度)之一。(可能还有规定的其它名称,但是我没有找到详细的列表。)我只用到Dark和Light,至于要不要高对比度,由你来决定了。
把AcrylicBrush移动到Dark里面,然后复制一份,放到Light里面。
这时整个App.xaml看起来像这样:

***附件停止解析***

要注意的是,Dark和Light这两个ResourceDictionary里面的项目应该是相同的(包括x:Name),只有一小部分作了修改。那么要修改什么呢?你肯定很清楚了。(肯定是修改颜色啊!)
最后,在MainPage.xaml里面,把{StaticResource MyBackgroundAcrylicBrush}改为{ThemeResource MyBackgroundAcrylicBrush}。

使用主题资源字典*必修(包括选学的人)

我们知道,MainPage.xaml里面的主Grid的默认背景是{ThemeResource ApplicationPageBackgroundThemeBrush}。那么,如果要自己做这个效果,应该怎么做呢?
在<ResourceDictionary>里面,加上:
[mw_shl_code=xml,true] <ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Dark">

</ResourceDictionary>
<ResourceDictionary x:Name="Light">

</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>[/mw_shl_code]

分别在这两个ResourceDictionary里面放一个SolidColorBrush。注意x:Name必须是相同的。
[mw_shl_code=xml,true] <ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Name="Dark">
<SolidColorBrush x:Key="MyBackgroundAcrylicBrush" Color="Black"/>
</ResourceDictionary>
<ResourceDictionary x:Name="Light">
<SolidColorBrush x:Key="MyBackgroundAcrylicBrush" Color="White"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>[/mw_shl_code]
最后,在MainPage.xaml.cs里面,把{ThemeResource ApplicationPageBackgroundThemeBrush}改为{ThemeResource MyBackgroundAcrylicBrush}。[page]专题一 开发一个媒体播放应用
课题8 仿造一个标题栏(4)

合并资源字典*选学

如果你一丝不苟地完成了必修和选学部分,你的代码应该是这样的:

***附件停止解析***

这里的代码有三个错误:
1. 设置了两次ThemeDictionaries
2. 纯色笔刷与亚克力笔刷有相同的x:Key
3. 10.0.15063.0版本不支持AcrylicBrush

对于第1个问题,通常的解决方案是:

***附件停止解析***

对于第2个问题,则是我人为制造出来的,是为解决第3个问题而服务的。那么,第3个问题怎么解决呢?这里要用到合并资源字典的知识。
首先,要理解合并资源字典中包含的x:Key是可以相同的。其次,相同的x:Key的覆盖顺序是:后添加的可以覆盖先添加的。在同一个xaml文档中,行号大的覆盖行号小的。
那么,自然就会想到,添加两个Windows运行时组件,一个的目标版本和最低版本都是Insider Preview,另一个的目标版本和最低版本都是15063。然后,分别把AcrylicBrush和SolidColorBrush放进这两个Windows运行时组件中。在电脑上编译运行的时候,两个组件都可以运行,则用亚克力效果覆盖纯色效果;在手机上运行的时候,最低版本为Insider Preview的组件无法运行,则使用纯色效果。

这个解决方案似乎不错(官网并没有针对XAML代码的版本自适应处理,如果专业开发者在场,欢迎提出你的解决方案),那么我们就开始吧。

第1步 添加两个Windows运行时组件,版本选择如上所述。然后,在主项目中添加这两个项目的引用。
(如果你忘记了怎么做,请复习课时1.2)
第2步 这时Visual Studio会自动打开两个Class1.cs文件。把它关闭,然后分别往这两个项目中添加一个资源字典。

***附件停止解析***

最后解决方案资源管理器看起来应该像这样(两个资源字典文件不一定同名,但要知道设置Source的时候会用到):

***附件停止解析***

第3步 把代码移过去。现在,两个资源字典的代码看起来像这样:

***附件停止解析***
图:高版本组件的资源字典

***附件停止解析***
图:低版本组件的资源字典

注意两个文件中的四个Brush的x:Key是相同的。ThemeDictionaries中相同的x:Key用于ThemeResource标记扩展,而两个文件中相同的x:Key用来使一个覆盖另一个。

第4步 在App.xaml中添加对低版本资源字典的引用。现在App.xaml的代码看起来像这样:

***附件停止解析***

第5步 在App.xaml.cs中添加对高版本资源字典的引用。怎么做?请往下看。

针对不同的系统版本号编写自适应代码*选学

所谓“自适应代码”,其实就是判断API是否存在。不同的版本号,其实就是所含的API不一样。一般而言,高版本的API会包含低版本的API所包含的全部内容,以及其它的一些新内容。而这些“新内容”就是需要判断的东西。
AcrylicBrush是秋季创意者更新才有的内容,因此,低版本的系统(包括最高版本的手机系统)是不能用AcrylicBrush的。(如果要用的话,可能需要自己写一个了。)AcrylicBrush是一个类,如果把它的命名空间写出来,是:
[mw_shl_code=vbnet,true]' Windows.UI.Xaml.Media.AcrylicBrush[/mw_shl_code]
我们就要判断上面这个东西是否存在。如果不存在,则是低版本,不引用高版本的资源字典;如果存在,则是高版本,引用高版本的资源字典。
之前提到过相同的x:Key的覆盖问题,后添加的可以覆盖先添加的。写在App.xaml文件里的内容,是在初始化这个App的时候就完成的。写在App.xaml.cs里的内容,如果不是在构造函数public App()里面,一般而言,都是在xaml文件处理完成以后再进行的。所以,把添加高版本资源字典的代码放进cs文档,可以使亚克力笔刷覆盖纯色笔刷。那么,我们就把相关的代码放在OnLaunched函数内。

判断亚克力笔刷是否存在的代码如下:
if(ApiInformation.IsTypePresent("Windows.UI.Xaml.Media.AcrylicBrush"))

往MergedDictionaries添加内容的代码如下:
this.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("ms-appx:///ComponentForInsiders/AcrylicBrushDictionary.xaml") });

还记得如何处理代码,使其在“启动”时才执行吗?可以在预启动的时候执行这些代码吗?
除了覆盖以外,是否有其它的解决方案呢?

合并资源字典*必修,选学的同学可跳过

请参见本版块置顶的UWP视频的第28集,我就偷个懒不写了。[page]下节预告

本系列教程定位是中级教程,如你所见,1.1到1.3都是UWP视频没有的内容,1.5开始回归,到1.8,与置顶的UWP视频又有些距离了。写这节课的时候,我自己都感觉有点力不从心(赶紧发两篇零基础文章以免自己不在状态)。然而我还是要继续下去的。到1.9,就开Template,这是在官方文档和网上的博客比较少涉及到的内容。

这个Template不简单。首先,你要看一看置顶的UWP视频第1至80集,并了解VisualState和Storyboard的用法。(你会发现VisualState还会有其它的用途,不仅是MinWindowWidth。)下节课见。

[backcolor=red]请投票选择“打卡签到”,谢谢。[/backcolor](注意顺序,这Discuz!的bug也不是一天两天了,我也很无语)

敬告:
为防止不可控的内容风险,本站已关闭新用户注册,新贴的发表及评论;
你现在看到的内容只是互联网用户曾经发表的言论快照,仅用于老用户留存纪念,且仅与科技行业相关,全部内容不代表本站观点及立场;
本站重新开放前已针对包括用户隐私、版权保护、信息安全、国家政策在内的各种互联网法律法规要求,执行了隐患内容的自查、屏蔽和删除;
本站目前所属个人主体,未有任何盈利安排与计划,且与原WFUN.COM所属公司不存在任何关联关系;
如果本帖内容或者相关资源侵犯到您的合法权益,或者您认为存在问题,那么请您务必点此举报或投诉!
全部回复:
Mr****in UID.2850453
2017-08-06 使用 Lumia 950 XL 回复

没人??

fivefish2016 UID.2856140
2017-11-05 使用 Lumia 950 XL 回复

冒泡。很专业

136****163 UID.1297611
2018-01-18 回复

弃坑了??等更新啊

artfly08 UID.2900999
2018-06-26 使用 Lumia 950 XL 回复

开发者真辛苦

artfly08 UID.2900999
2018-07-14 回复

支持

artfly08 UID.2900999
2018-07-14 回复

支持

本站使用Golang构建,点击此处申请开源鄂ICP备18029942号-4联系站长投诉/举报