1. 程式人生 > 其它 >WPF實現換膚(手動)

WPF實現換膚(手動)

一、三個基礎知識
1、分層式資源 Hierarchical Resource

FrameworkElement基類有一個Resources屬性,型別是ResourceDictionary。這個字典包含了一組鍵值對,每個鍵值對中的key和value都可以使任意的object。
Resource dictionaries 在程式中分層存放。當程式需要定位一個資源(例如Brush, Style,DataTemplate或其他物件)的時候,首先他檢查發起資源尋找請求的元素的Resource屬性。假如沒有找到,它會檢查該元素的父元素。假如還是沒有找到該資源,那麼它會一直向上,檢查每個祖先節點。假如仍然沒有找到,那麼它最後會問Application,它是否有這個resource。再沒找到,就會丟擲異常;

2、合併的資源字典 Merged Resource Dictionaries

ResourceDictionary 包含一個MergedDictionaries屬性,其型別是Collection,讓我們可以將其他ResourceDictionary合併到當前的ResourceDictionary中。

3、Dynamic Resource Reference

動態資源與靜態資源引用的區別;靜態資源引用,是在程式載入的時候讀取一次,後面資源變更了,是不是會再去讀取資源的;而動態資源引用,除了在程式載入時讀取外,如果資源發生了改變,程式會再次自動讀取;

詳情這裡:“https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/dynamicresource-markup-extension”

有更多的關於編寫此程式碼的資訊。

注意幾點:

  • 1)理論上ResourceDictionary中的KEY不應該是重複的;但是使用MergedDictionary合併的資源,它們中的KEY名稱重複是沒有關係的;
  • 2)資源尋找會返回MergedDictionary中最後一個與key對應的資源;這裡我們要注意MergedDictionary的合併順序。若MergedDictionary在--xaml中定義,那麼MergedDictionary的順序就是xaml中子元素的順序。假如一個key在ResourceDictionary中被定義了,同時又出現在一個被合併的子典中,那麼查詢過程會返回ResourceDictionary中的對應資源。這個規則對於靜態資源引用和動態資源引用都適用。詳細瞭解,可檢視https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/merged-resource-dictionaries
  • 3)在MergedDictionary中的資源會在資源查詢的過程中佔用一段空間,這段空間位於合併它們的資源之後
二、實現換膚步驟
1、新建WPF工程
<Window x:Class="WPFView.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFView"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Button Grid.Row="0" Grid.Column="0" Content="Button1"></Button>
        <Button Grid.Row="1" Grid.Column="1"  Content="Button2"></Button>
    </Grid>
</Window>
2、通過Style設定元素的樣式

 需要支援動態換膚的元素,通過Style設定元素的樣式;
通過編輯模板的方式,建立一個樣式

點選後,預設會生成在window.Resource下面;

3、將需要換膚的部分,提取出來儲存成ResouceDictionary

 在Window.Resource中; 把常量資源,單獨提取出來,儲存至新建立的資源字典;(新建ResourceDictionary可以通過右鍵快捷選單,選擇【新增】-【資源字典】的方式建立)
BlackBrush.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
    <SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
    <SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
    <SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
    <SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
    <SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
    <SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
    <SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
    <SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
</ResourceDictionary>

為了演示換膚,我們將BlackBrush.xaml複製一份,存為RedBrush.xaml;
將按鈕背景改成紅色

將Window.Resource剩餘部分,並儲存至單獨的ResouceDictionary,檔名為 Theme.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

            <Style x:Key="FocusVisual">
				<!--這裡省略掉-->
                </Setter>
            </Style>
    <Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
  		<!--這裡省略掉-->
    </Style>
</ResourceDictionary>

 至此,我們已經為需要換膚的按鈕,1)使用樣式來修改外觀 2)將樣式的常量部分提取出來,生成兩個資源字典檔案BlackBrush.xaml,RedBrush.xaml 3)將樣式Style單獨提取出來,生成Theme.xaml;

4、動態引用資源

 元素設定樣式時,引用style中的資源時,應採用DynamicResource方式使用資源;這樣我們就可以在執行時通過更改主題來讓這些元素應用新的主題資源了。

<Window x:Class="WPFView.MainWindow"
<!--這裡省略掉部分名稱空間-->
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <!--將資原始檔合併進來-->
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Theme.xaml"/>
                <ResourceDictionary Source="/Theme/RedBrush.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <!--按鈕的樣式動態引用資源-->
        <Button Grid.Row="0" Grid.Column="0" Content="Button1" Style="{DynamicResource ButtonStyle1}"></Button>
        <Button Grid.Row="1" Grid.Column="1"  Content="Button2" Style="{DynamicResource ButtonStyle1}"></Button>
    </Grid>
</Window>
5、手動換膚

修改<Window.Resource>資源,將RedBrush.xaml修改為:BlackBrush.xaml可以實現換膚