WPF Modern UI 主題更換原理

來源:互聯網
上載者:User

標籤:www.   ems   元素   erp   NPU   資源檔   img   欄位   initial   

WPF Modern UI 主題更換原理一 . 如何更換主題?

二 . 程式碼分析

代碼路徑 : FirstFloor.ModernUI.App / Content / SettingsAppearance.xaml

1.關鍵 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();}

這個函數使用 LINQApp.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>

到這裡整個主題更換的流程就很明朗了。

三 、總結

總結一下主題更換的簡要流程:

  1. ComboBox 綁定 SettingsAppearanceViewModel 類中的 ThemesSelectedTheme 兩個欄位。
  2. SettingsAppearanceViewModel.SelectedThemeset 的時候更改 AppearanceManager.Current.ThemeSource的值。
  3. AppearanceManager.Current.ThemeSource 被更改時進行佈景主題資源的置換以及其他一系列操作,最後觸發 AppearanceManager.Current.PropertyChanged 事件
  4. ModernWindow 中綁定到 AppearanceManager.Current.PropertyChanged 事件的函數 OnAppearanceManagerPropertyChanged 被觸發,開啟 backgroundAnimation 動畫。
四、下一篇參考本流程來自己實現一個簡單的主題更換功能

WPF Modern UI 主題更換原理

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.