WPF与Prism一并学习(五)

时间:2021-03-11 22:21:10

关于WPF的布局部分不想写了,感觉比较散乱,其实找本书看看基本也讲的八九不离十了,一般都讲的很清楚,又好懂

无非就是Grid,StackPanel,DockPanel,WrapPanel,Canvas那些,用的熟了自然也没什么障碍的.


唯一一个大多数书上写的不清不楚的就是"依赖属性"这个玩意,

MSDN是这样解释依赖属性的

当您定义自己的属性并需要它们支持 Windows Presentation Foundation (WPF) 功能的诸多方面(包括样式、数据绑定、继承、动画和默认值)时,应将其实现为依赖项属性。

那么根据我的理解,依赖属性就是普通属性之上,再附加一些WPF特有的功能,就变成WPF里面适用的属性了,这样的属性就叫依赖属性罗??


最近在看<<WPF编程宝典-使用C#2012和.NET4.5(第四版)>>这本书,看的是似懂非懂

书中几条主要脉络讲"依赖属性".


1.理解依赖项属性

       书中讲的就一句,"出于性能方面的考虑",接下来的话就有点不通顺了."如果不承受这一额外负担,普通属性就不能支持这些依赖项属性的所有功能",拜托!这句太不和谐了吧,看起来都不像中文啊,很绕不说,简直不知道它想说什么!再说,这也只是从性能方面来说,还有别的优点吗?


2.定义依赖项属性

       书中提到,定义依赖项属性应该始终保持可用,什么意思?又说到,甚至可能需要在多个类之间共享信息.必须将DependencyProperty对象定义 为与其相关联的类的静态字段.说了半天,总算说到点子上,怪了,谈第一点时不说为什么出于性能的考虑,语焉不详,第二点谈定义依赖项属性的时候,反而提了,原来就是要缓存这些相同的值,这些值在内存里只保有一份就可以了,不必拷贝多份,从而节约内存的使用.

       下面在网上找到一个比较能说明问题的例子,比如说Buttton这个对象,它必然继承自DependencyObject(所有控件的基类),但DependencyObject不是它的直接父类,它的直接父类是ButtonBase,它的整个继承链是这样的:

      Button->ButtonBase->ContentControl->Control->FrameworkElement->UIElement->Visual->DependencyObject->…

       如此一来,Button从父类继承的属性可想而知有多少了! 对Button来说,大多数属性并没有被修改,仍然保持着父类定义时的默认值。通常情况,在整个Button对象的生命周期里,也只有少部分属性被修改,大多数属性一直保持着初始值。每个字段,都需要占用4K等不等的内存,这里,就出现了期望可以优化的地方!

       我并没有亲自用Reflector去读DependencyObject的源码,大致看了一下网上和书上对源码的解释和引用,只知道里面定义了一个字典对象用来缓存在DependencyObject里注册的依赖属性项,这足以解释了之前的说法.


3.注册依赖属性

   这书里描述的是更加的不清晰,一上来就是一段代码

[csharp] view plain copy print ?
  1. static FrameworkElement()  
  2. {  
  3.           FramworkPropertyMetadata metadata =   
  4.                         new FrameworkPropertyMetadata(new Thickness(),FrameworkPropertyMetadataOptions.AffectsMeasure);  
  5.           MarginProperty = DependencyProperty.Register("Margin",typeof(Thickness),typeof(FrameworkElement),metadata,  
  6.                          new ValidateValueCallback(FrameworkElement.IsMarginValid));  
  7.           ...  
  8. }  

FramworkPropertyMetadata这个东西书上说," 指示希望通过依赖项属性使用什么服务(如支持数据绑定,动画以及日志)",什么意思?不明白的很!从名字上看,它是"Metadata",是元数据,

看MSDN对它的解释

报告或应用依赖项属性的元数据,具体来说,就是添加特定于框架的属性系统特征。

点这里

FramworkPropertyMetadata 又继承自 PropertyMetadata

又找到MSDN对它的解释

点这里

定义依赖项对象在应用于特定类型(包括该属性向其注册的条件)时行为的某些方面。


看了这些,真的是一头雾水,马上又找到MSDN上面一个完整例子下载来看

具有依赖项属性的自定义类的示例

分析一下这个程序中,注册并使用依赖属性的整个过程

a)首先定义了一个枚举类型

[csharp] view plain copy print ?
  1. public enum ShirtColors  
  2. {  
  3.     None,  
  4.     White,  
  5.     Red,  
  6.     Green,  
  7.     Yellow  
  8. }  
b)再把这个类型定义成一个依赖属性

[csharp] view plain copy print ?
  1. public static readonly DependencyProperty ShirtColorProperty = DependencyProperty.Register("ShirtColor"typeof(ShirtColors), typeof(Shirt), new FrameworkPropertyMetadata(ShirtColors.None));  
貌似FrameworkPropertyMetadata在这里的作用也只是给这个依赖属性一个初始的默认值.

