zoukankan      html  css  js  c++  java
  • 4 WPF依赖属性

    理解依赖属性

    依赖属性支持的特征包括:动画、数据绑定、样式。由WPF元素暴露的属性大部分都是依赖属性。依赖属性和常规属性的使用方法相同。

    WPF设计了依赖属性支持其特有的动态特性,并且不干扰其他系统的.net代码。

    定义依赖属性

    创造一个依赖属性的语法完全不同于创造一个普通的.NET属性。

    第一步是定义一个代表属性的对象。这是DependencyProperty类的一个实例。关于你属性的信息需要一直是可用的,甚至可能在类之间共享(常见在WPF元素中)。因此,你的DependencyProperty对象必须被定义为相关类的一个静态字段。

    例如,FrameworkElement类定义了一个Margin属性。Margin被所有元素共享,是一个依赖属性:

    public class FrameworkElement: UIElement, ...
    {
        public static readonly DependencyProperty MarginProperty;
        ...
    }

    根据命名约定,定义依赖属性的字段名字是正常属性加上单词Property后缀。那样,你能区分依赖属性定义和实际属性的名字。字段使用readonly关键字,这意味着它只能在FrameworkElement类的静态构造函数中设置,如何设置见下节。

    注册依赖属性

    下一步是注册你的依赖属性。因为要在使用属性之前完成注册,必须在相关类的一个静态构造函数中执行它。

    不能直接实例化DependencyProperty对象,因为DependencyProperty类没有公开的构造函数。代替,一个DependencyObject实例只能使用静态的DependencyProperty.Register()方法被创造。DependencyProperty对象被创造之后不能再修改,因为所有的DependencyProperty成员是只读的。代替,它们的值必须作为Register()方法的参数被提供。

    下面例子显示FrameworkElement类的Margin属性是如何被注册的:

    static FrameworkElement()
    {
        var metadata = new FrameworkPropertyMetadata(
          new Thickness(), FrameworkPropertyMetadataOptions.AffectsMeasure);
    
        MarginProperty = DependencyProperty.Register("Margin",
          typeof(Thickness), typeof(FrameworkElement), metadata,
          new ValidateValueCallback(FrameworkElement.IsMarginValid));
        ...
    }

    注册一个依赖属性包括二步。首先,你创造一个FrameworkPropertyMetadata对象,指明希望使用依赖属性的什么服务(诸如支持数据绑定,动画,和日志)。其次,依靠调用DependencyProperty.Register()静态方法注册属性。这时,你负责提供一些关键成分:

    • 属性名称
    • 属性的数据类型
    • 属性所在类的类型
    • 可选,附带有属性设置的FrameworkPropertyMetadata对象
    • 可选,执行属性验证的回调

    两个可选属性值得研究。FrameworkPropertyMetadata的详细描述见95页。

    包装依赖属性

    创造依赖属性的最后一步是用一个传统.NET属性包装它。WPF属性使用的是定义在DependencyObject基类的GetValue()和SetValue()方法。

    public Thickness Margin
    {
        set { SetValue(MarginProperty, value); }
        get { return (Thickness)GetValue(MarginProperty); }
    }

    当你创造属性包装时,你应该仅调用SetValue()和GetValue(),如在前例中。你不应该添加任何额外的代码验证值,引起事件,等等。那是因为另外的特征可能旁路属性包装,直接调用SetValue()和GetValue()。(一个例子是在运行时当一个编译XAML文件被解析时。)SetValue()和GetValue()都是公开的。

    验证输入值应使用DependencyProperty.ValidateValueCallback。

    引发事件应使用FrameworkPropertyMetadata.PropertyChangedCallback 。

    现在可以使用属性了:

    myElement.Margin = new Thickness(5);

    之后,可能希望移除局部值设置,仿佛你从未设置它。依靠从DependencyObject继承的ClearValue()方法。

    myElement.ClearValue(FrameworkElement.MarginProperty);

    使用依赖属性

    依赖属性支持两个关键的行为:改变通知和动态值求解。

    改变通知

    如果你希望对一个属性的改变作出反应,你有二个选择—创造一个绑定,或写一个触发器。但是,依赖属性没有提供响应属性值改变的事件。

    动态值求解

    依赖属性因动态值求解的行为而得名。一个依赖属性依赖于多个属性提供者,每个提供者带有它自己的优先级。当你从一个属性值取回一个值时,WPF属性系统经历一系列步骤求得最终值。首先,它通过考虑下列因素决定属性的基值。优先级从最低的到最高排列(最下面的赢):

    1. 默认值(由FrameworkPropertyMetadata对象设置)
    2. 继承的值(如果FrameworkPropertyMetadata.Inherits标记被设置,并且一个值已经应用到某个祖先元素)
    3. 主题样式值
    4. 工程样式值
    5. 本地值(使用代码或标记直接设置的值)

    基值不一定是最终的属性值,WPF通过4个步骤求得属性值:

    1. 基值
    2. 表达式(数据绑定和资源)
    3. 动画
    4. 通过CoerceValueCallback修正值

    共享依赖属性

    一些类共享依赖属性,即使他们有独立的类层次结构。例如,TextBlock.FontFamily和Control.FontFamily都指向同一个依赖属性,它实际上被定义在TextElement类中以及TextElement.FontFamilyProperty。TextElement类的静态构造函数注册属性,但是TextBlock和Control类的静态构造函数简单地调用DependencyProperty.AddOwner()方法重用它:

    TextBlock.FontFamilyProperty =
      TextElement.FontFamilyProperty.AddOwner(typeof(TextBlock));

    自定义类

    副作用

    附加依赖属性

    附加属性是依赖属性,并且它被WPF属性系统管理。区别是应用附加属性的类不是定义它的类。

    为定义一个附加属性,你使用RegisterAttached()方法而不是Register()。这是一个注册Grid.Row属性的例子:

    var metadata = new FrameworkPropertyMetadata(
      0, new PropertyChangedCallback(Grid.OnCellAttachedPropertyChanged));
    
    Grid.RowProperty = DependencyProperty.RegisterAttached("Row", typeof(int),
      typeof(Grid), metadata, new ValidateValueCallback(Grid.IsIntValueNotNegative));

    就像一个普通的依赖属性,你能提供一个FrameworkPropertyMetadata对象和一个ValidateValueCallback。

    当创造附加属性时,你没有定义.NET属性包装。那是因为附加属性能被设置在任何依赖对象上。例如,Grid.Row属性可能被设置在一个Grid对象上(如果你有一内部嵌套另一个Grid的Grid)或在另外的一些元素上。事实上,即使元素不在一个Grid内,和即使在你的元素树上不存在Grid对象,Grid.Row属性也能被设置在那个元素上。

    代替使用一个.NET属性包装,附加属性要求能调用的一对静态方法去设置和获得属性值。这些方法使用熟悉的SetValue()和GetValue()方法(从DependencyObject类继承)。静态的方法应该被命名SetPropertyName()和GetPropertyName()。

    这里是实现Grid.Row附加属性的静态方法:

    public static int GetRow(UIElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("");
        }
        return (int)element.GetValue(Grid.RowProperty);
    }
    public static void SetRow(UIElement element, int value)
    {
        if (element == null)
        {
            throw new ArgumentNullException("");
        }
        element.SetValue(Grid.RowProperty, value);
    }

    使用代码设置一个元素位于网格的第一行:

    Grid.SetRow(txtElement, 0);

    可选地,你能直接调用SetValue()或GetValue()方法,旁路掉静态方法:

    txtElement.SetValue(Grid.RowProperty, 0);

    属性验证

    WPF提供两种方法阻止无效值:

    • ValidateValueCallback:这回调能接受或拒绝新值。通常,这回调被用来捕获违反属性的约束明显错误。提供它作为DependencyProperty.Register()方法的参数。
    • CoerceValueCallback:这回调能修改新值为更可接受的值。通常,这回调被用来处理被设置在同一对象的依赖属性值之间的冲突。这些值可能单独是有效的,但是当应用在一起时不一致。为使用这回调,当创造FrameworkPropertyMetadata对象时,提供它作为构造函数的参数,此对象随后被传递到DependencyProperty.Register()方法。

    这里是当应用程序试图设置一个依赖属性时:所有的部件如何起作用:

    1. 首先,CoerceValueCallback方法有机会修改提供值(通常,为使它与其它属性一致)或返回DependencyProperty.UnsetValue,这完全拒绝改变。
    2. 其次,ValidateValueCallback被引发。这方法返回真接受一个值作为有效,或返回假拒绝它。不同于CoerceValueCallback,ValidateValueCallback不访问设置属性的实际对象,这意味着你不能检查其它的属性值。
    3. 最后,如果先前两个阶段都成功了,PropertyChangedCallback被触发。这时,你能引发事件去通知其它类。

    Validation回调

    相当于正常属性的set部分中的验证。

    其签名为接受一个object输入参数,返回布尔值。返回值为真表示接受,为假表示拒绝。

    private static bool IsMarginValid(object value)
    {
        Thickness thickness1 = (Thickness) value;
        return thickness1.IsValid(true, false, true, false);
    }

    有一个限制,它是一个静态方法,不能访问正被验证的对象,不能使用对象中的其他属性。

    Coercion回调

    用于验证互相关联的属性

    private static object CoerceMaximum(DependencyObject d, object value)
    {
        RangeBase base1 = (RangeBase)d;
        if (((double) value) < base1.Minimum)
        {
            return base1.Minimum;
        }
        return value;
    }
  • 相关阅读:
    resin
    tomcat
    vba打开输入文件
    获取文件夹下所有文件2
    获取文件夹下所有文件
    修改Execl中sheet名的指定字符串为指定字符串
    SpringMVC入门到精通(一)
    Java JDBC
    Java反射
    Java日期格式化
  • 原文地址:https://www.cnblogs.com/cuishengli/p/3112538.html
Copyright © 2011-2022 走看看