(4) C# 依赖项属性
把对象赋值给属性
一、依赖属性
1.定义依赖属性
一般情况下只是使用,但如果要自定义WPF控件或者给现有的WPF控件增加新功能时就需要自定义。
定义时需要用到特殊的语法:
- 静态的
- 使用readonly只能在构造时初始化
- 必须是DependencyProperty类实例
- 属性名称后面要带上Property
定义一个Margin依赖项属性的例子
public static readonly DependencyProperty MarginProperty;
2.注册依赖属性
(1)创建FrameworkPropertyMetadata对象,指明通过依赖项属性使用什么服务(支持数据绑定,动画,日志等)
FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(new Thickness(), FrameworkPropertyMetadataOptions.AffectsMeasure);
(2)调用DependencyProperty.Register()静态方法注册属性
五个参数
- 属性名
- 属性使用的数据类型
- 拥有该属性的类型
- 具有附加属性设置的FrameworkPropertyMetadata对象(此参数可选)
- 用于验证属性的回调函数(此参数可选)
在构造函数中
MarginProperty = DependencyProperty.Register("Margin", typeof(Thickness), typeof(FrameworkElement), metadata, new ValidateValueCallback(FrameworkElement.IsMarginValid));
3.封装WPF依赖属性
使用DependencyObject类的SetValue()和GetValue()方法
public Thickness Margin { set { SetValue(MarginProperty, value); } get { return (Thickness)GetValue(MarginProperty); } }
设置属性
element.Margin = new Thickness(5);
4.例子
一个按钮继承关系 Button--》ButtonBase--》ContentControl--》Control--》FrameworkElement--》UIElement--》Visual--》DependencyObject--》DispatcherObject--》Object
其中
- ButtonBase 表示所有 Button 控件的基类,在System.Windows.Controls.Primitives命名空间下(和winfrom下的ButtonBase虽然名字相同,但完全是两条不同的路线)
- ContentControl 表示包含一段任意类型内容的控件
- Control 表示用户界面 (UI) 元素的基类,这些元素使用 ControlTemplate 来定义其外观
- FrameworkElement 提供 WPF元素的属性、事件和方法的 WPF 框架级别集
- UIElement 是 WPF 核心级实现的基类,这些实现是在 WPF元素和基本表示特性上生成的
- Visual 为 WPF 中的呈现提供支持,其中包括命中测试、坐标转换和边界框计算
- DependencyObject 表示参与依赖属性系统的对象,如果设置依赖属性直接继承此类即可
完整代码:自定义FrameworkElement类
class FrameworkElement:UIElement { public static readonly DependencyProperty MarginProperty; static FrameworkElement() { FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(new Thickness(), FrameworkPropertyMetadataOptions.AffectsMeasure); MarginProperty = DependencyProperty.Register("Margin", typeof(Thickness), typeof(FrameworkElement), metadata, new ValidateValueCallback(FrameworkElement.IsMarginValid)); } private static bool IsMarginValid(object value) { throw new NotImplementedException(); } public Thickness Margin { set { SetValue(MarginProperty, value); } get { return (Thickness)GetValue(MarginProperty); } } }
去掉ValidateValueCallback参数
使用
FrameworkElement element = new FrameworkElement(); element.Margin = new Thickness(5);
4.依赖属性变更通知
有两种方式通知依赖属性变化 --后补
(1)属性值创建绑定
(2)编写能够自动改变其他属性或开始动画的触发器
5.共享依赖属性
查看TextBlock源码,没有用DependencyProperty.Register注册,而是使用了TextElement的FontFamilyProperty的依赖属性
FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(typeof(TextBlock));
6.附加依赖属性
附加属性被应用的类不是定义附加属性的那个类,
比如 DockPanel 的DockProperty属性
[CommonDependencyProperty] public static readonly DependencyProperty DockProperty; static DockPanel() { DockProperty = DependencyProperty.RegisterAttached("Dock", typeof(Dock), typeof(DockPanel), new FrameworkPropertyMetadata(Dock.Left, OnDockChanged), IsValidDock); }
DockPanel.Dock 属性作用到了Border上
<DockPanel LastChildFill="True"> <Border Height="25" Background="SkyBlue" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Top"> <TextBlock Foreground="Black">Dock = "Top"</TextBlock> </Border> <Border Height="25" Background="SkyBlue" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Top"> <TextBlock Foreground="Black">Dock = "Top"</TextBlock> </Border> <Border Height="25" Background="LemonChiffon" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Bottom"> <TextBlock Foreground="Black">Dock = "Bottom"</TextBlock> </Border> <Border Width="200" Background="PaleGreen" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Left"> <TextBlock Foreground="Black">Dock = "Left"</TextBlock> </Border> <Border Background="White" BorderBrush="Black" BorderThickness="1"> <TextBlock Foreground="Black">This content will "Fill" the remaining space</TextBlock> </Border> </DockPanel>
附加属性不需要进行属性封装,可以用于任何依赖对象
二、属性验证
1.验证回调
2.强制回调