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#相關的交流群。用於分享學習資料和討論問題。歡迎有興趣的小伙伴:QQ群:542633085

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM