Dino Windows 8 学习笔记(十)-- 一个异常引发的文章之Resource Dictionary

Dino Windows 8 学习笔记(十)-- 一个异常引发的文章之Resource Dictionary

本期提要:

        因为需要在代码中修改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文件中的代码进行讲解。
      

 1       < 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-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 中的资源呢?
1               < TextBlock  x:Name ="pageTitle"  Text ="{StaticResource AppName}"  Grid.Column ="1"  IsHitTestVisible ="false"  Style ="{StaticResource PageHeaderTextStyle}" />
2 
      这里有两个地方都引用了资源,第一个是Text属性,引用了Key值为”AppName“的资源,还有一个Style的属性,引用了"Common\StandardStyle.xaml”文件中的PageHeaderTextStyle资源。
      "Common/StandardStyles.xaml"文件中放了微软为我们准备的各种常用的资源,打开这个文件的话,不难发现,这就是一个Resource Dictionary,里面有各种Style。


对Resource Dictionary有个大概的印象了,我们就可以详细地探索一下Resource Dictionary中到底有神马东西了。

Resource Dictionary之进一步了解

1. 包含的内容
--------
      如果你打开"Common\StandardStyle.xaml“文件的话,你会发现里面有一堆的Style,不错,Style的共享是最常见的。下面的这个就是一个例子。

 1       < Style  x:Key ="BaselineTextStyle"  TargetType ="TextBlock"  BasedOn ="{StaticResource BasicTextStyle}" >
 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)

 1       < DataTemplate  x:Key ="StandardItemTemplate" >
 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。

1  < Thickness  x:Key ="AppBarBottomBorderThemeThickness" > 0,2,0,0 </ Thickness >

------
8. 还有一些你自己定义的类型,转换为本地资源的东西。自定义类型必须有一个默认构造函数,并且在继承关系中不能有UIElement类。这个用到过,当时在怀疑为什么有些可以转成本地资源,有些不可以,原来是有这些道道啊。
------
9. XAML固有数据类型
包括:x:Boolean -------区分大小写的。不能用x:Bool代替
         x:String 
         x:Double
         x:Int32
就这四个,示例如下:

1  < x:Double  x:Key ="AppBarThemeMinHeight" > 68 </ x:Double >
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. 其他

1  < FontFamily  x:Key ="SymbolThemeFontFamily" > Segoe UI Symbol </ FontFamily >
2  < SolidColorBrush  x:Key ="AppBarBackgroundThemeBrush"  Color ="#E5000000"   />

2. x:Key

      大家还记得我们简介里面提到过的么?所有的资源都是一个键值对,x:Key就是我们的键,其他的东西就是值。不设置键值将会引发异常,这些大家都有印象了。不过人们都很关心例外的情况,这两种例外的情况是:Control元素 的Template属性和具有TargetType属性的Style元素. 
      第一种情况我暂时还没有碰到,不过第二种适用于Style的可以解释下,我们用个例子能更好地解释一下:

1    < Style  x:Key ="TextButtonStyle"  TargetType ="Button" >
2           < Setter  Property ="MinWidth"  Value ="0" />
3           < Setter  Property ="MinHeight"  Value ="0" />
4           <!-- 此处省略一些 -->
5  </ Style >
 
      当我们在XAML文件中使用Button的时候,我们可以用StaticResource来引用这种Style

1  < Button  x:Name ="btn1"  Style ={StaticResource  TextButtonStyle} />
2  < Button  x:Name ="btn2" />

      如果我们这么做了,那么,btn1将是TextButton类型的,它的MInWidth属性是0, MinHeight属性是0. 而btn2将是默认类型,它的MinWidth和MinHeight属性将是其他值。

      如果我们的Style不使用Key的话,那么这种Style将适用于所有的TargetType。

1  < Style  TargetType ="Button" >  <!--没有Key-->
2           < Setter  Property ="MinWidth"  Value ="0" />
3           < Setter  Property ="MinHeight"  Value ="0" />
4           <!-- 省略 -->
5  </ Style >

      XAML文件中:
