博客园客户端(Universal App)开发随笔 -- 样式管理与夜间模式

以今天的眼光来看,一个好应用首先是要有好的用户体验。而好的用户体验最直观的就来自于用户界面。好的用户界面则需要好的设计,更需要好的实现。今天我们就向大家分享一下我们在使用Xaml实现界面设计上的一点心得。

样式管理

我们拿到的设计,大多是一张红线图,布满了距离,字号,色号,事无巨细的量化了我们的用户界面。如果我们就这样把各种属性照搬到上Xaml文件中,那看起来就非常不妙了,比如这样:

<TextBlock Text="首页"FontFamily =" Segoe WP "FontSize ="24"FontWeight =" Normal "TextTrimming =" CharacterEllipsis "TextWrapping =" Wrap "MaxLines ="2" Foreground="#FF297ACD " ></TextBlock>

一个看起来还好,但是通常我们的页面上不可能只有这一个控件。要是有上10个20个,那如果要修改其中一个属性可真是得看花了眼啊。

很幸运,我们在Universal应用的开发中可以把控件的相同属性归纳为资源(Resource),再在需要的时候应用到控件上就可以了。

有些很重要的单独的属性,以颜色为例,不同控件都会使用,却只有几个值,Universal应用可以把它单独抽象成一种 StaticResource 资源--Brush 。将它应用到控件的 Foreground 或者Background 之类的属性上时,就会像"brush(刷子)"一样把控件变为它的颜色。其中常用的SolidColorBrush 可以写成这样:

<SolidColorBrush x:Key=" PostTitleFont" Color="#FF297ACD"/>

将它放到资源字典或者页面的<Page.Resources> 中,就可以通过{StaticResource Brush的x:Key的值} 的形式来应用了。

而同一种控件的多个相同属性,我们可以把它们归纳成Style这种StaticResource资源,存在当前页面的 <Page.Resources> 中:

<Page.Resources>

<SolidColorBrush x:Key=" PostTitleFont" Color="#FF297ACD"/>

<Style x:Key="PostTitleFont" TargetType="TextBlock">

<Setter Property="FontFamily" Value="Segoe WP"/>

<Setter Property="FontSize" Value="24"/>

<Setter Property="FontWeight" Value="Normal"/>

<Setter Property="TextTrimming" Value="CharacterEllipsis"/>

<Setter Property="TextWrapping" Value="Wrap"/>

<Setter Property="MaxLines" Value="2"/>

<Setter Property="Foreground" Value="{StaticResourcePostTitleFont }"/>

</Style>

</Page.Resources>

Style 的 TargetType 属性指明了Style可以应用的元素,比如TargetType="TextBlock" 的Style是不能应用到Image 元素上的。而Style中包含的就是要应用的属性了,以 <Setter Property="属性名" Value="属性值"/> 这样的形式。

那么我们在当前页面的需要应用Sytle的地方,比如上面的情况,就只需要通过Style的x:Key 属性来应用:

<TextBlock Text="首页" Style="{StaticResource PivotTitleFont}"></TextBlock>

我们可以看到 Style="{StaticResource PivotTitleFont}" 中的 PivotTitleFont 就是 Style的 x:Key 属性,这样style就能够应用到 TextBlock 上了。

而且Style还能继承。比如我要一个 PostSubTitleFont, 只要字体比PostTitleFont 小一点。那我就可以这么写:

<Style x:Key="PostSubTitleFont" TargetType="TextBlock" BasedOn="{StaticResource PostTitleFont}">

<Setter Property="FontSize" Value="20"></Setter>

</Style>

这样一来如果我想改动它们的字体,只要在PostTitleFont中修改就可以了。是不是很方便?

当然我们通常也不止有一个页面,如果不想在每个页面中都把 Style 和 Brush 都贴一遍,就需要资源字典文件出场了。在我们的项目中添加一个资源字典文件:

再把我们的Style和 Brush都写在里面。然后在需要使用这些Style和 Brush的页面添加形如下面的代码把资源词典引用上就可以啦:

