WPF Modern UI 主题更换原理
2018-08-25 20:11
344 查看
WPF Modern UI 主题更换原理
一 . 如何更换主题?
二 . 代码分析
代码路径 : FirstFloor.ModernUI.App / Content / SettingsAppearance.xaml1.关键 XAML 代码
<ComboBox Grid.Row="1" Grid.Column="1" ItemsSource="{Binding Themes}" SelectedItem="{Binding SelectedTheme, Mode=TwoWay}" DisplayMemberPath="DisplayName" VerticalAlignment="Center" Margin="0,0,0,4" />
形如 Property = "{ Binding fieldname }" 这种格式的绑定,都是将控件属性绑定到当前 用户控件 DataContext 属性的对象中的。
那我们再打开 SettingsAppearance.cs 文件看一看后台代码:
public partial class SettingsAppearance : UserControl { public SettingsAppearance() { InitializeComponent(); // a simple view model for appearance configuration this.DataContext = new SettingsAppearanceViewModel(); } }
很显然,控件的 DataContext 属性引用到了 new SettingsAppearanceViewModel();
那再F12进去分析一下 SettingsAppearanceViewModel 这个类有哪些相关的东西:
2. Themes
首先看一下 Themes 这个属性 ,它是一个 LinkCollection 对象,并返回了 themes 字段:public LinkCollection Themes { get { return this.themes; } }
LinkCollection 继承自 ObservableCollection<Link> ,百度一下 ObservableCollection这个类,就知道它其实也是用来实现数据绑定的,当集合内的元素发生变化时,集合就会通知外部调用者,此处不多赘述。
此外,SettingsAppearance 类的构造函数中向 themes 添加了一些主题,这里就不贴代码了。
3. SelectedTheme
public Link SelectedTheme { get { return this.selectedTheme; } set { if (this.selectedTheme != value) { this.selectedTheme = value; OnPropertyChanged("SelectedTheme"); // and update the actual theme AppearanceManager.Current.ThemeSource = value.Source; } } }
当 SelectedTheme 更改时,set 方法会更改当前的主题源。
对 ThemeSource 这个属性一直F12下去,会找到一个方法
SetThemeSource:
private void SetThemeSource(Uri source, bool useThemeAccentColor) { if (source == null) { throw new ArgumentNullException("source"); } var oldThemeDict = GetThemeDictionary(); var dictionaries = Application.Current.Resources.MergedDictionaries; var themeDict = new ResourceDictionary { Source = source }; // if theme defines an accent color, use it var accentColor = themeDict[KeyAccentColor] as Color?; if (accentColor.HasValue) { // remove from the theme dictionary and apply globally if useThemeAccentColor is true themeDict.Remove(KeyAccentColor); if (useThemeAccentColor) { ApplyAccentColor(accentColor.Value); } } // add new before removing old theme to avoid dynamicresource not found warnings dictionaries.Add(themeDict); // remove old theme if (oldThemeDict != null) { dictionaries.Remove(oldThemeDict); } OnPropertyChanged("ThemeSource"); }
看一下第一个函数 GetThemeDictionary:
private ResourceDictionary GetThemeDictionary() { // determine the current theme by looking at the app resources and return the first dictionary having the resource key 'WindowBackground' defined. return (from dict in Application.Current.Resources.MergedDictionaries where dict.Contains("WindowBackground") select dict).FirstOrDefault(); }
这个函数使用 LINQ 从 App.xaml 定义的 MergedDictionaries 中搜索主题资源
看一下 App.xaml 里面的代码:
<Application x:Class="FirstFloor.ModernUI.App.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.xaml" /> <ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.Light.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application>
这里面预定义了两个资源,去相应的地方找到这两个资源文件,其中 ModernUI.Light.xaml 内有大量包含 "WindowBackground" 字符串的 Key ,那这个显然就是主题资源文件了。
接下来的逻辑就比较好理解了:调整 AccentColor ,加入新的主题,移除旧的主题,最后通知属性更改。
4. 动画
那主题更改的渐变动画效果是在哪里触发的呢?看一下 MainWindon 的基类 MorderWindow , 这里面监听了 AppearanceManager.Current.PropertyChanged 事件:
/// <summary> /// Initializes a new instance of the <see cref="ModernWindow"/> class. /// </summary> public ModernWindow() { // 其他代码 ... // listen for theme changes AppearanceManager.Current.PropertyChanged += OnAppearanceManagerPropertyChanged; } ... private void OnAppearanceManagerPropertyChanged(object sender, PropertyChangedEventArgs e) { // start background animation if theme has changed if (e.PropertyName == "ThemeSource" && this.backgroundAnimation != null) { this.backgroundAnimation.Begin(); } }
再找一下 backgroundAnimation 赋值的地方
/// <summary> /// When overridden in a derived class, is invoked whenever application code or internal processes call System.Windows.FrameworkElement.ApplyTemplate(). /// </summary> public override void OnApplyTemplate() { base.OnApplyTemplate(); // retrieve BackgroundAnimation storyboard var border = GetTemplateChild("WindowBorder") as Border; if (border != null) { this.backgroundAnimation = border.Resources["BackgroundAnimation"] as Storyboard; if (this.backgroundAnimation != null) { this.backgroundAnimation.Begin(); } } }
backgroundAnimation 其实是从资源文件中加载的,找到 ModernWindow.xaml 文件,相关代码:
<Border.Resources> <Storyboard x:Key="BackgroundAnimation"> <ColorAnimation Storyboard.TargetName="WindowBorderBackground" Storyboard.TargetProperty="Color" To="{DynamicResource WindowBackgroundColor}" Duration="0:0:.6" /> </Storyboard> </Border.Resources>
到这里整个主题更换的流程就很明朗了。
三 、总结
总结一下主题更换的简要流程:ComboBox 绑定 SettingsAppearanceViewModel 类中的 Themes 和 SelectedTheme 两个字段。
SettingsAppearanceViewModel.SelectedTheme在 set 的时候更改 AppearanceManager.Current.ThemeSource的值。
AppearanceManager.Current.ThemeSource 被更改时进行主题资源的置换以及其他一系列操作,最后触发 AppearanceManager.Current.PropertyChanged 事件
ModernWindow 中绑定到 AppearanceManager.Current.PropertyChanged 事件的函数 OnAppearanceManagerPropertyChanged 被触发,打开 backgroundAnimation 动画。
四、下一篇参考本流程来自己实现一个简单的主题更换功能
相关文章推荐
- 基于modern ui for wpf的在线公开课平台 之二 创建我的modern ui程序
- 基于modern ui for wpf的在线公开课平台 之四 ListBox+WrapPanel实现平铺效果
- Modern UI for WPF 开源项目(2):我的第一个Modern UI App
- 基于modern ui for wpf的在线公开课平台 之五 使用ListBox实现图片列表效果
- Modern UI for WPF 开源项目(3):用模板创建我的第一个Modern UI app
- 初识Modern UI for WPF
- WPF开源UI框架推荐 Modern UI
- (转)基于 WPF + Modern UI 的 公司OA小助手 开发总结
- Modern UI for WPF 开源项目(4):使用预定义的页面布局
- WPF and Silverlight 学习笔记(十九):WPF更换主题
- Modern UI for WPF 开源项目(5):定义logo
- WPF入门:用 Modern UI + Metro Chart 打造漂亮的图表(1)
- 【使用Modern UI快速开发WPF应用】
- Modern UI for WPF 初接触
- WPF实现主题更换的简单DEMO
- Modern UI for WPF的使用
- WPF实现主题更换的简单DEMO
- WPF DevExpress 更换主题
- Modern UI for WPF 开源项目(1):开篇
- 使用Modern UI for WPF的导航功能