一:背景 1. 講故事 這段時間分析了幾個和網路故障有關的.NET程式之後,真的越來越體會到電腦基礎課的重要,比如 電腦網路 課,如果沒有對 tcpip協議 的深刻理解,解決這些問題真的很難,因為你只能在高層做黑盒測試,你無法看到 tcp 層面的握手和psh通訊。 這篇我們通過兩個小例子來理解一 ...
WPF中我們引用資源時常常提到一個概念:pack URI
,這是WPF標識和引用資源最常見的方式,但不是唯一的方式。本文將介紹WPF中引用資源的幾種方式,並回顧一下pack URI
標識引用在不同位置的資源文件的寫法。
WPF中引用資源的幾種方式
WPF中使用URI標識和載入位於各種位置的文件,包括當前程式集資源文件、其他程式集資源文件、本地磁碟文件、網路共用文件、web站點文件。
程式集資源文件
程式集資源文件是最常見的一種情況。這裡程式集資源指的是資源文件屬性的生成操作(Build Action)為Resource
的文件,而非嵌入的資源(Emmbedded Resource)
。程式集中的資源文件通常使用相對URI來引用,例如:
<ImageBrush x:Key="imgbrush" ImageSource="images/111.jpg"/> //本地程式集中資源引用的寫法
<ImageBrush x:Key="imgbrush" ImageSource="/ResourceDll;component/images/111.jpg"/> //引用的程式集中資源引用的寫法
也可以使用絕對Pack URI
語法,例如
<ImageBrush x:Key="imgbrush" ImageSource="pack://application:,,,/images/111.jpg"/> //本地程式集中資源引用的寫法
<ImageBrush x:Key="imgbrush" ImageSource="pack://application:,,,/ResourceDll;component/images/111.jpg"/> //引用的程式集中資源引用的寫法
本地磁碟文件
直接引用本地磁碟文件的方式不常見。這種方式引用本地文件會占用文件,本地文件無法修改或者刪除,因此不推薦此方式。這裡只是舉例講解。
<ImageBrush x:Key="imgbrush" ImageSource="d:\\tmp\\新建文件夾\\123.jpg"/>
網路共用文件
網路共用文件和本地磁碟文件類似,會占用文件。可以使用UNC或者URI的方式引用。
<ImageBrush x:Key="imgbrush" ImageSource="\\192.168.0.1\tmp\新建文件夾\123.jpg"/> UNC方式引用
<ImageBrush x:Key="imgbrush" ImageSource="file://192.168.0.1\tmp\新建文件夾\123.jpg"/> URI方式引用
web站點文件
少數場景下會在WPF中使用web站點資源,比如用戶頭像。web站點資源主要以http/https協議的url載入,url作為URI的子集,因此可以直接引用。實際開發中不建議直接引用url,因為請求網路資源需要時間,這可能導致UI短暫卡頓。建議開啟線程把網路資源讀到記憶體中使用。
<ImageBrush x:Key="imgbrush" ImageSource="https://pic.cnblogs.com/default-avatar.png"/>
上述示例中都是在XAML中聲明式的語法引用資源,本質還是使用Uri類,因此在後臺代碼中使用Uri類就行。
// 絕對URI (預設)
Uri absoluteUri = new Uri("pack://application:,,,/images/111.jpg", UriKind.Absolute);
// 相對URI
Uri relativeUri = new Uri("images/111.jpg", UriKind.Relative);
Pack URI方案
pack URI
的語法看起來很奇怪,它是來自開放式打包約定 (OPC)規範中XPS(XML Paper Specification)標準,有使用openxml解析Word/PPT文件經驗的朋友可能熟悉這個規範。OPC 規範利用RFC 2396
(統一資源標識符 (URI):一般語法)的擴展性來定義pack URI
方案。
URI
所指定的方案(schemes)由其首碼定義;http
、ftp
、telnet
和file
是比較常見的協議方案(schemes)。pack URI
使用“pack”作為它的方案(schemes),並且包含兩個組件:授權和路徑。 pack URI
的格式為:pack://authority/path
。authority指定包含部件的包的類型,而path 指定部件在包內的位置。前邊示例代碼中application:,,,
就是授權(authority),/images/111.jpg
或者/ResourceDll;component/images/111.jpg
就是路徑(path)。這裡也可以理解為嵌套在方案(schemes)為pack://
的uri中的uri。由於是嵌套在內部的uri,授權(authority)原本應是application:///
中的斜杠轉義為逗號。路徑中必須對保留字元(如“%”和“?”)進行轉義。詳細信息可參閱開放式打包約定 (OPC)規範
標準的
URI
協議方案有30種左右,由隸屬於國際互聯網資源管理的非營利社團 ICANN(Internet Corporation for Assigned Names and Numbers,互聯網名稱與數字地址分配機構)的 IANA(Internet Assigned Numbers Authority,互聯網號碼分配局)管理頒佈。詳細協議方案參見:http://www.iana.org/assignments/uri-schemes
在WPF中,用程式(包)可以包含一個或多個文件(部件),包括:
- 當前程式集內的資源文件
- 引用的程式集內的資源文件
- 內容文件
- 源站點文件
為了訪問這些類型的文件,WPF 支持兩種授權:application:///
和siteoforigin:///
。 application:/// 授權標識在編譯時已知的應用程式數據文件,包括資源文件和內容文件。 siteoforigin:/// 授權標識源站點文件。 下圖顯示了每種授權的範圍。
pack URI語法示例
前邊提到pack URI
由授權和路徑組成,當前程式集、引用的程式集內的資源文件,以及內容文件的授權都是application:///
,源站點文件的授權是siteoforigin:///
(用於XAML瀏覽器應用程式)。
當前程式集資源文件
當前程式集資源文件的路徑是資源文件相對程式集項目文件夾根目錄的路徑。需要註意的是這裡所說的相對於程式集項目文件夾根目錄表達的是從哪裡開始作為根目錄進行定址,當使用pack://
這樣絕對URI
表示時,路徑應該用根目錄符號/
開始。下圖中111.jpg
位於項目的根目錄,它的pack URI
就是:
pack://application:,,,/111.jpg
BlindsShader.ps
位於子目錄中,它的pack URI
就是:
pack://application:,,,/Shader/ShaderSource/BlindsShader.ps
引用程式集資源文件
當需要引用另一個程式集中的資源文件時,路徑需要指明程式集的名稱。路徑需符合以下的格式:
pack://application:,,,AssemblyShortName{;Version}{;PublicKey};component/ResourceName
- AssemblyShortName是引用的程式集的短名稱,是必選項
- Version是引用的程式集的版本。此部分在載入兩個或多個具有相同短名稱的引用程式集時使用,是可選項。
- PublicKey是引用的程式集的簽名公鑰。此部分在載入兩個或多個具有相同短名稱的引用程式集時使用,是可選項。
- component指定所引用的程式集是從本地程式集引用的,此處是固定寫法
- ResourceName是資源文件的名稱,包括其相對於所引用程式集的項目文件夾根目錄的路徑。
內容文件
前邊提到的資源文件都是生成操作(Build Action)為Resource
的文件,是會編譯到程式集中。內容文件是生成操作(Build Action)為內容(Content)
的文件,並不會編譯到程式集中,通常是將文件屬性中複製到輸出目錄(CopyToOutputDirectory)
選為始終複製(Always)
或者如果較新則複製(PreserveNewest)
,將文件保存到程式運行目錄中。內容文件主要可以解決以下問題:
- 改變資源文件時,需要重新編譯應用程式;
- 資源文件比較大,導致編譯的程式集也比較大;
- WPF聲音文類不支持程式集資源,無法從資源流中析取音頻文件並播放。
內容文件本質上也是本地磁碟文件,但生成項目時,會將 AssemblyAssociatedContentFileAttribute
屬性編譯到每個內容文件的程式集的元數據內,AssemblyAssociatedContentFileAttribute
的值表示內容文件相對於其在項目中的位置的路徑[^2],可以採用pack URI
的方式載入。內容文件的路徑是其相對於應用程式的主可執行程式集的文件系統位置的路徑。其格式如下:
pack://application:,,,/ContentFile.wav
源站點文件
源站點文件主要針對XAML瀏覽器應用程式(XBAP)設計,編譯XAML瀏覽器應用程式(XBAP)將資源文件分離出程式集,減少文件大小,在需要請求下周源站點文件時,才下載它們到客戶端電腦[^2]。現在基本不適用該技術,本文不再詳細介紹,感興趣可以查看文末參考資料。
參考
[^1] https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/app-development/pack-uris-in-wpf?view=netframeworkdesktop-4.8
[^2] https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/app-development/wpf-application-resource-content-and-data-files?view=netframeworkdesktop-4.8