WPF学习笔记5. DependencyProperty
2010-10-11 16:08:41 来源:本站整理依赖属性的特点:
(1) 使用高效的稀疏存储系统,这意味着在不设置本地值的情况下,所有同类型对象的依赖属性都将共享默认设置,大大节约内存开销。
(2) 依赖属性具备变更通知(Change Notification)能力,当属性值发生变化时,可以通过预先注册的元数据信息触发联动行为。
(3) 依赖属性可以从其在树中的父级继承属性值。
(4) 依赖属性可以依据优先级从多个提供程序中获取最终值。
1. 依赖属性实现
依赖属性的实现很简单:
(1) 所在类型继承自 DependencyObject,几乎所有的 WPF 控件都间接继承自该类型。
(2) 使用 public static 声明一个 DependencyProperty,该字段才是真正的依赖属性 (字段)。
(3) 在静态构造中完成依赖属性的元数据注册,并获取对象引用。
(4) 提供一个依赖属性的实例化包装属性。注意使用 DependencyObject 相关方法作为读取/访问器。
public class MyClass : DependencyObject { public static readonly DependencyProperty TestProperty; static MyClass() { TestProperty = DependencyProperty.Register("Test", typeof(string), typeof(MyClass), new PropertyMetadata("Hello, World!", OnTestChanged)); } private static void OnTestChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { } public string Test { get { return (string)GetValue(TestProperty); } set { SetValue(TestProperty, value); } } }
提示: 在 VS2008 中可以使用 "propdp + TAB" 快速生成依赖属性代码。
2. 变更通知
当依赖属性值发生变化时,WPF 会通过预先注册的元数据 (Metadata) 信息完成某些 "关联行为" 调用。这样我们就可以在 XAML 的声明中完成行为控制,比如开始或停止动画。
我们试着将上面我们创建的自定义依赖属性作为源绑定给相关控件。
public partial class Window1 : Window { MyClass o; public Window1() { InitializeComponent(); o = new MyClass(); var binding = new Binding("Test") { Source = o, Mode = BindingMode.TwoWay }; this.textBox1.SetBinding(TextBox.TextProperty, binding); } private void btnTest_Click(object sender, RoutedEventArgs e) { o.Test = DateTime.Now.ToString(); } }
窗体初始化时,textBox1.Text 自动绑定为 MyClass.TestProperty 的默认值 "Hello, World!" (参考 MyClass 静态构造的元数据注册代码)。每当我们单击按钮修改依赖属性(o.Test)时,textBox1.Text 都将自动同步更新,这就是依赖属性的行为方式。当然,我们还应该提供一个完全基于 XAML 的声明方式演示,而不是上面的 C# 代码。
<Window x:Class="Learn.WPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1"> <Grid> <TextBox x:Name="textBox1" /> <Label x:Name="label1" Content="{Binding ElementName=textBox1, Path=Text}" /> </Grid> </Window>
label1.Content 绑定到 textBox1.Text 这个依赖属性上,每当我们修改 textBox1.Text 时,label1.Content 都会保持同步修改,这些动作无需我们编写任何代码。很显然,这大大简化了 XAML 的行为控制能力,尤其是对所谓富功能 (Rich functionality) 的控制。
WPF 提供了一种称之为 "触发器(Trigger)" 的机制来配合依赖属性工作,我们用一个简单的属性触发器看看效果。
<Window x:Class="Learn.WPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1"> <Grid> <Button x:Name="btnTest"> <Button.Style> <Style TargetType="{x:Type Button}"> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Foreground" Value="Red" /> </Trigger> </Style.Triggers> </Style> </Button.Style> </Button> </Grid> </Window>
当依赖属性 Button.IsMouseOver 发生变化时 (== True),将导致触发器执行,设置 Foreground=Red。很显然这比我们处理 MouseEnter 事件要简单得多,关键是 UI 设计人员无需编写代码即可得到所需的效果。除了属性触发器外,WPF 还提供了数据触发器和事件触发器。
赞助商链接