WPF and Silverlight 学习笔记(十三):依赖项属性和路由事件
一、依赖项属性(Dependency Property)
Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展公共语言运行时 (CLR) 属性的功能。这些服务通常统称为 WPF 属性系统。由 WPF 属性系统支持的属性称为依赖项属性。本概述介绍 WPF 属性系统以及依赖项属性的功能,这包括如何在可扩展应用程序标记语言 (XAML) 中和代码中使用现有的依赖项属性。
依赖项属性的用途在于提供一种方法来基于其他输入的值计算属性值。这些其他输入可以包括系统属性(如主题和用户首选项)、实时属性确定机制(如数据绑定和动画/演示图板)、重用模板(如资源和样式)或者通过与元素树中其他元素的父子关系来公开的值。另外,可以通过实现依赖项属性来提供独立验证、默认值、监视其他属性的更改的回调以及可以基于可能的运行时信息来强制指定属性值的系统。派生类还可以通过重写依赖项属性元数据(而不是重写现有属性的实际实现或者创建新属性)来更改现有属性的某些具体特征。
1、依赖项属性与CLR 包装属性
以Button的Backgroud为例,设置或获取其值可以有以下几种方式:
XAML文件中
1: <StackPanel>
2: <!--
3: 在所生成的代码中,XAML加载器将 XAML 属性的简单字符串值的
4: 类型转换为 WPF 类型(一种 Color,通过 SolidColorBrush)。
5: -->
6: <Button Margin="3" Background="Yellow" Content="Button A" />
7:
8: <!--
9: 使用嵌套元素的方式,设置Button.Backgroud的值
10: -->
11: <Button Margin="3" Content="Button B" x:Name="btn_ButtonB">
12: <Button.Background>
13: <SolidColorBrush Color="Gold" />
14: </Button.Background>
15: </Button>
16:
17: <!--预留给代码使用的控件-->
18: <Button Margin="3" Content="Button C" x:Name="btn_ButtonC" />
19: <Button Margin="3" Content="Button D" x:Name="btn_ButtonD" />
20: <TextBox Margin="3" x:Name="txt_Value1" />
21: <TextBox Margin="3" x:Name="txt_Value2" />
22: </StackPanel>
代码文件中:
1: // 通过包装的属性设置按钮的背景颜色
2: btn_ButtonC.Background = new SolidColorBrush(Colors.Red);
3:
4: // 通过依赖性属性的SetValue设置按钮的背景颜色
5: SolidColorBrush brush = new SolidColorBrush(Colors.Blue);
6: btn_ButtonD.SetValue(
7: Button.BackgroundProperty, brush);
8:
9: // 通过包装的属性获取ButtonB的背景颜色
10: SolidColorBrush b_Brush1 = (SolidColorBrush) (btn_ButtonB.Background);
11: txt_Value1.Text = b_Brush1.Color.ToString();
12:
13: // 通过依赖性属性的GetValue获取ButtonB的背景颜色
14: SolidColorBrush b_Brush2 = (SolidColorBrush) (btn_ButtonB.GetValue(
15: Button.BackgroundProperty));
16: txt_Value2.Text = b_Brush2.Color.ToString();
如果使用的是现有属性,则上述操作通常不是必需的(使用包装会更方便,并能够更好地向开发人员工具公开属性)。但是在某些情况下适合直接调用 API。
2、使用由依赖项属性提供的属性功能
依赖项属性提供用来扩展属性功能的功能,这与字段支持的属性相反。每个这样的功能通常都表示或支持整套 WPF 功能中的特定功能:
-
资源
-
数据绑定
-
样式
-
动画
-
元数据重写
-
属性值继承
-
WPF 设计器集成
3、自定义依赖项属性及重写依赖项属性
对于自定义依赖项属性,其所在的类型必须直接或间接继承System.Windows.DependencyObject类,依赖项属性是通过调用 Register 方法(或 RegisterReadOnly,自定义的只读的依赖项属性)在 WPF 属性系统中注册,并通过 DependencyProperty 标识符字段备份的属性。依赖项属性只能由 DependencyObject 类型使用,但 DependencyObject 在 WPF 类层次结构中的级别很高,因此,WPF 中的大多数可用类都支持依赖项属性。在对依赖项属性及CLR包装属性命名时必须满足:CLR包装属性名+Property=依赖项属性名。
例如:在某DependencyObject类的子类中定义:
1: // 定义并注册依赖项属性
2: public static readonly DependencyProperty AquariumGraphicProperty =
3: DependencyProperty.Register(
4: "AquariumGraphic", // 要注册的依赖项对象的名称
5: typeof(Uri), // 属性的类型
6: typeof(AquariumObject), // 正注册依赖项对象的所有者类型
7: new FrameworkPropertyMetadata( // 依赖项对象的属性元数据
8: null,
9: FrameworkPropertyMetadataOptions.AffectsRender,
10: new PropertyChangedCallback(OnUriChanged)
11: )
12: );
13:
14: // 定义CLR包装属性
15: public Uri AquariumGraphic
16: {
17: get { return (Uri)GetValue(AquariumGraphicProperty); }
18: set { SetValue(AquariumGraphicProperty, value); }
19: }
二、路由事件(RoutedEvent)
先看以下的应用程序:
1: <StackPanel>
2: <StackPanel Background="Blue" Margin="3" x:Name="panel_Blue">
3: <Button Margin="3" Content="Button A" x:Name="btn_B1" Click="btn_B_Click" />
4: <Button Margin="3" Content="Button B" x:Name="btn_B2" Click="btn_B_Click" />
5: </StackPanel>
6: <StackPanel Background="Green" Margin="3" x:Name="panel_Green" ButtonBase.Click="panel_G_Click">
7: <Button Margin="3" Content="Button A" x:Name="btn_G1" />
8: <Button Margin="3" Content="Button B" x:Name="btn_G2" />
9: </StackPanel>
10: <StackPanel Background="Red" Margin="3" x:Name="panel_Red" ButtonBase.Click="panel_R_Click">
11: <Button Margin="3" Content="Button A" x:Name="btn_R1" Click="btn_R1_Click" />
12: <Button Margin="3" Content="Button B" x:Name="btn_R2" Click="btn_R2_Click" />
13: </StackPanel>
14: </StackPanel>
1: private void btn_B_Click(object sender, RoutedEventArgs e)
2: {
3: MessageBox.Show(
4: "Click Blue Panel Button !",
5: "System Infomation",
6: MessageBoxButton.OK,
7: MessageBoxImage.Information);
8:
9: MessageBox.Show(
10: string.Format("sender Type is {0}.", sender.GetType().Name));
11:
12: Button sourceButton = (Button) (sender);
13: MessageBox.Show(
14: string.Format("Source Button is {0} !", sourceButton.Name),
15: "System Infomation",
16: MessageBoxButton.OK,
17: MessageBoxImage.Information);
18: }
19:
20: private void panel_G_Click(object sender, RoutedEventArgs e)
21: {
22: MessageBox.Show(
23: "Click Green Panel Button !",
24: "System Infomation",
25: MessageBoxButton.OK,
26: MessageBoxImage.Information);
27:
28: MessageBox.Show(
29: string.Format("sender Type is {0}.", sender.GetType().Name));
30:
31: MessageBox.Show(
32: string.Format("e.Source Type is {0}.", e.Source.GetType().Name));
33:
34: Button sourceButton = (Button)(e.Source);
35: MessageBox.Show(
36: string.Format("Source Button is {0} !", sourceButton.Name),
37: "System Infomation",
38: MessageBoxButton.OK,
39: MessageBoxImage.Information);
40: }
41:
42: private void panel_R_Click(object sender, RoutedEventArgs e)
43: {
44: MessageBox.Show(
45: "Click Red Panel Button !",
46: "System Infomation",
47: MessageBoxButton.OK,
48: MessageBoxImage.Information);
49: }
50:
51: private void btn_R1_Click(object sender, RoutedEventArgs e)
52: {
53: MessageBox.Show(
54: "Click Red Panel Button A !",
55: "System Infomation",
56: MessageBoxButton.OK,
57: MessageBoxImage.Information);
58: }
59:
60: private void btn_R2_Click(object sender, RoutedEventArgs e)
61: {
62: MessageBox.Show(
63: "Click Red Panel Button B !",
64: "System Infomation",
65: MessageBoxButton.OK,
66: MessageBoxImage.Information);
67:
68: e.Handled = true;
69: }
1、在panel_Blue中定义的三个按钮的Click事件属于“类事件”,即在类型对象中声明事件的绑定。此时事件响应方法中的sender指的就是由哪个对象引发的事件
2、在panel_Green中定义ButtonBase.Click="xxx",将其容器内所有的ButtonBase类型及其子类型的事件,统一绑定到一个事件处理方法上,统一处理。此时事件响应方法中的sender指的是panel_Green对象,而e.Source指的是引发ButtonBase.Click的某个按钮
3、路由事件的处理模型常用的有两种:
- 冒泡事件:由子控件位次向父容器传递,大部分的路由事件都是冒泡事件
- 隧道事件:由父容器位次向其子容器、控件传递,一般PreXXX事件属性隧道事件
4、使用路由事件响应方法中的e.Handled = true;意味着此事件已经被处理,将不再传递,默认e.Handled的值为false,意味着此路由事件还未处理完整,事件将依据其模型继续向下处理(即执行其他的事件处理方法)