4 WPF依赖属性
理解依賴屬性
依賴屬性支持的特征包括:動畫、數據綁定、樣式。由WPF元素暴露的屬性大部分都是依賴屬性。依賴屬性和常規屬性的使用方法相同。
WPF設計了依賴屬性支持其特有的動態特性,并且不干擾其他系統的.net代碼。
定義依賴屬性
創造一個依賴屬性的語法完全不同于創造一個普通的.NET屬性。
第一步是定義一個代表屬性的對象。這是DependencyProperty類的一個實例。關于你屬性的信息需要一直是可用的,甚至可能在類之間共享(常見在WPF元素中)。因此,你的DependencyProperty對象必須被定義為相關類的一個靜態字段。
例如,FrameworkElement類定義了一個Margin屬性。Margin被所有元素共享,是一個依賴屬性:
public class FrameworkElement: UIElement, ... {public static readonly DependencyProperty MarginProperty;... }根據命名約定,定義依賴屬性的字段名字是正常屬性加上單詞Property后綴。那樣,你能區分依賴屬性定義和實際屬性的名字。字段使用readonly關鍵字,這意味著它只能在FrameworkElement類的靜態構造函數中設置,如何設置見下節。
注冊依賴屬性
下一步是注冊你的依賴屬性。因為要在使用屬性之前完成注冊,必須在相關類的一個靜態構造函數中執行它。
不能直接實例化DependencyProperty對象,因為DependencyProperty類沒有公開的構造函數。代替,一個DependencyObject實例只能使用靜態的DependencyProperty.Register()方法被創造。DependencyProperty對象被創造之后不能再修改,因為所有的DependencyProperty成員是只讀的。代替,它們的值必須作為Register()方法的參數被提供。
下面例子顯示FrameworkElement類的Margin屬性是如何被注冊的:
static FrameworkElement() {var metadata = new FrameworkPropertyMetadata(new Thickness(), FrameworkPropertyMetadataOptions.AffectsMeasure);MarginProperty = DependencyProperty.Register("Margin",typeof(Thickness), typeof(FrameworkElement), metadata,new ValidateValueCallback(FrameworkElement.IsMarginValid));... }注冊一個依賴屬性包括二步。首先,你創造一個FrameworkPropertyMetadata對象,指明希望使用依賴屬性的什么服務(諸如支持數據綁定,動畫,和日志)。其次,依靠調用DependencyProperty.Register()靜態方法注冊屬性。這時,你負責提供一些關鍵成分:
- 屬性名稱
- 屬性的數據類型
- 屬性所在類的類型
- 可選,附帶有屬性設置的FrameworkPropertyMetadata對象
- 可選,執行屬性驗證的回調
兩個可選屬性值得研究。FrameworkPropertyMetadata的詳細描述見95頁。
包裝依賴屬性
創造依賴屬性的最后一步是用一個傳統.NET屬性包裝它。WPF屬性使用的是定義在DependencyObject基類的GetValue()和SetValue()方法。
public Thickness Margin {set { SetValue(MarginProperty, value); }get { return (Thickness)GetValue(MarginProperty); } }當你創造屬性包裝時,你應該僅調用SetValue()和GetValue(),如在前例中。你不應該添加任何額外的代碼驗證值,引起事件,等等。那是因為另外的特征可能旁路屬性包裝,直接調用SetValue()和GetValue()。(一個例子是在運行時當一個編譯XAML文件被解析時。)SetValue()和GetValue()都是公開的。
驗證輸入值應使用DependencyProperty.ValidateValueCallback。
引發事件應使用FrameworkPropertyMetadata.PropertyChangedCallback 。
現在可以使用屬性了:
myElement.Margin = new Thickness(5);之后,可能希望移除局部值設置,仿佛你從未設置它。依靠從DependencyObject繼承的ClearValue()方法。
myElement.ClearValue(FrameworkElement.MarginProperty);使用依賴屬性
依賴屬性支持兩個關鍵的行為:改變通知和動態值求解。
改變通知
如果你希望對一個屬性的改變作出反應,你有二個選擇—創造一個綁定,或寫一個觸發器。但是,依賴屬性沒有提供響應屬性值改變的事件。
動態值求解
依賴屬性因動態值求解的行為而得名。一個依賴屬性依賴于多個屬性提供者,每個提供者帶有它自己的優先級。當你從一個屬性值取回一個值時,WPF屬性系統經歷一系列步驟求得最終值。首先,它通過考慮下列因素決定屬性的基值。優先級從最低的到最高排列(最下面的贏):
基值不一定是最終的屬性值,WPF通過4個步驟求得屬性值:
共享依賴屬性
一些類共享依賴屬性,即使他們有獨立的類層次結構。例如,TextBlock.FontFamily和Control.FontFamily都指向同一個依賴屬性,它實際上被定義在TextElement類中以及TextElement.FontFamilyProperty。TextElement類的靜態構造函數注冊屬性,但是TextBlock和Control類的靜態構造函數簡單地調用DependencyProperty.AddOwner()方法重用它:
TextBlock.FontFamilyProperty =TextElement.FontFamilyProperty.AddOwner(typeof(TextBlock));自定義類
副作用
附加依賴屬性
附加屬性是依賴屬性,并且它被WPF屬性系統管理。區別是應用附加屬性的類不是定義它的類。
為定義一個附加屬性,你使用RegisterAttached()方法而不是Register()。這是一個注冊Grid.Row屬性的例子:
var metadata = new FrameworkPropertyMetadata(0, new PropertyChangedCallback(Grid.OnCellAttachedPropertyChanged));Grid.RowProperty = DependencyProperty.RegisterAttached("Row", typeof(int),typeof(Grid), metadata, new ValidateValueCallback(Grid.IsIntValueNotNegative));就像一個普通的依賴屬性,你能提供一個FrameworkPropertyMetadata對象和一個ValidateValueCallback。
當創造附加屬性時,你沒有定義.NET屬性包裝。那是因為附加屬性能被設置在任何依賴對象上。例如,Grid.Row屬性可能被設置在一個Grid對象上(如果你有一內部嵌套另一個Grid的Grid)或在另外的一些元素上。事實上,即使元素不在一個Grid內,和即使在你的元素樹上不存在Grid對象,Grid.Row屬性也能被設置在那個元素上。
代替使用一個.NET屬性包裝,附加屬性要求能調用的一對靜態方法去設置和獲得屬性值。這些方法使用熟悉的SetValue()和GetValue()方法(從DependencyObject類繼承)。靜態的方法應該被命名SetPropertyName()和GetPropertyName()。
這里是實現Grid.Row附加屬性的靜態方法:
public static int GetRow(UIElement element) {if (element == null){throw new ArgumentNullException("");}return (int)element.GetValue(Grid.RowProperty); } public static void SetRow(UIElement element, int value) {if (element == null){throw new ArgumentNullException("");}element.SetValue(Grid.RowProperty, value); }使用代碼設置一個元素位于網格的第一行:
Grid.SetRow(txtElement, 0);可選地,你能直接調用SetValue()或GetValue()方法,旁路掉靜態方法:
txtElement.SetValue(Grid.RowProperty, 0);屬性驗證
WPF提供兩種方法阻止無效值:
- ValidateValueCallback:這回調能接受或拒絕新值。通常,這回調被用來捕獲違反屬性的約束明顯錯誤。提供它作為DependencyProperty.Register()方法的參數。
- CoerceValueCallback:這回調能修改新值為更可接受的值。通常,這回調被用來處理被設置在同一對象的依賴屬性值之間的沖突。這些值可能單獨是有效的,但是當應用在一起時不一致。為使用這回調,當創造FrameworkPropertyMetadata對象時,提供它作為構造函數的參數,此對象隨后被傳遞到DependencyProperty.Register()方法。
這里是當應用程序試圖設置一個依賴屬性時:所有的部件如何起作用:
Validation回調
相當于正常屬性的set部分中的驗證。
其簽名為接受一個object輸入參數,返回布爾值。返回值為真表示接受,為假表示拒絕。
private static bool IsMarginValid(object value) {Thickness thickness1 = (Thickness) value;return thickness1.IsValid(true, false, true, false); }有一個限制,它是一個靜態方法,不能訪問正被驗證的對象,不能使用對象中的其他屬性。
Coercion回調
用于驗證互相關聯的屬性
private static object CoerceMaximum(DependencyObject d, object value) {RangeBase base1 = (RangeBase)d;if (((double) value) < base1.Minimum){return base1.Minimum;}return value; }轉載于:https://www.cnblogs.com/cuishengli/archive/2013/06/01/3112538.html
總結
- 上一篇: 哈工大密码学实验(CA证书认证系统)
- 下一篇: Android实现Telnet客户端