<Page.Resources>

<ResourceDictionary>

<ResourceDictionary.MergedDictionaries>

<ResourceDictionary Source="Dictionary.xaml" />

</ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

</Page.Resources>

当然,我们既然是Universal应用,能不能把Styl和 Brushe在Windows应用和Windows Phone应用间共享呢?答案是可以的。我们只要把资源文件放在.share项目下面,再在App.xaml中添加它的引用就可以:

<Application.Resources>

<ResourceDictionary>

<ResourceDictionary.MergedDictionaries>

<ResourceDictionary Source="Dictionary.xaml" />

</ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

</Application.Resources>

这样Dictionary.xaml中的Style和 Brush就在Windows应用和Windows Phone应用的所有页面都有效了。

等等,那Windows应用和Windows Phone应用间有所区别的Style和 Brush怎么办呢?

也有办法。只要在Windows项目和Windows Phone项目中各添加一个同名的资源词典文件比如DifferentDictionary.xaml,把不同的Style和 Brush写入,再在App.xaml中引用就能够实现了。

<Application.Resources>

<ResourceDictionary>

<ResourceDictionary.MergedDictionaries>

<ResourceDictionary Source="Dictionary.xaml" />

<ResourceDictionary Source="DifferentDictionary.xaml" />

</ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

</Application.Resources>

这样,我们通过Resource 和资源词典文件大大简化了用户界面设计的实现过程,是不是很方便呢?

夜间模式

说到阅读软件的用户体验,就不得不提到夜间模式了。下面就说说我们在Windows Phone应用上实现夜间模式的过程吧。

大家可能都在Windows Phone 设置的 开始屏幕+主题 中设置过背景和主题色,当我们修改了背景的 黑/白 后,几乎所有的系统应用的背景色和文字的颜色都立刻改变了,连重启应用都不用。

我们实现的夜间模式正是利用了这个功能。

在Windows Phone应用中,如果我们设置了当前 Page的 RequestedTheme 属性为Light 或者Dark的话就相当于我们在系统设置里修改了背景黑/白。比如通常情况下我们新建一个应用,默认的情况下都是黑底白字:

而如果我们把Page的 RequestedTheme 属性设为的话,可以看到在设计视图中应用变成了白底黑字:

那么我们通过settings页面的一个 ToggleSwitch控件实时切换RequestedTheme这一属性,就可以实时控制应用的各种颜色如背景色,文字颜色为Light或Dark对应的颜色。我们把Light设为日间模式,Dark设为夜间模式。简单一点的情况下可以把 ToggleSwitch 的Toggled事件响应方法设为如下:

private void ts_LightMode_Toggled(object sender, RoutedEventArgs e)

{

if (this.RequestedTheme == ElementTheme.Light)

{

this.RequestedTheme = ElementTheme.Dark;

}

else

{

this.RequestedTheme = ElementTheme.Light;

}

}

接下来如何设置日间模式/夜间模式对应的颜色呢?

在之前的样式管理一节中我们提到了通过{StaticResource PivotTitleFont } 的形式来应用 SolidColorBrush 资源,但是我们在资源 PostTitleFont 定义的时候只写了一个值,系统会自动帮我们转换么?事实上并没有那么简单。我们需要通过 ThemeResource 类型的资源预先设置好2种模式对应的颜色。应用了这种 ThemeResource 资源以后,这个控件就能够响应 RequestedTheme 属性的变化,即时切换显示对应模式的颜色。

那么怎样加入这种资源呢?我们可以打开我们的资源字典 Dictionary.xaml ,在里面加入这样一段代码:

<ResourceDictionary.ThemeDictionaries>

<ResourceDictionary x:Key="Light">

</ResourceDictionary>

<ResourceDictionary x:Key="Dark">

</ResourceDictionary>

</ResourceDictionary.ThemeDictionaries>

这里 x:Key 分别为Dark/ Light 的 ResourceDictionary 中,就是我们存放对应两种模式的资源的地方。比如我们希望页面背景在日间模式下是 蓝色,夜间模式下是深灰色,就可以这么写:

