依赖项属性
依赖项属性时WPF对.NET属性系统的扩展,用途在于提供一种方法来基于其他输入的值计算属性值,也就是说依赖项属性的值可能是通过以下途径获取:1.通过主题和用户首选项等系统属性;2.通过数据绑定或动画等来实时确定;3.通过资源和样式;4通过在元素树中的父子关系继承而来等。而且依赖项属性可通过独立验证、默认值、回调方法来修改属性值。
下面来看看定义一个依赖项属性的过程:
1. 定义依赖项属性
依赖项属性对象都是DependencyProperty类的实例,且考虑到依赖项属性可以被父子元素之间继承,即在不同类之间共享,所以依赖项属性都为静态字段。如下:
public static readonly DependencyProperty MarginProperty;
2 注册依赖项属性
通过以下代码注册依赖项属性:
FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(
new Thickness(), FrameworkPropertyMetadataOption.AffectsMeasure);
MarginProperty = DependencyProperty.Register("Margin",
typedef(Thickness), typeof(FrameworkElement), metadata,
new ValidateValueCallback(FrameworkElement.IsMarginValid));
注册依赖项属性要经过上面两步:
1.定义依赖项属性
创建FrameworkPropertyMetadata对象作为依赖项属性的默认值。上例中使用的构造函数中第一个参数为默认值,第二个参数为确定概述行的行为(如值更改发生的时间是否属性值继承等)。还有另一些重载的构造函数,有的含有指定属性值改变时的回调函数;
2. 注册依赖项属性
然后通过DependencyProperty.Register方法注册事件,第一个参数确定依赖项属性的名称,第二个参数确定依赖项属性值的类型,第三个参数确定依赖项属性的所有者,第四个参数确定默认值,最后一个参数确定验证属性的回调函数
3. 添加属性包装器
依赖项属性的存取器要使用DependencyObject中的GetValue和SetValue方法来存取,示例如下:
public Thickness Margin
{
set { SetValue(MarginProperty, value); }
get { return (Thickness)GetValue(MarginProperty); }
}
如上过程便可创建一个依赖项属性。
依赖项属性的一个重要功能是动态值识别,WPF确定一个属性的值会按下列顺序确定基本值:
1. 默认值
2. 继承而来的值
3. 来自主题样式的值
4. 来自项目样式的值
5. 本地值
而基本值未必是最后从属性中检索到的值,因为还存在其他改变属性值的提供者。所以WPF决定属性值得过程为:
1. 确定基本值
2. 如果属性值是个表达式,则对表达式进行求值
3. 如果属性是动画的目标,则应用动画
4. 调用回调函数修正属性值
下面使用一个完整的示例展示依赖项属性的使用。在WPF中没有通知栏控件,所以这儿使用WinForm中的NotifyIcon控件封装出一个WPF通知控件,具体代码如下:
1 public class NotificationAreaIcon : FrameworkElement 2 { 3 //对Forms.NotifyIcon控件封装 4 public static Forms.NotifyIcon notifyIcon; 5 6 //添加鼠标点击路由事件 7 public static readonly RoutedEvent MouseClickEvent = EventManager.RegisterRoutedEvent( 8 "MouseClick", RoutingStrategy.Bubble, typeof(MouseButtonEventHandler), typeof(NotificationAreaIcon)); 9 10 //添加鼠标双击路由事件 11 public static readonly RoutedEvent MouseDoubleClickEvent = EventManager.RegisterRoutedEvent( 12 "MouseDoubleClick", RoutingStrategy.Bubble, typeof(MouseButtonEventHandler), typeof(NotificationAreaIcon)); 13 14 //注册依赖项属性Icon 15 public static readonly DependencyProperty IconProperty = 16 DependencyProperty.Register("Icon", typeof(ImageSource), typeof(NotificationAreaIcon)); 17 18 //注册依赖项属性Text 19 public static readonly DependencyProperty TextProperty = 20 DependencyProperty.Register("Text", typeof(string), typeof(NotificationAreaIcon)); 21 22 //注册依赖项属性MenuItems 23 public static readonly DependencyProperty FormsContextMenuProperty = 24 DependencyProperty.Register("MenuItems", typeof(List<Forms.MenuItem>), typeof(NotificationAreaIcon), new PropertyMetadata(new List<Forms.MenuItem>())); 25 26 //初始化事件 27 protected override void OnInitialized(EventArgs e) 28 { 29 base.OnInitialized(e); 30 31 // Create and initialize the window forms notify icon based 32 notifyIcon = new Forms.NotifyIcon(); 33 notifyIcon.Text = Text; 34 if (!DesignerProperties.GetIsInDesignMode(this)) 35 { 36 notifyIcon.Icon = FromImageSource(Icon); 37 } 38 notifyIcon.Visible = FromVisibility(Visibility); 39 40 if (this.MenuItems != null && this.MenuItems.Count > 0) 41 { 42 notifyIcon.ContextMenu = 43 new System.Windows.Forms.ContextMenu(this.MenuItems.ToArray()); 44 } 45 46 notifyIcon.MouseDown += OnMouseDown; 47 notifyIcon.MouseUp += OnMouseUp; 48 notifyIcon.MouseClick += OnMouseClick; 49 notifyIcon.MouseDoubleClick += OnMouseDoubleClick; 50 51 Dispatcher.ShutdownStarted += OnDispatcherShutdownStarted; 52 } 53 54 private void OnDispatcherShutdownStarted(object sender, EventArgs e) 55 { 56 notifyIcon.Dispose(); 57 } 58 59 public void DestroyIcon() 60 { 61 notifyIcon.Dispose(); 62 } 63 64 private void OnMouseDown(object sender, Forms.MouseEventArgs e) 65 { 66 OnRaiseEvent(MouseDownEvent, new MouseButtonEventArgs( 67 InputManager.Current.PrimaryMouseDevice, 0, ToMouseButton(e.Button))); 68 } 69 70 private void OnMouseUp(object sender, Forms.MouseEventArgs e) 71 { 72 OnRaiseEvent(MouseUpEvent, new MouseButtonEventArgs( 73 InputManager.Current.PrimaryMouseDevice, 0, ToMouseButton(e.Button))); 74 } 75 76 private void OnMouseDoubleClick(object sender, Forms.MouseEventArgs e) 77 { 78 OnRaiseEvent(MouseDoubleClickEvent, new MouseButtonEventArgs( 79 InputManager.Current.PrimaryMouseDevice, 0, ToMouseButton(e.Button))); 80 } 81 82 private void OnMouseClick(object sender, Forms.MouseEventArgs e) 83 { 84 OnRaiseEvent(MouseClickEvent, new MouseButtonEventArgs( 85 InputManager.Current.PrimaryMouseDevice, 0, ToMouseButton(e.Button))); 86 } 87 88 private void OnRaiseEvent(RoutedEvent handler, MouseButtonEventArgs e) 89 { 90 e.RoutedEvent = handler; 91 RaiseEvent(e); 92 } 93 94 public List<Forms.MenuItem> MenuItems 95 { 96 get { return (List<Forms.MenuItem>)GetValue(FormsContextMenuProperty); } 97 set { SetValue(FormsContextMenuProperty, value); } 98 } 99 100 public ImageSource Icon 101 { 102 get { return (ImageSource)GetValue(IconProperty); } 103 set { SetValue(IconProperty, value); } 104 } 105 106 public string Text 107 { 108 get { return (string)GetValue(TextProperty); } 109 set { SetValue(TextProperty, value); } 110 } 111 112 public event MouseButtonEventHandler MouseClick 113 { 114 add { AddHandler(MouseClickEvent, value); } 115 remove { RemoveHandler(MouseClickEvent, value); } 116 } 117 118 public event MouseButtonEventHandler MouseDoubleClick 119 { 120 add { AddHandler(MouseDoubleClickEvent, value); } 121 remove { RemoveHandler(MouseDoubleClickEvent, value); } 122 } 123 124 #region Conversion members 125 126 private static Drawing.Icon FromImageSource(ImageSource icon) 127 { 128 if (icon == null) 129 { 130 return null; 131 } 132 Uri iconUri = new Uri(icon.ToString()); 133 return new Drawing.Icon(Application.GetResourceStream(iconUri).Stream); 134 } 135 136 private static bool FromVisibility(Visibility visibility) 137 { 138 return visibility == Visibility.Visible; 139 } 140 141 private MouseButton ToMouseButton(Forms.MouseButtons button) 142 { 143 switch (button) 144 { 145 case Forms.MouseButtons.Left: 146 return MouseButton.Left; 147 case Forms.MouseButtons.Right: 148 return MouseButton.Right; 149 case Forms.MouseButtons.Middle: 150 return MouseButton.Middle; 151 case Forms.MouseButtons.XButton1: 152 return MouseButton.XButton1; 153 case Forms.MouseButtons.XButton2: 154 return MouseButton.XButton2; 155 } 156 throw new InvalidOperationException(); 157 } 158 159 #endregion Conversion members 160 }