微软动作真是快,本来想写 WP8.1RT 系列,结果刚整理了一点就出 Win10 UAP 了。不过还好 RT 到 Win10 的差别还不算太大。前两天参加了 Win10 开发极客秀,虽然没获奖,不过在韦恩卑鄙的帮助下顺利将澎湃新闻 WP8.1 版升级到了 Win10UAP ,使用了一些新的特性,最近争取有时间慢慢把一些东西总结一下。
今天先说一下如何在 Win10 UAP 中切换主题模式。
切换日间、夜间主题模式这个功能我从 WP8 就实现了,并封装成了一个库,用在我所有的 WP8 的 app 里。到了 WP8.1 因为系统主题样式都改了,又重写了一遍。还没来得及整理写 blog , Win10 的样式又改了 …… 吐槽不完啊简直。不过思路都是一样的,现在以 Win10 版本为例总结一下。
UAP的样式和以前的版本基本一样,都是一些类似css的东西,我们通过覆盖系统的style,就可以实现自己的主题样式。首先找到UAP的style的位置:
C:/Program Files (x86)/Windows Kits/10/Include/10.0.10069.0/winrt/xaml/design/themeresources.xaml
打开这个文件,可以看到里面存放的是系统默认的主题样式。
顺便说一下,WP8 和WP8.1 的默认主题样式也可以找到,位置不一样。
新建个UAP项目。因为我习惯用MVVM-Sidekick来做,所以以后都会基于这个框架来做,顺便给@韦恩卑鄙 做下广告^_^
这个例子就叫ThemeDemo。确定。
等待框架程序创建完成。
可见现在Win10 UAP的项目只有一个,与WP8.1时代分别为PC和手机新建项目的方式已经不同了。这样可以更方便。
用VS2015RC打开刚才找到的系统默认样式文件,如图:
这样看比较乱,把代码折叠一下:
这样看就清楚了,包括了Default、Light和高对比对三种主题样式,和各种style、字体、字体大小、各种Color和Brush、控件的style等等,现在我们需要提取出来进行修改。
在项目中添加一个名为CustomTheme的文件夹。
一般只需要对Dark和Light分别处理即可,比如我想Dark的背景色不是纯黑,Light的背景色不是纯白等等。
新建两个xaml文件,命名为ThemeResourcesDark.xaml和ThemeResourcesLight.xaml,根节点这样写:
然后把系统样式文件里有关Color和Brush的部分复制过来,Default对应Dark,Light对应Light。
控件的style另外建个文件CustomStyleResources.xaml,把控件的style复制过来,因为不同主题下控件只是背景色不同,margin、padding这些属性都是一致的。
我还添加了一套FlatUI的颜色资源,FlatUIColorsResources.xaml,里面存放了各种Flat风格的Color和Brush来方便使用。
打开App.xaml,添加以下代码:
控件的style和主题的style都引入进来了,顺便说一下,控件模板等东西不要往App.xaml里堆,多了显得太乱,应该都统一放到资源文件里进行管理。
现在运行程序,样子是默认的,还是白底黑字。因为 RequestedTheme ="Light" 。
现在我们修改个背景色看看。打开MainPage.xaml 可以看到以下代码:
也就是说,根Grid的背景色名字是 ApplicationPageBackgroundThemeBrush 。
然后去ThemeResourcesLight.xaml文件里找这个资源,改一下颜色:
修改的颜色最好加个注释。
然后跑一下:
好了背景色已经变了,不再是纯白了。然后可以继续改前景色、Dark主题的背景色、前景色……。
UAP的主题是通过 RequestedTheme 来设置的,可以在页面中绑定一个属性来实现切换。
打开MainPage_Model.cs,添加一个属性,输入代码段propvm按Tab
/// <summary>
/// 当前主题
/// </summary>
public ElementTheme CurrentTheme
{
get { return _CurrentThemeLocator( this ).Value; }
set
{
_CurrentThemeLocator( this ).SetValueAndTryNotify( value );
}
}
#region Property ElementTheme CurrentTheme Setup
protected Property < ElementTheme > _CurrentTheme = new Property < ElementTheme > { LocatorFunc = _CurrentThemeLocator };
static Func < BindableBase , ValueContainer < ElementTheme >> _CurrentThemeLocator = RegisterContainerLocator< ElementTheme >( "CurrentTheme" , model => model.Initialize( "CurrentTheme" , ref model._CurrentTheme, ref _CurrentThemeLocator, _CurrentThemeDefaultValueFactory));
static Func < ElementTheme > _CurrentThemeDefaultValueFactory = () => { return ElementTheme .Default; };
#endregion
在初始化VM的时候,给其赋值:
添加一个Command,输入propcmd按Tab
/// <summary>
/// 切换日间夜间模式
/// </summary>
public CommandModel < ReactiveCommand , String > CommandSetCustomTheme
{
get { return _CommandSetCustomThemeLocator( this ).Value; }
set { _CommandSetCustomThemeLocator( this ).SetValueAndTryNotify( value ); }
}
#region Property CommandModel<ReactiveCommand, String> CommandSetCustomTheme Setup
protected Property < CommandModel < ReactiveCommand , String >> _CommandSetCustomTheme = new Property < CommandModel < ReactiveCommand , String >> { LocatorFunc = _CommandSetCustomThemeLocator };
static Func < BindableBase , ValueContainer < CommandModel < ReactiveCommand , String >>> _CommandSetCustomThemeLocator = RegisterContainerLocator< CommandModel < ReactiveCommand , String >>( "CommandSetCustomTheme" , model => model.Initialize( "CommandSetCustomTheme" , ref model._CommandSetCustomTheme, ref _CommandSetCustomThemeLocator, _CommandSetCustomThemeDefaultValueFactory));
static Func < BindableBase , CommandModel < ReactiveCommand , String >> _CommandSetCustomThemeDefaultValueFactory =
model =>
{
var resource = "SetCustomTheme" ; // Command resource
var commandId = "SetCustomTheme" ;
var vm = CastToCurrentType(model);
var cmd = new ReactiveCommand (canExecute: true ) { ViewModel = model }; //New Command Core
cmd
.DoExecuteUIBusyTask(
vm,
async e =>
{
//Todo: Add SetCustomTheme logic here, or
await MVVMSidekick.Utilities. TaskExHelper .Yield();
if (vm.CurrentTheme == ElementTheme .Dark || vm.CurrentTheme == ElementTheme .Default)
{
vm.CurrentTheme = ElementTheme .Light;
}
else
{
vm.CurrentTheme = ElementTheme .Dark;
}
}
)
.DoNotifyDefaultEventRouter(vm, commandId)
.Subscribe()
.DisposeWith(vm);
var cmdmdl = cmd.CreateCommandModel(resource);
cmdmdl.ListenToIsUIBusy(model: vm, canExecuteWhenBusy: false );
return cmdmdl;
};
#endregion
ElementTheme和ApplicationTheme是不同的,后者是App的属性,前者可以应用于UIElement。所以可以把这个属性绑定到根Grid上。
在页面中加一个Button,Content设置为"切换主题",然后把Command绑定到 CommandSetCustomTheme 上
跑一下看看:
可以顺利切换了,而且背景色和前景色也变成了我们设置的样子,不是纯黑纯白了。
其实TextBlock和Button并没有设置背景色前景色,都是继承系统的,所以不用特别设置。其他的控件也一样,如果需要特殊处理就自己添加样式即可。
顺便说一下CustomStyleResources.xaml的作用,我建议把控件的样式写在这里,覆盖系统默认的。比如可以把所有的Grid都更改一下背景色,就可以在这里改,或者改全局Pivot的头部margin之类的。
忘了最后附上源码:
链接: http://pan.baidu.com/s/1mgFvXos 密码: wnck