WPF入门教程系列十四——依赖属性(四)
六、依賴(lài)屬性回調(diào)、驗(yàn)證及強(qiáng)制值
??? 我們通過(guò)下面的這幅圖,簡(jiǎn)單介紹一下WPF屬性系統(tǒng)對(duì)依賴(lài)屬性操作的基本步驟:
?
借用一個(gè)常見(jiàn)的圖例,介紹一下WPF屬性系統(tǒng)對(duì)依賴(lài)屬性操作的基本步驟:?
- 第一步,確定Base Value,對(duì)同一個(gè)屬性的賦值可能發(fā)生在很多地方。比如控件的背景(Background),可能在Style或者控件的構(gòu)造函數(shù)中都對(duì)它進(jìn)行了賦值,這個(gè)Base Value就要確定這些值中優(yōu)先級(jí)最高的值,把它作為Base Value。
- 第二步,估值。如果依賴(lài)屬性值是計(jì)算表達(dá)式(Expression),比如說(shuō)一個(gè)綁定,WPF屬性系統(tǒng)就會(huì)計(jì)算表達(dá)式,把結(jié)果轉(zhuǎn)化成一個(gè)實(shí)際值。
- 第三步,動(dòng)畫(huà)。動(dòng)畫(huà)是一種優(yōu)先級(jí)很高的特殊行為。如果當(dāng)前屬性正在作動(dòng)畫(huà),那么因動(dòng)畫(huà)而產(chǎn)生的值會(huì)優(yōu)于前面獲得的值,這個(gè)也就是WPF中常說(shuō)的動(dòng)畫(huà)優(yōu)先。
- 第四步,強(qiáng)制。如果我們?cè)贔rameworkPropertyMetadata中傳入了 CoerceValueCallback委托,WPF屬性系統(tǒng)會(huì)回調(diào)我們傳入的的delagate,進(jìn)行屬性值的驗(yàn)證,驗(yàn)證屬性值是否在我們?cè)试S的范圍之內(nèi)。例如強(qiáng)制設(shè)置該值必須大于于0小于10等等。在屬性賦值過(guò)程中,Coerce擁有 最高的優(yōu)先級(jí),這個(gè)優(yōu)先級(jí)要大于動(dòng)畫(huà)的優(yōu)先級(jí)別。
- 第五步,驗(yàn)證。驗(yàn)證是指我們注冊(cè)依賴(lài)屬性如果提供了ValidateValueCallback委托,那么最后WPF會(huì)調(diào)用我們傳入的delegate,來(lái)驗(yàn)證數(shù)據(jù)的有效性。當(dāng)數(shù)據(jù)無(wú)效時(shí)會(huì)拋出異常來(lái)通知。
那么應(yīng)該如何使用這些功能呢?
前面我們講了基本的流程,下面我們就用一個(gè)小的例子來(lái)進(jìn)行說(shuō)明:
XAML的代碼如下:
?
<Window x:Class="WpfApp1.WindowValid"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title=" WindowValid " Height="300" Width="400"><Grid><StackPanel> <Button Name="btnDPTest" Click="btnDPTest_Click" >屬性值執(zhí)行順序測(cè)試</Button></StackPanel></Grid></Window>?
C#的代碼如下:
?
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Shapes;using System.Windows.Threading;using WpfApp1.Models;namespace WpfApp1{/// <summary>/// WindowThd.xaml 的交互邏輯/// </summary>public partial class WindowValid: Window{public WindowValid (){InitializeComponent();}private void btnDPTest_Click(object sender, RoutedEventArgs e){SimpleDP test = new SimpleDP();test.ValidDP = 1;} }}using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows;namespace WpfApp1.Models{public class SimpleDP : DependencyObject{public static readonly DependencyProperty ValidDPProperty =DependencyProperty.Register("ValidDP", typeof(int), typeof(SimpleDP),new FrameworkPropertyMetadata(0,FrameworkPropertyMetadataOptions.None,new PropertyChangedCallback(OnValueChanged),new CoerceValueCallback(CoerceValue)),new ValidateValueCallback(IsValidValue));public int ValidDP{get { return (int)GetValue(ValidDPProperty); }set { SetValue(ValidDPProperty, value); }}private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){Console.WriteLine("當(dāng)屬性值的OnValueChanged方法被調(diào)用,屬性值為: {0}", e.NewValue);}private static object CoerceValue(DependencyObject d, object value){Console.WriteLine("當(dāng)屬性值的CoerceValue方法被調(diào)用,屬性值強(qiáng)制為: {0}", value);return value;}private static bool IsValidValue(object value){Console.WriteLine("當(dāng)屬性值的IsValidValue方法被調(diào)用,對(duì)屬性值進(jìn)行驗(yàn)證,返回bool值,如果返回True表示嚴(yán)重通過(guò),否則會(huì)以異常的形式拋出: {0}", value);return true;} }}?
結(jié)果如下:
?
當(dāng)ValidDP屬性變化之后,PropertyChangeCallback就會(huì)被調(diào)用。可以看到結(jié)果并沒(méi)有完全按照我們先前的流程先 Coerce后Validate的順序執(zhí)行,有可能是WPF內(nèi)部做了什么特殊處理,當(dāng)屬性被修改時(shí),首先會(huì)調(diào)用Validate來(lái)判斷傳入的value是 否有效,如果無(wú)效就不繼續(xù)后續(xù)的操作,這樣可以更好的優(yōu)化性能。從上面的結(jié)果上看出,CoerceValue后面并沒(méi)有立即ValidateValue, 而是直接調(diào)用了PropertyChanged。這是因?yàn)榍懊嬉呀?jīng)驗(yàn)證過(guò)了value,如果在Coerce中沒(méi)有改變value,那么就不用再驗(yàn)證了。如 果在 Coerce中改變了value,那么這里還會(huì)再次調(diào)用ValidateValue操作,和前面的流程圖執(zhí)行的順序一樣,在最后我們會(huì)調(diào)用 ValidateValue來(lái)進(jìn)行最后的驗(yàn)證,這就保證最后的結(jié)果是我們希望的那樣了。
上面簡(jiǎn)單介紹了處理流程,下面我們就以一個(gè)案例來(lái)具體看一看上面的流程到底有沒(méi)有出入。
?
依賴(lài)屬性代碼文件如下:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows;namespace WpfApp1.Controls{class MyValiDP:System.Windows.Controls.Control{ //注冊(cè)Current依賴(lài)屬性,并添加PropertyChanged、CoerceValue、ValidateValue的回調(diào)委托public static readonly DependencyProperty CurrentValueProperty = DependencyProperty.Register("CurrentValue",typeof(double),typeof(MyValiDP),new FrameworkPropertyMetadata(Double.NaN,FrameworkPropertyMetadataOptions.None,new PropertyChangedCallback(OnCurrentValueChanged),new CoerceValueCallback(CoerceCurrentValue)),new ValidateValueCallback(IsValidValue));//屬性包裝器,通過(guò)它來(lái)暴露Current的值public double CurrentValue{get { return (double)GetValue(CurrentValueProperty); }set { SetValue(CurrentValueProperty, value); }}//注冊(cè)Min依賴(lài)屬性,并添加PropertyChanged、CoerceValue、ValidateValue的回調(diào)委托public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register("MinValue",typeof(double),typeof(MyValiDP),new FrameworkPropertyMetadata(double.NaN,FrameworkPropertyMetadataOptions.None,new PropertyChangedCallback(OnMinValueChanged),new CoerceValueCallback(CoerceMinValue)),new ValidateValueCallback(IsValidValue));//屬性包裝器,通過(guò)它來(lái)暴露Min的值public double MinValue{get { return (double)GetValue(MinValueProperty); }set { SetValue(MinValueProperty, value); }}//注冊(cè)Max依賴(lài)屬性,并添加PropertyChanged、CoerceValue、ValidateValue的回調(diào)委托public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register("MaxValue",typeof(double),typeof(MyValiDP),new FrameworkPropertyMetadata(double.NaN,FrameworkPropertyMetadataOptions.None,new PropertyChangedCallback(OnMaxValueChanged),new CoerceValueCallback(CoerceMaxValue)),new ValidateValueCallback(IsValidValue));//屬性包裝器,通過(guò)它來(lái)暴露Max的值public double MaxValue{get { return (double)GetValue(MaxValueProperty); }set { SetValue(MaxValueProperty, value); }}//在CoerceCurrent加入強(qiáng)制判斷賦值private static object CoerceCurrentValue(DependencyObject d, object value){MyValiDP g = (MyValiDP)d;double current = (double)value;if (current < g.MinValue) current = g.MinValue;if (current > g.MaxValue) current = g.MaxValue;return current;}//當(dāng)Current值改變的時(shí)候,調(diào)用Min和Max的CoerceValue回調(diào)委托private static void OnCurrentValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){d.CoerceValue(MinValueProperty);d.CoerceValue(MaxValueProperty);}//當(dāng)OnMin值改變的時(shí)候,調(diào)用Current和Max的CoerceValue回調(diào)委托private static void OnMinValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){d.CoerceValue(MaxValueProperty);d.CoerceValue(CurrentValueProperty);}//在CoerceMin加入強(qiáng)制判斷賦值private static object CoerceMinValue(DependencyObject d, object value){MyValiDP g = (MyValiDP)d;double min = (double)value;if (min > g.MaxValue) min = g.MaxValue;return min;}//在CoerceMax加入強(qiáng)制判斷賦值private static object CoerceMaxValue(DependencyObject d, object value){MyValiDP g = (MyValiDP)d;double max = (double)value;if (max < g.MinValue) max = g.MinValue;return max;}//當(dāng)Max值改變的時(shí)候,調(diào)用Min和Current的CoerceValue回調(diào)委托private static void OnMaxValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){d.CoerceValue(MinValueProperty);d.CoerceValue(CurrentValueProperty);}//驗(yàn)證value是否有效,如果返回True表示驗(yàn)證通過(guò),否則會(huì)提示異常public static bool IsValidValue(object value){Double v = (Double)value;return (!v.Equals(Double.NegativeInfinity) && !v.Equals(Double.PositiveInfinity));}}}?
?
XAML代碼如下:
<Window x:Class="WpfApp1.WindowProcess"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:WpfApp1.Controls"Title="WindowProcess" Height="400" Width="500"><Grid><StackPanel Orientation="Vertical"><local:MyValiDP x:Name="myValiDP1" MaxValue="500" MinValue="0" /><Label Content="可以設(shè)置最小值為0和最小大值為500" Height="30"/><StackPanel Orientation="Horizontal" Height="60"><Label Content="當(dāng)前值為 : "/><Label Background="Yellow" BorderBrush="Black" BorderThickness="1"IsEnabled="False" Content="{Binding ElementName=myValiDP1, Path=CurrentValue}" Height="25" VerticalAlignment="Top" /></StackPanel><WrapPanel ><Label Content="最小值" /><Slider x:Name="sliderMin" Minimum="-200" Maximum="100" Width="300" ValueChanged="sliderMin_ValueChanged" SmallChange="10" /><Label Content="{Binding ElementName=sliderMin, Path=Value}" /></WrapPanel><WrapPanel ><Label Content="最大值" /><Slider x:Name="sliderMax" Minimum="200" Maximum="800" Width="300" ValueChanged="sliderMax_ValueChanged" SmallChange="10" /><Label Content="{Binding ElementName=sliderMax, Path=Value}" /></WrapPanel></StackPanel></Grid></Window>?
?
C#代碼如下:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Shapes; namespace WpfApp1 {/// <summary>/// WindowProcess.xaml 的交互邏輯/// </summary>public partial class WindowProcess : Window{public WindowProcess(){InitializeComponent();//設(shè)置Current的值myValiDP1.CurrentValue = 100;}private void sliderMin_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e){//設(shè)置Current的值myValiDP1.CurrentValue = (int)sliderMin.Value;}private void sliderMax_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e){//設(shè)置Current的值myValiDP1.CurrentValue = (int)sliderMax.Value;}}}示例效果如下圖。
在上面的例子中,一共有三個(gè)依賴(lài)屬性相互作用——CurrentValue、MinValue和MaxValue,這些屬性相互作 用,但它們的規(guī)則是MinValue≤CurrentValue≤MaxValue。根據(jù)這個(gè)規(guī)則,當(dāng)其中一個(gè)依賴(lài)屬性變化時(shí),另外兩個(gè)依賴(lài) 屬性必須進(jìn)行適當(dāng)?shù)恼{(diào)整,這里我們要用到的就是CoerceValue這個(gè)回調(diào)委托,那么實(shí)現(xiàn)起來(lái)也非常的簡(jiǎn)單,注冊(cè)MaxValue的時(shí)候加入 CoerceValueCallback,在CoerceMaxValue函數(shù)中做處理:如果Maximum的值小于MinValue,則使 MaxValue值等于MinValue;同理在CurrentValue中也加入了CoerceValueCallback進(jìn)行相應(yīng)的強(qiáng)制 處理。然后在MinValue的ChangedValueCallback被調(diào)用的時(shí)候,調(diào)用CurrentValue和MaxValue的 CoerceValue回調(diào)委托,這樣就可以達(dá)到相互作用的依賴(lài)屬性一變應(yīng)萬(wàn)變的”千機(jī)變“。
???? 換句話說(shuō),當(dāng)相互作用的幾個(gè)依賴(lài)屬性其中一個(gè)發(fā)生變化時(shí),在它的PropertyChangeCallback中調(diào)用受它影響的依賴(lài)屬性的CoerceValue,這樣才能保證相互作用關(guān)系的正確性。 前面也提高ValidateValue主要是驗(yàn)證該數(shù)據(jù)的有效性,最設(shè)置了值以后都會(huì)調(diào)用它來(lái)進(jìn)行驗(yàn)證,如果驗(yàn)證不成功,則拋出異常。
?
轉(zhuǎn)載于:https://www.cnblogs.com/chillsrc/p/4688983.html
總結(jié)
以上是生活随笔為你收集整理的WPF入门教程系列十四——依赖属性(四)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 第一百零四天 how can I 坚持
- 下一篇: iOS - - JSON 和 XML解析