首页 > 的SilverlightWPF >的DependencyProperty:审定,胁迫及变化处理(第一部分:WPF中)

的DependencyProperty:审定,胁迫及变化处理(第一部分:WPF中)

介绍

依赖项属性是WPF的一个重大创新。 一个依赖项属性作为一个CLR属性相同的简单和熟悉的编程接口,但允许其值可动态决定/其他属性,样式,模板,数据绑定,动画,元素组成,类的继承等一样,受多种因素的改变,可以实现提供自足默认值,验证,胁迫和事件逻辑。 Silverlight的实现WPF属性系统功能的一个子集,因此,WPF和Silverlight之间有显着性差异。 WPF和Silverlight之间的直线移植可能是不可能的,主要的源代码的变化,可能需要。 这三部分后系列将使用一个简单的例子来演示WPF和Silverlight相依性属​​性,两个属性系统,以及如何从一个到另一个港口之间的差异,实现共同的模式,然后使用我写的NumericUpDown控制Silverlight工具包演示了如何复杂和棘手的依赖项属性可以在Silverlight。 我只会集中审定,胁迫和变化,在这一系列处理。 当我发现的时候,我可能会写上依赖项属性的其他方面。

WPF依赖属性概述

MSDN有良好的依赖项属性概述 ,所以我将只调用了三个核心类和两个重要的WPF属性系统的编程接口的方法:

  • DependencyProperty的 ,它提供了属性,如名称,OwnerType,PropertyType,如注册,RegisterAttached,RegiterReadOnly,RegisterAttachedReadOnly方法:
     (DependencyPropertyValueSerializer))] [TypeConverter的(“System.Windows.Markup.DependencyPropertyConverter,PresentationFramework,版本= 4.0.0.0,文化=中立,PublicKeyToken =列出,自空 ”),ValueSerializer(typeof运算 (DependencyPropertyValueSerializer))] 
    公共密封类的DependencyProperty
    {
    / /字段
    公共静态只读对象 UnsetValue的;

    / /方法
    公众的DependencyProperty AddOwner(类型ownerType);
    “公共 DependencyProperty的AddOwner”(类型ownerType,PropertyMetadata typeMetadata);
    公共重写 GetHashCode()方法;
    公共 PropertyMetadata可用getMetaData(类型forType);
    公共 PropertyMetadata可用getMetaData(DependencyObject的DependencyObject的);
    公共 PropertyMetadata可用getMetaData(DependencyObjectType dependencyObjectType);
    ); 公共的布尔 IsValidType( 对象值 );
    ); 公共,BOOL IsValidValue( 对象值 );
    公共无效 OverrideMetadata(类型forType,PropertyMetadata typeMetadata);
    公共无效 OverrideMetadata(类型forType,PropertyMetadata typeMetadata,关键的DependencyPropertyKey);
    name, Type propertyType, Type ownerType); 公共静态的DependencyProperty注册( 字符串名称,类型propertyType,ownerType型);
    name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata); 公共静态的DependencyProperty注册( 字符串名称,类型propertyType,键入ownerType,PropertyMetadata typeMetadata);
    name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback); 公共静态的DependencyProperty注册( 字符串名称,类型propertyType,类型ownerType,PropertyMetadata typeMetadata,ValidateValueCallback validateValueCallback;);
    name, Type propertyType, Type ownerType); 公共静态的DependencyProperty RegisterAttached( 字符串名称,的类型propertyType,类型ownerType);
    name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata); 公共静态的DependencyProperty RegisterAttached( 字符串名称,的类型propertyType,类型ownerType,defaultMetadata PropertyMetadata);
    name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback); 公共静态的DependencyProperty RegisterAttached( 字符串名称,的类型propertyType,类型ownerType PropertyMetadata defaultMetadata,ValidateValueCallback validateValueCallback)的;
    name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata); 公共静态的DependencyPropertyKey RegisterAttachedReadOnly(类型ownerType, 字符串名称,类型propertyType,PropertyMetadata defaultMetadata);
    name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback); 公共静态的DependencyPropertyKey RegisterAttachedReadOnly( 字符串名称,的类型propertyType,类型ownerType,PropertyMetadata defaultMetadata,ValidateValueCallback的validateValueCallback);
    name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata); 公共静态的DependencyPropertyKey RegisterReadOnly(类型ownerType, 字符串名称,类型propertyType,PropertyMetadata typeMetadata);
    name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback); 公共静态的DependencyPropertyKey RegisterReadOnly( 字符串名称,的类型propertyType,类型ownerType,PropertyMetadata typeMetadata,ValidateValueCallback的validateValueCallback);
    覆盖公共字符串的ToString();

    / /属性
    公共 PropertyMetadata DefaultMetadata {获取;}
    公共诠释 GlobalIndex的{得到;}
    公共字符串名称{获取;}
    公共的类型OwnerType {获得;}
    公共的类型PropertyType {获得;}
    公共BOOL只读{获取;}
    公共 ValidateValueCallback ValidateValueCallback的{获取;}
    }

  • DependencyObject的 ,它提供了类似的GetValue,SetValue的,ReadLocalValue,CoerceValue,ClearValue访问/修改一个依赖项属性的值的方法:
     , typeof (NameScope))] [TypeDescriptionProvider(typeof运算 (DependencyObjectProvider)),NameScopeProperty(“名称范围 ”,typeof运算 (名称范围))] 
    公共类 DependencyObject的:DispatcherObject的
    {
    / /方法
    公共 DependencyObject的();
    公共无效 ClearValue(DependencyProperty的DP);
    公共无效 ClearValue(关键的DependencyPropertyKey);
    公共的无效 CoerceValue(DependencyProperty的DP);
    obj); 公共密封覆盖布尔等于( 对象obj);
    公共密封覆盖的GetHashCode();
    “的公共 LocalValueEnumerator GetLocalValueEnumerator();
    公共对象的GetValue(DependencyProperty的DP);
    公共的无效 InvalidateProperty(DependencyProperty的DP);
    保护虚拟无效 OnPropertyChanged(DependencyPropertyChangedEventArgs E);
    公共,对象 ReadLocalValue(DependencyProperty的DP);
    ); 公共无效 SetCurrentValue(DP的DependencyProperty 对象的值 );
    ); 公共无效的SetValue(DependencyProperty的DP, 对象的值 );
    ); 公共无效的SetValue(关键的DependencyPropertyKey, 对象的值 );

    / /属性
    公共 DependencyObjectType DependencyObjectType的{获取;}
    set; } 内部,UINT EffectiveValuesCount {[FriendAccessAllowed]; 私人集;}
    公共BOOL的 IsSealed {获取;}
    }

  • PropertyMetadata,这使得依赖产权登记过程中指定的默认值,胁迫和改变处理逻辑:
     公共类 PropertyMetadata 
    {
    / /方法
    公共 PropertyMetadata();
    defaultValue); 公共 PropertyMetadata( 对象的DefaultValue);
    公共 PropertyMetadata(propertyChangedCallback PropertyChangedCallback);
    defaultValue, PropertyChangedCallback propertyChangedCallback); 公共 PropertyMetadata( 对象的DefaultValue,PropertyChangedCallback propertyChangedCallback);
    defaultValue, PropertyChangedCallback propertyChangedCallback, CoerceValueCallback coerceValueCallback); 公共 PropertyMetadata( 对象的DefaultValue,PropertyChangedCallback propertyChangedCallback,CoerceValueCallback coerceValueCallback;;);
    保护虚拟无效合并(PropertyMetadata baseMetadata,DependencyProperty的DP);
    保护的虚拟的无效 OnApply(DP的DependencyProperty,TARGETTYPE型);

    / /属性
    公共 CoerceValueCallback的CoerceValueCallback {获取;设置;}
    公共对象的DefaultValue {;}
    保护BOOL的 IsSealed {获取;}
    公共 PropertyChangedCallback PropertyChangedCallback {;}
    }

  • DependencyProperty.Register的*方法和他们的重载
  • PropertyMetadata构造

