如果希望在多個項目之間共享資源,可創建資源字典。資源字典只是XAML文檔,除了存儲希望使用的資源外,不做其他任何事情。
一、創建資源字典
下面是一個資源字典示例,它包含一個資源:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <ImageBrush x:Key="TileBrush" TileMode="Tile" ViewportUnits="Absolute" Viewport="0 0 32 32" ImageSource="happyface.jpg" Opacity="0.3"></ImageBrush> </ResourceDictionary>
當為應用程序添加資源字典時,務必將Build Action設置為Page(與其他任意XAML文件一樣)。這樣可保證為了獲得最佳性能而將資源字典編譯為BAML。不過,將資源字典的Build Action設置為Resource也是非常完美的,這樣它會被嵌入到程序集中,但不會被編譯。當然,在運行時解析它的速度要稍慢一些。
二、使用資源字典
為了使用資源字典,需要將其合並到應用程序某些位置的資源集合中。例如,可在特定窗口中執行此操作,但通常將其合並到應用程序的資源集合中。如下所示:
<Application x:Class="Resources.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Menu.xaml"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="AppBrushes.xaml"/> <ResourceDictionary Source="WizardBrushes.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application>
上面的標記通過明確創建ResourceDictionary對象進行工作。資源集合總是ResourceDictionary對象,但這只是需要明確指向細節從而可以設置ResourceDictionary.MergedDictionaries屬性的一種情況。如果不執行這一步驟,MergedDictionaries屬性將為空。
MergedDictionaries是ResourceDictionary對象的一個集合,可使用該集合提供自己希望使用的資源的集合。這個示例中有兩個資源集合:一個在AppBrusheds.xaml資源字典中定義,另一個在WizardBrushes.xaml中定義。
如果希望添加自己的資源並合並到資源字典中,只需要在MergedDictionaries部分之前或之后放置資源就可以了,如下所示:
<Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="AppBrushes.xaml"/> <ResourceDictionary Source="WizardBrushes.xaml"/> </ResourceDictionary.MergedDictionaries> <ImageBrush x:Key="GraphicalBrush1"></ImageBrush> <ImageBrush x:Key="GraphicalBrush2"></ImageBrush> </ResourceDictionary> </Application.Resources>
使用資源字典的一個原因是為了定義一個或多個可重用的應用程序“皮膚",可將”皮膚“應用到控件上。另一個原因是為了存儲需要被本地化的內容。
三、在程序集之間共享資源
如果希望在多個應用程序之間共享資源字典,可復制並分發包含資源字典的XAML文件。這是最簡單的方法,但這樣不能對版本進行任何控制。更有條理的方法是將資源字典編譯到單獨的類庫程序集中,並分發組件。
當共享包含一個或多個資源字典的編譯過的程序集時,還需要面對另一個挑戰——需要有一種方法提取所希望的資源並在應用程序中使用資源。為此,可使用兩種方法。最直觀的解決方法是使用代碼創建合適的ResourceDictionary對象。例如,如果類庫程序集中有名為ReusableDictionary.xaml的資源字典,那么可使用下面的代碼手動創建該資源字典:
ResourceDictionary resourceDictionary=new ResourceDictionary(); resourceDictionary.Source=new Uri("ResourceLibrary;component/ReusableDictionary.xaml",UriKind.Relative);
上面的代碼片段使用了在本章前面介紹的pack URI語法。它構造了一個相對URI,該URI指向另一個程序集中名為ReusableDictionary.xaml的編譯過的XAML資源。一旦創建ResourceDictionary對象,就可以從集合中手動檢索所需的資源了:
cmd.Background=(Brush)resourceDictionary["TileBrush"];
然而,不必手動指定資源。當加載新的資源字典時,窗口中的所有DynamicResource引用都會被自動重新評估。
如果不想編寫任何代碼,還有另一種選擇。可使用ComponentResourceKey標記擴展,該標記擴展是專門針對這種情況而設計的。使用ComponentResourceKey為資源創建鍵名。通過執行這一步驟,告知WPF准備在程序集之間共享資源。
在繼續執行任何操作前,需要確保已經為資源字典提供了正確的名稱。為了讓這種技巧生效,必須將資源字典放置到generic.xaml文件中,並且必須將該文件放到應用程序文件夾的Themes子文件夾中。generic.xaml文件中的資源被認為是默認主題的一部分,並且它們總是可用的。
下圖顯示了合理的文件組織方式,頂部的項目名為ResourceLibray,generic.xaml文件被放在正確的文件夾中。底部的項目名為Resources。該項目有指向ResourceLibrary項目的引用,所以該項目可使用ResourceLibray項目中包含的資源。
下一步是為存儲在ResourceLibray程序集中希望共享的資源創建名。當使用ComponentResourceKey時,需要提供兩部分信息:類庫程序集中類的引用和描述性的資源ID。類引用是WPF允許的其他程序集共享資源的關鍵部分。當使用資源時,需要提供相同的類引用和資源ID。
該類的實際外觀並不重要,它不需要包含代碼。定義該類型的程序集就是ComponentResourceKey將要從中查找資源的程序集。
下面是generic.xaml文件的完整標記,它包含了一個單獨資源——一個使用不同圖形的ImageBrush對象:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:ResourceLibrary" > <ImageBrush x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:CustomResources}, ResourceId=SadTileBrush}" TileMode="Tile" ViewportUnits="Absolute" Viewport="0 0 32 32" ImageSource="ResourceLibrary;component/sadface.jpg" Opacity="0.3"> </ImageBrush> </ResourceDictionary>
如果眼光敏銳的話,將在該例中發現一個意外細節。ImageSource屬性不在被設置為圖像名(sadface.jpg),而是使用更復雜的相對URI,明確地指示圖像時ResourceLibray組件的一部分。這是必須的步驟,因為將在其他應用程序中使用該資源。如果只是使用圖像名,應用程序就會在它自己的資源中查找圖像。真正需要的是指定存儲圖像的組件的相對URI。
現在已經創建了資源字典,可在另一個應用程序中使用它了。首先,確保已經為類庫程序集定義了前綴,如下所示:
<Window x:Class="Resources.ResourceFromLibrary" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:res="clr-namespace:ResourceLibrary;assembly=ResourceLibrary" Title="ResourceFromLibrary" Height="300" Width="300" >
然后可使用包含ComponentResourceKey的DynamicResource(這是合理的,因為ComponentResourceKey是資源名)。在使用資源字典的應用程序中使用ComponentResourceKey,就是在類庫中使用的ComponentResourceKey。在此,提供了對同一個類的引用和相同的資源ID。唯一的區別是可能使用不同的XAML名稱空間前綴。該例使用res前綴而不是local前綴。從而強調CustomResources類是在另一個程序集中定義的這樣一個事實:
<Button Background="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type res:CustomResources}, ResourceId=SadTileBrush}}" Padding="5" Margin="5" FontWeight="Bold" FontSize="14"> A Resource From ResourceLibrary</Button>
現在該例完成了。但還可以采用一個附加步驟,使資源更容易使用。可定義一個靜態屬性,讓它返回需要使用的正確ComponentResourceKey。通常在組件的類中定義該屬性,如下所示:
public class CustomResources { public static ComponentResourceKey SadTileBrush { get { return new ComponentResourceKey( typeof(CustomResources), "SadTileBrush"); } } }
現在可使用Static標記擴展訪問該屬性並應用資源,而不必在標記中使用很長的ComponentResourceKey:
<Button Background="{DynamicResource {x:Static res:CustomResources.SadTileBrush}}" Padding="5" Margin="5" FontWeight="Bold" FontSize="14"> A Resource From ResourceLibrary </Button>
在本質上,這種便捷方法與前面介紹的SystemXxx類使用相同的技術。例如,當檢索SystemColors.WindowTextBrushKey時,所接受的也是正確的資源鍵對象。唯一的區別是,它是私有SystemResourceKey(而不是ComponentResourceKey)的一個實例。這兩個類都繼承自ResourceKey抽象類。