WinRT:デフォルトのシステムリソースカラーを変更してアプリのテーマカラーを一括変更
WindowsRuntimeのXAMLでは多くのコントロールがデフォルトで紫色のテーマカラーを使っています。
ProgressBarとかSliderとかの色が紫になってるアレです。
これを変更するためにはControlTemplateを変更したりすれば良いのですがいちいちすべてのコントロールのControlTemplateを変更していてはめんどくさいです。
というわけで紫っぽいテーマカラーを一括変更するクラスを作りました。
使い方
こんなクラスをAttachedTheme.csとしてプロジェクトにコピペして追加しましょう。
using System; using System.Collections.Generic; using System.Text; using Windows.UI.Xaml; using Windows.UI.Xaml.Media; namespace Theme { public class ThemeBrush { public string Name { get; set; } public int Level { get; set; } } public class AttachedTheme { //Level is light or deep or normal color level private static List<ThemeBrush> _systemBrushes = new List<ThemeBrush>() { new ThemeBrush{Name="ComboBoxItemSelectedBackgroundThemeBrush",Level=1}, new ThemeBrush{Name="ComboBoxItemSelectedPointerOverBackgroundThemeBrush",Level=0}, new ThemeBrush{Name="ComboBoxSelectedBackgroundThemeBrush",Level=1}, new ThemeBrush{Name="ComboBoxSelectedPointerOverBackgroundThemeBrush",Level=0}, new ThemeBrush{Name="HyperlinkForegroundThemeBrush",Level=0}, new ThemeBrush{Name="HyperlinkPointerOverForegroundThemeBrush",Level=0}, new ThemeBrush{Name="HyperlinkPressedForegroundBrush",Level=1}, new ThemeBrush{Name="IMECandidateSelectedBackgroundThemeBrush",Level=2}, new ThemeBrush{Name="ListBoxItemSelectedBackgroundThemeBrush",Level=2}, new ThemeBrush{Name="ListBoxItemSelectedPointerOverBackgroundThemeBrush",Level=1}, new ThemeBrush{Name="ListViewItemDragBackgroundThemeBrush",Level=2}, new ThemeBrush{Name="ListViewItemSelectedBackgroundThemeBrush",Level=1}, new ThemeBrush{Name="ListViewItemSelectedPointerOverBackgroundThemeBrush",Level=0}, new ThemeBrush{Name="ListViewItemSelectedPointerOverBorderThemeBrush",Level=0}, new ThemeBrush{Name="ProgressBarForegroundThemeBrush",Level=1}, new ThemeBrush{Name="ProgressBarIndeterminateForegroundThemeBrush",Level=0}, new ThemeBrush{Name="SearchBoxButtonBackgroundThemeBrush",Level=1}, new ThemeBrush{Name="SearchBoxButtonPointerOverBackgroundThemeBrush",Level=0}, new ThemeBrush{Name="SearchBoxHitHighlightForegroundThemeBrush",Level=1}, new ThemeBrush{Name="SearchBoxHitHighlightSelectedForegroundThemeBrush",Level=0}, new ThemeBrush{Name="SliderTrackDecreaseBackgroundThemeBrush",Level=1}, new ThemeBrush{Name="SliderTrackDecreasePointerOverBackgroundThemeBrush",Level=0}, new ThemeBrush{Name="SliderTrackDecreasePressedBackgroundThemeBrush",Level=0}, new ThemeBrush{Name="TextSelectionHighlightColorThemeBrush",Level=2}, new ThemeBrush{Name="ToggleSwitchCurtainBackgroundThemeBrush",Level=1}, new ThemeBrush{Name="ToggleSwitchCurtainPointerOverBackgroundThemeBrush",Level=0}, new ThemeBrush{Name="ToggleSwitchCurtainPressedBackgroundThemeBrush",Level=0}, }; public static SolidColorBrush GetThemeBrush(DependencyObject obj) { return (SolidColorBrush)obj.GetValue(ThemeBrushProperty); } public static void SetThemeBrush(DependencyObject obj, SolidColorBrush value) { obj.SetValue(ThemeBrushProperty, value); } // Using a DependencyProperty as the backing store for ThemeBrush. This enables animation, styling, binding, etc... public static readonly DependencyProperty ThemeBrushProperty = DependencyProperty.RegisterAttached("ThemeBrush", typeof(SolidColorBrush), typeof(AttachedTheme), new PropertyMetadata(null, (s, e) => { RewriteSystemResource(GetThemeBrush(s)); })); public static SolidColorBrush GetLightThemeBrush(DependencyObject obj) { return (SolidColorBrush)obj.GetValue(LightThemeBrushProperty); } public static void SetLightThemeBrush(DependencyObject obj, SolidColorBrush value) { obj.SetValue(LightThemeBrushProperty, value); } // Using a DependencyProperty as the backing store for LightThemeBrush. This enables animation, styling, binding, etc... public static readonly DependencyProperty LightThemeBrushProperty = DependencyProperty.RegisterAttached("LightThemeBrush", typeof(SolidColorBrush), typeof(AttachedTheme), new PropertyMetadata(null, (s, e) => { RewriteSystemResource(GetLightThemeBrush(s),0); })); public static SolidColorBrush GetNormalThemeBrush(DependencyObject obj) { return (SolidColorBrush)obj.GetValue(NormalThemeBrushProperty); } public static void SetNormalThemeBrush(DependencyObject obj, SolidColorBrush value) { obj.SetValue(NormalThemeBrushProperty, value); } // Using a DependencyProperty as the backing store for NormalTheme. This enables animation, styling, binding, etc... public static readonly DependencyProperty NormalThemeBrushProperty = DependencyProperty.RegisterAttached("NormalThemeBrush", typeof(SolidColorBrush), typeof(AttachedTheme), new PropertyMetadata(null, (s, e) => { RewriteSystemResource(GetNormalThemeBrush(s), 1); })); public static SolidColorBrush GetDeepThemeBrush(DependencyObject obj) { return (SolidColorBrush)obj.GetValue(DeepThemeBrushProperty); } public static void SetDeepThemeBrush(DependencyObject obj, SolidColorBrush value) { obj.SetValue(DeepThemeBrushProperty, value); } // Using a DependencyProperty as the backing store for DeepThemeBrush. This enables animation, styling, binding, etc... public static readonly DependencyProperty DeepThemeBrushProperty = DependencyProperty.RegisterAttached("DeepThemeBrush", typeof(SolidColorBrush), typeof(AttachedTheme), new PropertyMetadata(null, (s, e) => { RewriteSystemResource(GetDeepThemeBrush(s), 2); })); public static void RewriteSystemResource(SolidColorBrush themeBrush,int level) { if (themeBrush != null) { foreach (ThemeBrush tb in _systemBrushes) { if (tb.Level == level) { Application.Current.Resources[tb.Name] = themeBrush; } } } } public static void RewriteSystemResource(SolidColorBrush themeBrush) { if (themeBrush != null) { foreach (ThemeBrush tb in _systemBrushes) { SolidColorBrush applyBrush = themeBrush; Application.Current.Resources[tb.Name] = applyBrush; } } } } }
あとはAppクラスのOnLaunchedでRewriteSystemResourceメソッドを呼ぶだけです。*1
Theme.AttachedTheme.RewriteSystemResource(new SolidColorBrush(Color.FromArgb(255,54,104,87)));
これでさまざまなコントロールに使われている紫の色が一括変更できました。
XAMLから使う
AttachedThemeクラスはstaticメソッドを呼ぶだけでC#から一行でテーマカラーを変更できますが添付プロパティを提供しているのでXAMLからも呼び出せます。
XAMLから呼び出すと色を #366857 こんな感じのHTML記法?で指定できるというメリットがあります。
こんな感じで最初に呼ばれるPageの属性にxmlnsでTheme名前空間を指定し、AttachedThemeのThemeBrushプロパティを指定します。
xmlns:t="using:Theme" t:AttachedTheme.ThemeBrush="#365768"
濃度レベルを分ける
デフォルトで用意されている紫の色も、濃さが微妙に違います。 薄い、普通、濃いの3段階指定できるようにしたので3つ指定すると濃さによって色を指定できます。
LightThemeBrush、NormalThemeBrush、DeepThemeBrushがあります。
これでアプリのテーマカラーを一括で指定できるようになりました。
しくみ
AttachedThemeクラスのしくみとしては、システムリソースをちまちま変更する方法でテーマカラー一括変更を実現しています。
コントロールの紫色は、システムリソースで指定されているため、このシステムリソースを変更してやれば色を変えることができるわけです。
システムリソースで紫色のものを濃さを3段階で主観評価したらこんな感じになりました。
ComboBoxItemSelectedBackgroundThemeBrush | 普通 |
---|---|
ComboBoxItemSelectedPointerOverBackgroundThemeBrush | 薄い |
ComboBoxSelectedBackgroundThemeBrush | 普通 |
ComboBoxSelectedPointerOverBackgroundThemeBrush | 薄い |
HyperlinkForegroundThemeBrush | 薄い |
HyperlinkPointerOverForegroundThemeBrush | 薄い |
HyperlinkPressedForegroundBrush | 普通 |
IMECandidateSelectedBackgroundThemeBrush | 濃い |
ListBoxItemSelectedBackgroundThemeBrush | 濃い |
ListBoxItemSelectedPointerOverBackgroundThemeBrush | 普通 |
ListViewItemDragBackgroundThemeBrush | 濃い |
ListViewItemSelectedBackgroundThemeBrush | 普通 |
ListViewItemSelectedPointerOverBackgroundThemeBrush | 薄い |
ListViewItemSelectedPointerOverBorderThemeBrush | 薄い |
ProgressBarForegroundThemeBrush | 普通 |
ProgressBarIndeterminateForegroundThemeBrush | 薄い |
SearchBoxButtonBackgroundThemeBrush | 普通 |
SearchBoxButtonPointerOverBackgroundThemeBrush | 薄い |
SearchBoxHitHighlightForegroundThemeBrush | 普通 |
SearchBoxHitHighlightSelectedForegroundThemeBrush | 薄い |
SliderTrackDecreaseBackgroundThemeBrush | 普通 |
SliderTrackDecreasePointerOverBackgroundThemeBrush | 薄い |
SliderTrackDecreasePressedBackgroundThemeBrush | 薄い |
TextSelectionHighlightColorThemeBrush | 濃い |
ToggleSwitchCurtainBackgroundThemeBrush | 普通 |
ToggleSwitchCurtainPointerOverBackgroundThemeBrush | 薄い |
ToggleSwitchCurtainPressedBackgroundThemeBrush | 薄い |
そして薄い=0、普通=1、濃い=2で値をつけ、Listに入れます。
private static List<ThemeBrush> _systemBrushes = new List<ThemeBrush>() { new ThemeBrush{Name="ComboBoxItemSelectedBackgroundThemeBrush",Level=1}, new ThemeBrush{Name="ComboBoxItemSelectedPointerOverBackgroundThemeBrush",Level=0}, new ThemeBrush{Name="ComboBoxSelectedBackgroundThemeBrush",Level=1}, new ThemeBrush{Name="ComboBoxSelectedPointerOverBackgroundThemeBrush",Level=0}, new ThemeBrush{Name="HyperlinkForegroundThemeBrush",Level=0}, new ThemeBrush{Name="HyperlinkPointerOverForegroundThemeBrush",Level=0}, new ThemeBrush{Name="HyperlinkPressedForegroundBrush",Level=1}, new ThemeBrush{Name="IMECandidateSelectedBackgroundThemeBrush",Level=2}, new ThemeBrush{Name="ListBoxItemSelectedBackgroundThemeBrush",Level=2}, new ThemeBrush{Name="ListBoxItemSelectedPointerOverBackgroundThemeBrush",Level=1}, new ThemeBrush{Name="ListViewItemDragBackgroundThemeBrush",Level=2}, new ThemeBrush{Name="ListViewItemSelectedBackgroundThemeBrush",Level=1}, new ThemeBrush{Name="ListViewItemSelectedPointerOverBackgroundThemeBrush",Level=0}, new ThemeBrush{Name="ListViewItemSelectedPointerOverBorderThemeBrush",Level=0}, new ThemeBrush{Name="ProgressBarForegroundThemeBrush",Level=1}, new ThemeBrush{Name="ProgressBarIndeterminateForegroundThemeBrush",Level=0}, new ThemeBrush{Name="SearchBoxButtonBackgroundThemeBrush",Level=1}, new ThemeBrush{Name="SearchBoxButtonPointerOverBackgroundThemeBrush",Level=0}, new ThemeBrush{Name="SearchBoxHitHighlightForegroundThemeBrush",Level=1}, new ThemeBrush{Name="SearchBoxHitHighlightSelectedForegroundThemeBrush",Level=0}, new ThemeBrush{Name="SliderTrackDecreaseBackgroundThemeBrush",Level=1}, new ThemeBrush{Name="SliderTrackDecreasePointerOverBackgroundThemeBrush",Level=0}, new ThemeBrush{Name="SliderTrackDecreasePressedBackgroundThemeBrush",Level=0}, new ThemeBrush{Name="TextSelectionHighlightColorThemeBrush",Level=2}, new ThemeBrush{Name="ToggleSwitchCurtainBackgroundThemeBrush",Level=1}, new ThemeBrush{Name="ToggleSwitchCurtainPointerOverBackgroundThemeBrush",Level=0}, new ThemeBrush{Name="ToggleSwitchCurtainPressedBackgroundThemeBrush",Level=0}, };
これをforeachで回してちまちまリソースカラーを変更する処理をAttachedThemeクラスでは行っています。