淺談WPF依賴項屬性

淺談WPF依賴項屬性

0. 引言

依賴項屬性雖然在使用上和CLR屬性同樣,可是它是WPF特有的,不一樣於CLR屬性。只是封裝爲咱們經常使用CLR的屬性,在語法使用上和CLR屬性同樣。WPF中一些功能:動畫,屬性綁定,樣式等都是以依賴項屬性爲基礎的。WPF中元素的屬性大部分都是依賴項屬性。html

依賴項屬性和CLR屬性最主要的區別是:CLR屬性是經過一個私有的字段來讀取。而依賴項屬性則是經過繼承在DependencyObjectGetValue()SetValue()方法動態的讀取屬性值。app

就是說當設置一個依賴項屬性值時,並非直接給一個對象的字段賦值,而是在一個DependencyObject對象的字典中設置鍵值對。Key值爲屬性的名稱,Value值爲你要賦值的值。函數

爲何使用依賴項屬性:字體

  • 減小內存
    當UI控件的90%以上的屬性一般停留在初始值時,爲每一個屬性存儲字段會是一個巨大的消耗。依賴項屬性則是隻在實例中存儲修改的屬性,那些默認值只在依賴項屬性中存儲一次。(如字體屬性,並非每一個元素都存儲一個字體屬性,字體屬性只存儲一次,其餘元素則是繼承該字體屬性。即:值繼承)動畫

  • 值繼承
    當訪問依賴項屬性時,該屬性值將經過使用一個值解析策略來解決:若是沒有設置本地值,則依賴屬性將導航到邏輯樹,直到找到一個值爲止。當在根元素上設置FontSize時,它將應用該根元素的全部子元素的全部文本塊,除非在子元素中從新設置了FontSize值。.net

  • 更改通知
    依賴屬性有一個內置的變動通知機制。經過在屬性元數據中註冊一個回調,當屬性值被更改時,就會獲得通知。這也是數據綁定所使用的。code


1. 自定義依賴項屬性

若是但願本來不支持數據綁定,動畫,或其餘WPF功能的代碼實現這些功能時,就要建立依賴項屬性。htm

1.1 聲明依賴項屬性

聲明一個DependencyProperty類型的字段,表示依賴項屬性的對象。該屬性的值應該始終保持可用,同時爲了保證在多個類之間共享該屬性信息,必須將DependencyProperty對象定義爲與其類關聯的靜態字段,WPF爲了確保在建立DependencyProperty對象後不能修改該對象,因此全部的DependencyProperty成員都是隻讀的。
(如:Margin屬性,全部的類都共享該屬性,因此該屬性必須是與類自己關聯。)對象

//聲明一個當前時間的依賴項屬性
public static readonly DependencyProperty CurrentTimeProperty;

1.2 註冊依賴項屬性

在使用該屬性代碼以前要先完成註冊(即該屬性和被註冊的類和數據類型,默認值,及一些事件關聯起來)完成,所以要在與其關聯的靜態構造函數中進行。WPF爲了確保DependencyProperty對象不能被直接實例化,使DependencyProperty類沒有公有的構造函數。只能使用靜態方法DependencyPropery.Register()建立DependencyProperty實例。這些註冊的值必須做爲Register()方法的參數來提供。繼承

public class MyClockControl1
{
    CurrentTimeProperty = DependencyProperty.Register( "CurrentTime", typeof(DateTime),
     typeof(MyClockControl), new FrameworkPropertyMetadata(DateTime.Now),validateValueCallback);

 
    static bool validateValueCallback(object data)
    {
        //自定義驗證
        return true;
    }
}

Register()註冊方法一共有五個參數,其中前三個爲必選,後兩個爲可選參數。

  • 第一個參數:表示CLR屬性的名稱。
  • 第二個參數:表示依賴項屬性的數據類型
  • 第三個參數:表示註冊的類的類型
  • 第四個參數:表示該依賴項屬性的默認值
  • 第五個參數:表示該依賴項屬性的回調驗證

