微軟動作真是快,本來想寫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
2015-12-29 update:
更新10586後,該檔案的位址在:
C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.10586.0\Generic
裡面有generic.xaml和themeresources.xaml
打開這個檔案,可以看到裡面存放的是系統預設的主題樣式。
順便說一下,WP8 和WP8.1 的預設主題樣式也可以找到,位置不一樣。
一、建立UAP項目
建立個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
///切換日間夜間模式 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; }; |
ElementTheme和ApplicationTheme是不同的,後者是App的屬性,前者可以應用于UIElement。是以可以把這個屬性綁定到根Grid上。
在頁面中加一個Button,Content設定為"切換主題",然後把Command綁定到CommandSetCustomTheme上
跑一下看看:
可以順利切換了,而且背景色和前景色也變成了我們設定的樣子,不是純黑純白了。
其實TextBlock和Button并沒有設定背景色前景色,都是繼承系統的,是以不用特别設定。其他的控件也一樣,如果需要特殊處理就自己添加樣式即可。
六、設定控件的style
順便說一下CustomStyleResources.xaml的作用,我建議把控件的樣式寫在這裡,覆寫系統預設的。比如可以把所有的Grid都更改一下背景色,就可以在這裡改,或者改全局Pivot的頭部margin之類的。
忘了最後附上源碼:
連結: http://pan.baidu.com/s/1mgFvXos 密碼: wnck