WPF中的Dependency Property(1)

来源:互联网 发布:家境好的女生特点知乎 编辑:程序博客网 时间:2024/04/29 15:35

WPF中引入了一种新的属性——Dependency Property,这一属性的应用贯穿于整个WPF中,用来实现Style、绑定、动画等。之所以称其为Dependency Property,是因为它们依赖一些其他的property和外在的影响,在任何时刻都是依靠多个提供程序来决定它的值的。这些提供程序可以是从父元素中进行的属性值继承(不要和类之间的继承关系混淆了),或者是一段一直在改变的动画等。

FontSize为例,在传统的.NET编程中,对象的font size会被存放在此类的private字段中,或许会用下面的默认值进行初始化:

private double fntsize = 11;

private成员变量应该会以publicFontSize property出现:

public double FontSize

{

    get

    {

        return fntsize;

    }

    set

    {

        fntsize = value;

        ...

    }

}

set 访问器中出现的“”指的是:这里需要更多的代码。或许此控件需要改变其尺寸,或者至少需要被重绘,或许FontSizeChanged事件会发生,或许需要为此FontSize的继承,一一列举元素tree的后代等。

这一切,在WPFDependency Property中就没那么麻烦了,一切都是“自动”的,不需要写额外的代码。因此,Dependency Property的最大特征是其内建的变化通知的能力。提供这样的能力给属性,其动力在于能够在声明标记中直接启用富功能。WPF友好声明设计的关键在于它使用了很多属性。例如,Button控件有96个公共属性,属性可以方便地在XAML设置而不用写后台代码。但是如果Dependency Property没有额外的垂直传递,在不写额外代码的情况下,很难在设置属性这样简单的动作中获得想要的结果。

在本文中,我们将要简单得看一下Dependency Property的实现,让讨论更加具体。然后我们再深入分析以下三个方面:

1)         变化通知能力

2)         属性值继承

3)         支持多个提供程序

 

一个标准的依赖属性实现(下面的代码可能接近FontSizeProperty如何在Control类中实现):

public class Control : FrameworkElement

{

    public static readonly DependencyProperty FontSizeProperty;

    static Control()

    {

        FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();

        metadata.DefaultValue = 11;

        metadata.AffectsMeasure = true;

        metadata.Inherits = true;

        metadata.IsNotDataBindable = false;

        metadata.DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

        metadata.PropertyChangedCallback = new PropertyChangedCallback(OnFontSizeChanged);

        FontSizeProperty = DependencyProperty.Register("FontSize"typeof(double), typeof(Control),

            metadatanew ValidateValueCallback(ValidateFontSize));

    }

 

   // .NET 属性包装器(可选)

    public double FontSize

    {

        get

        {

            return (double)GetValue(FontSizeProperty);

        }

        set

        {

            SetValue(FontSizePropertyvalue);

        }

    }

 

    // 属性改变的回调(可选)

    static void OnFontSizeChanged(DependencyObject objDependencyPropertyChangedEventArgs e)

    {

        ...

    }

 

    // 属性值验证的回调(可选)

    static bool ValidateFontSize(object obj)

    {

        double dFontSize = (double)obj;

        return dFontSize > 0 && dFontSize <= 35791;

    }

}

FontSizeProperty被称为Dependency Property,它是publicstatic,这意味着此成员变量和类有关联,而非和对象有关联。静态的只读成员只能够在“类成员变量定义本身”或“静态构造函数”中被设定。一般来说,类是通过调用静态的Dependency.Register方法,该方法需要一个名称(FontSize)、一个属性类型(double)以及拥有这个属性的类(Control)。通过不同的Register方法重载,你可以传入metadata(元数据)来告诉WPF该属性的默认值、如何处理该属性、如何处理属性值改变的回调、如何处理强制值转换以及如何验证值等。

最后,那个叫做FontSize的属性会调用继承自System.Windows.DependencyObjectGetValueSetValue方法来实现自己的访问器。System.Windows.DependencyObject是底层基类,这是拥有Dependency Property的类必须继承的。GetValue返回最后一个由SetValue设置的值,如果SetValue从未调用过,那么就是该属性注册时的默认值。FontSize属性包装器并不是必需的,Control的使用者可能会直接调用GetValueSetValue方法,因为它们是公开的。以.NET属性包装器的方式实现会让读写属性显得自然,并且还允许通过XAML设置属性。

注意:在运行时,XAML绕过了.NET属性包装器直接设置依赖属性

虽然XAML编译器在编译时是依靠该属性包装器的,但在运行时WPF是直接调用GetValueSetValue的!因此,为了让使用XAML设置属性与使用过程式代码设置属性保持一致,在属性包装器中除了GetValueSetValue调用以外,不应该包含任何其他逻辑,这是至关重要的。如果需要添加自定义逻辑,应该在注册的回调函数中添加。

即便传递到GetValueSetValue方法内当参数的Dependency Property对象是静态的,GetValueSetValue却是实例方法,它们值的设定和获取都和特定的实例有关。而且,GetValueSetValue内部使用了高效的稀疏存储系统,与传统的.NET属性相比,Dependency Property的实现节省了保存每个实例所需要的内存。

Dependency Property的好处远不止内存使用这一项。它把用来检查线程访问、请求容器元素重新呈现、变化通知等相当一部分的代码集中起来,并作标准化处理,而这部分代码原本是要由属性实现者自己来写的。

关于要深入分析的三部分,将在其他的Post中进行说明。

 

A Za A Za Fighting

0 0