例子

下面的例子中有一个简单的类MyButton的,从Button类继承并实现一个依赖项属性MyValue。 MyValue属性的值必须介于0到10强制执行,其验证的逻辑IsValidMyValue()。 MyValue财产的有效价值取决于它的默认值(0),用户输入(一组请求参数),从基类继承其IsEnabled属性值。 最后的依赖,实施强制的逻辑CoerceMyValue和依赖改变的事件的句柄OnIsEnabledChanged()。 整体MyValue执行演示在WPF依赖属性的共同模式:

  • CLR包装:{公众诠释MyValue获得;设置;}
  • 依赖属性标识符: 公共静态只读DependencyProeprty MyValueProperty
  • 产权制度登记:DependencyProperty.Register的 ()
  • 验证逻辑:IsValidMyValue静态方法
  • 胁迫逻辑:CoerceMyValue的静态方法

这是常见的,并建议也实现改变处理逻辑通过:

  • PropertyChangedCallback OnMyValueChanged静态方法
  • 保护虚拟无效OnMyValueChanged的(oldValue,newValue)以提高Changed事件,子类重写
  • 公共静态只读RoutedEvent的MyValueChangedEvent路由事件标识符和事件登记
  • 公共的事件RoutedPropertyChangedEventHandler <int>的MyValueChanged客户端的事件

