c# wpf如何更好的使用Application程序集資源

  這一篇單獨拿出來分析這個程序集資源,為的就是不想讓大傢把程序集資源和exe程序強關聯,因為程序集資源實際上是二進制資源,後續編譯過程中會被嵌入到程序集中,而為瞭更方便的使用資源,我們要好好梳理一下程序集資源相關的知識。(例如多語言資源,多工程、多項目使用的公共資源文件)。

1)在程序集中添加資源

我們通過向項目添加文件並嘗試修改資源文件屬性看有什麼不同的結果。

在工程上右鍵=》添加=》新建文件夾=》改名為Images=》回車=》在Images文件夾上右鍵=》添加=》現有項=》右下角文件類別選擇所有文件=》找到你想導入的圖片=》點擊添加,結果如下圖.

這樣就添加好瞭一個文件,我們在添加的圖片上右鍵屬性看到生成操作為Resource,我們添加代碼如下:

<Window x:Class="ApplicationResources.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:ApplicationResources"
  mc:Ignorable="d"
  Title="MainWindow" Height="450" Width="800">
 <Grid>
  <Image Source="/Images/takenotes.png" Width="230" Height="130"/> 
 </Grid>
</Window>

我們在工程上點擊編譯,生成成功後我們去看編譯結果,發現文件夾下沒有這個image,我們使用反編譯ILSpy工具查看生成的這個exe,我們看到Png文件作為資源被放在瞭resources裡,(這裡有一個小插曲,不知道為什麼,我的PNG圖片在最開始反編譯的時候沒有。直到我添加瞭上面Image的代碼反編譯才出來。以後會分析這個問題。這裡記錄一下)

而修改圖片的屬性為Content並修改復制到輸出目錄為始終復制。重新編譯工程。

我們看到瞭Debug文件夾下多瞭一個Images文件夾,裡面包含瞭我們的圖片。而反編譯程序集集中就沒有這個資源文件瞭,它從資源裡更換到目錄下瞭。這樣的話,程序集因為沒有包含瞭資源文件大小就發生瞭變化。

通過修改資源的屬性,資源在編譯過程中會放置在不同的地方,這是其中2種比較常用的設置屬性。通過這種設置程序集資源的方式易於更新,隻需要在桌面資源管理器種替換掉文件並重新編譯程序就能完成資源文件的替換,非常方便。資源文件可以放在主工程文件下,也可以選擇放在單獨的DLL下,具體看你的設計需要啦。因為接下來我們會詳細講如何讀取這些資源。即使跨瞭DLL。

2)在程序集中查找資源

  因為是在講Application的資源所以我們先看Application下對資源的查找方法,在App.xaml的Application上按下F12:

我們看到瞭返回StreamResourceInfo類型的方法又3個。GetContentStream()、GetRemoteStream()、GetResourceStream()。這裡隻講GetResourceStream()其他的可以自己在對應的方法上按F1查看文檔。(記得把資源屬性設置成Resouce)

我們使用這種在程序集管理所有資源的方式,我們可以自己實現很多種自己管理資源的想法。可以把代碼添加到自己的工程裡調試一下看看自己感興趣的內容,代碼如下:

using System;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Windows;
using System.Windows.Resources;

namespace ApplicationResources
{
 /// <summary>
 /// MainWindow.xaml 的交互邏輯
 /// </summary>
 public partial class MainWindow : Window
 {
  public MainWindow()
  {
   InitializeComponent();
   //方法1使用StreamResourceInfo接收GetResourceStream()讀取到的內容。
   StreamResourceInfo sri = Application.GetResourceStream(new Uri("Images/takenotes.png", UriKind.Relative));
   string type = sri.ContentType;
   var stream = sri.Stream;
   //方法2,還記得我們反編譯時候看到的資源嗎?從Assembly取我們要的資源。
   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, true))
    {
     UnmanagedMemoryStream s = (UnmanagedMemoryStream)set.GetObject("Images/takenotes.png", true);
    }
   //遍歷所有資源
   ResourceManager rmResources = new ResourceManager(resourceName, assembly);
   using (ResourceSet set = rmResources.GetResourceSet(CultureInfo.CurrentCulture, true, true))
   {
    foreach (DictionaryEntry item in set)
    {
     Debug.WriteLine($"ResourceSet DictionaryEntry as {item.Key.ToString()}");
    }
   }

  }
 }
}

