深刻淺出話資源

轉自:http://blog.csdn.net/lisenyang/article/details/18312503

咱們把有用的東西稱爲資源。「兵馬未動,糧草先行」-----程序中的各類數據就是算法的原料和糧草。程序中能夠存放數據的地方有不少,能夠放在數據庫裏、能夠存儲在變量裏。介於數據庫存儲和變量存儲之間,咱們還能夠把數據存儲在程序主體以外的文件裏。外部文件與程序主體分離,這就有可能丟失或者損壞,編譯器容許咱們把外部文件編譯進程序主體、稱爲程序主體不可分割的一部分。這就是傳統意義上的程序資源(也稱爲二進制資源)。html

WPF不但支持程序級的傳統資源,同時還推出了獨具特點的對象級資源,每一個界面元素均可以攜帶本身的資源並可被本身的子級元素共享。好比後面的章節咱們會講到模板、程序樣式和主題就常常放在對象資源裏面。這樣一來,在WPF程序中數據就分爲4個等級存儲了:數據庫裏的數據至關於存放在倉庫裏面,資源文件裏的數據就至關於放進了旅行箱裏,WPF對象資源裏面的數據至關於存放在攜帶的揹包裏,變量裏面的數據至關於拿在手裏。算法

1.1       WPF對象資源的定義和查找數據庫

每一個WPF界面元素都有一個名爲Resource的屬性,這個屬性繼承至FrameworkElement類,其類型爲ResourceDictionary。ResourceDictionary可以以鍵值對的形式存儲資源,當要使用到某個資源的時候,使用鍵值對的形式獲取資源對象。在保存資源時,ResourceDictionary視資源對象爲Object類型,因此再使用資源時先要對資源對象進行類型轉換,XAML編譯器可以根據Attribute自動識別資源類型,若是類型不對就會拋出異常,但在C#中檢索到資源對象以後,類型轉換的事情就只能由咱們本身來作了。編程

ResourceDictionary能夠存儲任意類型的對象。在XAML代碼中向Resource添加資源時須要把正確的命名空間引入到XAML代碼中,讓咱們來看一個例子:app

 

[html]  view plain copy print ?
 
 
  1. <Window x:Class="WpfApplication1.Window31"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         xmlns:sys="clr-namespace:System;assembly=mscorlib"  
  5.         Title="Window31" FontSize="16" SizeToContent="Height">  
  6.     <Window.Resources>  
  7.         <ResourceDictionary>  
  8.             <sys:String x:Key="str">沉舟側畔千帆過,病樹前頭萬木春。</sys:String>  
  9.             <sys:Double x:Key="db">3.1415926</sys:Double>  
  10.         </ResourceDictionary>  
  11.     </Window.Resources>  
  12.     <StackPanel>  
  13.         <TextBlock Text="{ StaticResource ResourceKey=str}"></TextBlock>  
  14.         <!--<TextBlock Text="{StaticResource ResourceKey=db}"></TextBlock>-->  
  15.     </StackPanel>  
  16. </Window>  


首先咱們將System命名空間引入XAML代碼中並映射爲sys名稱空間,而後在Windows.Resource裏面添加了兩個資源條目,一個是string類型,一個是double類型。最後咱們用兩個textBlock來消費這兩個資源(被註釋掉的代碼由於數據類型不匹配而拋出異常)。程序運行效果以下圖:編程語言

 

由於在XAML代碼裏面能夠對集合類容及標籤擴展進行簡寫,因此上面代碼更常見的書寫格式是這樣:編輯器

 

[html]  view plain copy print ?
 
 
  1. <Window x:Class="WpfApplication1.Window31"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         xmlns:sys="clr-namespace:System;assembly=mscorlib"  
  5.         Title="Window31" FontSize="16" SizeToContent="Height">  
  6.     <Window.Resources>  
  7.             <sys:String x:Key="str">沉舟側畔千帆過,病樹前頭萬木春。</sys:String>  
  8.             <sys:Double x:Key="db">3.1415926</sys:Double>  
  9.     </Window.Resources>  
  10.     <StackPanel>  
  11.         <TextBlock Text="{ StaticResource str}"></TextBlock>  
  12.         <!--<TextBlock Text="{StaticResource ResourceKey=db}"></TextBlock>-->  
  13.     </StackPanel>  
  14. </Window>  