c)然后定义一个属性包装器对它进行包装

[csharp] view plain copy print ?
  1. public ShirtColors ShirtColor  
  2. {  
  3.     get { return (ShirtColors)GetValue(ShirtColorProperty); }  
  4.     set { SetValue(ShirtColorProperty, value); }  
  5. }  
为什么上面的set,get跟普通类里面的不同,还要调用 GetValue(),SetValue()这两个方法?
MSDN对GetValue这么解释

点这里

返回 DependencyObject 的此实例上的依赖项属性的当前有效值。

跟普通属性的区别可能就体现在"有效值"这三个字上!

d)定义依赖属性的时候,还可以定义事件,下面两个一个是改变属性时触发的,一个是验证时触发的

[csharp] view plain copy print ?
  1. public static readonly DependencyProperty ShirtTypeProperty = DependencyProperty.Register(  
  2.     "ShirtType",  
  3.     typeof(ShirtTypes),   
  4.     typeof(Shirt),   
  5.     new FrameworkPropertyMetadata(  
  6.         ShirtTypes.None,  
  7.         new PropertyChangedCallback(OnShirtTypeChangedCallback)  
  8.     ), new ValidateValueCallback(ShirtValidateCallback));  

这个程序的逻辑大体是这样

WPF与Prism一并学习(五)
如上图,在点击"Bowling"和"Dress"时,下面的Button Color 才会出现,点其它选项就隐藏起来,

这个例子的全部代码如下

