本期提要:
因為需要在代碼中修改TextBox的Style屬性,但是我卻把本來適用于TextBlock的Style賦值給了TextBox,這樣導致了一個異常的彈出。開始以為是在ResourceDictionary中查找的方法不對,所以對Resource Dictionary進行了了解。就是本文的內容了。本文主要參考MSDN的教程,并佐以實例和自己的理解,希望對大家有所幫助。本章內容沒有圖例,所以我盡量把排版弄好一些。
Resource Dictionary簡介
一言以蔽之,Resource Dictionary是放置資源的地方,以方便在其他地方進行引用。既然是字典,那么就應該是以鍵值對的形式存在。事實確實是如此,Resource Dictionary中的資源都有一個x:Key,其他的都是它的值。如果試圖給Resource Dictionary添加一個沒有Key的子元素,會拋出一個解析異常或者run-time異常。有兩種例外的元素不需要Key,我們會在后面介紹。
Resource Dictionary的常用的場景是在XAML中使用,在XAML中定義一個資源,在其他地方對其進行StaticResouce引用。在代碼中使用也是可以的,這樣可以允許我們在運行時對Resource Dictionary根據我們的設想進行相應的調整。
為了對Resource Dictionary有個直觀的理解,我們摘取了App.XAML文件中的代碼進行講解。
2 <ResourceDictionary>
3 <ResourceDictionary.MergedDictionaries>
4
5 <!--
6 Styles that define common aspects of the platform look and feel
7 Required by Visual Studio project and item templates
8 -->
9 <ResourceDictionary Source="Common/StandardStyles.xaml"/>
10 </ResourceDictionary.MergedDictionaries>
11
12 <!-- Application-specific resources -->
13
14 <x:String x:Key="AppName">Dino-Dino</x:String>
15 </ResourceDictionary>
16 </Application.Resources>
17 </Application>
這是一個典型的Resource,包括兩個ResourceDictionary,并且這兩個ResourceDictionary都沒有Key,也沒有其他名字。其中第2行的ResourceDictionary包含了一個Key值為”AppName“的x:String類型的子元素,這個Resource Dictionary叫做主要資源字典。第3行的意思是,我們第2行的ResourceDictionary還有一個外部的資源字典,合并在這里,這個外部資源字典的路徑在第9行給出,第9行的資源字典叫做合并資源字典。
如何使用這個Resource Dictionary 中的資源呢?
2
"Common/StandardStyles.xaml"文件中放了微軟為我們準備的各種常用的資源,打開這個文件的話,不難發現,這就是一個Resource Dictionary,里面有各種Style。
對Resource Dictionary有個大概的印象了,我們就可以詳細地探索一下Resource Dictionary中到底有神馬東西了。
Resource Dictionary之進一步了解
1. 包含的內容
--------
如果你打開"Common\StandardStyle.xaml“文件的話,你會發現里面有一堆的Style,不錯,Style的共享是最常見的。下面的這個就是一個例子。
2 <Setter Property="LineHeight" Value="20"/>
3 <Setter Property="LineStackingStrategy" Value="BlockLineHeight"/>
4 <!-- Properly align text along its baseline -->
5 <Setter Property="RenderTransform">
6 <Setter.Value>
7 <TranslateTransform X="-1" Y="4"/>
8 </Setter.Value>
9 </Setter>
10 </Style>
------
另外還有 繼承自FramworkTemplate的模板(包括ControlTemplate、DataTemplate)
2 <Grid HorizontalAlignment="Left">
3 <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
4 <Image Source="{Binding Image}" Stretch="UniformToFill"/>
5 </Border>
6 <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
7 <TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>
8 <TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
9 </StackPanel>
10 </Grid>
11 </DataTemplate>
3.Storyboard
4. Transform
5. Metrix,Matrix3D
6. Point
上面的都沒用過,所以也沒例子。
------
7. 其他與UI相關的結構,如Thickness和CornerRadius。
------
8. 還有一些你自己定義的類型,轉換為本地資源的東西。自定義類型必須有一個默認構造函數,并且在繼承關系中不能有UIElement類。這個用到過,當時在懷疑為什么有些可以轉成本地資源,有些不可以,原來是有這些道道啊。
------
9. XAML固有數據類型
包括:x:Boolean -------區分大小寫的。不能用x:Bool代替
x:String
x:Double
x:Int32
就這四個,示例如下:
2 <x:Boolean>True</x:Boolean>
3 <x:String x:Key="AppName">Dino-Dino</x:String>
4 <x:Int32 x:Key="GamNanStyle">12</x:Int32>
------
10. 其他
2 <SolidColorBrush x:Key="AppBarBackgroundThemeBrush" Color="#E5000000" />
2. x:Key
大家還記得我們簡介里面提到過的么?所有的資源都是一個鍵值對,x:Key就是我們的鍵,其他的東西就是值。不設置鍵值將會引發異常,這些大家都有印象了。不過人們都很關心例外的情況,這兩種例外的情況是:Control元素 的Template屬性和具有TargetType屬性的Style元素.
第一種情況我暫時還沒有碰到,不過第二種適用于Style的可以解釋下,我們用個例子能更好地解釋一下:
2 <Setter Property="MinWidth" Value="0"/>
3 <Setter Property="MinHeight" Value="0"/>
4 <!--此處省略一些-->
5 </Style>
當我們在XAML文件中使用Button的時候,我們可以用StaticResource來引用這種Style
2 <Button x:Name="btn2"/>
如果我們這么做了,那么,btn1將是TextButton類型的,它的MInWidth屬性是0, MinHeight屬性是0. 而btn2將是默認類型,它的MinWidth和MinHeight屬性將是其他值。
如果我們的Style不使用Key的話,那么這種Style將適用于所有的TargetType。
2 <Setter Property="MinWidth" Value="0"/>
3 <Setter Property="MinHeight" Value="0"/>
4 <!--省略-->
5 </Style>
XAML文件中:
2 <Button x:Name="btn4"/>
所以說,你要讓所有的Button都是一個類型的話,在Resource Dictionary中在Style中不要給它Key值就好了,如果你需要一個特殊的Style,StaticResource應用那個Key值的Style吧,少年。
3. 直接資源和應用資源
我們一直在談Resource Dictionary(資源字典)的問題,突然跳轉到資源上來了。不奇怪,因為我們的資源字典是放在這兩個資源Collection中的。
其實這兩個概念從字面上就可以很好地理解。直接資源:在該頁面上直接可以引用的資源。應用資源:應用級別的資源,所有的頁面都可以引用。了解么?不了解也無所謂,直接資源一般放在某一個page.XAML文件中,在文件的開始就定義好,以便本頁面可以直接引用。應用資源:一般放在App.XAML文件中。
這是直接資源的示例:
2 <CollectionViewSource
3 x:Name="groupedItemsViewSource"
4 Source="{Binding Groups}"
5 IsSourceGrouped="true"
6 ItemsPath="Items"/>
7
8 <DataTemplate x:Key="SmallDateTemplate">
9 <Grid Width="190" Height="80" Background="#421A5B22">
10 </DataTemplate>
11 </Page.Resources>
這個Page的資源中包含了兩個元素,一個是CollectionViewSource,一個是DataTemplate,你或許會說,為什么CollectionViewSource沒有Key?你這樣是錯的。呵呵,恭喜你,在Resource Dictionary中必須要包含Key的觀念已經深植入你的意識中,這太好了。但是,這個元素并不在一個Dictionary中。。。
你可以在MainPage.XAML中使用這些資源,但是你不能在Page2.XAML中使用。這就是直接資源的定義吧,你可以看到自己院子里面的東西,其他人卻因為隔著圍墻看不到里面的東西。(有邏輯么?貌似沒有)
而應用資源一般放在App.XAML中,并且放在Application.Resources中:
2 <ResourceDictionary>
3 <ResourceDictionary.MergedDictionaries>
4
5 <!--
6 Styles that define common aspects of the platform look and feel
7 Required by Visual Studio project and item templates
8 -->
9 <ResourceDictionary Source="Common/StandardStyles.xaml"/>
10 </ResourceDictionary.MergedDictionaries>
11
12 <!-- Application-specific resources -->
13
14 <x:String x:Key="AppName">Dino-DailyTask</x:String>
15 </ResourceDictionary>
16 </Application.Resources>
17 </Application>
你又要說了,<ResourceDictionary.MergedDictionaries>在Dcitionary中,沒有Key,并且里面的<ResourceDictionary>也是沒有Key的。額,你是對的,他們確實沒有。微軟告訴我們,他們不需要有,并且,里面的ResourceDictionary只能有一個Source,而且就足夠了。
4. 如何引用資源字典中的資源,引用順序是如何的?
為了解決這個問題,我打亂了MSDN中教程的順序,從應用資源開始說起,這里面的道道不多,很簡單,你了解了就easy to use了。
1). 主要資源字典、合并資源字典、主題資源字典
如何引用?用StaticResource,之前我們都講過了,這里就不贅述了。那么StaticResource是怎么查找這些資源的呢?我們有直接資源,有應用資源,應用資源中還有主資源和合并資源,這個順序是如何?這些資源中可不可以有相同的key?
我們已經知道了直接資源和應用資源,也了解了應用資源是應用級別的資源。而這三種資源字典都是應用資源中的概念,我再貼個代碼讓大家有個直觀的了解:
2 <ResourceDictionary><!--主要資源字典 Main Dictionary-->
3 <ResourceDictionary.MergedDictionaries><!--合并資源字典 Merged Dictionary-->
4 <ResourceDictionary Source="Common/StandardStyles.xaml"/>
5 </ResourceDictionary.MergedDictionaries>
6
7 <!-- Application-specific resources -->
8
9 <x:String x:Key="AppName">Dino-DailyTask</x:String>
10 </ResourceDictionary>
11 </Application.Resources>
12 </Application>
主題資源字典不見了。。。它存在于我們的合并資源字典中,一般都是這么一個構成的方式,下面的代碼是StandardStyle.XAML文件中的部分片段:
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
4
5 <!-- Non-brush values that vary across themes -->
6
7 <ResourceDictionary.ThemeDictionaries>
8 <ResourceDictionary x:Key="Default">
9 <x:String x:Key="BackButtonGlyph"></x:String>
10 <x:String x:Key="BackButtonSnappedGlyph"></x:String>
11 </ResourceDictionary>
12
13 <ResourceDictionary x:Key="HighContrast">
14 <x:String x:Key="BackButtonGlyph"></x:String>
15 <x:String x:Key="BackButtonSnappedGlyph"></x:String>
16 </ResourceDictionary>
17 </ResourceDictionary.ThemeDictionaries>
合并字典(Merged Dictionary),它的目的就是為了使用戶可以引用外部的文件擴充自己的資源。代碼中也可以看出,我們可以使用StandardStyle.XAML中的資源服務本程序。合并字典只能由一個Source屬性,并且不能有Key值。
主題字典是一種特殊類型的合并字典,用于保存各種資源,具體資源取決于用戶當前在其 PC 上使用的主題。例如,“輕”主題可能使用白色畫筆,而默認主題可能使用黑色畫筆。畫筆會更改,但使用畫筆作為資源的控件創作過程可能是相同的,只需引用一個主題資源。
此處的每個 ResourceDictionary 元素必須有一個 x:Key 值。該值是一個命名相關主題的字符串—例如,"Default" 或 "HighContrast“
同樣與合并字典一樣,多次定義同一個鍵是合法的,只要在每個主題字典單元中是唯一的。事實上,這是一種特意的設計:每個主題字典應該有一組相同的鍵。否則,任何缺少某個鍵的主題都可能在加載該主題時導致問題。不同于合并字典,每個主題的定義順序無關緊要。對于主題字典,要用于資源查找的活動字典始終在運行時確定。一般而言,查找邏輯基于活動主題到一個特定主題字典的 x:Key 的映射。
了解了么?
2). 資源的查找順序:
知道了這些字典了之后,我們有個級別的概念了,就好理解資源的查找順序了,我看還是上個圖吧,比較好理解一些

