作者:Tony Qu
前兩天Family Show 2.0終於發布了(有關Family Show的項目資訊大家可以看這裡)。出於興趣,這兩天對這個項目中用到的一些技術作了一些研究。首先吸引我的就是這個動態換膚功能,請看下面兩張圖:
這張是Black Skin的效果
這張是Silver Skin的效果
我們會發現整個視窗風格都變了,很酷吧。。。
我先來介紹一下ResourceDictionary。ResourceDictionary中文譯做資源字典,顧名思義,就是一個用來存放資源的集合。ResourceDictionary元素中可以直接嵌入資源,如下所示:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="MainBackgroundBrush" Color="#FF202020"/>
<LinearGradientBrush x:Key="PanelGradientBrush" EndPoint="1,0.5" StartPoint="0,0.5">
<GradientStop Color="#FF555555" Offset="0"/>
<GradientStop Color="#FF1C1C1C" Offset="1"/>
</LinearGradientBrush>
</ResourceDictionary>
也可以切入另一個ResourceDictionary,如下所示:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources\Style1.xaml"/>
<ResourceDictionary Source="Resources\Style2.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
這裡的MergedDicionaries是關鍵,它會把內嵌資源檔案合并起來,就像在一個資源中一樣,但要注意一點——內嵌資源檔案必須把ResourceDictionary作為根項目。
接下來的一個重點就是動態資源引用。在WPF中資源引用分為動態和靜態兩種,所謂動態資源引用就是一旦資源內容改變就會影響相應的內容,例如Window的背景動態引用了一個SolidColorBrush資源,一旦這個SolidColorBrush改變,那麼背景同時也會改變;而靜態資源引用則是僅在資源第一次使用時載入,所以如果使用的靜態資源引用,即使之後資源的內容變化了,也不會對應用了資源的元素或屬性產生任何影響。由於Family Show要實現的是動態換膚,靜態資源引用恐怕是無能為力了,所以得用動態資源,例如:
<Border x:Name="Header" Background="{DynamicResource BackgroundBrush}" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1,1,1,0" CornerRadius="5,5,0,0">
<TextBlock Text="Add a family member" TextWrapping="Wrap" Margin="15,5,10,5" Foreground="{DynamicResource HeaderFontColor}" FontSize="18" VerticalAlignment="Center" FontWeight="Bold" x:Name="HeaderTextBlock"/>
</Border>
這裡的Border.Background動態引用了BackgroundBrush資源Border.BorderBrush則引用了BorderBrush資源,TextBlock.Foreground則引用了HeaderFontColor資源。那麼這些資源分別儲存在哪裡呢?原來它們都儲存在每個Skin的BrushResources.xaml中,如下:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- The Background Brush is used as the background for the Headers and Footers -->
<SolidColorBrush x:Key="BackgroundBrush" Color="#FF202020"/>
<!-- The Border Brush is used as the color for most borders -->
<SolidColorBrush x:Key="BorderBrush" Color="#FF747474"/>
<SolidColorBrush x:Key="HeaderFontColor" Color="#FFE6E6E6"/>
</ResourceDictionary>
所以,如果大家為找不到這些資源而發愁,這就是答案。
另外,之所以可以在Family Show中直接用{DynamicResource XXX}這樣的形式直接引用資源,還有一個原因,那就是這些資源都被作為了應用程式級資源,讓我們看看App.xaml中的代碼:
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Microsoft.FamilyShow.App"
StartupUri="MainWindow.xaml"
>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Use the Black skin by default -->
<ResourceDictionary Source="Skins\Black\BlackResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
我們會發現原來預設情況下會把Skins\Black\BlackResources.xaml作為應用程式級資源引入,這也是為什麼我們預設看到的是Black Skin,而不是Silver Skin。這裡也順便提一下,WPF的資源都是有作用範圍的,有應用程式級的資源,也有Window級的資源,還有控制項級的資源,所以在使用一個資源之前一定要先確認是否在它的作用範圍之內。
但這裡只解決了預設的Skin載入,如何在程式運行時實現資源替換呢?答案就是下面這段代碼: /**//// <summary>
/// Command handler for ChangeSkinCommand
/// </summary>
private void ChangeSkin(object sender, ExecutedRoutedEventArgs e)
{
ResourceDictionary rd = new ResourceDictionary();
rd.MergedDictionaries.Add(Application.LoadComponent(new Uri(e.Parameter as string, UriKind.Relative)) as ResourceDictionary);
Application.Current.Resources = rd;
}
ChangeSkin其實是一個事件處理常式,對應於ChangeSkin路由事件。(關於路由事件的知識由於超出了這篇文章的範疇,就不做展開了,我可能會在另一篇文章中單獨講解,這裡你只要把它當作一個普通事件來理解就可以了。)這個事件處理常式是在點擊Skin菜單中的功能表項目時觸發的。其實代碼還是比較簡單的——首先建立一個ResourceDictionary,然後把新的資源字典載入進來放入MergedDictionary中,最後把這個資源字典作為當前應用程式的資源。
到此,我想你對Family Show 2.0動態換膚的實現已經理解得差不多了。當然這隻是WPF中換膚的基礎而已,其實WPF換膚不僅僅可以換某個控制項的顏色,還可以換布局、樣式等,這可以讓你的應用程式產生奪人眼球的效果,但由於布局和樣式在WPF中是兩個很大的主題,在本文中無法一一道來,建議大家可以去看看WPF Unleashed的第8章“資源”和第10章“樣式、模板、皮膚和主題”,相信會對理解資源和進階換膚有很大協助。