再查找資源時,先查找控件本身的Resource屬性,若是沒有這個資源程序會沿着邏輯樹向上一級進行查找,若是連最頂端容器都沒有這個資源,程序就會查找Application.Resource(也就是程序的頂級資源)。若是尚未找到,那麼就只能拋出異常了。工具

 

這就比如每一個界面元素都有本身的一個揹包,裏面可能裝有各類各樣的資源,使用的時候打開找一找,若是沒有找到還能夠去翻看上一層控件的揹包,直至找到這個資源或報告沒有這個資源爲止。學習

若是想在C#代碼裏面使用XAML代碼裏面定義的資源,大概格式是這樣:ui

 

[csharp]  view plain copy print ?
 
 
  1. private void Window_Loaded(object sender, RoutedEventArgs e)  
  2. {  
  3.     string text = (string)this.FindResource("str");  
  4.     txt0.Text = text;  
  5. }  


或者你明確知道資源放在那個資源字典裏,就能夠這樣檢索資源:

 

 

[csharp]  view plain copy print ?
 
 
  1. private void Window_Loaded(object sender, RoutedEventArgs e)  
  2. {  
  3.     string text = (string)this.Resources["str"];  
  4.     txt0.Text = text;  
  5. }  


你可能會想,若是把資源想CSS或者JS同樣放在獨立的文件夾裏,使用時成套引用、重用時便於分發豈不更好?WPF的資源固然能夠作到這一點;ResourceDictionary具備一個名爲Source的屬性,只要把包含資源定義的文件路徑賦值給這個屬性就一切搞定了!舉個例子,http://wpf.codeplex.com中包含了不少官方/半官方的WPF資源,其中包括WPF工具包和一組很是漂亮的程序皮膚,這些皮膚以資源的形式放在XAML文件中,使用時僅須要將相應的XAML文件添加進項目並使用Source屬性進行引用,你的程序就馬上變的光鮮照人。

 

 

[html]  view plain copy print ?
 
 
  1. <ResourceDictionary Source="ShinyRed.xaml">  
  2.               
  3. </ResourceDictionary>  

運行效果以下圖:

 

1.2         且「動」且「靜」用資源

當資源被存儲進資源詞典以後,咱們可使用兩種方式來使用這些資源-----靜態方式和動態方式。Static和Dynamic兩個詞都是咱們的老朋友了,當這對詞同時出現的時候Static指的是程序的非執行狀態而Dynamic指的是程序的運行狀態。對於資源的使用,Static和Dynamic也是這個意思。靜態資源使用StackResource指的是程序載入內存時對資源的一次性使用,以後就不在去訪問這個資源了;動態資源(DynamicResource)使用指的是在程序運行過程當中仍然回去訪問資源。顯然若是你肯定某些資源在程序初始化的時候只使用一次、以後不會再改變,就應該使用StaticResource,而程序運行過程當中還有可能改變資源應該以DynamicResource形式使用。拿程序的主題來舉例,若是程序的皮膚在運行過程當中始終不變,以Static形式來使用資源就能夠了。若是在程序運行過程當中容許用戶更改皮膚或者配色方案則必須使用DynamicResource來使用資源。

請看下面這個例子,我在Windows資源字典裏放置了兩個TextBlock類型資源,並分別以StaticResource和DynamicResource方式使用之:

 

[html]  view plain copy print ?
 
 
  1. <Window x:Class="WpfApplication1.Window32"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         Title="Window32" FontSize="16" WindowStyle="ToolWindow">  
  5.     <Window.Resources>  
  6.         <ResourceDictionary>  
  7.             <TextBlock x:Key="res1">海上生明月</TextBlock>  
  8.             <TextBlock x:Key="res2">海上生明月</TextBlock>  
  9.         </ResourceDictionary>  
  10.     </Window.Resources>  
  11.     <StackPanel>  
  12.         <Button Content="{StaticResource res1}" Margin="5"></Button>  
  13.         <Button Content="{DynamicResource res2}" Margin="5"></Button>  
  14.         <Button Content="Update" Margin="5" Click="Button_Click"></Button>  
  15.     </StackPanel>  
  16. </Window>  


