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();}

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

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

三 、總結

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

  1. ComboBox 綁定 SettingsAppearanceViewModel 類中的 Themes 和 SelectedTheme 兩個欄位。
  2. SettingsAppearanceViewModel.SelectedTheme在 set 的時候更改 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.