导航

WPF的依赖属性

使用依赖属性:

public class SimpleDO : DependencyObject
   {
     public static readonly DependencyProperty IsActiveProperty =
       DependencyProperty.Register("IsActive", typeof(bool), typeof(SimpleDO),
         new PropertyMetadata((bool)false));
   public bool IsActive
    {
        get { return (bool)GetValue(IsActiveProperty); }
          set { SetValue(IsActiveProperty, value); }
     }
 }

可以这样验证:

1 SimpleDO sDo = new SimpleDO();
2 sDo.IsActive = true;

处理DP的规则:
按照优先级高低从左往右排列:

Validate <- Coerce <- Apply Animation <- Evaluate(If an Expression) <- Determine Base Value

更具体一点是,当我们访问一个DP,它会按照以下顺序给我们相应的值(从高到低)

 

一个简单的例子,知道什么时候执行Validate 和 CoerceValue:

public class SimpleDO : DependencyObject
    {
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(double), typeof(SimpleDO),
                new FrameworkPropertyMetadata((double)0.0,
                    FrameworkPropertyMetadataOptions.None,
                    new PropertyChangedCallback(OnValueChanged),
                    new CoerceValueCallback(CoerceValue)),
                    new ValidateValueCallback(IsValidateValue));

        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Console.WriteLine("ValueChanged new value is {0}", e.NewValue);
        }

        private static object CoerceValue(DependencyObject d, object value)
        {
            Console.WriteLine("CoerceValue value is {0}", value);
            value = 4.0;
            return value;
        }

        private static bool IsValidateValue(object value)
        {
            Console.WriteLine("ValidateValue value is {0}", value);
            return true;
        }
    }

按照如下方式调用:

1 SimpleDO sDo = new SimpleDO();
2 sDo.Value = 1;

我们从输出结果可以看出,先执行 Validate 然后是 CoerceValue, 最后是ValueChanged, 在前面的每一步都可以改变Value的值。

下面一段代码可以演示一下动画对DP值得影响:

public class SimpleDOUI : UIElement
    {
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(double), typeof(SimpleDOUI),
                new FrameworkPropertyMetadata((double)30.0,
                    FrameworkPropertyMetadataOptions.None,
                    new PropertyChangedCallback(OnValueChanged),
                    new CoerceValueCallback(CoerceValue)),
                    new ValidateValueCallback(IsValidateValue));

        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Console.WriteLine("ValueChanged new value is {0}", e.NewValue);
        }

        private static object CoerceValue(DependencyObject d, object value)
        {
            Console.WriteLine("CoerceValue value is {0}", value);
            return value;
        }

        private static bool IsValidateValue(object value)
        {
            Console.WriteLine("ValidateValue value is {0}", value);
            return true;
        }
    }     

按照以下方式执行:

SimpleDOUI sDo = new SimpleDOUI();
DoubleAnimation animation = new DoubleAnimation(1, 20, new Duration(TimeSpan.FromMilliseconds(5000)), FillBehavior.Stop); sDo.BeginAnimation(SimpleDOUI.ValueProperty, animation);

结果是Vlaue的值从 1 一直变化到 20,最后还将变未默认值 30.0, 除非将FillBehavior.HoldEnd, Value将变为 20. 如果显示的将sDo.Value = 40 (这个是LocalValue), 你得到的值仍然会是20, 应为animation的优先级要高。

在DP的定义中,我们会用到PropertyMetadata, 可以在子类中重写它,WPF中有FrameworkPropertyMetadata, UIPropertyMetadata以及PropertyMetadata,他们的继承关系是F -> U -> P.

最复杂的F的构造函数是:

public FrameworkPropertyMetadata( object defaultValue, 
                                   FrameworkPropertyMetadataOptions flags, 
                                   PropertyChangedCallback propertyChangedCallback, 
                                   CoerceValueCallback coerceValueCallback,
                                   bool isAnimationProhibited,
                                   UpdateSourceTrigger defaultUpdateSourceTrigger);

三个Callback我们已经用代码演示过了, 我们来看看FrameworkPropertyMetadataOptions (WPF4.5):

