原文WPF學習之資源-Resourceshtml
WPF經過資源來保存一些能夠被重複利用的樣式,對象定義以及一些傳統的資源如二進制數據,圖片等等,而在其支持上也更能體現出這些資源定義的優越性。好比經過ResourceDictionary的支持就能夠經過資源來實現換膚功能,在ExpressionBlend中設計的酷炫造型也能夠經過導出成資源來很容易的被程序員所引用,本地化的實現,訪問另外程序集的嵌入式資源等等。這些都給咱們提供了豐富的手段經過資源訪問架構來構建豐富的富媒體應用程序。本文簡單講解了WPF Resources的分類及其常見用法,並簡單觸及用ResourceDictionary來管理多個Resources文件(這是換膚的基礎)。程序員
在WPF中的資源不只依賴於核心.NET的資源系統,在其基礎上也添加了對兩種不一樣資源類型的支持:二進制資源和邏輯資源。而對於這些資源類型的構建動做也有了更多的支持選項。web
1. 二進制資源windows
二進制資源實際上是一些傳統的資源項,好比位圖,音頻文件,視頻文件,鬆散文件(Loose file)等等。對於這些資源項咱們能夠將其存儲爲鬆散文件,或者編譯進程序集中。這與傳統的.NET程序實際上是相通的,但在WPF中提供了兩種對二進制資源的構建選項:架構
· Resource: 將資源放入程序集中(若是是有本地化支持的話會編譯到對應語言集的子程序集中。ide
· Content:將這個資源做爲一個鬆散文件加入到程序集中,程序集會記錄對應的文件是否存在及其路徑。這就至關於咱們web開發中經常使用的構建動做。函數
對於MSBuild來講這也是默認的構建類型,例如,post
<Content Include="Images\Go.ico" />性能 <Content Include="Images\Go.jpg" />學習 <Content Include="Images\Go2.gif" /> <Content Include="Images\Go2.jpg" /> <Content Include="Images\information16.png" /> <Content Include="Images\pass16.png" /> <Content Include="Images\pass32.png" /> <Content Include="Images\unknown16.png" /> <Content Include="Images\warning.gif" /> <Content Include="Images\warning16.png" /> <EmbeddedResource Include="LoginForm.resx"> <SubType>Designer</SubType> <DependentUpon>LoginForm.cs</DependentUpon> </EmbeddedResource> <EmbeddedResource Include="OptionsForm.resx"> <SubType>Designer</SubType> <DependentUpon>OptionsForm.cs</DependentUpon> </EmbeddedResource> <EmbeddedResource Include="PageHistory.resx"> <SubType>Designer</SubType> <DependentUpon>PageHistory.cs</DependentUpon> </EmbeddedResource> |
上圖所示是一個普通的WinForm應用程序的項目文件,對於添加到其內部的二進制資源文件其默認的構建動做即是Content-代表其做爲一個鬆散文件存儲,只要保證其對應路徑的文件存在則能夠自動加載(而無需再你的打包文件中必須包含)。而你也會看到EmbeddedResource構建動做,這是WinForm的構建動做,它和Resource構建動做很類似,會在程序集中嵌入一個二進制資源,可是WPF中由於嵌入式資源比WPF還要優先,因此須要儘可能避免使用。
之因此推薦使用Resource和Content構建類型是由於這樣嵌入的資源能夠很容易的在XAML中被引用,並且對於WPF的統一資源識別符也是專門針對這兩種構建動做而設計的。相反地,對於EmbeddedResource構建動做嵌入的資源是不能在XAML中被引用的,除非自定義代碼。
· 訪問二進制資源
訪問二進制資源最普通的就是對鬆散文件的訪問,這和普通的.NET應用程序沒什麼兩樣,直接看例子吧:
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button Background="Yellow" BorderBrush="Red" Margin="5"> <Image Height="21" Source="zoom.gif"/> </Button> <Button Background="Yellow" BorderBrush="Red" Margin="5"> <Image Height="21" Source="defaultThumbnailSize.gif"/> </Button> <Button Background="Yellow" BorderBrush="Red" Margin="5"> <Image Height="21" Source="previous.gif"/> </Button> </StackPanel> |
上述的的鬆散文件只須要以Content構建加入項目便可. 這只是普通的訪問方式,固然也是咱們經常使用的,但也有不少你可能須要用到哦,不妨看看:
資源URI |
資源 |
zoom.gif |
存放於當前程序集;或是添加到項目的鬆散文件。 |
Folder/zoom.gif |
若是內部有目錄結構時加入相對目錄結構。 |
C:\Images\zoom.gif |
絕對路徑的鬆散文件 |
\\FileServer\Images\zoom.gif |
共享目錄的鬆散文件 |
http://my.net/zoom.gif |
位於某個站點上的鬆散文件 |
AssemblyReference;Component/ResourceName |
訪問嵌入到另一個程序集或EXE文件內的資源。Component是關鍵字,必須寫。例如: MyDll;Component/Images/zoom.gif; |
pack://siteOfOrigin:,,,/Images/zoom.gif |
訪問位於部署位置的資源。 |
2. 邏輯資源
邏輯資源是WPF特有的資源類型,它是存儲在元素的Resources屬性中的.NET對象,一般須要共享給多個子元素。換句話說,你能夠聲明一個SolidColorBrush對象看成一個邏輯資源,你也能夠聲明一個Style,而後再後續的XAML中簡單經過{StaticResource ResourceName}來使用。資源定義須要有一個在ResourceDictionary中惟一的關鍵字x:Key(單獨的ResourceDictionary中的鍵名不能夠重複,多個ResourceDictionary中鍵名能夠重複,會根據在邏輯數上的lookup的順序來就近生效)。例以下邊的邏輯資源聲明:
<Window x:Class="WpfApplication1.Window2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window2" Height="300" Width="400"> <Window.Resources> <SolidColorBrush x:Key="buttonBackground">Yellow</SolidColorBrush> <SolidColorBrush x:Key="borderBrush">Red</SolidColorBrush> <LinearGradientBrush x:Key="backgroundBrush" StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="Blue" Offset="0"></GradientStop> <GradientStop Color="White" Offset="0.5"></GradientStop> <GradientStop Color="Red" Offset="1"></GradientStop> </LinearGradientBrush> </Window.Resources> <Window.Background> <StaticResource ResourceKey="backgroundBrush" /> </Window.Background> <Grid> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Margin="5" Height="28"> <Image Height="21" Source="Images\zoom.gif"></Image> </Button> <Button Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Margin="5" Height="28"> <Image Height="21" Source="Images\previous.gif"></Image> </Button> <Button Background="{StaticResource buttonBackground}" BorderBrush="{DynamicResource borderBrush}" Margin="5" Height="28"> <Image Height="21" Source="Images\next.gif"></Image> </Button> </StackPanel> </Grid> </Window> |
一個資源能夠被以StaticResource或者DynamicResource的方式來引用,這是以標記擴展(StaticResource/DynamicResource Markup Extension)來標註的。去以StaticResource仍是以DynamicResource來引用一個資源,這取決於:
· 你是以什麼樣的方式來爲你的應用建立資源的:每一個Page?一個Application?一個鬆散XAML文件?仍是一個只包含資源定義的程序集?
· 須要在運行時更新你的資源嗎?
· 查找資源的行爲—向前查詢?
· 資源自己的行爲和屬性
3. Static Resource – 靜態資源
StaticResource僅僅會被應用一次---在第一次須要資源時加載。並且這種引用方式不支持向前加載,全部的資源定義必須在引用以前定義。StaticResource一般用在:
· 設計的APP是將全部的資源放入Page或者App這個級別的Resource Dictionary中的,並且不須要在運行時從新計算—例如只保存一些鬆散文件,邏輯資源的聲明等。
· 不須要給DependencyObject或者Freezable的對象設置屬性。
· Resource Dictionary將被編譯進DLL.
· 須要給不少的Dependency Property賦值。
將一個資源以Static Resource引用,須要用到Static Resource Markup Extension。它在已經定義的資源中查詢特定key的value爲XAML的某個屬性賦值。這個查詢的行爲與load-time查找相似,在當前Page的XAML中或者全部Application的Resources中查找,並在運生成運行時對象。
XAML Attribute Usage
<object property=」StaticResource key}」 … /> |
XAML Object Element Usage
<object> <object.Property> <StaticResource ResourceKey = 「key」 …/> </object.Property> </object> |
前者是咱們常常會用到的方式,好比給一個屬性賦予一個早先定義的話刷格式:
<Button Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Margin="5" Height="28"> |
然後者咱們在前面的例子中也看到了,用法稍微特殊,其實在這個狀況下這個資源確定是一個邏輯資源,至關於一段聲明對象的代碼。好比給window設背景:
<Window.Background> <StaticResource ResourceKey="backgroundBrush" /> </Window.Background> |
Static Resource的查找行爲
· 首先檢查此對象自己的Resources集合內是否有匹配值(根據ResourceKey)
· 其次會在邏輯樹中向上搜尋父元素的Resource Dictionary.
· 最後會檢查Root級別的好比Page,Window,Application等。
4. Dynamic Resource – 動態資源
與Static Resource不一樣的是,Dynamic Resource能夠在程序運行時從新評估/計算資源來生成對應的對象/值,它支持向前引用,只要請求的key在整個應用程序內的任何Resources Dictionary定義過就能夠被加載。若是有多個相同的key存在,則最後搜索到的資源爲有效。
動態資源經常使用於如下狀況:
· 資源直到運行時才能被取定其值的。這些包含想系統資源,或者經過用戶交互/用戶能夠設定的值。例如你能夠用Setter Property語法來引用一些系統資源像SystemColors, SystemFonts等,這些是真正的Dynamic Resource,由於他們是來自用戶的運行環境。
· 在Custom control中有建立/引用主題風格的需求.
· 在運行過程當中調整(好比添加或者合併)ReourceDictionary.
· 須要向前引用的場景。
· 建立的Style的值與當前用戶設定的主題或其餘設定有關的。
· 運行過程當中可能更改邏輯樹的次序的。
下面的代碼片斷演示瞭如何在XAML中引用SystemFonts,這須要用DynamicResource標記:
<Style x:Key="SimpleFont" TargetType="{x:Type Button}"> <Setter Property = "FontSize" Value= "{DynamicResource {x:Static SystemFonts.IconFontSizeKey}}"/> <Setter Property = "FontWeight" Value= "{DynamicResource {x:Static SystemFonts.MessageFontWeightKey}}"/> <Setter Property = "FontFamily" Value= "{DynamicResource {x:Static SystemFonts.CaptionFontFamilyKey}}"/> </Style> |
Dynamic Resource的查找行爲
· 首先遍歷請求對象自己定義的Resources集合。
· 而後遍歷邏輯樹上當前請求對象的父對象,直到遍歷到Root(如Page.Reources, Window.Resources, UserControl.Resources等)
· 隨後會遍歷應用程序的Resources(即Application.Resources)
· 進而會Check當前激活的Theme的資源。
· 最後纔會去遍歷System Resources.
在程序中你能夠經過myWindow.Resources[「key」]的方式來直接訪問一個資源。另外,WPF還提供了TryFindResource(key)和FindResource(key)來支持資源搜索。FindResource方法在沒找到資源的狀況下會觸發ResourceReferenceKeyNotFoundException異常。
其實經過上邊的示例咱們能夠很清楚的看到,在使用靜態資源的地方咱們每每均可以使用動態資源,他們並無什麼合適與否之說,而選擇它們中的哪個,徹底取決於你是否須要資源的使用者發現更新。咱們能夠再來比較一下兩者的區別:
· 對於資源的更新會反映在那些使用了動態資源的元素上,這是他們最主要的區別。
· 性能上:由於動態資源要跟蹤變化,因此須要佔用更多的資源。而靜態資源每每是在window或page加載以後來引用,動態資源會改善加載時間。但靜態資源在使用時卻會有些許性能的提高。
· 動態資源只能設置依賴屬性只,而靜態資源能夠在任何地方使用。好比,咱們能夠聲明一個邏輯資源把它看成一個元素來用,而動態資源卻沒法作到:
<Window x:Class="WpfApplication1.Window3" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window3" Height="300" Width="300"> <Window.Resources> <Button Background="Blue" Margin="5" Height="28" x:Key="prev"> <Image Height="21" Source="Images\previous.gif"></Image> </Button> </Window.Resources> <Grid> <Button Height="20" Width="70" Content="Content" /> <StaticResource ResourceKey="prev" /> </Grid> </Window> |
· 當你在XAML中使用StaticResource時,是不支持Forward Reference的,也就是說任何資源必須在XAML文件中聲明以後纔可使用。若是是在同一個元素中定義,則只能使用Dynamic Resource。
<Window x:Class="WpfApplication1.Window3" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window3" Height="300" Width="300" Background="{DynamicResource winBackground}"> <Window.Resources> <Button Background="Blue" Margin="5" Height="28" x:Key="prev"> <Image Height="21" Source="Images\previous.gif"></Image> </Button> <LinearGradientBrush x:Key="winBackground" StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="Blue" Offset="0"></GradientStop> <GradientStop Color="White" Offset="0.5"></GradientStop> <GradientStop Color="Red" Offset="1"></GradientStop> </LinearGradientBrush> </Window.Resources> <Grid> <Button Height="20" Width="70" Content="Content" /> <StaticResource ResourceKey="prev" /> </Grid> </Window> |
5. 資源的應用
前變也已經說過能夠引用別的程序集的資源,那麼到底如何引用呢?另外,咱們都是在說XAML中引用資源,那麼代碼中又該如何去作呢?咱們又一般會在什麼地方定義資源呢?這裏就來講一下這幾個問題,以及某些特殊狀況下的定義。
· 共享資源
默認狀況下,當有一個資源被引用到多個地方是,使用的都是同一個對象實例,這一般是理想的行爲。但你一樣也能夠把x:Shared=」False」來讓每一個引用資源的地方都生成一個不一樣的對象實例,這樣能夠獨立進行修改。這一般用於多邏輯資源的聲明。
· 程序代碼中定義和應用資源
在代碼中定義一個新的Resource,你須要首先獲得一個ResourceDictionary的實例,而後再建立一個新的資源並將這個資源加入到ResourceDictionary的實例中。而在訪問資源時,你須要用到myWindow.Resources[「key」]或者object.FindResource(key)函數。注意myWindows是你當前window的實例,而在用FindResource時,前邊的object表明的是這個資源所在的ResourceDictionary的父對象。
private void Window_Loaded(object sender, RoutedEventArgs e) { Window3 window = new Window3(); window.Resources.Add("buttonBackground", new SolidColorBrush(Color.FromRgb(0,255,0))); window.Resources.Add("borderBrush", new SolidColorBrush(Color.FromRgb(255, 0, 0)));
btnContent.Background = (Brush)window.FindResource("buttonBackground"); btnContent.BorderBrush = (Brush)window.FindResource("borderBrush"); } |
注意在找不到資源時會拋出一個ResourceReferenceKeyNotFoundException異常,因此儘可能調用TryFindResource方法更好些,若是失敗將會返回null.
上邊的例子是針對StaticResource來講的,它就至關於這段代碼:
<Button x:Name="btnContent" Canvas.Left="50" Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Content="Content" /> |
|
但對於DynamicResource來講,須要調用這個元素的SetResourceReference方法來更新依賴屬性的綁定。下邊的兩端代碼是相等的:
<Button x:Name="btnContent" Canvas.Left="50" Background="{DynamicResource buttonBackground}" BorderBrush="{ DynamicResource borderBrush}" Content="Content" /> |
btnContent.SetResourceReference(Button.BackgroundProperty, "buttonBackground"); btnContent.SetResourceReference(Button.BorderBrushProperty, "borderBrush"); |
SetResourceReference是能夠在資源被加載到某個Resource Dictionary以前調用的,即使是FindResource會失敗,但引用的創建仍然有效。
· 從另外一個程序集中訪問嵌入式資源
除了能夠用特定的URI來訪問別的程序中的二進制資源外,WPF能夠從另一個程序集中獲取邏輯資源,這得用到ComponentResourceKey標記。要使用ComponentResourceKey,每一個資源都必須有一個鍵名。而後你能夠經過這樣的方式訪問:
<Button Background=」{DynamicResource {x:Static otherAssembly: MyClass.MyClassBrushKey }}」 /> |
· Styles 和Implicit Keys
樣式是最多見的一種資源,並且它老是被定義在Resource Dictionary中,爲了來重用。Style其實就是一系列分組的Setter的集合,用來設定邏輯資源的屬性值,它有一種比較特殊的情形就是Implicit Keys,能夠不聲明一個x:Key的名字,而只設置x:TargetType的值,這樣面對的就是對於全部這個類型的控件都使用這個樣式。下邊的示例中x:Key的值其實就是type-Button。
<Style TargetType="Button"> <Setter Property="Background"> <Setter.Value> <LinearGradientBrush> <GradientStop Offset="0.0" Color="AliceBlue"/> <GradientStop Offset="1.0" Color="Salmon"/> </LinearGradientBrush> </Setter.Value> </Setter> <Setter Property="FontSize" Value="18"/> </Style> |
Style也是資源的一種---從某種意義上來講,它很相似於咱們給普通HTML中的元素創建CSS. 對於Designer來講可能輕鬆一些哦。
6. Resource Dictionary –資源字典
全部的資源項在最終都會被整合到Resource Dictionary中的,也就是說不管是FrameworkElement的Resources,仍是Window的Resources,仍是Application的Resources,仍是特定的ResourceDictionary中定義的resources在整個應用編譯執行的時候實際上他們都在一塊兒的做爲可遍歷集合共同存在於一個相對會話空間內的。
咱們也提到過Resource的key是能夠被容許有相同的,這樣在遍歷不一樣相對地址的Resource Dictionary時會根據StaticResource或者DynamicResource的lookup behavior來肯定哪一個有效。一般爲了維護和靈活性的考慮,咱們一般會將Resource Dictionary文件分紅好幾個,但在某些場合下咱們只須要用其中某些資源,那麼我麼能夠將資源從幾個獨立的文件中提取併合並,那麼能夠這麼作:
<Window.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Diction1.xaml"></ResourceDictionary> <ResourceDictionary Source="Diction2.xaml"></ResourceDictionary> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Window.Resources> |
注意,在資源合併後,仍然會出現重複值的狀況,那麼最後取出的資源獲勝。
7. Localization – 本地化
本地化和換膚其實都是在用ResourceDictionary來作文章的。說白了,Localization就是用不一樣語言下取不一樣事先設定好的資源來顯示而已。要作到這些很容易,4步就能夠輕鬆實現:
· 定義Resource Dictionary來包含不一樣語言下要顯示的資源項。
建立單獨的Resource Dictionary文件,並以語言自己名字來命名,並把en-US來做爲默認語言環境(這裏順便就命名爲default.xaml了)
Default.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String x:Key="Title_PM">Project Manager</sys:String> <sys:String x:Key="Title_PL">Project Lead</sys:String> <sys:String x:Key="Title_SD">Senior Developer</sys:String> <sys:String x:Key="Title_SA">System Architecture</sys:String>
</ResourceDictionary> |
zh-CN.xaml (注意對.NET命名空間的引用)
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String x:Key="Title_PM">項目經理</sys:String> <sys:String x:Key="Title_PL">項目主管</sys:String> <sys:String x:Key="Title_SD">資深開發工程師</sys:String> <sys:String x:Key="Title_SA">系統架構師</sys:String>
</ResourceDictionary> |
· 給應用程序添加默認資源:其實就是將默認的Resource Dictionary加入到Application的全局Resource裏邊。
<Application x:Class="WpfApplication1.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Window1.xaml"> <Application.Resources>
<ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Language\default.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary>
</Application.Resources> </Application> |
· 在Application啓動時根據不一樣語言來加載以語言命名的XAML文件(Resource Dictionary)。由於對於重名的資源,後來加載的資源將會勝出,因此以當前語言名加載的XAML文件中的資源項將會被引用。這就是多語言的本質!
public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e);
LoadLanguage(); }
private void LoadLanguage() { CultureInfo currentCultureInfo = CultureInfo.CurrentCulture; ResourceDictionary langRd = null; try { langRd = Application.LoadComponent( new Uri(@"Language\" + currentCultureInfo.Name + ".xaml", UriKind.Relative)) as ResourceDictionary; } catch { }
if (langRd != null) { if (this.Resources.MergedDictionaries.Count > 0) { this.Resources.MergedDictionaries.Clear(); } this.Resources.MergedDictionaries.Add(langRd); } } } |
· 在XAML中引用資源。
<TextBlock Canvas.Top="50" Width="100" Height="24" Text="{StaticResource Title_PM}" /> |
· 大功告成,運行程序你會看到默認的語言的顯示:Project Manager.固然若是你的默認文化是英語的話。用程序換成中文試試結果?沒問題,在LoadLanguage()以前更改語言便可:
base.OnStartup(e); CultureInfo info = new CultureInfo("zh-CN"); Thread.CurrentThread.CurrentCulture = info; Thread.CurrentThread.CurrentUICulture = info; LoadLanguage(); |
簡單吧?呵呵。有關Resource的東西基本上就這麼多了,換膚咱們再開闢另外一個話題來談吧。這但是WPF夠炫的Feature之一哦。。。。
一些與本文有關的小代碼片斷及Localization的示例請點擊這裏下載。
WPF經過資源來保存一些能夠被重複利用的樣式,對象定義以及一些傳統的資源如二進制數據,圖片等等,而在其支持上也更能體現出這些資源定義的優越性。好比經過ResourceDictionary的支持就能夠經過資源來實現換膚功能,在ExpressionBlend中設計的酷炫造型也能夠經過導出成資源來很容易的被程序員所引用,本地化的實現,訪問另外程序集的嵌入式資源等等。這些都給咱們提供了豐富的手段經過資源訪問架構來構建豐富的富媒體應用程序。本文簡單講解了WPF Resources的分類及其常見用法,並簡單觸及用ResourceDictionary來管理多個Resources文件(這是換膚的基礎)。
在WPF中的資源不只依賴於核心.NET的資源系統,在其基礎上也添加了對兩種不一樣資源類型的支持:二進制資源和邏輯資源。而對於這些資源類型的構建動做也有了更多的支持選項。
1. 二進制資源
二進制資源實際上是一些傳統的資源項,好比位圖,音頻文件,視頻文件,鬆散文件(Loose file)等等。對於這些資源項咱們能夠將其存儲爲鬆散文件,或者編譯進程序集中。這與傳統的.NET程序實際上是相通的,但在WPF中提供了兩種對二進制資源的構建選項:
· Resource: 將資源放入程序集中(若是是有本地化支持的話會編譯到對應語言集的子程序集中。
· Content:將這個資源做爲一個鬆散文件加入到程序集中,程序集會記錄對應的文件是否存在及其路徑。這就至關於咱們web開發中經常使用的構建動做。
對於MSBuild來講這也是默認的構建類型,例如,
<Content Include="Images\Go.ico" /> <Content Include="Images\Go.jpg" /> <Content Include="Images\Go2.gif" /> <Content Include="Images\Go2.jpg" /> <Content Include="Images\information16.png" /> <Content Include="Images\pass16.png" /> <Content Include="Images\pass32.png" /> <Content Include="Images\unknown16.png" /> <Content Include="Images\warning.gif" /> <Content Include="Images\warning16.png" /> <EmbeddedResource Include="LoginForm.resx"> <SubType>Designer</SubType> <DependentUpon>LoginForm.cs</DependentUpon> </EmbeddedResource> <EmbeddedResource Include="OptionsForm.resx"> <SubType>Designer</SubType> <DependentUpon>OptionsForm.cs</DependentUpon> </EmbeddedResource> <EmbeddedResource Include="PageHistory.resx"> <SubType>Designer</SubType> <DependentUpon>PageHistory.cs</DependentUpon> </EmbeddedResource> |
上圖所示是一個普通的WinForm應用程序的項目文件,對於添加到其內部的二進制資源文件其默認的構建動做即是Content-代表其做爲一個鬆散文件存儲,只要保證其對應路徑的文件存在則能夠自動加載(而無需再你的打包文件中必須包含)。而你也會看到EmbeddedResource構建動做,這是WinForm的構建動做,它和Resource構建動做很類似,會在程序集中嵌入一個二進制資源,可是WPF中由於嵌入式資源比WPF還要優先,因此須要儘可能避免使用。
之因此推薦使用Resource和Content構建類型是由於這樣嵌入的資源能夠很容易的在XAML中被引用,並且對於WPF的統一資源識別符也是專門針對這兩種構建動做而設計的。相反地,對於EmbeddedResource構建動做嵌入的資源是不能在XAML中被引用的,除非自定義代碼。
· 訪問二進制資源
訪問二進制資源最普通的就是對鬆散文件的訪問,這和普通的.NET應用程序沒什麼兩樣,直接看例子吧:
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button Background="Yellow" BorderBrush="Red" Margin="5"> <Image Height="21" Source="zoom.gif"/> </Button> <Button Background="Yellow" BorderBrush="Red" Margin="5"> <Image Height="21" Source="defaultThumbnailSize.gif"/> </Button> <Button Background="Yellow" BorderBrush="Red" Margin="5"> <Image Height="21" Source="previous.gif"/> </Button> </StackPanel> |
上述的的鬆散文件只須要以Content構建加入項目便可. 這只是普通的訪問方式,固然也是咱們經常使用的,但也有不少你可能須要用到哦,不妨看看:
資源URI |
資源 |
zoom.gif |
存放於當前程序集;或是添加到項目的鬆散文件。 |
Folder/zoom.gif |
若是內部有目錄結構時加入相對目錄結構。 |
C:\Images\zoom.gif |
絕對路徑的鬆散文件 |
\\FileServer\Images\zoom.gif |
共享目錄的鬆散文件 |
http://my.net/zoom.gif |
位於某個站點上的鬆散文件 |
AssemblyReference;Component/ResourceName |
訪問嵌入到另一個程序集或EXE文件內的資源。Component是關鍵字,必須寫。例如: MyDll;Component/Images/zoom.gif; |
pack://siteOfOrigin:,,,/Images/zoom.gif |
訪問位於部署位置的資源。 |
2. 邏輯資源
邏輯資源是WPF特有的資源類型,它是存儲在元素的Resources屬性中的.NET對象,一般須要共享給多個子元素。換句話說,你能夠聲明一個SolidColorBrush對象看成一個邏輯資源,你也能夠聲明一個Style,而後再後續的XAML中簡單經過{StaticResource ResourceName}來使用。資源定義須要有一個在ResourceDictionary中惟一的關鍵字x:Key(單獨的ResourceDictionary中的鍵名不能夠重複,多個ResourceDictionary中鍵名能夠重複,會根據在邏輯數上的lookup的順序來就近生效)。例以下邊的邏輯資源聲明:
<Window x:Class="WpfApplication1.Window2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window2" Height="300" Width="400"> <Window.Resources> <SolidColorBrush x:Key="buttonBackground">Yellow</SolidColorBrush> <SolidColorBrush x:Key="borderBrush">Red</SolidColorBrush> <LinearGradientBrush x:Key="backgroundBrush" StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="Blue" Offset="0"></GradientStop> <GradientStop Color="White" Offset="0.5"></GradientStop> <GradientStop Color="Red" Offset="1"></GradientStop> </LinearGradientBrush> </Window.Resources> <Window.Background> <StaticResource ResourceKey="backgroundBrush" /> </Window.Background> <Grid> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Margin="5" Height="28"> <Image Height="21" Source="Images\zoom.gif"></Image> </Button> <Button Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Margin="5" Height="28"> <Image Height="21" Source="Images\previous.gif"></Image> </Button> <Button Background="{StaticResource buttonBackground}" BorderBrush="{DynamicResource borderBrush}" Margin="5" Height="28"> <Image Height="21" Source="Images\next.gif"></Image> </Button> </StackPanel> </Grid> </Window> |
一個資源能夠被以StaticResource或者DynamicResource的方式來引用,這是以標記擴展(StaticResource/DynamicResource Markup Extension)來標註的。去以StaticResource仍是以DynamicResource來引用一個資源,這取決於:
· 你是以什麼樣的方式來爲你的應用建立資源的:每一個Page?一個Application?一個鬆散XAML文件?仍是一個只包含資源定義的程序集?
· 須要在運行時更新你的資源嗎?
· 查找資源的行爲—向前查詢?
· 資源自己的行爲和屬性
3. Static Resource – 靜態資源
StaticResource僅僅會被應用一次---在第一次須要資源時加載。並且這種引用方式不支持向前加載,全部的資源定義必須在引用以前定義。StaticResource一般用在:
· 設計的APP是將全部的資源放入Page或者App這個級別的Resource Dictionary中的,並且不須要在運行時從新計算—例如只保存一些鬆散文件,邏輯資源的聲明等。
· 不須要給DependencyObject或者Freezable的對象設置屬性。
· Resource Dictionary將被編譯進DLL.
· 須要給不少的Dependency Property賦值。
將一個資源以Static Resource引用,須要用到Static Resource Markup Extension。它在已經定義的資源中查詢特定key的value爲XAML的某個屬性賦值。這個查詢的行爲與load-time查找相似,在當前Page的XAML中或者全部Application的Resources中查找,並在運生成運行時對象。
XAML Attribute Usage
<object property=」StaticResource key}」 … /> |
XAML Object Element Usage
<object> <object.Property> <StaticResource ResourceKey = 「key」 …/> </object.Property> </object> |
前者是咱們常常會用到的方式,好比給一個屬性賦予一個早先定義的話刷格式:
<Button Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Margin="5" Height="28"> |
然後者咱們在前面的例子中也看到了,用法稍微特殊,其實在這個狀況下這個資源確定是一個邏輯資源,至關於一段聲明對象的代碼。好比給window設背景:
<Window.Background> <StaticResource ResourceKey="backgroundBrush" /> </Window.Background> |
Static Resource的查找行爲
· 首先檢查此對象自己的Resources集合內是否有匹配值(根據ResourceKey)
· 其次會在邏輯樹中向上搜尋父元素的Resource Dictionary.
· 最後會檢查Root級別的好比Page,Window,Application等。
4. Dynamic Resource – 動態資源
與Static Resource不一樣的是,Dynamic Resource能夠在程序運行時從新評估/計算資源來生成對應的對象/值,它支持向前引用,只要請求的key在整個應用程序內的任何Resources Dictionary定義過就能夠被加載。若是有多個相同的key存在,則最後搜索到的資源爲有效。
動態資源經常使用於如下狀況:
· 資源直到運行時才能被取定其值的。這些包含想系統資源,或者經過用戶交互/用戶能夠設定的值。例如你能夠用Setter Property語法來引用一些系統資源像SystemColors, SystemFonts等,這些是真正的Dynamic Resource,由於他們是來自用戶的運行環境。
· 在Custom control中有建立/引用主題風格的需求.
· 在運行過程當中調整(好比添加或者合併)ReourceDictionary.
· 須要向前引用的場景。
· 建立的Style的值與當前用戶設定的主題或其餘設定有關的。
· 運行過程當中可能更改邏輯樹的次序的。
下面的代碼片斷演示瞭如何在XAML中引用SystemFonts,這須要用DynamicResource標記:
<Style x:Key="SimpleFont" TargetType="{x:Type Button}"> <Setter Property = "FontSize" Value= "{DynamicResource {x:Static SystemFonts.IconFontSizeKey}}"/> <Setter Property = "FontWeight" Value= "{DynamicResource {x:Static SystemFonts.MessageFontWeightKey}}"/> <Setter Property = "FontFamily" Value= "{DynamicResource {x:Static SystemFonts.CaptionFontFamilyKey}}"/> </Style> |
Dynamic Resource的查找行爲
· 首先遍歷請求對象自己定義的Resources集合。
· 而後遍歷邏輯樹上當前請求對象的父對象,直到遍歷到Root(如Page.Reources, Window.Resources, UserControl.Resources等)
· 隨後會遍歷應用程序的Resources(即Application.Resources)
· 進而會Check當前激活的Theme的資源。
· 最後纔會去遍歷System Resources.
在程序中你能夠經過myWindow.Resources[「key」]的方式來直接訪問一個資源。另外,WPF還提供了TryFindResource(key)和FindResource(key)來支持資源搜索。FindResource方法在沒找到資源的狀況下會觸發ResourceReferenceKeyNotFoundException異常。
其實經過上邊的示例咱們能夠很清楚的看到,在使用靜態資源的地方咱們每每均可以使用動態資源,他們並無什麼合適與否之說,而選擇它們中的哪個,徹底取決於你是否須要資源的使用者發現更新。咱們能夠再來比較一下兩者的區別:
· 對於資源的更新會反映在那些使用了動態資源的元素上,這是他們最主要的區別。
· 性能上:由於動態資源要跟蹤變化,因此須要佔用更多的資源。而靜態資源每每是在window或page加載以後來引用,動態資源會改善加載時間。但靜態資源在使用時卻會有些許性能的提高。
· 動態資源只能設置依賴屬性只,而靜態資源能夠在任何地方使用。好比,咱們能夠聲明一個邏輯資源把它看成一個元素來用,而動態資源卻沒法作到:
<Window x:Class="WpfApplication1.Window3" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window3" Height="300" Width="300"> <Window.Resources> <Button Background="Blue" Margin="5" Height="28" x:Key="prev"> <Image Height="21" Source="Images\previous.gif"></Image> </Button> </Window.Resources> <Grid> <Button Height="20" Width="70" Content="Content" /> <StaticResource ResourceKey="prev" /> </Grid> </Window> |
· 當你在XAML中使用StaticResource時,是不支持Forward Reference的,也就是說任何資源必須在XAML文件中聲明以後纔可使用。若是是在同一個元素中定義,則只能使用Dynamic Resource。
<Window x:Class="WpfApplication1.Window3" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window3" Height="300" Width="300" Background="{DynamicResource winBackground}"> <Window.Resources> <Button Background="Blue" Margin="5" Height="28" x:Key="prev"> <Image Height="21" Source="Images\previous.gif"></Image> </Button> <LinearGradientBrush x:Key="winBackground" StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="Blue" Offset="0"></GradientStop> <GradientStop Color="White" Offset="0.5"></GradientStop> <GradientStop Color="Red" Offset="1"></GradientStop> </LinearGradientBrush> </Window.Resources> <Grid> <Button Height="20" Width="70" Content="Content" /> <StaticResource ResourceKey="prev" /> </Grid> </Window> |
5. 資源的應用
前變也已經說過能夠引用別的程序集的資源,那麼到底如何引用呢?另外,咱們都是在說XAML中引用資源,那麼代碼中又該如何去作呢?咱們又一般會在什麼地方定義資源呢?這裏就來講一下這幾個問題,以及某些特殊狀況下的定義。
· 共享資源
默認狀況下,當有一個資源被引用到多個地方是,使用的都是同一個對象實例,這一般是理想的行爲。但你一樣也能夠把x:Shared=」False」來讓每一個引用資源的地方都生成一個不一樣的對象實例,這樣能夠獨立進行修改。這一般用於多邏輯資源的聲明。
· 程序代碼中定義和應用資源
在代碼中定義一個新的Resource,你須要首先獲得一個ResourceDictionary的實例,而後再建立一個新的資源並將這個資源加入到ResourceDictionary的實例中。而在訪問資源時,你須要用到myWindow.Resources[「key」]或者object.FindResource(key)函數。注意myWindows是你當前window的實例,而在用FindResource時,前邊的object表明的是這個資源所在的ResourceDictionary的父對象。
private void Window_Loaded(object sender, RoutedEventArgs e) { Window3 window = new Window3(); window.Resources.Add("buttonBackground", new SolidColorBrush(Color.FromRgb(0,255,0))); window.Resources.Add("borderBrush", new SolidColorBrush(Color.FromRgb(255, 0, 0)));
btnContent.Background = (Brush)window.FindResource("buttonBackground"); btnContent.BorderBrush = (Brush)window.FindResource("borderBrush"); } |
注意在找不到資源時會拋出一個ResourceReferenceKeyNotFoundException異常,因此儘可能調用TryFindResource方法更好些,若是失敗將會返回null.
上邊的例子是針對StaticResource來講的,它就至關於這段代碼:
<Button x:Name="btnContent" Canvas.Left="50" Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Content="Content" /> |
|
但對於DynamicResource來講,須要調用這個元素的SetResourceReference方法來更新依賴屬性的綁定。下邊的兩端代碼是相等的:
<Button x:Name="btnContent" Canvas.Left="50" Background="{DynamicResource buttonBackground}" BorderBrush="{ DynamicResource borderBrush}" Content="Content" /> |
btnContent.SetResourceReference(Button.BackgroundProperty, "buttonBackground"); btnContent.SetResourceReference(Button.BorderBrushProperty, "borderBrush"); |
SetResourceReference是能夠在資源被加載到某個Resource Dictionary以前調用的,即使是FindResource會失敗,但引用的創建仍然有效。
· 從另外一個程序集中訪問嵌入式資源
除了能夠用特定的URI來訪問別的程序中的二進制資源外,WPF能夠從另一個程序集中獲取邏輯資源,這得用到ComponentResourceKey標記。要使用ComponentResourceKey,每一個資源都必須有一個鍵名。而後你能夠經過這樣的方式訪問:
<Button Background=」{DynamicResource {x:Static otherAssembly: MyClass.MyClassBrushKey }}」 /> |
· Styles 和Implicit Keys
樣式是最多見的一種資源,並且它老是被定義在Resource Dictionary中,爲了來重用。Style其實就是一系列分組的Setter的集合,用來設定邏輯資源的屬性值,它有一種比較特殊的情形就是Implicit Keys,能夠不聲明一個x:Key的名字,而只設置x:TargetType的值,這樣面對的就是對於全部這個類型的控件都使用這個樣式。下邊的示例中x:Key的值其實就是type-Button。
<Style TargetType="Button"> <Setter Property="Background"> <Setter.Value> <LinearGradientBrush> <GradientStop Offset="0.0" Color="AliceBlue"/> <GradientStop Offset="1.0" Color="Salmon"/> </LinearGradientBrush> </Setter.Value> </Setter> <Setter Property="FontSize" Value="18"/> </Style> |
Style也是資源的一種---從某種意義上來講,它很相似於咱們給普通HTML中的元素創建CSS. 對於Designer來講可能輕鬆一些哦。
6. Resource Dictionary –資源字典
全部的資源項在最終都會被整合到Resource Dictionary中的,也就是說不管是FrameworkElement的Resources,仍是Window的Resources,仍是Application的Resources,仍是特定的ResourceDictionary中定義的resources在整個應用編譯執行的時候實際上他們都在一塊兒的做爲可遍歷集合共同存在於一個相對會話空間內的。
咱們也提到過Resource的key是能夠被容許有相同的,這樣在遍歷不一樣相對地址的Resource Dictionary時會根據StaticResource或者DynamicResource的lookup behavior來肯定哪一個有效。一般爲了維護和靈活性的考慮,咱們一般會將Resource Dictionary文件分紅好幾個,但在某些場合下咱們只須要用其中某些資源,那麼我麼能夠將資源從幾個獨立的文件中提取併合並,那麼能夠這麼作:
<Window.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Diction1.xaml"></ResourceDictionary> <ResourceDictionary Source="Diction2.xaml"></ResourceDictionary> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Window.Resources> |
注意,在資源合併後,仍然會出現重複值的狀況,那麼最後取出的資源獲勝。
7. Localization – 本地化
本地化和換膚其實都是在用ResourceDictionary來作文章的。說白了,Localization就是用不一樣語言下取不一樣事先設定好的資源來顯示而已。要作到這些很容易,4步就能夠輕鬆實現:
· 定義Resource Dictionary來包含不一樣語言下要顯示的資源項。
建立單獨的Resource Dictionary文件,並以語言自己名字來命名,並把en-US來做爲默認語言環境(這裏順便就命名爲default.xaml了)
Default.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String x:Key="Title_PM">Project Manager</sys:String> <sys:String x:Key="Title_PL">Project Lead</sys:String> <sys:String x:Key="Title_SD">Senior Developer</sys:String> <sys:String x:Key="Title_SA">System Architecture</sys:String>
</ResourceDictionary> |
zh-CN.xaml (注意對.NET命名空間的引用)
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String x:Key="Title_PM">項目經理</sys:String> <sys:String x:Key="Title_PL">項目主管</sys:String> <sys:String x:Key="Title_SD">資深開發工程師</sys:String> <sys:String x:Key="Title_SA">系統架構師</sys:String>
</ResourceDictionary> |
· 給應用程序添加默認資源:其實就是將默認的Resource Dictionary加入到Application的全局Resource裏邊。
<Application x:Class="WpfApplication1.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Window1.xaml"> <Application.Resources>
<ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Language\default.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary>
</Application.Resources> </Application> |
· 在Application啓動時根據不一樣語言來加載以語言命名的XAML文件(Resource Dictionary)。由於對於重名的資源,後來加載的資源將會勝出,因此以當前語言名加載的XAML文件中的資源項將會被引用。這就是多語言的本質!
public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e);
LoadLanguage(); }
private void LoadLanguage() { CultureInfo currentCultureInfo = CultureInfo.CurrentCulture; ResourceDictionary langRd = null; try { langRd = Application.LoadComponent( new Uri(@"Language\" + currentCultureInfo.Name + ".xaml", UriKind.Relative)) as ResourceDictionary; } catch { }
if (langRd != null) { if (this.Resources.MergedDictionaries.Count > 0) { this.Resources.MergedDictionaries.Clear(); } this.Resources.MergedDictionaries.Add(langRd); } } } |
· 在XAML中引用資源。
<TextBlock Canvas.Top="50" Width="100" Height="24" Text="{StaticResource Title_PM}" /> |
· 大功告成,運行程序你會看到默認的語言的顯示:Project Manager.固然若是你的默認文化是英語的話。用程序換成中文試試結果?沒問題,在LoadLanguage()以前更改語言便可:
base.OnStartup(e); CultureInfo info = new CultureInfo("zh-CN"); Thread.CurrentThread.CurrentCulture = info; Thread.CurrentThread.CurrentUICulture = info; LoadLanguage(); |
簡單吧?呵呵。有關Resource的東西基本上就這麼多了,換膚咱們再開闢另外一個話題來談吧。這但是WPF夠炫的Feature之一哦。。。。
一些與本文有關的小代碼片斷及Localization的示例請點擊這裏下載。