的DependencyProperty:审定,胁迫及变化处理(第三部分:依赖项属性代码段)
简介
这是三个部分组成的系列的最后一部分就如何落实与验证,胁迫和WPF和Silverlight事件依赖项属性。 我在第一部分中,我实现了一个简单的依赖项属性和调试通过演示如何依赖项属性的WPF和WPF依赖属性实施的共同模式。 在第一部分中,我没有在Silverlight相同。 因为Silverlight只支持PropertyChangedCallback,但不CoerceValueCallback和ValidateValueCallback,所以验证,胁迫和处理所有的变化都与PropertyChangedCallback实施。 这将导致PropertyChangedCallback被递归调用时验证或胁迫变化的依赖项属性的有效值。 加上其他限制Silverlight的产权制度,它可以得到非常棘手的依赖项属性的正确实施。 这篇文章中,我将展示的Silverlight RangeBase控件的一些不起眼的行为及其原因,进一步证明这可如何棘手。 最后,我将提供的代码片段实现依赖属性的实现,我在讨论的完整模式部分,那些读通过一系列奖励 ![]()
Silverlight的RangeBase控制
概述
滚动条,进度条和滑块继承RangeBase,实现的强制约束的最小,最大和值的依赖项属性,最低<=值<=最大。
这听起来很简单
但RangeBase控制“的行为可以得到很奇怪。 以几个例子:
- <Slider x:Name="sl"/>给你一个滑块与最小= 0,值= 0,最大值= 10,而<Slider x:Name="sl2" Minimum="-1" />产生一个滑块与最小= -1,值= 0,最大= 0。
- 点击SL2的滑块拖动拇指在SL2重点,打向右或向上箭头键,然后改变sl2.Maximum为正数10,sl2.Value奇迹般地从0变化到0.1。
- 像价值double.NaN,一个无效的值设置SL2的财产,将抛出一个异常,但sl2.Value改变double.NaN反正,没有ValueChanged事件被触发。
源代码
下面是代码,我们将调试和试验:
- Page.xaml中:
- page.xaml.cs:
Slider2 : Slider { protected override void OnMinimumChanged( double oldMinimum, double newMinimum) { base .OnMinimumChanged(oldMinimum, newMinimum); } protected override void OnMaximumChanged( double oldMaximum, double newMaximum) { base .OnMaximumChanged(oldMaximum, newMaximum); } protected override void OnValueChanged( double oldValue, double newValue) { base .OnValueChanged(oldValue, newValue); } protected override void OnKeyDown(System.Windows.Input.KeyEventArgs e) { base .OnKeyDown(e); } } public partial class Page : UserControl { public Page() { InitializeComponent(); } // the button click event handler provides a chance to break into debugger // and experiment with RangeBase validation, coercion and change handling. private void btn_Click( object sender, RoutedEventArgs e) { } } } {/ /子类的滑块可以轻松 打破公共类Slider2:属性更改和降低事件的关键 。滑块{保护覆盖无效OnMinimumChanged(双oldMinimum,双newMinimum){基地OnMinimumChanged(oldMinimum,newMinimum);}保护覆盖无效OnMaximumChanged(双保护覆盖无效 OnValueChanged( 双 oldValue, 双 newValue){ 基地 OnValueChanged(oldValue,newValue);} 保护覆盖无效 OnKeyDown(System.Windows.Input.KeyEventArgs oldMaximum, 双 newMaximum){ 基地 OnMaximumChanged(oldMaximum,newMaximum);} E){。 基地 OnKeyDown(E);}} 公共部分类页:UserControl的公共页面(){的InitializeComponent();} / /按钮的Click事件处理程序提供了一个机会打破RangeBase验证调试/ /和实验,胁迫和改变处理。 无效 btn_Click( 对象发件人,发送RoutedEventArgs){}}}
调试和实验
生成并运行上述简单的Silverlight应用程序,你会看到下面的屏幕截图。 请注意两个滑块之间的拇指位置的差异:
点击休息! 按钮打入其在Visual Studio中的事件处理程序,检查滑块SL和SL2的价值:
- <Slider x:Name="sl"/>:SL Mininum正确的价值=值= 0最大值= 10,而<Slider x:Name="sl2" Minimum="-1"/> SL2最小=为-1,但最大=值= 0;
对于那些谁读第二部分,现在熟悉的执行模式,这是SL2的古怪行为的根源:
- 叶_initialMax RangeBase,_initialVal,_requestedMax,_requestedVal由CLR初始化为默认值0。 这是确定的Value属性,因为它默认反正到零;但它最大的属性,默认为1其DependencyProperty.Register的呼叫,并使用它的默认值作为其有效价值的错误:
- 将sl2.Minimum设置为-1触发OnMinimumPropertyChanged - > CoerceMaximum,它使用的CLR初始化_requestedMax要挟最大。 由于_requestedMax是0,而不是目前最大的有效值为1和0恰好是大于最小的电流值-1,所以最大的变化为0:
- <Slider x:Name="sl">默认的最大值10,而不是1的原因是,它的默认模板有一个<Setter Property="Maximum" Value="10"/>。 此行还修复_requestedMax未初始化的问题。 这是为什么sl._requestedMax是在上面的屏幕截图10。
接下来,设置了一个破发点,在每一个功能,让应用程序运行。 选择SL2的拇指在它的重点,打向右或向上箭头键,它分成onKeyDown事件处理程序。 检查SL2的价值:_requestedVal为0;通过base.OnKeyDown(E)的步骤;再次检查SL2:_requestedVal 变为 0.1 。 这是因为base.OnKeyDown处理向右/向上箭头键,中风和尝试增加值的0.1 SmallIncrement;,这将触发OnValuePropertyChanged - > CoerceValue,然后胁迫价值为0,因为最大的是0,但它会记住的要求通过设置_requestedVal以0.1的增量。
虽然还在里面“即时”窗口中,设置sl2.Maximum为10。 这会触发OnMaxiumPropertyChanged:
这将触发CoerceValue,检查_requestedVal,其价值是现在的0.1。 由于0.1是在[-1,10]范围内,因此值改变至0.1:
最后,让我们的设定值,以double.NaN。 这将触发一个ArgumentException,如预期:
,但其值改为以double.NaN无论如何,并没有ValueChanged事件是因为ArgumentException的发射:
依赖项属性的代码段
现在,你可能得到的想法,这是相当棘手的实施,验证,胁迫和变化正确处理在Silverlight相依性属性。 为了使这更容易,下面是一个代码段,为实施上述模式大部分代码。 最初是由泰德Glaza撰写此代码段,我修改了它提供完整的模式。 你可以删除你不需要像那些不需要验证,胁迫,或改变处理,简单的依赖项属性的东西。 你仍然需要在自己的验证,胁迫,和改变处理逻辑填补像修改的IsValid美元的财产,强迫美元财产美元,美元财产美元的PropertyChanged,或美元财产,更改等功能,并添加一个单一的定义私人诠释_nestLevel。 但至少你不要担心世界粮食计划署和Silverlight属性系统之间的差异,而只注重验证,胁迫和改变处理逻辑本身。
> < Shortcut > sdp </ Shortcut > < Description > Code snippet for a dependency property with validation, coercion and changed event. </ Description > < Author > Ning依赖项属性</ TITLE> < 快捷方式 > SDP </ 快捷键 > < 说明 >与验证,胁迫和更改事件的依赖项属性的代码片段。</描述> < 作者 >宁 > < Default > MyProperty </ Default > </ Literal > < Literal > < ID > type </ ID > < ToolTip > Property type </ ToolTip > < Default > object </ Default > </ Literal > < Literal > < ID > defaultValue </ ID > < ToolTip > Default名称</ 工具提示 > < 默认 > MyProperty </ 预设 > </ literal>的< 文字 > < 编号 >键入</ 编号 > < 工具 提示 >物业类型</ 工具 提示> < 默认>对象</预设> </ literal>的 < 文字 > < 编号 >的DefaultValue </ ID> < 工具提示 >默认 > /// Gets or sets the value of $property$ dependency property.公共财产$类型$ $ / / / < 摘要 > / / /获取或设置美元财产美元的依赖项属性的值。 > public $type$ $property$ { get { return ($type$)GetValue($property$Property); } set { SetValue($property$Property, value); } } /// < summary > /// Identifies the $property$ dependency property. / / / </ 摘要 >公共$类型$属性$ {{返回($类型$)的GetValue(美元属性$物业);} {的SetValue($属性$属性,值);}} / / / < 摘要 > / / /标识的美元财产美元的依赖项属性。 > public static readonly $SystemWindowsDependencyProperty$ $property$Property = $SystemWindowsDependencyProperty$.Register( "$property$", typeof($type$), typeof($classname$), new $SystemWindowsPropertyMetadata$($defaultValue$, On$property$PropertyChanged)); /// < summary > /// $property$Property property changed handler. / / / </ 摘要 >公共静态只读美元SystemWindowsDependencyProperty $属性$属性= $ SystemWindowsDependencyProperty美元。寄存器(“$属性$”,typeof运算($类型$),typeof运算(的$ className $),美元的新SystemWindowsPropertyMetadata $($的DefaultValue美元,在美元的财产的PropertyChanged美元)); / / / < 摘要 > / / / $属性$属性属性更改处理程序。 > /// < param name ="d" > $classname$ that changed its $property$. </ param > /// < param name ="e" > Event arguments. </ param > private static void On$property$PropertyChanged($SystemWindowsDependencyObject$ d, $SystemWindowsDependencyPropertyChangedEventArgs$ e) { $classname$ source = ($classname$)d; $type$ newValue = ($type$)e.NewValue; $type$ oldValue = ($type$)e.OldValue; // validate newValue if (!IsValid$property$(newValue)) { // revert back to e.OldValue source._nestLevel++; source.SetValue(e.Property, e.OldValue); source._nestLevel--; // throw ArgumentException throw new ArgumentException("Invalid $property$Property value", "e"); } if (source._nestLevel == 0) { // remember initial state source._initial$property$ = oldValue; source._requested$property$ = newValue; } source._nestLevel++; // coerce newValue $type$ coercedValue = ($type$)Coerce$property$(d, e.NewValue); if (newValue != coercedValue) { // always set $property$Property to coerced value source.$property$ = coercedValue; } source._nestLevel--; if (source._nestLevel == 0 && source.$property$ != source._initial$property$) { // fire changed event only at root level and when there is indeed a change source.On$property$Changed(oldValue, source.$property$); } } /// < summary > /// $property$Property validation handler. / / / </ 摘要 > / / / < 参数 名称 =“D”的$ className $,改变美元的财产美元。</ PARAM> / / / < 参数 名称 =“E”>事件参数</ 参数 >私有静态无效美元财产美元的PropertyChanged($ SystemWindowsDependencyObject $ D,$ SystemWindowsDependencyPropertyChangedEventArgs $ E){$类名源=(的$ className $)D $类型$ newValue =($类型$)e.NewValue; $类型$ oldValue =($类型$)e.OldValue; / /验证newValue如果{/ /恢复到e.OldValue source._nestLevel +(的IsValid属性$(newValue)!); source.SetValue(e.Property,e.OldValue) ; source._nestLevel - / /抛出ArgumentException的抛出新的ArgumentException(“无效的属性$属性值”,“E”);}如果(source._nestLevel == 0){/ /记住初始状态source._initial美元财产$ = oldValue; source._requested美元财产美元= newValue;} source._nestLevel + + / /强制newValue类型$ coercedValue =($类型$)强迫美元属性$(D,e.NewValue);!如果(newValue = coercedValue ){/ /总是设置属性$物业价值的源泉裹挟美元财产美元= coercedValue;} source._nestLevel - - ;!(source._nestLevel == 0&&源属性$ = source._initial美元财产){/ /火改变只在根级别事件时的确有变化source.On美元财产变更(oldValue,源元的财产);}} / / / < 摘要> / / /美元财产美元财产验证处理程序。 > /// < param name ="value" > New value of $property$Property. </ param > /// < returns > /// Returns true if value is valid for $property$Property, false otherwise. / / / </ 摘要 > / / / < 参数 名称 =“值”> $属性$物业的新价值。</ PARAM> / / / < 返回 > / / /返回true,如果值是有效的$属性$物业否则为false。 > private static bool IsValid$property$($type$ value) { return true; } /// < summary > /// $property$Property coercion handler. / / / </>私有静态布尔的IsValid美元财产美元($类型$){返回true;} / / / < 摘要 > / / / $财产$财产胁迫处理程序。 > /// < param name ="d" > $classname$ that changed its $property$. </ param > /// < param name ="value" > Event arguments. </ param > /// < returns > /// Coerced effective value of $property$Property from input parameter value. / / / </ 摘要 > / / / < 参数 名称 =“D”的$ className $,改变美元的财产。</ PARAM> / / / < 参数 名称 =“值”>事件参数</ 参数 > / / / < 返回 > / / /胁迫的有效价值的物业属性$从输入参数值。 > private static object Coerce$property$($SystemWindowsDependencyObject$ d, object value) { $classname$ source = ($classname$)d; $type$ newValue = ($type$)value; return newValue; } /// < summary > /// $property$Property changed event. / / / </>私有静态对象强制属 性$($ SystemWindowsDependencyObject $ D,对象的值){的$ className $源=(的$ className $)D; $类型$ newValue =($类型$)值,返回newValue ;} / / / < 摘要 > / / / $物业美元属性更改事件。 > public event RoutedPropertyChangedEventHandler < $type$ > $property$Changed; /// < summary > /// Called by On$property$PropertyChanged static method to fire $property$Changed event. / / / </ 摘要 >公共事件RoutedPropertyChangedEventHandler <$类型$> $财产$改变; / / / < 摘要 > / / /调用静态方法$物业美元的PropertyChanged在火属性$ Changed事件。 > /// < param name ="oldValue" > The old value of $property$. </ param > /// < param name ="newValue" > The new value of $property$. </ param > protected virtual void On$property$Changed($type$ oldValue, $type$ newValue) { RoutedPropertyChangedEventArgs < $type$ > e = new RoutedPropertyChangedEventArgs < $type$ > (oldValue, newValue); if ($property$Changed != null) { $property$Changed(this, e); } } /// < summary > /// Cached previous value of $property$Property. / / / </ 摘要 > / / / < 参数 名称 =“oldValue”>旧值美元的财产。</ 参数 > / / / < 参数 名称 =“newValue”>美元的新的价值的财产。 / PARAM>保护虚拟无效;如果(美元财产美元财产变更($类型$ oldValue,美元键入$ newValue){RoutedPropertyChangedEventArgs <$类型$> E =新RoutedPropertyChangedEventArgs <$类型$>(oldValue,newValue) != NULL){$属性$改变(这,E);}} / / / < 摘要 > / / /缓存以前的$属性$物业的价值。 > private $type$ _initial$property$ = $defaultValue$; /// < summary > /// Cached originally requested value of $property$Property by user. / / / </ 摘要 >私人$类型$ _initial美元财产美元= $的DefaultValue美元; / / / < 摘要 > / / /缓存最初请求的属性$用户属性的值。 > private $type$ _requested$property$; #endregion public $type$ $property$ ]] > </ Code > </ Snippet > </ CodeSnippet > </ CodeSnippets > / / / </ 摘要 >私人$类型$ _requested美元财产美元;#endregion公共$类型$财产$]]> </ CODE> </ 片段 > </ CodeSnippet> </ CodeSnippets>
结论
我希望一系列帮助你理解和执行WPF和Silverlight相依性属性。 WPF和Silverlight是伟大的平台,将软件开发,可能比什么的Win32以前根本。
一如往常,反馈,建议,更正欢迎。 谢谢!








最近的评论