public enum FrameworkPropertyMetadataOptions
    {
        // Summary:
        //     No options are specified; the dependency property uses the default behavior
        //     of the Windows Presentation Foundation (WPF) property system.
        None = 0,
        //
        // Summary:
        //     The measure pass of layout compositions is affected by value changes to this
        //     dependency property.
        AffectsMeasure = 1,
        //
        // Summary:
        //     The arrange pass of layout composition is affected by value changes to this
        //     dependency property.
        AffectsArrange = 2,
        //
        // Summary:
        //     The measure pass on the parent element is affected by value changes to this
        //     dependency property.
        AffectsParentMeasure = 4,
        //
        // Summary:
        //     The arrange pass on the parent element is affected by value changes to this
        //     dependency property.
        AffectsParentArrange = 8,
        //
        // Summary:
        //     Some aspect of rendering or layout composition (other than measure or arrange)
        //     is affected by value changes to this dependency property.
        AffectsRender = 16,
        //
        // Summary:
        //     The values of this dependency property are inherited by child elements.
        Inherits = 32,
        //
        // Summary:
        //     The values of this dependency property span separated trees for purposes
        //     of property value inheritance.
        OverridesInheritanceBehavior = 64,
        //
        // Summary:
        //     Data binding to this dependency property is not allowed.
        NotDataBindable = 128,
        //
        // Summary:
        //     The System.Windows.Data.BindingMode for data bindings on this dependency
        //     property defaults to System.Windows.Data.BindingMode.TwoWay.
        BindsTwoWayByDefault = 256,
        //
        // Summary:
        //     The values of this dependency property should be saved or restored by journaling
        //     processes, or when navigating by Uniform resource identifiers (URIs).
        Journal = 1024,
        //
        // Summary:
        //     The subproperties on the value of this dependency property do not affect
        //     any aspect of rendering.
        SubPropertiesDoNotAffectRender = 2048,

大致分为两类,有Affect的,表示这个属性变化后,要重新测量,绘制等。另一类,拿属性继承作为例子。WPF的依赖属性的可继承性是依附于对象的。 (疑问? 这个DP需要在每个对象里都定义一边吗?)

 

我们可以调用DependencyPropertyHelper的GetValueSource方法来获得当前依赖属性的信息:

ValueSource source = DependencyPropertyHelper.GetValueSource(sDo, SimpleDO.ValueProperty);

其中ValueSource:

public struct ValueSource
 {
    public BaseValueSource BaseValueSource { get; }
    public bool IsAnimated { get; }
    public bool IsCoerced { get; }
    public bool IsExpression { get; }
 }

其中的IsAnimated,IsCoerced,IsExpression用来指示当前依赖属性的状态,BaseValueSource指示当前BaseValue的优先级。它有

public enum BaseValueSource
 {
    Unknown = 0,
    Default = 1,
    Inherited = 2,
    DefaultStyle = 3,
    DefaultStyleTrigger = 4,
    Style = 5,
    TemplateTrigger = 6,
    StyleTrigger = 7,
    ImplicitStyleReference = 8,
    ParentTemplate = 9,
    ParentTemplateTrigger = 10,
    Local = 11,
 }

我们可以显示的读出LocalValue:
DependencyObject提供了ReadLocalValue函数来读取当前的LocalValue

public object ReadLocalValue(DependencyProperty dp);

那么LocalValue和EffctiveValue的区别在哪呢?DependencyObject提供了GetValue方法来取得属性值,这个值就是EffctiveValue, 有一个例子(我已验证过)

1: Slider slider = new Slider();
  2: slider.Minimum = 0;
  3: slider.Maximum = 10;
  4: slider.Value = 3;
  5:  
  6: slider.Minimum = 4; //After set, Value = 4; Value's Local Value = 3;
  7: slider.Minimum = 1; //After set, Value = Value's Local Value = 3;
  8: slider.Minimum = 13; //After set, Value = Maximum = 13;

第6行,当设置了Minimum=4后,Value的Coerce会被调用,在Coerce中,因为Value值(3)小于Minmum(4),Value值被强制为4。但Value的Local值仍然被保留,使用ReadLocalValue函数可以查看到Value的LocalValue仍然为3。第7行,Minimum的值为1后,在Value的Coerce中,因为Value的LocalValue(3)大于1,所以最终取得的Value和LocalValue都为3。

 

最后为Coerce 举个例子:

public class MyClockControl : FrameworkElement
{
// Dependency Property
public static readonly DependencyProperty CurrentTimeProperty = 
     DependencyProperty.Register( "CurrentTime", typeof(DateTime),
     typeof(MyClockControl), new FrameworkPropertyMetadata( DateTime.Now, 
                       OnCurrentTimePropertyChanged, 
                       OnCoerceCurrentTimeProperty ),
                       OnValidateCurrentTimeProperty ));
 
// .NET Property wrapper
public DateTime CurrentTime
{
    get { return (DateTime)GetValue(CurrentTimeProperty); }
    set { SetValue(CurrentTimeProperty, value); }
}


// Value Changed Callback
private static void OnCurrentTimePropertyChanged(DependencyObject source, 
        DependencyPropertyChangedEventArgs e)
{
    MyClockControl control = source as MyClockControl;
    DateTime time = (DateTime)e.NewValue;
    // Put some update logic here...
}

// Coerce Value Callback
private static object OnCoerceTimeProperty( DependencyObject sender, object data )
{
    if ((DateTime)data > DateTime.Now )
    {
        data = DateTime.Now;
    }
    return data;
}

// Validation Callback
private static bool OnValidateTimeProperty(object data)
{
    return data is DateTime;
}


}

 最后补充一下依赖属性的继承,刚刚提到FontSize是怎么继承的,具体的例子为 Window里有个Button,当我在window里设置FontSize = 15时,它对Button也是有效的:

首先FontSize是定义在Control中的,而且Window和Button的基类里都有Control, 这就是说他们都有FontSize的定义。所以DP的继承的前提是都有同样的DP定义。

 

posted on 2015-05-04 18:57  水中游  阅读(208)  评论(0)    收藏  举报