1  < Button  x:Name ="btn3" />
2  < Button  x:Name ="btn4" />
      btn3, btn4的MInWidth属性都是0, MinHeight属性都是0。 这有点象CSS,如果不指定类型x:Class的话,将会适用于所有Targettype.现在又来了个btn5,<Button x:Name="btn5" Style ={StaticResouce DinoStyleButton}/>,假设我们有DinoStyleButton的话,那么btn5将不再是MinWidth属性为0,而是DinoStyleButton中的MinWidth中的值。
      所以说,你要让所有的Button都是一个类型的话,在Resource Dictionary中在Style中不要给它Key值就好了,如果你需要一个特殊的Style,StaticResource应用那个Key值的Style吧,少年。

3. 直接资源和应用资源

       我们一直在谈Resource Dictionary(资源字典)的问题,突然跳转到资源上来了。不奇怪,因为我们的资源字典是放在这两个资源Collection中的。
       其实这两个概念从字面上就可以很好地理解。直接资源:在该页面上直接可以引用的资源。应用资源:应用级别的资源,所有的页面都可以引用。了解么?不了解也无所谓,直接资源一般放在某一个page.XAML文件中,在文件的开始就定义好,以便本页面可以直接引用。应用资源:一般放在App.XAML文件中。     
       这是直接资源的示例:

 1  < Page .Resources >
 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中:

 1       < 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?
        我们已经知道了直接资源和应用资源,也了解了应用资源是应用级别的资源。而这三种资源字典都是应用资源中的概念,我再贴个代码让大家有个直观的了解:
      
 1       < Application .Resources >
 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文件中的部分片段:

 1  < ResourceDictionary
 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" > &#xE071; </ x:String >
10               < x:String  x:Key ="BackButtonSnappedGlyph" > &#xE0BA; </ x:String >
11           </ ResourceDictionary >
12 
13           < ResourceDictionary  x:Key ="HighContrast" >
14               < x:String  x:Key ="BackButtonGlyph" > &#xE071; </ x:String >
15               < x:String  x:Key ="BackButtonSnappedGlyph" > &#xE0C4; </ x:String >
16           </ ResourceDictionary >
17       </ ResourceDictionary.ThemeDictionaries >
      主要字典可以包括其他键控资源,也可以包含一个MergedDictionary属性,你只能往这个属性中添加ResourceDictionary,添加的这个ResourceDictionary就叫做合并字典。
      合并字典(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,但是如果不在同一级别,那么这个规则是可以被打破的。什么叫不是一个级别?
         直接资源和应用资源不在同一级别。
         主要字典和合并字典和主题字典也不在同一级别。
         不同的合并字典也不在同一级别。
         不同的主题字典也不在同一级别。

 1 
 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中有一个
 
1  < Style  x:Key ="DinoDinoStyleButton"  TargetType ="Button" >< Setter  Property ="BackgroundColor"  Value ="#ffffffff" />   </ Style >

      在我的MainPage.Xaml文件中我这么引用:

1  < Button  x:Name ="DinoButton"  Style ={StaticResource  DinoDinoStyleButton} />

      如果我有其他要求的话,并且我不希望修改MainPage.Xaml文件,那么我可以根据合并字典的查找顺序在rd2.xaml中创建一个键值相同的Style:

1  < Style  x:Key ="DinoDinoStyleButton"  TargetType ="Button" >< Setter  Property ="BackgroundColor"  Value ="#11111111" />   </ Style >

      这样就改变了,这就是我理解的回退值机制。
      为了保证回退机制可以使用,在主要字典中必须不能包含相同的键值。你懂的,如果那样的话,就会优先找懂啊主要字典中的键,永远也不会找到合并字典中去。

      这里有个文件可以让大家了解相关的主题字典:使用文本编辑器打开 \(Program Files)\windows kits\8.0\Include\winrt\xaml\design 中的 XAML 文件,可以看到其主题资源的定义。

5). 杂项
     1.  向前引用:一个资源必须定义了之后才能被引用。所以我们的资源一般放在接近开头的位置进行定义。

     2.  UserControl比较特殊,UserControl 必须能够支持在自己的定义范围查找序列中查找该资源—,也就是说它不能访问应用资源。

6). 在代码中使用资源字典

      使用Lookup方法(C++),下面是如何在应用资源里面查找相应的代码:
      

1  if (App::Current -> Resources -> HasKey(L " CaptionTextStyle " ))
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上的内容看懂了,又用自己的话描述一番,感觉还是挺清楚的。虽然东西有点多,但是还是很有帮助的。
      下一章的东西还在待定。。。


 

你可能感兴趣的:(Dino Windows 8 学习笔记(十)-- 一个异常引发的文章之Resource Dictionary)