熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

Microsoft .NET 框架資源基礎

2022-06-13   來源: .NET編程 

  摘要Chris Sells 討論無類型清單資源和有類型資源它們是受 Microsoft NET 框架支持的兩種資源他定義了這兩種資源並介紹了如何在您自己的應用程序中使用它們下載 winformsexe 示例文件

  假設要在應用程序中通過從文件加載位圖來設置窗體的背景圖像

  public Form() { // Load a file from the file system thisBackgroundImage = new Bitmap(@C:\WINDOWS\Web\Wallpaper\Azuljpg); }

  該代碼的問題是並非所有 Microsoft Windows 的安裝實例都有 Azuljpg即使是那些確實具有該文件的安裝實例該文件可能也不在安裝實例的相同位置即使您與應用程序一起交付該圖片節省空間的用戶也可能決定刪除它這會導致您的應用程序出錯確保圖片或任何文件與代碼在一起的唯一安全方式是將它作為資源嵌入並加載

清單資源

  資源是在編譯時添加到程序集中的例如如果您使用命令行編譯器則可以使用 /resource 開關嵌入資源

  C:\>cscexe myAppcs /resource:c:\windows\web\wallpaper\Azuljpg

  /resource 開關將文件作為資源嵌入嵌入時使用文件名(沒有路徑)作為資源名稱文件嵌入到程序集的清單 資源集中程序集的清單由一組作為程序集一部分的元數據組成該元數據的一部分是與每個嵌入資源關聯的名稱和數據執行 ildasm 時可以在清單部分看見程序集清單資源的列表如圖 所示

  C:\>ildasmexe myAppexe




winforms02202003-fig01

  圖 ildasm 顯示嵌入資源


  可以像 ildasm 一樣枚舉清單資源的列表這需要使用 systemreflectionassembly 類的 getmanifestresourcenames 方法

  using SystemReflection; // Get this types assembly Assembly assem = thisGetType()Assembly; // Enumerate the assemblys manifest resources foreach( string resourceName in assemGetManifestResourceNames() ) { MessageBoxShow(resourceName); }

  一旦通過枚舉清單資源或硬編碼一個您想要的清單資源而知道了清單資源的名稱就可以通過 assembly 類的 getmanifestresourcestream 方法將該清單資源作為原始字節流進行加載如下所示

  using SystemIO; public Form() { // Get this types assembly Assembly assem = thisGetType()Assembly; // Get the stream that holds the resource // NOTE: Make sure not to close this stream! // NOTE: Also be very careful to match the case // on the resource name itself Stream stream = assemGetManifestResourceStream(Azuljpg); // Load the bitmap from the stream thisBackgroundImage = new Bitmap(stream); }

  因為資源可以像類型名稱一樣有沖突所以最好用資源自己的命名空間來嵌入資源該操作可以使用 /resource 開關的擴展格式來完成

  C:\>csc myAppcs /resource:c:\\azuljpgResourcesAppAzuljpg

  注意在要嵌入的文件名的逗號後面使用的備用資源名稱備用資源名稱允許您為資源任意地提供時間嵌套名稱不管文件名是什麼它是設置在程序集中的備用名稱如圖 所示


 

  winforms02202003-fig02



  圖 使用備用名稱的嵌入資源


  下面是使用備用名稱的更新後的資源加載代碼

  public Form() { // Get this types assembly Assembly assem = thisGetType()Assembly; // Load a resource with an alternate name Stream stream = assemGetManifestResourceStream(ResourcesAppAzuljpg); // Load the bitmap from the stream thisBackgroundImage = new Bitmap(stream); }

  為了更方便如果您的資源和加載資源的類碰巧使用了相同的命名空間則可以將類的類型作為可選的第一參數傳遞給 getmanifestresourcestream

  namespace ResourcesApp { public class Form : Form { public Form() { // Get this types assembly Assembly assem = thisGetType()Assembly; // Load the resource using a namespace // Will load resource named ResourcesAppAzuljpg Stream stream = assemGetManifestResourceStream(thisGetType() Azuljpg); // Load the bitmap from the stream thisBackgroundImage = new Bitmap(stream); } } }

  GetManifestResourceStream 將使用如下格式編寫資源名稱

  <namespace><fileName>

  在加載某些類型(比如 bitmap 類)時使用類型和文件名也是有用的這樣可以通過提供構造函數避免由您自己打開流

  namespace ResourcesApp {
public class Form : Form { public Form() {
// Get this types assembly Assembly assem = thisGetType()Assembly;
// Load the bitmap directly from the manifest resources thisBackgroundImage = new Bitmap(thisGetType() Azuljpg);
} }}

Visual Studio NET 中的清單資源

  如果(大多數情況下)您使用 Visual Studio?NET 來開發和構建程序集則用命令行嵌入清單資源的方法不可能非常吸引您這種情況下您可以將資源添加到 Windows 窗體項目中該方法將把合適的命令行參數傳遞給編譯器

  要將資源添加到項目中請在 Solution Explorer 中右鍵單擊項目然後選擇 add New Item並選擇您想作為資源嵌入的文件文件將復制到項目的目錄中但仍然不會被嵌入要使文件作為資源嵌入請右鍵單擊文件並選擇 properties然後將 Build Action 從 content(默認)更改為 embedded Resource如圖 所示




winforms02202003-fig03

  圖 將文件的 Build Action 設置為 Embedded Resource


  這種嵌入資源的方法會使 Visual Studio NET 為您創建一個備用資源名其組成類似這樣

  <defaultNamespace><folderName><fileName>

  資源名稱的默認命名空間部分是項目本身的默認命名空間它是通過 Solution Explorer>(右鍵單擊)>properties>common Properties>general>default Namespace 來設置的由於這是在生成新類時新類得到的相同命名空間所以這就使通過使用類型和部分資源名稱來加載資源變得很方便如果文件碰巧位於項目的子文件夾中就需要在文件夾名稱中包括子文件夾並用點替換反斜槓例如一個名為 Azuljpg 的位圖位於項目根下面的 foo\bar 文件夾中要加載它就需要這樣做

  // If this code called within the ResourcesAppForm class // and the file is \foo\bar\Azuljpg // will load ResourcesAppfoobarAzuljpg thisBackgroundImage = new Bitmap(thisGetType() foobarAzuljpg);


 

有類型資源

  盡管文件有擴展名但清單資源是在沒有類型信息的情況下嵌入的例如如果 Azuljpg 文件的名稱實際上是 Azulquux這對於 bitmap 類來說是沒有差別的因為這個類將通過查看數據本身來確定其類型(JPEGPNGGIF 等)這就需要由您來將每個資源的類型正確映射為加載該資源所需的對象的類型

  但如果您願意多走一步則可以用一個類型來標記資源 框架支持用於資源的一組擴展元數據其中包括兩種格式的 MIME 類型信息一個是文本格式另一個是二進制格式這兩種格式都有內置的讀取器以便在運行時取得類型正確的資源

  基於文本的格式是特定 框架的 XML 格式稱為 ResX(resx 文件)不考慮其 XML 基礎該格式不是專門為人工閱讀而設計的(XML 格式很少是這樣的)但是Visual Studio NET 仍然為 resx 文件提供了一個基本編輯器要將新的 resx 文件添加到 Visual Studio NET 項目中請從 project 菜單中選擇 add New Item然後選擇 assembly Resource File 模板如圖 所示




winforms02202003-fig04

  圖 resx 文件添加到項目中


  到寫本文時為止即使空的 resx 文件也是 行 XML而其中大多數是架構信息架構允許 resx 文件中有任意數目的項每項都有名稱注釋類型和 MIME 類型 顯示了有兩個項的 resx 文件即名為 mystring 的字符串和名為 myimage 的圖像




winforms02202003-fig05

  圖 設計器的數據視圖中簡單的 resx 文件


  遺憾的是只有字符串項能夠在 resx 編輯器的數據視圖中實際進行編輯任何二進制數據都需要手動直接輸入到 XML 中(而且只能是 base 編碼)因此直接使用 resx 文件只對字符串資源有用(盡管間接使用會使 resx 文件對任何種類的數據都非常有用我們隨後將討論這一點)

  來自 systemresources 命名空間的 resxresourcereader 類將分析 XML 文件並公開一組命名的有類型的值要取得具體的項需要查找它

  using SystemCollections; using SystemResources; public Form() { using( ResXResourceReader reader = new ResXResourceReader(@Resourceresx) ) { foreach( DictionaryEntry entry in reader ) { if( entryKeyToString() == MyString ) { // Set form caption from string resource thisText = entryValueToString(); } } } }

  使用 add New Item 對話框將 resx 文件添加到項目中會使該文件作為 Embedded Resource 添加進項目而編譯項目時則會導致 resx 數據作為嵌套資源 嵌入(嵌套資源是分組到命名容器中的資源)容器的名稱與作為資源添加的任何文件相同只是不使用 resx 擴展名使用 resource 擴展名假定一個項目的默認命名空間是 resourcesapp 而 resx 文件名為 resourcesresx則嵌套資源的容器名為 resourcesappresourcesresx如圖 中的 ildasm 所示




winforms02202003-fig06

  圖 嵌入的 resources 文件


  resources 擴展名來自於在將 resx 文件作為資源嵌入之前 Visual Studio NET 處理該文件時所使用的工具工具名稱是 resgenexe它用來將 resx XML 格式編譯為二進制格式可以手動將 resx 文件編譯成 resources 文件如下所示

  C:\> resgenexe Resourceresx

  在將 resx 文件編譯成 resources 文件以後就可以使用 systemresources 命名空間中的 resourcereader 來枚舉它

  using( ResourceReader reader = new ResourceReader(@Resourceresources) ) { foreach( DictionaryEntry entry in reader ) { string s = stringFormat({} ({})= {} entryKey entryValueGetType() entryValue); MessageBoxShow(s); } }

  除了類的名稱和輸入格式ResourceReader 類的使用方法與 resxresourcereader 相同包括都不能隨機訪問命名項

  所以雖然您可以將 resx 文件轉換成 resources 文件並使用 /resource 編譯器命令行開關嵌入它但容易得多的方法是直接在項目中讓 Visual Studio NET 接受被標記為 Embedded Resources 的 resx 文件然後將它編譯進 resources 文件並嵌入它如圖 和圖 所示一旦將 resources 文件捆綁為資源訪問 resources 文件中的資源就只需執行兩個步驟的過程

  // Load embedded resources file using( Stream stream = assemGetManifestResourceStream( thisGetType() Resourceresources) ) { // Find resource in resources file using( ResourceReader reader = new ResourceReader(stream) ) { foreach( DictionaryEntry entry in reader ) { if( entryKeyToString() == MyString ) { // Set form caption from string resource thisText = entryValueToString(); } } } }

  因為 resourcereader 和 resxresourcereader 都需要該兩步過程才能找到具體的資源因此 NET 框架提供了 resourcemanager 類該類公開了一個更簡單的使用模型


 


 

資源管理器

  ResourceManager 類也來自 SystemResources 命名空間該類包裝了 ResourceReader用於在構造時枚舉資源並使用其名稱公開它們

  public Form() { // Get this types assembly Assembly assem = thisGetType()Assembly; // Load the resources file into the ResourceManager // Assumes a file named Resourceresx as part of the project ResourceManager resman = new ResourceManager(ResourcesAppResource assem); // Set form caption from string resource thisText = (string)resmanGetObject(MyString); // The hard way thisText = resmanGetString(MyString); // The easy way }

  用來查找 resources 文件的命名方式與命名任何其他種類的資源相同(注意追加到 resourceresources 文件中的項目默認命名空間的使用方法)只是 resources 擴展名是假定的並且不能包括在名稱中為了更方便如果您碰巧將一個 resx 文件命名為類型名稱resources 文件和程序集的名稱將從類型確定

  // Use the type to determine resource name and assembly ResourceManager resman = new ResourceManager(thisGetType());

  一旦已經創建了資源管理器的實例就可以通過使用 getobject 方法並強制轉換為合適的類型從而按名稱找到嵌套資源如果使用 resx 文件來處理字符串資源則可以使用 getstring 方法該方法將執行到 systemstring 類型的強制轉換


 

設計器資源

  缺少用於 resx 文件的合適的編輯器使它們在使用除字符串資源以外的任何其他資源時非常困難您不僅必須通過手動編寫代碼才能在運行時輸入數據而且無法在設計時看見資源的使用情況例如窗體的背景圖像

  幸運的是設計器再次在這裡幫助了我們如果打開 Visual Studio NET Solution Explorer並選擇 show All Files 按鈕您將看見每個組件(無論它是窗體控件還是簡單的組件)都有相應的 resx 文件這是為了讓資源與組件的屬性保持關聯這種關聯是在 Property Browser 中設置的例如如果設置窗體的 backgroundimage 屬性那麼不僅在設計器中窗體將顯示背景圖像而且窗體的 resx 文件將包含該圖像的對應項同樣如果在相同窗體上設置 PictureBox 控件的 image 屬性resx 文件同樣會增大以便包括該資源這兩項都可以在圖 中看到




winforms02202003-fig07

  圖 組件的 resx 文件


  每個組件的 resx 文件將作為 resources 文件進行編譯和嵌入就像已經將您自己的 resx 文件添加到項目中一樣這將使資源能夠在運行時被組件使用除了組件的 resx 文件中的項之外設計器還會將代碼添加到 initializecomponent 中以便加載組件的資源管理器並使用從資源獲得的對象來填充組件的屬性

  namespace ResourcesApp { public class Form : Form { private void InitializeComponent() { ResourceManager resources = new ResourceManager(typeof(Form)); thispictureBoxImage = (SystemDrawingBitmap)resourcesGetObject(pictureBoxImage); thisBackgroundImage = (SystemDrawingBitmap)resourcesGetObject($thisBackgroundImage); } } }

  注意 resourcemanager 對象是使用組件的類型來構造的該類型用來構造組件的 resources 資源名稱還要注意設計器在命名資源時所使用的命名約定對於組件字段上的屬性名稱的格式是

  <fieldName><propertyName>

  對於組件本身的屬性名稱格式是

  $this<propertyName>

  如果您想添加供組件本身使用的自定義字符串屬性您可以這樣做但要確保與設計器生成的名稱格式不同


From:http://tw.wingwit.com/Article/program/net/201311/11450.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.