【WPF學習】第二十八章 程序集資源

  WPF應用程序中的程序集資源與其餘.NET應用程序中的程序集資源在本質上是相同的。基本概念是爲項目添加文件,從而Visual studio可將其嵌入到編譯過的應用程序的EXE或DLL文件中。WPF程序集資源與其餘應用程序中的程序集資源之間的重要區別是引用他們的尋址系統不一樣。編程

  在前面章節已討論過程序集資源的工做原理。由於每次編譯應用程序時,項目中的每一個XAML文件都轉換爲解析效率更高的BAML文件。這些BAML文件做爲獨立資源嵌入到程序集中。添加本身的資源一樣很容易。瀏覽器

1、添加資源安全

  可經過向項目添加文件,並在Properties窗口中將其Build Action屬性設置爲Resource來添加本身的資源。這是須要完成的所有工做——這確實是好消息。app

  爲更加合理地組織資源,可在項目中建立子文件夾(在Solution Explorer中右擊項目名稱,而後選擇Add|New Folder菜單項),而後使用這些子文件夾組織不一樣類型的資源。工具

  以這種方式添加的資源易於更新。只須要替換文件並從新編譯應用程序便可。例如,可在Windows瀏覽器中將全部新文件複製到指定文件夾中。只要替換在項目中包含的文件的內容,就沒必要在Visual Studio中再採起任何其餘特殊步驟(除了實際編譯應用程序外)。測試

  爲成功地使用程序集資源,務必注意如下兩點:ui

  不能將Build Action屬性錯誤地設置爲Embedded Resource。儘管全部程序集資源都被定義爲嵌入的資源,但Embedded Resource生成操做會在另外一個更難訪問的位置放置二進制數據。在WPF應用程序中,假定老是使用Resource生成類型。this

  不要將Project Properties窗口中使用Resource選項卡。WPF不支持這種類型的資源URI。spa

  好奇的編程人員天然但願瞭解嵌入到程序集中的資源到底發生了什麼變化。WPF將他們和其餘BAML資源合併到單獨的流中。單獨的資源流使用如下格式命名AssemblyName.g.resources。.net

  若是想要實際查看在編譯過的程序集中嵌入的資源,可以使用反編譯工具。例如,使用Reflector(http://reflector.net)的更出色工具來深刻挖掘資源。

  除全部圖像和音頻文件外,還可看到用於應用程序中窗口的BAML資源。在WPF中,文件中的空格不會引發問題,由於Visual Studio足夠智能,它可以正確地略過他們。當應用程序被編譯過以後,你可能還會注意到文件名變成了小寫形式。

2、檢索資源

  顯然,添加資源很是容易,但到底如何使用他們呢?能夠採用多種方法來使用資源。

  低級方法是檢索封裝數據的StreamResourceInfo對象,而後決定如何使用該對象。可經過代碼,使用靜態方法Application.GetResourceStream()完成該工做。例如,下面的代碼爲winter.jpg圖像獲取StreamResourceInfo對象:

StreamResourceInfo sri=Application.GetResourceStream(new Uri("image/winter.jpg",UriKind.Relative));

  一旦獲得StreamResourceInfo對象,就能夠獲得兩部分信息。ContentType屬性返回一個描述數據類型的字符串——在該例中是image/jpg。Stream屬性返回一個UnmanagedMemoryStream對象,可以使用該對象讀取數據,一次讀取一個字節。

  GetResourceStream()的確是一個頗有用的輔助方法,它封裝了ResourceManager類和ResourceSet類。這些類是.NET Framework資源體系的核心,自從.NET 1.0開始就提供了這些類。若是不使用GetResourceStream()方法,就須要具體訪問AssemblyName.g.resources資源流(這是存儲全部WPF資源的地方),並查找所需的對象。下面是完成這一操做的很是簡單的代碼:

Assembly assembly=Assembly.GetAssembly(this.GetType());
string resourceName=assembly.GetName().Name+".g";
ResourceManager rm=new ResourceManager(resourceName,assembly);

using(ResourceSet set=rm.GetResourceSet(CultureInfo.CurrentCulture,tur,true))
{
    UnmanagedMemoryStream s;
    s=(UnmanagedMemoryStream)set.GetOjbect("images/winter.jpg",true);
}

  經過ResourceManager類和ResourceSet類還可完成其餘一些Application類自身不能完成的工做。例如,下面的代碼片斷會向你現實在AssemblyName.g.resources資源流中全部嵌入資源的名稱:

Assembly assembly=Assembly.GetAssembly(this.GetType());
string resourceName=assembly.GetName().Name+".g";
ResourceManager rm=new ResourceManager(resourceName,assembly);
using(ResourceSet set=rm.GetResourceSet(CultureInfo.CurrentCulture,true,ture))
{
    foreach(DictionaryEntry res in set)
    {
        MessageBox.Show(res.Key.ToSting());
    }
}

 雖然GetResourceStream()方法可提供幫助,但直接檢索資源還可能會遇到麻煩,問題是使用該方法獲得的相對低級的UnmanagedMemoryStream對象,該對象自己沒有什麼用處,須要將它轉換成一些更有意義的數據,例如具備屬性和方法的高級對象。

  WPF提供了幾個專門使用資源的類。這些類不要求提取資源(這很是混亂且不是類型安全的),他們使用資源的名稱訪問資源。例如,若是但願在WPF的Image元素中顯示Blue.jpg圖像,可以使用下面的標記:

<Image Source="Images/Blue.jpg"></Image>

  注意反斜槓變成了正斜槓,由於這是WPF做用URI的約定(實際上這兩種方式均可行,但爲了連貫起見,建議使用正斜槓)。

  可以使用代碼完成相同的工做。對於Image元素,只須要將Source屬性設置爲BitmapImage對象,該對象使用URI肯定但願顯示的圖像的位置,能夠像下面這樣指定徹底限定的文件路徑:

img.Source = new BitmapImage(new Uri("d:\images\winter.jpg",));

  但若是使用相對URI,就可從程序集中提取不一樣資源,並將他們傳遞給圖像,並且不須要使用UnmanagedMemoryStream對象:

img.Source = new BitmapImage(new Uri("images/winter.jpg", UriKind.Relative));

  該技術經過在基本應用程序URI的末尾處加上images/winter.jpg構造了URI。大多數狀況下不須要考慮URI語法——只要遵循相對URI,剩下的工做就由程序集負責了。然而有些狀況下,更詳細理解URI系統的很是重要的,當但願訪問嵌入到另外一個程序集中額資源時更是如此。

3、pack URI

  WPF使用pack URI語法尋址編譯過的資源(好比用於頁面的BAML)。上一節的Image對象和標籤使用相對URI來引用資源,以下所示:

  images/winter.jpg

  這與下面更繁瑣的絕對URI是等效的:

  pack://application:,,,/images/winter.jpg

  當爲一幅圖像設置源時可以使用這種絕對URI,儘管這種方法沒有任何優勢:

img.Source = new BitmapImage(new Uri("pack://application:,,,/images/winter.jpg"));

  pack URI語法來自XPS(XML Paper Specification,XML頁面規範)標準。它看起來很是奇怪,由於它在一個URI中嵌入了另外一個URI。三個逗號實際上時三個轉義的斜槓。換句話說,上面顯示的包含應用成功需URI的pack URI是以application:///開頭的。

  位於其餘程序集中的資源

  使用pack URI還可檢索嵌入到另外一個庫中的資源(換句話說,在應用程序中使用的DLL程序集中的資源)。這種狀況下須要使用以下語言:

pack://application:,,,/AssemblyName;component/ResourceName

  例如,若是圖像唄嵌入到引用的名爲ImageLibrary的程序集中,將須要使用以下URI:

img.Source=new BitmapImage(new Uri("pack://application:,,,/ImageLibrary;component/images/winter.jpg"));

  或從更實用的角度看,可以使用等價的相對URI:

img.Source=new BitmapImage(new Uri("ImageLibrary;component/images/winter.jpg",UriKind.Relative));

  若是使用強命名的程序集,可以使用包含版本和/或公鑰標記的限定程序集引用代替程序集的名稱。使用分號隔離每段信息,並在版本號數字以前添加字符v.下面是一個使用版本號的示例:

image.Source=new BitmapImage(new Uri("ImageLibrary;v1.25;component/images/winter.jpg",UriKind.Relative));

  下面的示例同時使用了版本號和公鑰標記:

image.Source=new BitmapImage(new Uri("ImageLibrary;v1.25;dc642a7f5bd64912;component/images/winter.jpg",UriKind.Relative));

4、內容文件

  當嵌入文件做爲資源時,會將文件放到編譯過的程序集中,而且能夠確保文件老是可用的。對於部署而言這是理想選擇,而且可避免可能存在的問題。然而在有些狀況下,使用這種方法並不方便:

  •   但願改變資源文件,又不想從新編譯應用程序。
  •   資源文件很是大。
  •   資源文件是可選的,而且能夠不隨程序集一塊兒部署。
  •   資源是聲音文件。

  顯然,可事業可以應用程序部署文件,併爲應用程序添加代碼,進而從硬盤驅動器中讀取這些文件來解決該問題。然而,WPF還有更方便的選擇,使這一過程更加容易管理。可將這些未編譯的文件專門標記爲內容文件。

  不能將內容文件嵌入到程序集中。然而,WPF爲程序集添加了AssemblyAssociatedContentFile特性,公告每一個內容文件的存在。該特性還記錄了每一個內容文件相對可執行文件的位置(指示內容文件是否和可執行文件位於同一文件夾中,或者位於某個子文件夾中)。最方便的是,當爲可以理解資源的元素(如Image類)使用內容文件時,可以使用相同的URI系統。

  爲測試該技術,爲項目添加聲音文件,在Solution Exporer中選擇該文件,並在Properties窗口中將Build Action屬性改成Content,確保將Copy to Output Directory屬性設置爲Copy Always,以確保當生產項目時將聲音文件複製到輸出目錄中。

  如今可以使用相對URI,將MediaElement元素指向內容文件:

<MediaElement Name="Sound" Source="Sounds/start.wav" LoadedBehavior="Manual"></MediaElement>
相關文章
相關標籤/搜索