下面是示例的源代码:

  • Window1.xaml中:
  • window1.xaml.cs:
     System.Windows; using System.Windows.Controls; namespace WpfApp1 { // sample class to demonstrate how to implement a DependencyProperty public class MyButton : Button { // DP: CLR wrapper public int MyValue { get { return ( int )GetValue(MyValueProperty); } set { SetValue(MyValueProperty, value ); } } // DP: dependency property identifier & registration public static readonly DependencyProperty MyValueProperty = 使用系统; 使用 System.Windows; 使用 System.Windows.Controls的命名空间 WpfApp1 {/ /示例演示了如何实现一个DependencyProperty  MyButton的按钮类{/ / DP:CLR包装 MyValue {{ 返回(INT )的GetValue(MyValueProperty);} {的SetValue(MyValueProperty  );}} / / DP的依赖属性标识符登记 公共静态只读的DependencyProperty MyValueProperty =  IsValidMyValue( object value ) { int newValue = ( int ) value ; return newValue >= 0 && newValue <= 10; } // DP: coercion callback private static object CoerceMyValue(DependencyObject d, object value ) { MyButton ctrl = (MyButton)d; int newValue = ( int ) value ; return ctrl.IsEnabled ? 的DP:验证回调 私有静态布尔 IsValidMyValue( 对象值 ){newValue =(int) ; 回报 newValue> = 0 && newValue <= 10;} / / DP的:强迫回调 私有静态对象 CoerceMyValue(DependencyObject的D, 对象的值 ) {MyButton的CTRL =(MyButton的)D; newValue =(int) ; ctrl.IsEnabled 回报 OnMyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { MyButton ctrl = (MyButton)d; int oldValue = ( int )e.OldValue; int newValue = ( int )e.NewValue; ctrl.OnMyValueChanged(oldValue, newValue); } // RE: changed event identifier & registration public static readonly RoutedEvent MyValueChangedEvent = EventManager.RegisterRoutedEvent( "MyValueChanged" , RoutingStrategy.Bubble, typeof (RoutedPropertyChangedEventHandler< int >), typeof (MyButton)); // RE: changed event public event RoutedPropertyChangedEventHandler< int > MyValueChanged; // RE: use a protected virtual to raise event protected virtual void OnMyValueChanged( int oldValue, int newValue) { RoutedPropertyChangedEventArgs< int > e = new RoutedPropertyChangedEventArgs< int >(oldValue, newValue); e.RoutedEvent = MyValueChangedEvent; RaiseEvent(e); } // DP: event handler to trigger re-calculation of MyValue because of dependency change private static void OnIsEnabledChanged( object sender, DependencyPropertyChangedEventArgs e) { MyButton ctrl = (MyButton)sender; ctrl.CoerceValue(MyButton.MyValueProperty); } public MyButton() { IsEnabledChanged += OnIsEnabledChanged; } } /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { public Window1() { InitializeComponent(); } private void mybtn_Click( object sender, RoutedEventArgs e) { } private void mybtn_MyValueChanged( object sender, RoutedPropertyChangedEventArgs< int > e) { int oldValue = e.OldValue; int newValue = e.NewValue; } } } Math.Min(newValue)以5:Math.Max(newValue)以6;} / / DP:改变回调 私有静态无效 OnMyValueChanged(DependencyObject的D,DependencyPropertyChangedEventArgs E){MyButton的CTRL =(MyButton的)D; oldValue =(INT e.OldValue)newValue =(INT)e.NewValue ctrl.OnMyValueChanged(oldValue,newValue)以;} / /重新更改事件标识及注册 公共静态只读 RoutedEvent的MyValueChangedEvent EventManager.RegisterRoutedEvent(“MyValueChanged”,RoutingStrategy。泡沫,typeof运算 (RoutedPropertyChangedEventHandler < 整数>),typeof运算 (MyButton的))/ /重新改变事件 公共事件 RoutedPropertyChangedEventHandler的< 诠释 > MyValueChanged的; / /重新使用受保护的虚拟提高事件 保护虚拟无效OnMyValueChanged(oldValue, newValue)以{RoutedPropertyChangedEventArgs < 整数 >电子=  RoutedPropertyChangedEventArgs <>(oldValue,newValue)以; e.RoutedEvent = MyValueChangedEvent;的RaiseEvent(E);} / / DP:触发事件处理程序的重新计算,因为依赖改变 私人 的MyValue 静态无效 OnIsEnabledChanged( 对象发件人,DependencyPropertyChangedEventArgsé){MyButton的CTRL =(MyButton的)发件人; ctrl.CoerceValue(MyButton.MyValueProperty);} 公共 MyButton的(){IsEnabledChanged + = OnIsEnabledChanged的;}} / / / / / / <summary> Window1.xaml中/ / / </摘要> 公共部分类 Window1的互动逻辑 :窗口{ 公共 Window1的(){的InitializeComponent();}的私人作废 mybtn_Click( 对象发件人,RoutedEventArgs E){}的私人无效 mybtn_MyValueChanged的( 对象发件人,RoutedPropertyChangedEventArgs < 整数 > E){INT的 oldValue e.OldValue; 诠释 newValue = e.NewValue;}} 

如何依赖于WPF知识产权工作

为了演示如何在WPF依赖属性,我设置了一个破发点的大部分功能,使用即时调试窗口,检查的价值观和改变状态。

注册

  • 在程序启动时,DependencyProperty.Register的执行,它调用,反过来IsValidMyValue验证默认值0:

    validation called for default value

有效的价值

在MyButton的实例,点击触发破发点上mybtn_Click事件处理程序,然后使用“即时”窗口中,检查MyValue财产。 如下所示:

  • 即使mybtn.MyValue没有分配任何价值,它有一个有效的价值为0,这依赖项属性的默认值,如显示通过的mybtn.MyValue mybtn.GetValue的结果。
  • 重要的是,CLR包装和GetValue / SetValue调用应始终具有相同的效果。 这是由CLR包装只是一个暂时的GetValue / SetValue和没有更多的包装。
  • mybtn.ReadLocalValue(MyButton.MyValueProperty)结果显示,有没有MyValue财产mybtn实例的本地存储。
  • 当mybtn.MyValue初始值,没有任何变化通知,作为OnMyValueChanged是不是在程序开始调用。

    DP has default value, no local storage

验证

在“即时”窗口,设置命令“mybtn.MyValue = -1”输入mybtn.MyValue到一个无效的值-1:

  • 被称为CLR的包装,这反过来又调用SetValue的,这会触发验证新值-1。

    validation called for setting value
  • IsValidMyValue新值-1返回false,WPF属性系统,然后抛出ArgumentException:

    validation failure triggers argument exception in setting
  • 通过三种方式检查的价值MyValue:CLR包装的GetValue,ReadLocalValue:什么都没有改变。 这是重要的,因为在Silverlight状态变化,甚至无效的设置操作,所以我们不得不编写代码来恢复状态,我会出现在两个系列的一部分。

    No state change after invalid set operation

强迫

再次在“即时”窗口,尝试设置mybtn.MyValue至8日,这是有效的,但由于mybtn.IsEnabled是真实的,有效的值必须小于或等于5:

  • 首先,验证被称为新的价值8,通过CLR包装和SetValue:

    validation called during setting
  • IsValidMyValue(8)返回true,所以被称为强制逻辑CoerceMyValue,通过CLR包装的SetValue,UpdateEffectiveValue,ProcessCoerceValue:

    Coercion called during setting
  • CoerceMyValue(8)返回一个不同的值5,所以WPF属性系统调用IsValidMyvalue受胁迫的有效值为5:

    Coerced value goes through validation as well
  • IsValidMyValue(5)返回true,所以WPF属性系统调用改变逻辑OnMyValueChanged oldValue和newValue为0,为5。 这两个值的值是有效的,因为有老没有在mybtn实例值,新的价值实际上是8。 OnMyValueChanged轮流致电其proctected的虚拟版本,提高MyValueChanged路由事件,并执行MyValueChanged事件处理程序,如果有任何。
    Changed callback to raise changed event
  • 检查MyValue财产的价值:两个CLR包装和GetValue返回新的有效值为5,但GetLocalValue返回8,记住原来的用户请求。 这是非常重要的,我们将看到未来。

    Coercion causes local value and effective value difference

动态重新计算的依赖性变动

现在让我们设置mybtn.IsEnabled为false,在“即时”窗口。 这变化之一MyValue的依赖关系,这应该引起MyValue财产的有效价值的重新计算,如果我有编码是正确的(我做了,当然 :-)

  • 首先,请注意,IsEnabled的变化处理是同步的,也就是说,被称为OnIsEnabledChanged前“mybtn.IsEnabled的假”的回报。 这是很重要的,因为IsEnabled的变化处理上的Silverlight异步。
  • 我们要触发重新计算,因为产权制度不知道MyValue取决于对IsEnabled的。 这是通过处理IsEnabledChanged事件(IsEnabledChanged + = OnIsEnabledChanged MyButton的()),并呼吁从内OnIsEnabledChanged事件处理程序CoerceValue的。
  • 在页转到CoerceValue调用UpdateEffectiveValue,ProcessCoerceValue,那么我们的强制逻辑CoerceMyValue。 请注意,CoerceMyValue被称为newValue 8,而不是目前的有效值为5。 原设置请求参数8记住mybtn本地实例,ReadLocalValue结果之前所示。 这一点很重要,因为Silverlight不这样做,这使得实施强制非常棘手,将在Silverlight RangeBase和它的子类的错误,如Slider和ScrollBar的博客系列的第3部分所示。

    Dependency change triggers coercion
  • 由于mybtn.IsEnabled现在是假的,最初请求的值变为有效,所以CoerceMyValue回报8。
  • 8从以前的5有效价值是不同的,,,所以UpdateEffectieValue呼吁NotifyPropertyChange,最终要求我们改变的逻辑oldValue和newValue 5 8 OnMyValueChanged。 发生这种情况即使没有MyValue财产的一套新的操作。 这是IsEnabled的依赖关系变化造成MyValue有效的值的变化,在变成触发器MyValue属性更改通知和事件处理,以及这一切发生的同步。

    Coercion triggers change notification
  • 胁迫,变更通知和事件处理结束后,检查MyValue财产的价值:现在所有这三种方法,(mybtn.MyValue mybtn.GetValue,mybtn.ReadLocalValue)返回相同的结果。

    Effective value and local value the same
  • 最后一个实验:的类型mybtn.ClearVa​​lue在立即窗口中清除依赖项属性的本地值(8),这反过来又触发改变处理逻辑OnMyValueChanged,0 newValue(MyButton.MyValueProperty),新的有效的价值来自MyValue的默认属性。

    ClearValue triggers change handling

结论

正如我们可以看到从上面的例子和实验,WPF有一个强大的知识产权制度,这使得实施和使用依赖属性很容易。 这是一个很大的Silverlight虽然棘手,因为Silverlight实现的WPF功能的一个子集。 在后系列的第二部分,我将重新在Silverlight实现上面的例子,并展示WPF和Silverlight之间的差异。 在第三部分,我将使用RangeBase的NumericUpDown来展示Silverlight中,这是这系列的原始动机上的疑难杂症。 敬请期待!

PS系列的第一篇文章,结束了很多时间,耗费更多的时间比我预想的,所以我会停在这里。 如果有足够的兴趣,我可以上传你的项目实验,并规定执行WPF依赖属性大部分管道的代码片段。 谢谢!

Technorati的标签:
善于交际,分享!
  1. 还没有评论。
  1. 还没有引用通告。


+七= 12