界面上的第三個按鈕負責在程序運行過程當中對資源詞典裏面的兩個資源進行改變:

 

 

[csharp]  view plain copy print ?
 
 
  1. private void Button_Click(object sender, RoutedEventArgs e)  
  2. {  
  3.     this.Resources["res1"] = new TextBlock() { Text="天涯共此時"};  
  4.     this.Resources["res2"] = new TextBlock() { Text = "天涯共此時" };  
  5. }  

實際上,由於第一個按鈕是以靜態方式使用資源,儘管資源已經更新它也不知道。運行程序,單擊第三個按鈕,效果以下圖:

 

   

1.3       向程序集中添加二進制資源

對於資源這個概念,對於WPF初學者會感到迷惑,由於早在WPF出現以前Window應用程序就已經可以攜帶資源了。Windows應用程序資源的道理和WinZip或WinRAR壓縮包的原理差很少,其實是吧一些應用程序必須使用的資源和應用程序自身打包在一塊兒,這樣資源就不會意外丟死了(反作用就是應用程序體積會變大)。常見的應用程序資源有圖標、圖片、文本、音頻、視頻等,各類編程語言的編譯器或者資源編譯器都有能力把這些文件編譯進目標文件(最終的.exe文件或者.dll文件)。資源文件在目標文件裏以二進制數據形式存在、造成目標文件的資源段(Resource Section),使用時數據會被提取出來。

爲了避免把資源詞典裏的資源和應用程序裏面內嵌的資源搞混,咱們明確稱呼資源詞典裏面的資源爲「WPF資源」或「對象資源」,稱呼應用程序內嵌資源爲「程序集資源」或者「二進制資源」。特別提醒一點,WPF中寫在<Application.Resource>...</Application.Resource>標籤內的資源仍然是WPF資源而非二進制資源。

下面讓咱們看看如何向WPF程序中添加二進制資源並使用它們。

若是要添加的資源是字符串而非文件,咱們可使用應用程序名稱空間下的Resources.resx資源文件。打開資源文件的方法是項目管理器中展開Properties文件夾,並雙擊下面的Resources.resx資源文件。以下圖所示:

 

Resources.resx文件內容的組織形式也是「鍵-值」對,編譯後,Resources.resx會造成Properties名稱空間中的Resource類,使用這個類的方法或屬性就能獲取資源。爲了讓XAML編譯器可以訪問這個類,必定要把Resources.resx的訪問級別由Internal改成public。利用資源文件編輯器,能夠資源文件的字符串裏添加兩個條目,而後分別在XAML代碼和C#代碼中訪問他們。

在XAML代碼中使用Resources.resx中的資源,須要把程序的Properties名稱映射爲XAML名稱空間,而後使用x:Static標籤擴展來訪問資源。

 

[html]  view plain copy print ?
 
 
  1. <Window x:Class="WpfApplication1.Window33"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         xmlns:prop="clr-namespace:WpfApplication1.Properties"  
  5.         Title="Window33" Height="300" Width="300">  
  6.     <Grid>  
  7.         <Grid.RowDefinitions>  
  8.             <RowDefinition Height="23" />  
  9.             <RowDefinition Height="4" />  
  10.             <RowDefinition Height="23" />  
  11.         </Grid.RowDefinitions>  
  12.         <Grid.ColumnDefinitions>  
  13.             <ColumnDefinition Width="Auto" />  
  14.             <ColumnDefinition Width="4" />  
  15.             <ColumnDefinition Width="*" />  
  16.         </Grid.ColumnDefinitions>  
  17.         <TextBlock x:Name="txtName" Text="{x:Static prop:Resources.userName}"></TextBlock>  
  18.         <TextBlock x:Name="txtPass" Grid.Row="2"></TextBlock>  
  19.         <TextBox BorderBrush="Black" Grid.Column="2"></TextBox>  
  20.         <TextBox BorderBrush="Black" Grid.Row="2" Grid.Column="2"></TextBox>  
  21.     </Grid>  
  22.      
  23. </Window>  

C#中訪問Resources.resx的資源與使用通常的別無二致:

 

 

[csharp]  view plain copy print ?
 
 
  1. public Window33()  
  2. {  
  3.     InitializeComponent();  
  4.     this.txtPass.Text = Properties.Resources.pass;  
  5. }  

