WPF Resource資源,wpfresource資源
WPF不但支援程式級的傳統資源,同時還推出了獨具特色的對象級資源,每個介面元素都可以攜帶自己的資源並可被自己的子級元素共用。比如後面的章節我們會講到模板、程式樣式和主題就經常放在對象資源裡面。這樣一來,在WPF程式中資料就分為4個等級儲存了:資料庫裡的資料相當於存放在倉庫裡面,資源檔裡的資料就相當於放進了旅行箱裡,WPF對象資源裡面的資料相當於存放在攜帶的背包裡,變數裡面的資料相當於拿在手裡。
1. WPF對象資源的定義和尋找
每個WPF介面元素都有一個名為Resource的屬性,這個屬性繼承至FrameworkElement類,其類型為ResourceDictionary。ResourceDictionary能夠以鍵值對的形式儲存資源,當要使用到某個資源的時候,使用鍵值對的形式擷取資來源物件。在儲存資源時,ResourceDictionary視資來源物件為Object類型,所以再使用資源時先要對資來源物件進行類型轉換,XAML編譯器能夠根據Attribute自動識別資源類型,如果類型不對就會拋出異常,但在C#中檢索到資來源物件之後,類型轉換的事情就只能由我們自己來做了。ResourceDictionary可以儲存任意類型的對象。在XAML代碼中向Resource添加資源時需要把正確的命名空間引入到XAML代碼中,讓我們來看一個例子:
<Window x:Class="WpfApplication10.wnd101" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib" Title="MainWindow" Height="120" Width="525"> <Window.Resources> <ResourceDictionary> <sys:String x:Key="_str">字串</sys:String> <sys:Double x:Key="_db">89.7898</sys:Double> </ResourceDictionary> </Window.Resources> <StackPanel> <!--C#中可以使用FindResource尋找對象資源--> <TextBlock x:Name="_txtBlock" FontSize="16" Text="{StaticResource ResourceKey=_str}" Height="28"/> </StackPanel> </Window>
首先我們將System命名空間引入XAML代碼中並映射為sys名稱空間,然後在Windows.Resource裡面添加了兩個資源條目,一個是string類型,一個是double類型。最後我們用textBlock來消費這個資源。程式運行效果如:
尋找資源時,先尋找控制項自己的Resource屬性,如果沒有這個資來源程式會沿著邏輯樹狀結構向上一級進行尋找,如果連最頂端容器都沒有這個資源,程式就會尋找Application.Resource(也就是程式的頂級資源)。如果還沒有找到,那麼就只能拋出異常了。
這就好比每個介面元素都有自己的一個背包,裡面可能裝有各種各樣的資源,使用的時候開啟找一找,如果沒有找到還可以去翻看上一層控制項的背包,直至找到這個資源或報告沒有這個資源為止。
你可能會想,如果把資源像CSS或者JS一樣放在獨立的檔案夾裡,使用時成套引用、重用時便於分發豈不更好?WPF的資源當然可以做到這一點;ResourceDictionary具有一個名為Source的屬性,只要把包含資源定義的檔案路徑賦值給這個屬性就一切搞定了!例如把皮膚以資源的形式放在XAML檔案中,使用時僅需要將相應的XAML檔案添加進項目並使用Source屬性進行引用,你的程式就立刻變的光鮮照人。
<Window.Resources> <!--用於一鍵換膚--> <ResourceDictionary Source="ShinyRed.xaml"></ResourceDictionary> </Window.Resources>
2. 且“動”且“靜”用資源
當資源被儲存進資源詞典之後,我們可以使用兩種方式來使用這些資源-----靜態方式和動態方式。靜態資源使用StackResource指的是程式載入記憶體時對資源的一次性使用,之後就不在去訪問這個資源了;動態資源(DynamicResource)使用指的是在程式運行過程中仍然會去訪問資源。顯然如果你確定某些資源在程式初始化的時候只使用一次、之後不會再改變,就應該使用StaticResource,而程式運行過程中還有可能改變資源應該以DynamicResource形式使用。拿程式的主題來舉例,如果程式的皮膚在運行過程中始終不變,以Static形式來使用資源就可以了。如果在程式運行過程中允許使用者更改皮膚或者色彩配置則必須使用DynamicResource來使用資源。
請看下面這個例子,我在Windows資源字典裡放置了兩個TextBlock類型資源,並分別以StaticResource和DynamicResource方式使用之:
<Window x:Class="WpfApplication10.wnd102" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="wnd102" Height="120" Width="300"> <Window.Resources> <ResourceDictionary> <TextBlock x:Key="_txt1" Text="海上生明月"></TextBlock> <TextBlock x:Key="_txt2" Text="海上生明月"></TextBlock> </ResourceDictionary> </Window.Resources> <StackPanel> <Button x:Name="_btn1" Content="{StaticResource _txt1}" Height="28" ></Button> <!--動態資源運行時,才能更新--> <Button x:Name="_btn2" Content="{DynamicResource _txt2}" Height="28" ></Button> <Button x:Name="_btn3" Content="Update" Height="28" Click="_btn3_Click" ></Button> </StackPanel></Window>
按鈕負責在程式運行過程中對資源詞典裡面的兩個資源進行改變:
private void _btn3_Click(object sender, RoutedEventArgs e){ this.Resources["_txt1"] = new TextBlock { Text = "更新成功" }; this.Resources["_txt2"] = new TextBlock { Text = "更新成功" };}
3. 向程式集中添加二進位資源
常見的應用程式資源有表徵圖、圖片、文本、音頻、視頻等,各種程式設計語言的編譯器或者資源編譯器都有能力把這些檔案編譯進目標檔案(最終的.exe檔案或者.dll檔案)。資源檔在目標檔案裡以位元據形式存在、形成目標檔案的資源段(Resource Section),使用時資料會被提取出來。
為了不把資源詞典裡的資源和應用程式裡面內嵌的資源搞混,我們明確稱呼資源詞典裡面的資源為“WPF資源”或“對象資源”,稱呼應用程式內嵌資源為“程式集資源”或者“二進位資源”。特別提醒一點,WPF中寫在<Application.Resource>...</Application.Resource>標籤內的資源仍然是WPF資源而非二進位資源。
下面讓我們看看如何向WPF程式中添加二進位資源並使用它們。
Resources.resx檔案內容的組織形式也是“鍵-值”對,編譯後,Resources.resx會形成Properties名稱空間中的Resource類,使用這個類的方法或屬性就能擷取資源。為了讓XAML編譯器能夠訪問這個類,一定要把Resources.resx的存取層級由Internal改為public。利用資源檔編輯器,可以資源檔的字串裡添加兩個條目,然後分別在XAML代碼和C#代碼中訪問他們。
在XAML代碼中使用Resources.resx中的資源,需要把程式的Properties名稱映射為XAML名稱空間,然後使用x:Static標籤擴充來訪問資源。
<Window x:Class="WpfApplication10.wnd103" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prop="clr-namespace:WpfApplication10.Properties" FontSize="16" Title="wnd103" Height="130" Width="340"> <StackPanel> <!--C#中可以使用Properties.Resources.訪問--> <TextBlock x:Name="_txtBlock1" Text="{x:Static prop:Resources.password}"></TextBlock> <TextBlock x:Name="_txtBlock2" Text="{x:Static prop:Resources.userName}"></TextBlock> </StackPanel></Window>
有一點特別提醒大家,如果想讓外部檔案編譯進二進位資源,必須在屬性視窗把檔案的Build Action屬性值設為Resource。並不是每種檔案都會自動化佈建為Resource,比片檔案會,MP3檔案就不會,一般情況下,如果Build Action的值設為Resource,則Copy to Output Directory屬性設定為Do Not Copy;如果不希望以資源的形式使用外部檔案,可以把Build Action屬性設定為None,而把Copy to Output Directory設定為Copy Always。另外,Build Action屬性的下拉式清單裡面有一個頗具迷惑性的值Embeded Resource,不要選擇這個值。
4. 使用PACK URI路徑訪問二進位資源
WPF對二進位資源的訪問有自己的一套方法,稱為PACK URI路徑。有時候死記硬背能夠讓讀者快速學習又能協助作者偷點懶。比如,WPF的PACK URI路徑,你只需要記住這個格式就可以了:
pack://application,,,[/程式集名稱;][可選版本號碼;][檔案夾名稱/][檔案名稱]
而實際上pack://applicationi,,,可以省略、程式集名稱和版本號碼常使用省略值,所以剩下的就只有這個了:
[檔案夾名稱/][檔案名稱]
我們看看例子:
<Window x:Class="WpfApplication10.wnd104" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="wnd104" Height="200" Width="300"> <Grid> <Image x:Name="_image" Source="Resources/Images/Aodi.jpg" Stretch="Fill"></Image> </Grid></Window>
也可使用C#訪問:
_image.Source = new BitmapImage(new Uri(@"Resources/Images/Aodi.jpg", UriKind.Relative));
參考:《深入淺出WPF》