<ResourceDictionary.ThemeDictionaries>

<ResourceDictionary x:Key="Light">

<SolidColorBrush x:Key="myPageBackground" Color="Blue"></SolidColorBrush>

</ResourceDictionary>

<ResourceDictionary x:Key="Dark">

<SolidColorBrush x:Key="myPageBackground" Color="DarkGray"></SolidColorBrush>

</ResourceDictionary>

</ResourceDictionary.ThemeDictionaries>

在我们的page中就可以这样应用 ThemeResource 资源了:

<Page

x:Class="my_universal.CnblogsMainPage"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:local="using:my_universal"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

mc:Ignorable="d"

RequestedTheme="Light"

Background="{ThemeResourcemyPageBackground }">

是不是一点都不难呢?

另外我们还发现有两点值得注意:

一是我们可以覆盖系统的 ThemeResource 。我们可以选中任意一个 TextBlock 控件,打开属性栏,选择 Foreground 属性的画笔资源。

可以看到下方以ThemeBrush结尾的都是系统的 ThemeResource 资源,只要在我们的 x:Key 分别为Dark/ Light 的 ResourceDictionary 里加入和它们同名的资源,就能够覆盖它们了。

二是对于Flyout 类型的控件,应用到它上面的 ThemeResource 资源是相反的。就是说当RequestedTheme 属性为 Light 时,Flyout 类型的控件会应用 Dark ResourceDictionary 中的资源。反之亦然。

小结

今天就先向大家分享这些心得,欢迎大家继续关注和拍砖,让我们共同进步。

我们的已经发布的应用和代码可以在下面找到:

Windows Phone Store App link:

http://www.windowsphone.com/zh-cn/store/app/博客园-uap/500f08f0-5be8-4723-aff9-a397beee52fc

Windows Store App link:

http://apps.microsoft.com/windows/zh-cn/app/c76b99a0-9abd-4a4e-86f0-b29bfcc51059

GitHub open source link:

https://github.com/MS-UAP/cnblogs-UAP

MSDN Sample Code:

https://code.msdn.microsoft.com/CNBlogs-Client-Universal-9c9692d1

时间: 2024-09-30 15:03:40

博客园客户端(Universal App)开发随笔 -- 样式管理与夜间模式的相关文章

博客园客户端(Universal App)开发随笔 – App也需要物流前的打包

想起来一个笑话:唐僧一行千辛万苦来到佛祖面前,准备取经,佛祖轻声问了一句:汝等带U盘了吗?师徒4人立刻昏倒在地.好吧,扯远了,不过是想告诉大家准备工作一定要到位. Universal App 的打包发布其实与传统的 Windows Phone / Windows Store App 项目并无太大不同,但是鉴于总有人会在这些步骤中遇到问题,而且即使是发布过几个应用的开发者也不例外,所以对这个看起来比较繁琐的过程做一个小结还是必要的. 打包前的准备工作 1. 修改编译模式 在调试程序时,我们通常会选

博客园客户端(Universal App)开发随笔 - Setting Page的实现方法与经验

前言 几乎所有的移动 App 都会为用户提供一个设置页面(Setting Page 或 Preference Page),来满足大家对于一个 App 众口难调的需求.虽然有一种说法表示,最好的 App 不需要设置,一切默认呈现给用户的就是最好的选择.但是对于大多数开发人员来说,这样的境界不是可以简单达到的:而且对于部分“设置控”用户来说,没有设置页面怎么看都觉得少了些什么.所以对于大部分 App 来说,设置页面还是一个必备品. 本文将分别介绍 Windows 和 Windows Phone 的设

博客园客户端(Universal App)开发随笔 - UAP中的项目和目录组织

前言 本篇随笔主要是分享一下做一个Universal App的目录组织结构,说明什么样的结构才能适合一个团队开发,使开发成员之间的相互影响最小.这些组织好的项目和目录会成为一个公共的规约,大家都能知道什么东西应该放在哪里,或者在什么地方能找到什么功能.这对于避免重复写code或者最大限度地复用code来说是至关重要的. 这种工程结构和目录划分虽然没有什么理论基础,但是是经过我们很多项目的经验总结出来的,如果是个人开发者,也严重建议参考此组织结构,能够帮助你理清思路,做好功能设计和类设计. 项目组