[csharp] view plain copy print ?
  1. using System;  
  2. using System.Windows;  
  3. using System.Windows.Controls;  
  4. using System.Windows.Data;  
  5. using System.Windows.Documents;  
  6. using System.Windows.Media;  
  7. using System.Windows.Shapes;  
  8.   
  9.   
  10. namespace SDKSample  
  11. {  
  12.     public partial class DPCustom  
  13.     {  
  14.         private Shirt myShirt;  
  15.         public void OnInit(object sender, EventArgs e)  
  16.         {  
  17.             myShirt = new Shirt();  
  18.             PopulateListbox(ShirtChoice, Enum.GetValues(typeof(ShirtTypes)));  
  19.             PopulateListbox(ShirtColorChoice, Enum.GetValues(typeof(ShirtColors)));  
  20.             PopulateListbox(ButtonChoice, Enum.GetValues(typeof(ButtonColors)));  
  21.             myShirt.ButtonColorChanged += new RoutedEventHandler(UIButtonColorChanged);  
  22.         }  
  23.         private void PopulateListbox (FrameworkElement fe, Array values) {  
  24.             ListBox lb = fe as ListBox;  
  25.             for (int j = 1; j < values.Length; j++)  
  26.             {  
  27.                 ListBoxItem lbi = new ListBoxItem();  
  28.                 lbi.Content = (Enum)values.GetValue(j);  
  29.                 lb.Items.Add(lbi);  
  30.             }  
  31.         }  
  32.         private void ChooseShirt(object sender, SelectionChangedEventArgs e)  
  33.         {  
  34.             ListBoxItem lbi = ((e.Source as ListBox).SelectedItem as ListBoxItem);  
  35.             myShirt.ShirtType = (ShirtTypes)lbi.Content;  
  36.         }  
  37.         private void ChooseShirtColor(object sender, SelectionChangedEventArgs e)  
  38.         {  
  39.             ListBoxItem lbi = ((e.Source as ListBox).SelectedItem as ListBoxItem);  
  40.             myShirt.ShirtColor = (ShirtColors)lbi.Content;  
  41.         }  
  42.         private void ChooseButtonColor(object sender, SelectionChangedEventArgs e)  
  43.         {  
  44.             ListBoxItem lbi = ((e.Source as ListBox).SelectedItem as ListBoxItem);  
  45.             myShirt.ButtonColor = (ButtonColors)lbi.Content;  
  46.         }  
  47.         private void UIButtonColorChanged(object sender, RoutedEventArgs e)  
  48.         {  
  49.             Shirt s =  (Shirt)e.Source;  
  50.             ButtonColors b = s.ButtonColor;  
  51.             if (b == ButtonColors.None)  
  52.             {  
  53.                 ButtonChoice.Visibility = Visibility.Hidden;  
  54.                 ButtonChoiceLabel.Visibility = Visibility.Hidden;  
  55.             }  
  56.             else  
  57.             {  
  58.                 ButtonChoice.Visibility = Visibility.Visible;  
  59.                 ButtonChoiceLabel.Visibility = Visibility.Visible;  
  60.             }  
  61.         }  
  62.     }  
  63.     public enum ShirtTypes {  
  64.         None,  
  65.         Tee,  
  66.         Bowling,  
  67.         Dress,  
  68.         Rugby  
  69.     }  
  70.     public enum ShirtColors  
  71.     {  
  72.         None,  
  73.         White,  
  74.         Red,  
  75.         Green,  
  76.         Yellow  
  77.     }  
  78.     public enum ButtonColors  
  79.     {  
  80.         None,  
  81.         Black,  
  82.         White,  
  83.         Brown,  
  84.         Gray  
  85.     }  
  86.   
  87.     public class Shirt : Canvas {  
  88.         public static readonly DependencyProperty ShirtColorProperty = DependencyProperty.Register("ShirtColor"typeof(ShirtColors), typeof(Shirt), new FrameworkPropertyMetadata(ShirtColors.None));  
  89.         public ShirtColors ShirtColor  
  90.         {  
  91.             get { return (ShirtColors)GetValue(ShirtColorProperty); }  
  92.             set { SetValue(ShirtColorProperty, value); }  
  93.         }  
  94.         public static readonly DependencyProperty ShirtTypeProperty = DependencyProperty.Register(  
  95.             "ShirtType",  
  96.             typeof(ShirtTypes),   
  97.             typeof(Shirt),   
  98.             new FrameworkPropertyMetadata(  
  99.                 ShirtTypes.None,  
  100.                 new PropertyChangedCallback(OnShirtTypeChangedCallback)  
  101.             ), new ValidateValueCallback(ShirtValidateCallback));  
  102.         public ShirtTypes ShirtType {  
  103.             get {return(ShirtTypes)GetValue(ShirtTypeProperty);}  
  104.             set {SetValue(ShirtTypeProperty,value);}  
  105.         }  
  106. //<SnippetValidateValueCallback>  
  107.         private static bool ShirtValidateCallback(object value)  
  108.         {  
  109.             ShirtTypes sh = (ShirtTypes) value;  
  110.             return (sh==ShirtTypes.None || sh == ShirtTypes.Bowling || sh == ShirtTypes.Dress || sh == ShirtTypes.Rugby || sh == ShirtTypes.Tee);  
  111.   
  112.         }  
  113. //</SnippetValidateValueCallback>  
  114.         private static void OnShirtTypeChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)  
  115.         {  
  116.             d.CoerceValue(ButtonColorProperty);  
  117.         }  
  118. //<SnippetPropertyChangedCallbackPlusDPDef>  
  119.         public static readonly DependencyProperty ButtonColorProperty = DependencyProperty.Register(  
  120.             "ButtonColor",  
  121.             typeof(ButtonColors),   
  122.             typeof(Shirt),   
  123.             new FrameworkPropertyMetadata(  
  124.             ButtonColors.Black,  
  125.             new PropertyChangedCallback(OnButtonColorChangedCallback),  
  126.             new CoerceValueCallback(CoerceButtonColor))  
  127.         );  
  128.         public ButtonColors ButtonColor  
  129.         {  
  130.             get { return (ButtonColors)GetValue(ButtonColorProperty); }  
  131.             set { SetValue(ButtonColorProperty, value); }  
  132.         }  
  133.         private static void OnButtonColorChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)  
  134.         {  
  135.             RoutedEventArgs newargs = new RoutedEventArgs(ButtonColorChangedEvent);  
  136.             (d as FrameworkElement).RaiseEvent(newargs);  
  137.         }  
  138. //</SnippetPropertyChangedCallbackPlusDPDef>  
  139. //<SnippetCoerceValueCallback>  
  140.         private static object CoerceButtonColor(DependencyObject d, object value)  
  141.         {  
  142.             ShirtTypes newShirtType = (d as Shirt).ShirtType;  
  143.             if (newShirtType == ShirtTypes.Dress || newShirtType == ShirtTypes.Bowling)  
  144.             {  
  145.                 return ButtonColors.Black;                
  146.             }  
  147.             return ButtonColors.None;  
  148.         }  
  149. //</SnippetCoerceValueCallback>  
  150. //<SnippetEventManagerClass>  
  151.         public static readonly RoutedEvent ButtonColorChangedEvent = EventManager.RegisterRoutedEvent("ButtonColorChanged",RoutingStrategy.Bubble,typeof(DependencyPropertyChangedEventHandler),typeof(Shirt));  
  152.   
  153.         public event RoutedEventHandler ButtonColorChanged  {  
  154.             add {AddHandler(ButtonColorChangedEvent,value);}  
  155.             remove { RemoveHandler(ButtonColorChangedEvent, value); }  
  156.         }  
  157. //</SnippetEventManagerClass>  
  158.     }  
  159.   
  160.   
  161. }  

上面一共用了三种委托

CoerceValueCallback             (为只要重新计算依赖项属性值或专门请求强制转换时就调用的方法提供一个模板)

PropertyChangedCallback    (表示在依赖项对象的有效属性值更改时调用的回调。)

ValidateValueCallback           (表示用作回调的方法,用于验证依赖项属性的值是否有效。)


这个例子可能举的不算太好,MSDN就是这样.

下面有个博客园的例子,感觉比较符合实战

点这里