運行效果以下圖:

 

使用Resources.resx最大的好處就是便於程序國際化,本地化。若是你想把界面改成英文版,只須要把資源裏的值改成英文就能夠了,以下圖所示,由於在程序中訪問資源使用的是資源的名,因此代碼無需改動:

若是要添加的資源不是字符串,而是圖標、圖片、音頻或者視屏。方法就不是使用Resources.resx了,WPF不支持這麼作。在WPF使用外部文件做爲資源,僅須要將其簡單的放入項目便可。方法是在項目管理器上右擊項目名稱,在彈出的菜單裏選擇New-->NewFolder,按須要新建幾層文件夾來存放資源,而後在恰當的文件夾上右擊,在彈出的菜單裏選擇Add--->Existing Item...,在文件對話框裏選擇文件後單擊Add按鈕,文件就以資源的形式加入項目中了。

若是在程序裏面添加一個MP3文件和一個圖片文件,結果文件的體積會膨脹好幾兆。以下圖:

有一點特別提醒你們,若是想讓外部文件編譯進二進制資源,必須在屬性窗口把文件的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,不要選擇這個值。

1.4                使用PACK URI路徑訪問二進制資源

WPF對二進制資源的訪問有本身的一套方法,稱爲PACK URI路徑。有時候死記硬背可以讓讀者快速學習又能幫助做者偷點懶。好比,WPF的PACK URI路徑,你只須要記住這個格式就能夠了:

 

[html]  view plain copy print ?
 
 
  1. pack://application,,,[/程序集名稱;][可選版本號;][文件夾名稱/][文件名稱]  


而實際上pack://applicationi,,,能夠省略、程序集名稱和版本號常使用省略值,因此剩下的就只有這個了:

 

 

[html]  view plain copy print ?
 
 
  1. [文件夾名稱/][文件名稱]  

前面的例子中,咱們向資源中添加了一張名爲20090102191236877.gif的圖片,它在項目中的路徑是Resource/Image/20090102191236877.gif,原封不動,使用這個路徑就能夠訪問到圖片了。咱們用這個圖片填充一個<Image/>元素並把<image/>元素做爲窗體的背景。

 

 

[html]  view plain copy print ?
 
 
  1. <Window x:Class="WpfApplication1.Window34"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         Title="Window34">  
  5.     <Grid>  
  6.         <Image Source="Resource/Image/20090102191236877.gif" x:Name="img0" Stretch="Fill"></Image>  
  7.     </Grid>  
  8. </Window>  

 

 

[html]  view plain copy print ?
 
 
  1. <Window x:Class="WpfApplication1.Window34"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         Title="Window34">  
  5.     <Grid>  
  6.         <Image Source="pack://application:,,,/Resource/Image/20090102191236877.gif" x:Name="img0" Stretch="Fill"></Image>  
  7.     </Grid>  
  8. </Window>  

與之等價的C#代碼以下:

 

 

[csharp]  view plain copy print ?
 
 
  1. public Window34()  
  2. {  
  3.     InitializeComponent();  
  4.     Uri imageURI = new Uri(@"Resource/Image/20090102191236877.gif",UriKind.Relative);  
  5.     this.img0.Source = new BitmapImage(imageURI);  
  6. }  

 

 

[csharp]  view plain copy print ?
 
 
  1. public Window34()  
  2. {  
  3.     InitializeComponent();  
  4.     Uri imageURI = new Uri(@"pack://application:,,,/Resource/Image/20090102191236877.gif",UriKind.Absolute);  
  5.     this.img0.Source = new BitmapImage(imageURI);  
  6. }  


運行效果以下圖所示:

 

在使用pack uri路徑時有如下幾點須要注意:

 

    • Pack URI使用的是從右向左的正斜線(/)表示路徑。
    • 使用所略寫意味着相對路徑,C#代碼中的UriKind必須爲Relative並且表明根目錄的/能夠省略。
    • 使用完整寫法時是絕對路徑,C#代碼中的UriKind必須爲Absolute而且表明根目錄的/不能省略。
    • 使用相對路徑能夠藉助相似DOS的語法進行導航,好比./表明同級目錄,../表明父級目錄。
相關文章
相關標籤/搜索