1. 二進(jìn)制資源WPF 支持三種方式的二進(jìn)制資源,這些資源可以非常方便地在 XAML 中使用。
Resource: 將資源嵌入程序集中,和 Embedded Resource 有點(diǎn)像。區(qū)別在于 WPF 將相關(guān)資源打包到 .Resources
文件,然后再由編譯器嵌入到程序集文件中。WPF 默認(rèn)的 URI 訪問方式是不支持 Embedded Resource 的。
Content:
資源不會(huì)嵌入到程序集,僅僅在程序集清單中添加一條記錄,這和以往我們所熟悉的方式一樣。資源文件必須隨其他程序集文件一起部署到目標(biāo)目錄。
Loose File: 這類資源通常是運(yùn)行期動(dòng)態(tài)確定或加入的。
WPF 通過統(tǒng)一資源標(biāo)識(shí)符(URI)訪問相關(guān)資源文件。
(1) 訪問 Resource / Content 資源
<Image Source="/a.png" /> <Image Source="/xxx/x.png" />
(2) 訪問松散資源文件(Loose File)
<Image Source="pack://siteOfOrigin:,,,/a.png" /> <Image Source="c:\test\a.png" />
XAML 編譯時(shí)需要驗(yàn)證所有資源有效性,因此在使用松散資源文件時(shí)必須使用有效的 URI 路徑。 "pack://" 表示 Package
URI,"pack://siteOfOrigin:,,," 表示從部署位置開始,相應(yīng)的還有 "pack://application:,,," 上面的
Resource 資源全路徑應(yīng)該是 "pack://application:,,,/a.png",只不過通常情況下我們使用省略寫法而已。
<Image Source="pack://application:,,,/a.png" />
其他的 URI 寫法還包括:
<Image Source="http://www.qidian.com/images/logo.gif" /> <Image Source="\\server1\share\logo.gif" /> <Image Source="file://c:/test/logo.gif" />
(3) 訪問其他程序集中的資源文件
提供一個(gè)可替換的專用資源 DLL 也是一種常用編程手法,尤其是多語(yǔ)言或者換膚機(jī)制。WPF 訪問其他程序集資源文件的語(yǔ)法有點(diǎn)古怪。
pack://application:,,,/AssemblyReference;Component/ResourceName
其他程序集的資源必須以 Resource 方式嵌入。
不能省略 "/AssemblyReference" 前面的反斜杠。
Component 是關(guān)鍵字,必須包含在 URI 中。
<Image Source="pack://application:,,,/Learn.Library;component/s.gif" /> <Image Source="/Learn.Library;component/s.gif" />
2. 邏輯資源
邏輯資源是一些存儲(chǔ)在元素 Resources 屬性中的對(duì)象,這些 "預(yù)定義" 的對(duì)象被一個(gè)或多個(gè)子元素所引用或共享。
<Window x:Class="Learn.WPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1"> <Window.Resources> <ContentControl x:Key="label1">Hello, World!</ContentControl> <ImageSource x:Key="image1">/a.png</ImageSource> </Window.Resources> <Grid> <Label Content="{StaticResource label1}" /> <Image Source="{StaticResource image1}" /> </Grid> </Window>
當(dāng)然,我們也可以寫成下面這種格式。
<Window x:Class="Learn.WPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1"> <Window.Resources> <ContentControl x:Key="label1">Hello, World!</ContentControl> <ImageSource x:Key="image1">/a.png</ImageSource> </Window.Resources> <Grid> <Label> <Label.Content> <StaticResource ResourceKey="label1" /> </Label.Content> </Label> <Image> <Image.Source> <StaticResource ResourceKey="image1" /> </Image.Source> </Image> </Grid> </Window>
我們甚至可以用邏輯資源定義一個(gè)完整的子元素。
<Window x:Class="Learn.WPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1"> <Window.Resources> <Label x:Key="label1" Content=" Hello, World!" /> <Image x:Key="image1" Source="/a.png" /> </Window.Resources> <Grid> <StaticResource ResourceKey="label1" /> <StaticResource ResourceKey="image1" /> </Grid> </Window>
邏輯資源有個(gè)限制,那就是這些繼承自 Visual 的元素對(duì)象只能有一個(gè)父元素,也就是說不能在多個(gè)位置使用,因?yàn)槎啻我玫氖峭粋€(gè)對(duì)象實(shí)例。
<Window x:Class="Learn.WPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1"> <Window.Resources> <Image x:Key="image1" Source="/a.png" /> </Window.Resources> <Grid> <StaticResource ResourceKey="image1" /> <StaticResource ResourceKey="image1" /> </Grid> </Window>
你將看到下面這樣一個(gè)錯(cuò)誤提示。
解決方法就是添加 "x:Shared=False",這樣每次引用都會(huì)生成一個(gè)新的邏輯資源對(duì)象。
<Window x:Class="Learn.WPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1"> <Window.Resources> <Image x:Key="image1" x:Shared="False" Source="/a.png" /> </Window.Resources> <Grid> <StaticResource ResourceKey="image1" /> <StaticResource ResourceKey="image1" /> </Grid> </Window>
資源查找: 引用邏輯資源時(shí)首先會(huì)查找父元素 Resources 集合,如未找到,會(huì)逐級(jí)檢查更上層的父元素。如果直到根元素依然未找到有效的邏輯資源定義,那么
WPF 會(huì)檢查 Application.Resources (App.xaml 中定義) 和系統(tǒng)屬性集合(SystemParamters
等)。每個(gè)獨(dú)立的資源字典中鍵名不能重復(fù),但在多個(gè)不同層級(jí)的資源字典中允許重復(fù),離資源引用最近的那個(gè)邏輯資源項(xiàng)被優(yōu)先采納。
<Window x:Class="Learn.WPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1"> <Window.Resources> <Label x:Key="label1" Content=" Hello, World!" /> </Window.Resources> <Grid> <Grid.Resources> <Label x:Key="label1" Content=" Hello, C#!" /> </Grid.Resources> <StackPanel> <StaticResource ResourceKey="label1" /> </StackPanel> </Grid> </Window>
將顯示 "Hello, C#!"。
邏輯資源的引用方式又分類 "靜態(tài)(StaticResource)" 和 "動(dòng)態(tài)(DynamicResource)"
兩種方式,區(qū)別在于靜態(tài)引用僅在第一次資源加載時(shí)被應(yīng)用,而動(dòng)態(tài)引用則會(huì)在資源被更改時(shí)重新應(yīng)用。
<Window x:Class="Learn.WPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1"> <Window.Resources> <ContentControl x:Key="label1" x:Shared="False">Hello, World!</ContentControl> </Window.Resources> <x:Code> private void button1_Click(object sender, RoutedEventArgs e) { this.Resources["label1"] = "Hello, C#!"; } </x:Code> <Grid> <StackPanel> <Label x:Name="label1" Content="{StaticResource label1}" /> <Label x:Name="label2" Content="{DynamicResource label1}" /> <Button x:Name="button1" Click="button1_Click">Test</Button> </StackPanel> </Grid> </Window>
單擊按鈕后,你會(huì)發(fā)現(xiàn) label2 實(shí)時(shí)反應(yīng)了資源的修改。
動(dòng)態(tài)資源只能用于設(shè)置依賴屬性值,因此它不能像靜態(tài)資源那樣引用一個(gè)完整的元素對(duì)象。
<Window x:Class="Learn.WPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1"> <Window.Resources> <Label x:Key="label" x:Shared="False" Content="Hello, World!" /> </Window.Resources> <Grid> <StackPanel> <DynamicResource ResourceKey="label" /> </StackPanel> </Grid> </Window>
這將導(dǎo)致出現(xiàn)下圖這樣的異常,而靜態(tài)資源則沒有這個(gè)問題。
當(dāng)然,靜態(tài)資源也有另外一個(gè)麻煩,就是不支持前向引用(Forward Reference),也就是說我們必須先定義資源,然后才能使用靜態(tài)引用。
<Window x:Class="Learn.WPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1"> <Grid> <StackPanel> <Label Content="{StaticResource label}" /> </StackPanel> </Grid> <Window.Resources> <ContentControl x:Key="label">Hello, World!</ContentControl> </Window.Resources> </Window>
屬于靜態(tài)資源的異常出現(xiàn)了,當(dāng)然動(dòng)態(tài)資源是沒有這個(gè)問題的。
如果有必要的話,我們可以將邏輯資源分散定義在多個(gè) XAML 文件(Resource Dictionary) 中。
Dictionary1.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <ContentControl x:Key="label1">Hello, World!</ContentControl> </ResourceDictionary>
Dictionary2.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <ContentControl x:Key="label2">Hello, C#!</ContentControl> </ResourceDictionary>
Window1.xaml
<Window x:Class="Learn.WPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1"> <Window.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Dictionary1.xaml" /> <ResourceDictionary Source="Dictionary2.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Window.Resources> <Grid> <StackPanel> <Label Content="{DynamicResource label1}" /> <Label Content="{DynamicResource label2}" /> </StackPanel> </Grid> </Window>
如果合并的字典中有重復(fù)的鍵值,那么最后加入的資源將優(yōu)先。比如 Dictionary1.xaml 和 Dictionary2.xaml 都有一個(gè)
x:Key="label1" 的資源,那么 Dictionary2.label1 將獲勝。
在程序代碼中我們可以用 FindResource/TryFindResource 設(shè)置靜態(tài)資源,用 SetResourceReference
設(shè)置動(dòng)態(tài)資源。
this.labelA.Content = (ContentControl)this.labelA.FindResource("key1"); // StaticResource this.labelB.SetResourceReference(Label.ContentProperty, "key2"); // DynamicResource
|