WPF目前給我們封裝瞭一些訪問資源的方法,我們使用XAML和CS下傳入相對和絕對路徑來演示:

<Window x:Class="ApplicationResources.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:ApplicationResources"
  mc:Ignorable="d" Loaded="Window_Loaded"
  Title="MainWindow" Height="450" Width="800">
 <Grid>
  <StackPanel> 
  <Image Source="Images/takenotes.png" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left"/>
  <Image Source="d:\Image\takenotes.png" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left"/>
   <Image x:Name="imgAbsolutePath" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left"/>
   <Image x:Name="imgRelativePath" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left"/>
  </StackPanel>
 </Grid>
</Window>
 private void Window_Loaded(object sender, RoutedEventArgs e)
  {
   imgAbsolutePath.Source = new BitmapImage(new Uri(@"d:\Image\takenotes.png", UriKind.Absolute));
   imgRelativePath.Source = new BitmapImage(new Uri("Images/takenotes.png",UriKind.Relative)); 
  }

在這個基礎上WPF支持pack URI。pack URI語法可以尋址包含在編譯過的程序中的資源,(從名字上理解package URI?)使用pack URI可以讓我們更加規范的使用資源文件,也更加方便。

如下這2段代碼是等效的因為沒有跨程序集。

 <Image Source="Images/takenotes.png" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left"/>
 <Image Source="pack://application:,,,/Images/takenotes.png" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left"/>

我新建瞭一個程序集,放置在ImageLibrary程序集下同樣目錄的資源。

Xaml寫法:

   <Image Source="Images/takenotes.png" Width="120" VerticalAlignment="Top" HorizontalAlignment="Left"/>
   <Image Source="pack://application:,,,/Images/takenotes.png" Width="120" VerticalAlignment="Top" HorizontalAlignment="Left"/> 
   <Image Source="pack://application:,,,/ImageLibrary;component/Images/takenotes.png" Width="120" VerticalAlignment="Top" HorizontalAlignment="Left"/> 

CS寫法:

private void Window_Loaded(object sender, RoutedEventArgs e)
  {
   //絕對路徑
   imgAbsolutePath.Source = new BitmapImage(new Uri(@"d:\Image\takenotes.png", UriKind.Absolute));
   //相對路徑
   imgRelativePath.Source = new BitmapImage(new Uri("Images/takenotes.png",UriKind.Relative));
   //當前程序集pack URI 語法
   imgRelativePath.Source = new BitmapImage(new Uri("pack://application:,,,/Images/takenotes.png"));
   //跨程序集使用資源 pack URI 語法
   imgCrossAssemblyPath.Source = new BitmapImage(new Uri("pack://application:,,,/ImageLibrary;component/Images/takenotes.png")); 
  }

這樣就可以把資源單獨存放在一個DLL裡。然後剩下的就看自己怎麼使用拉。

我們主工程下一般會保持當前App.xaml的當前資源結構。我們把資源都放在 Application.Current.Resources.MergedDictionaries內。通過MergedDictionaries更好的動態替換同類型的資源(比如多語言的一種實現方式)。

var targetResource = Application.Current.Resources.MergedDictionaries.FirstOrDefault(x => x.Source != null && x.Source.OriginalString.Contains(uri));
 if (targetResource != null)
 {
   Application.Current.Resources.MergedDictionaries.Remove(targetResource);
 }
 //添加與系統語言對應的語言包
 Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary
 {
  Source = new Uri($"pack://application:,,,/ApplicationResources;component/Lang/{rcName}.xaml", UriKind.RelativeOrAbsolute)
 });

這樣的話,就能輔助我們更好的使用資源拉,是不是發現寫代碼如果保持正確的解耦,寫代碼會就越來越簡單?

以上就是c# wpf如何更好的使用Application程序集資源的詳細內容,更多關於wpf使用Application程序集資源的資料請關註WalkonNet其它相關文章!