每一個依賴屬性都提供了更改通知、值強制和驗證的回調。這些回調能夠經過FrameworkPropertyMetadata來實現。

如:

new FrameworkPropertyMetadata( DateTime.Now, OnCurrentTimePropertyChanged, OnCoerceCurrentTimeProperty ),OnValidateCurrentTimeProperty );

1.2.1 值更改回調

值更改回調是一個靜態方法,每當該屬性值更改時,都會調用這個回調方法。

private static void OnCurrentTimePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
    MyClockControl control = source as MyClockControl;
    DateTime time = (DateTime)e.NewValue;
}

1.2.2 屬性值強制回調驗證

在執行屬性驗證回調前先執行該方法,嘗試糾正不合法的屬性值。

private static object OnCoerceTimeProperty( DependencyObject sender, object data )
{
    if ((DateTime)data > DateTime.Now )
    {
        data = DateTime.Now;
    }
    return data;
}

1.2.3 屬性驗證回調

但依賴項屬性的值發生變化時調用該回調方法。該回調方法驗證屬性值是否合法,若是不合法,返回false,拋出異常。

private static bool OnValidateTimeProperty(object data)
{
    return data is DateTime;
}

1.3 添加屬性包裝器

依賴項屬性的包裝器,是經過DependencyObject的GetValue()和SetValue()方法來包裝。

public DateTime CurrentTime
{
    get { return (DateTime)GetValue(CurrentTimeProperty); }
    set { SetValue(CurrentTimeProperty, value); }
}

完整的代碼以下:

// Dependency Property
public static readonly DependencyProperty CurrentTimeProperty = 
     DependencyProperty.Register( "CurrentTime", typeof(DateTime),
     typeof(MyClockControl), new FrameworkPropertyMetadata(DateTime.Now));
 
// .NET Property wrapper
public DateTime CurrentTime
{
    get { return (DateTime)GetValue(CurrentTimeProperty); }
    set { SetValue(CurrentTimeProperty, value); }
}

static bool validateValueCallback(object data)
{
    //自定義驗證
    return true;
}

自定義一個依賴項屬性,須要挺多的套路,固然這些套路不須要你一個一個代碼來敲,在Visual Studio能夠輸入propdp再點擊兩次tab鍵,就能夠建立依賴項屬性模板。 只需修改模板的屬性名稱和類型便可。

模板代碼以下:

public int MyProperty
{
    get { return (int)GetValue(MyPropertyProperty); }
    set { SetValue(MyPropertyProperty, value); }
}

// Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
    DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));

2. 附加屬性

附加屬性也是一種依賴項屬性。與其餘依賴項屬性不一樣的時,其餘的依賴項屬性是應用到被註冊的類上,而附加屬性則是應用到其餘的類上。

好比:Canvas的類的TopLeft等爲附加屬性,其餘的元素並無該屬性,只有Canvas類有,在使用Canvas.Top時,若是爲每一個元素都定義一個這樣的依賴項屬性,那麼就會大量的重複性代碼,且不可維護更改。若是隻在Canvas類型上定義這個依賴項屬性,其餘的元素只繼承使用該屬性就好。附加屬性就是這樣。

附加屬性的定義和依賴項屬性的定義幾乎同樣。惟一不一樣的是註冊是經過調用DependencyProperty.GegisterAttached()方法來實現,且屬性包裝器爲靜態方法。

如:

public static readonly DependencyProperty TopProperty =
    DependencyProperty.RegisterAttached("Top", 
    typeof(double), typeof(Canvas),
    new FrameworkPropertyMetadata(0d,
        FrameworkPropertyMetadataOptions.Inherits));
 
public static void SetTop(UIElement element, double value)
{
    element.SetValue(TopProperty, value);
}
 
public static double GetTop(UIElement element)
{
    return (double)element.GetValue(TopProperty);
}

至此就能夠像使用普通的CLR屬性同樣使用WPF的依賴項屬性。


若有不對,請多多指教!


參考列表:

相關文章
相關標籤/搜索