用文字描述就是:先在自己的Resource里面查找,如果沒有,向父類控件的Resource中查找,如果沒有,向文件的根級別方向查找,一般就會到Page.Resource中去了,如果這里面依然沒有,就會向Application.Resource中查找。如果到達了合并字典了,我們看到箭頭是先到了Dictionary2, 而不是首先添加的Dictionary1,至于為什么,微軟是規則制定者, 我不太清楚。
3). 使用相同的Key
前面已經說了,在一個Dictionary中不能使用相同的Key,但是如果不在同一級別,那么這個規則是可以被打破的。什么叫不是一個級別?
直接資源和應用資源不在同一級別。
主要字典和合并字典和主題字典也不在同一級別。
不同的合并字典也不在同一級別。
不同的主題字典也不在同一級別。
2 <Application.Resources>
3 <ResourceDictionary>// 主要資源字典
4 <SolidColorBrush Color="#d0157820" x:Key="muddyBrush"/>//主要資源中可以有其他鍵控資源
5 <ResourceDictionary.MergedDictionaries>// 合并資源字典
6 <ResourceDictionary Source="rd1.xaml" />//ResourceDictionary中不能有其他東西,只能由Source
7 <ResourceDictionary Source="rd2.xaml" />
8 </ResourceDictionary.MergedDictionaries>
9 </ResourceDictionary>
10 </Application.Resources>
4). 資源回退機制
我們可以利用合并資源字典的查找順序和使用相同鍵值這一特性來創建資源回退值的優先級順序。比如說:rd1.xaml中有一個
在我的MainPage.Xaml文件中我這么引用:
如果我有其他要求的話,并且我不希望修改MainPage.Xaml文件,那么我可以根據合并字典的查找順序在rd2.xaml中創建一個鍵值相同的Style:
這樣就改變了,這就是我理解的回退值機制。
為了保證回退機制可以使用,在主要字典中必須不能包含相同的鍵值。你懂的,如果那樣的話,就會優先找懂啊主要字典中的鍵,永遠也不會找到合并字典中去。
這里有個文件可以讓大家了解相關的主題字典:使用文本編輯器打開 \(Program Files)\windows kits\8.0\Include\winrt\xaml\design 中的 XAML 文件,可以看到其主題資源的定義。
5). 雜項
1. 向前引用:一個資源必須定義了之后才能被引用。所以我們的資源一般放在接近開頭的位置進行定義。
2. UserControl比較特殊,UserControl 必須能夠支持在自己的定義范圍查找序列中查找該資源—,也就是說它不能訪問應用資源。
6). 在代碼中使用資源字典
使用Lookup方法(C++),下面是如何在應用資源里面查找相應的代碼:
2 {
3 auto style = safe_cast<Windows::UI::Xaml::Style^>(App::Current->Resources->Lookup("CaptionTextStyle"));
4 //auto style = safe_cast<Windows::UI::Xaml::Style^>(App::Application::Current->Resources->Lookup(L"TitleTextStyle"));
5 if(style != nullptr)
6 textblock->Style = style;
7
8 }
這里又回到開頭說的問題了,因為我把TargetType為TextBlock的Style賦值給了TextBox,所以造成了異常,所以在代碼中進行操作的時候,一定要了解你的右值和左值是否對應。
在論壇里面問到了原因,但是還是把相關的Dictionary知識了解了一下,以前只知道隨便用,現在大概了解它的機制了。把MSDN上的內容看懂了,又用自己的話描述一番,感覺還是挺清楚的。雖然東西有點多,但是還是很有幫助的。
下一章的東西還在待定。。。