在定義任何類型的屬性時,都需要面對錯誤設定屬性的可能性。對于傳統的.NET屬性,可嘗試在屬性設定器中捕獲這類問題。但對于依賴項屬性而言,這種方法不合适,因為可能通過WPF屬性系統使用SetValue()方法直接設定屬性。
作為代替,WPF提供了兩種方法來阻止非法值:
- ValidateValueCallback:該回調函數可接受或拒絕新值。通常,該回調函數用于捕獲違反屬性限制的明顯錯誤。可作為DependencyProperty.Register()方法的一個參數提供該回調函數。
- CoerceValueCallback:該回調函數可将新值修改為更能被接受的值。該回調函數通常用于處理為相同對象設定的依賴項屬性值互相沖突的問題。這些值本身可能是合法的,但當同時應用時它們是不相容的。為了使用這個回調函數,當建立FrameworkPropertyMetadata對象時(然後該對象将被傳遞到DependencyProperty.Register()方法),作為構造函數的一個參數提供該回調函數。
下面是當應用程式試圖設定依賴項屬性時,所有這些内容的作用過程:
(1)首先,CoerceValueCallback方法有機會修改提供的值(通常,使提供的值和其他屬性相容),或者傳回DependencyProperty.UnsetValue,這會完全拒絕修改。
(2)接下來激活ValidateValueCallback方法。該方法傳回true以接受一個值作為合法值,或者傳回false拒絕值。與CoerceValueCallback方法不同,ValidateValueCallback方法不能通路設定屬性的實際對象,這意味着你不能檢查其他屬性值。
(3)最後,如果前兩個階段都獲得成功,就會觸發PropertyChangedCallback方法。此時,如果希望為其他類提供通知,可以引發更改事件。
一、驗證回調
正如前面所看到的,DependencyProperty.Register()方法接受可選的驗證回調函數:
static FrameworkElement(){
FrameworkPropertyMetadata metadata=new FrameworkPropertyMetadata(new Thickness(),FrameworkPropertyMetadataOptions.AffectsMeasure);
MarginProperty=DependencyProperty.Register("Margin",typeof(Thickness),typeof(FrameworkElement),metadata,new ValidateValueCallback(FrameworkElement.IsMarginValid));
....
}
可使用這個回調函數驗證,驗證通常應被添加到屬性過程的設定部分。提供的回調函數必須指向一個接受對象參數并傳回Boolean值得方法。傳回true以接受對象是合法的,傳回false拒絕對象。
對FrameworkElement.Margin屬性的驗證十分枯燥乏味,因為它依賴于内部的Thickness.IsValid()方法。該方法確定目前使用的Thickness對象(表示邊距)是合法的。例如,可能構造了一個完全可以接受的Thickness對象(卻不适于設定邊距)。一個例子是Thickness對象使用了負值。如果提供的Thickness對象對于邊距時是不合法的。IsMarginValid方法将傳回false:
public static bool IsMarginValid(object value)
{
Thickness thickness1=(Thickness)value;
return thickness1.IsValid(true,false,true,false);
}
對于驗證回調函數有一個限制:它們必須是靜态方法而且無權通路正在被驗證的對象。所有能夠獲得的資訊隻有剛剛應用的數值。盡管這樣更便于重用屬性,但可能無法建立考慮其他屬性的驗證例程。典型的例子是具有Maximum和Minimum屬性的元素。顯然,為Maximum屬性設定的值不能小于為Minimum屬性設定的值。但是,不能使用驗證回調函數來實施這一邏輯,因為一次之恩你通路一個屬性。
二、強制回調
通過FrameworkPropertyMetadata對象使用CoerceValueCallback回調函數。下面是示例:
FrameworkPropertyMetadata metadata=new FrameworkPropertyMetadata(new Thickness(),FrameworkPropertyMetadataOptions.AffectsMeasure);
metadata.CoerceValueCallback=new CoerceValueCallback(CoerceMaximum);
DependencyProperty.Register("Maxium",typeof(double),typeof(RangeBase),metadata);
可以通過CoerceValueCallback回調函數處理互相關聯的屬性。例如,ScrollBar控件提供了Maximum、Minimum和Value屬性,這些屬性都繼承自RangeBase類。保持對這些屬性進行調整的一種方法是使用屬性強制。
例如,當設定Maximum屬性時,必須使用強制以確定不能小于Minimum屬性的值:
private static object CoerceMaximum(DependencyObject d,object value)
{
RangeBase base1=(RangeBase)d;
if((double)value)<base1.Minimum)
{
return base1.Minimum;
}
return value;
}
換句話說,如果應用于Maximum屬性的值小于Minimum屬性的值,就用Minimum屬性的值設定Maximum屬性。注意,CoerceValueCallback傳遞兩個參數——準備使用的數值和該數值将要應用到得對象。
當設定Value屬性時,會發生類似的強制過程。對Value屬性進行強制,確定不會超出由Minimum和Maximum屬性定義的範圍,使用下面的代碼:
internal static object ConstrainToRange(DependencyObject d,object value)
{
double newValue=(double)value;
RangeBase base1=(RangeBase)d;
double minimum=base1.Minimum;
if(newValue<minimum)
{
return minimum;
}
double maximum=bas1.Maximum;
if(newValue>maximum)
{
return maximum;
}
return newValue;
}
Minimum屬性根本不使用值強制。相反,一旦值發生變化,就觸發PropertyChangedCallback,然後通過手動觸發Maximum和Value屬性的強制過程,使它們适應Minimum屬性值得變化:
private static void OnMinimumChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
RangeBase base1=(RangeBase)d;
...
base1.CoerceValue(RangeBase.MaximumProperty);
base1.CoerceValue(RangeBase.ValueProperty);
}
類似地,一旦設定或強制Maximum屬性的值,那麼也會手動強制Value屬性以适應Maximum屬性值得變化:
private static void OnMaximumChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
RangeBase base1=(RangeBase)d;
...
base1.CoerceValue(RangeBase.ValueProperty);
base1.OnMaximumChanged((double)e.OldValue,(double)e.NewValue);
}
如果設定的值互相沖突,最終結果是Minimum屬性具有優先權,其次是Maximum屬性(并且可能會被Minimum屬性強制),最後是Value屬性(并且可能會被Maximum和Minimum屬性強制)。
作者:Peter Luo
出處:https://www.cnblogs.com/Peter-Luo/
本文版權歸作者和部落格園共有,歡迎轉載,但必須給出原文連結,并保留此段聲明,否則保留追究法律責任的權利。