博客园客户端(Universal App)开发随笔 – UAP 项目的发布

在费尽无数脑细胞开发出一款 App 并且通过测试,自我感觉良好的时候,我们一定希望我们的 App 能够尽快出现在商店的首页上,被人下载,使用,打分,评价,反馈,甚至是被无情地喷.但据本人经验,通常从项目打包好(打包的具体注意事项,请参见前文),到通过审核,其他用户在商店可以看到,往往都像新手第一次编译程序一样,一波三折,弹出不同的错误和警告,这个时候,只有耐下性子,一个一个问题都解决了,才能顺利通过审核.所以我觉得还是很有必要写点什么,讲一下 App 发布时的注意事项,省得大家多走弯路. 虽说开

博客园客户端(Universal App)开发随笔 - 应用漫游数据(roaming)

当用户在多个windows设备上使用应用时,我们希望用户的某些设置和数据可以在多个之间共享,比如我们新版本中收藏的文章.通常情况下要实现这种功能,我们可能需要自己弄个服务器或者使用onedrive等网站来存放文件,但是对于我们这种小型的应用有点过了,额外增加了代码和维护的工作.这种时候使用windows roaming就是一个比较方便的选择. 所谓的漫游数据,MSDN上是这样介绍的: Windows 8 可在用户设备之间自动传输某些应用数据.应用开发人员无需再执行费力的工作.漫游应用数据为最终用

博客园客户端(Universal App)开发随笔 -- 数据基础准备

在开始之前,我们先了解下博客园提供的接口: 博客: http://wcf.open.cnblogs.com/blog/help新闻: http://wcf.open.cnblogs.com/news/help 以博客园_48小时阅读排行为例,返回的Xml如下图(RSS,如果你用IE打开的话,会提示你订阅..). 博客园的大部分API返回的都是RSS(还提供分页!),如果只是做一个简单的RSS reader,可以直接用SyndicationClient,在RetrieveFeedAsync后会把X

博客园客户端(Universal App)开发随笔 -- App的精灵:自定义控件

前言 拿到一个App的需求后,对于前端工程师来说,第一步要干什么?做Navigation规划!第二步要干什么?做页面分解!页面分解如何做?首先要确定UI Element的容器,其次要抽象UI Element本身,也就是要做一堆自定义控件,最终组成整个页面.今天我们就说说自定义控件如何实现吧. 感性认识 在我们的博客园UAP的Windows Phone的版本中,一个最重要的自定义控件就是PostControl,它的样子如下图中红色矩形内所示. 这个控件在无数页面中都要用到,而且有几种变种.上面看到

博客园客户端(Universal App)开发随笔 -- 增量加载 (incremental loading)

在我们的应用(博客园UAP)中,加入了ListView上拉获取更多内容的功能(GridView也可以),这个功能是通过ISupportIncrementalLoading接口实现的,这是个从Windows 8就开始提供的接口(当然你可以通过ScrollViewer来实现这个功能,只不过稍微麻烦点,还要自己再封装..). 这个接口的定义十分简单: public interface ISupportIncrementalLoading { bool HasMoreItems { get; } IAs

博客园客户端(Universal App)开发随笔 - Hello, Universal

在移动和云成为微软的主题之后,Windows Phone 8.1 手机和Windows 8.1平板也渐渐成熟,吸引了越来越多的开发者.为了让开发者们高效的开发一款跨手机和平板的应用,微软顺势推出了Universal应用的概念. 今天我们想向大家分享怎样用C#和XAML语言开始开发一个跨Windows 8.1和 Windows Phone 8.1 的 Universal 应用. 准备 首先硬件方面我们的计算机要支持Hyper-V功能或者有一部运行Windows Phone 8.1 操作系统的手机.