程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> ASP編程 >> ASP技巧 >> wpf控件開發基礎(3) -屬性系統(2)

wpf控件開發基礎(3) -屬性系統(2)

編輯:ASP技巧

屬性默認值,可以保證屬性的有效性.
屬性驗證有效性,可以對輸入的屬性進行校驗
屬性強制回調, 即不管屬性有無發生變化,都要做出通知.
屬性變更通知,當屬性發生變化可以通知程序作出一系列的處理.
這裡還沒WPF什麼事,我們來看看依賴屬性是如何解決以上問題的.

內容概要
定義第一個最簡單的依賴屬性
依賴屬性值基本操作
屬性包裝器
屬性元數據(PRopertyMetadata)
屬性元數據基本行為
MSDN的原話雖然生硬,但准確定性毋庸置疑.當理解後再來看別有一番體會.

一.定義第一個最簡單的依賴屬性

MSDN原話:Windows Presentation Foundation (WPF) 提供了一組服務,這些服務可用於擴展公共語言運行時 (CLR) 屬性的功能。這些服務通常統稱為 WPF 屬性系統。由 WPF 屬性系統支持的屬性稱為依賴項屬性。
我們來定義一個Age依賴屬性,如下代碼

public class DPCustomPeople
{
    public static readonly DependencyProperty AgeProperty =
        DependencyProperty.Register("Age", typeof(int), typeof(DPCustomPeople));

    public void DisplayAgeProperty()
    {
        Console.WriteLine("DPName:" + DPCustomPeople.AgeProperty.Name);
        Console.WriteLine("DPPropertyType:" + DPCustomPeople.AgeProperty.PropertyType);
        Console.WriteLine("DPOwnerType:" + DPCustomPeople.AgeProperty.OwnerType);
    }
}
然後調用輸出結果

class Program
{
    static void Main(string[] args)
    {
        DPCustomPeople people = new DPCustomPeople();
        people.DisplayAgeProperty();
    }
}
你可能對DependencyProperty類比較陌生,DependencyProperty類提供了依賴屬性的一些基本特征

注冊依賴屬性的方法是調用DependencyProperty的靜態Register方法,其提供了多個重載方法,但以下三個步驟是必須的.注冊完畢是其是一個靜態屬性

提供注冊的名字(Name)"Age"
注冊屬性類型(PropertyType)typeof(int)
注冊該依賴屬性的所有者類型(OwnerType)typeof(DPCustomPeople)
注意:屬性名字,屬性類型,屬性所有者類型一經注冊將無法更改

以下為輸出結果

 

二.依賴屬性值基本操作(取值與賦值)

定義了Age依賴屬性以後,那麼我們理應可以對屬性進行取值,賦值操作.DependencyProperty本身並不提供這些操作,而是由DependencyObject來負責

DependencyObject 表示一個參與依賴項屬性系統的對象.

所以要求定義的類要繼承自DependencyObject,那麼改寫DPCustomPeople

public class DPCustomPeople:System.Windows.DependencyObject
{
}
基本的取值賦值操作GetValue和SetValue方法

public void DPPropertyBasicOperator()
{
    Console.WriteLine("Age:" + this.GetValue(DPCustomPeople.AgeProperty));
    this.SetValue(DPCustomPeople.AgeProperty, 24);
    Console.WriteLine("ChangedAge:" + this.GetValue(DPCustomPeople.AgeProperty));
}
輸出結果

 

三.屬性包裝器
用GetValue和SetValue方法對值操作不大美觀,所以我們可以對其包裝一下,定義Age屬性

public int Age
{
    get { return (int)GetValue(AgeProperty); }
    set { SetValue(AgeProperty, value); }
}
注意:依賴屬性包裝命名規是把後面的Property去掉

public void DPPropertyBasicOperatorUsingProperty()
{
    Console.WriteLine("Age:" + this.Age);
    this.Age=24;
    Console.WriteLine("ChangedAge:" + this.Age);
}

以上的代碼看起來是不是更加的簡潔呢

四.屬性元數據(PropertyMetadata)
MSDN原話:Windows Presentation Foundation (WPF) 屬性系統包括一個元數據報告系統,該系統不局限於可以通過反射或常規公共語言運行時 (CLR) 特征報告的關於某個屬性的內容。

說到屬性元數據,第一個讓人想到的就是.Net的Attribute

public class Person
{
    [DefaultValue(200),Category("Layout")]
    public int Width { get; set; }
}
Attribute需要借助Visual Studio的力量,使得IDE對Attribute進行很友好的支持,或者依靠反射來賦值.

但離開這些技術的,通過正常途徑,new出一個新的實例,加了Attribute的屬性毫無效果.我們不能依賴這些Attribute來保證屬性的一些基本特性(如默認值).依賴屬性的屬性元數據與上述描述的元數據不同.

依賴項屬性的元數據

可以由定義依賴項屬性的類來唯一地指定
可以在依賴項屬性添加到另一個類時進行更改
可以由所有從定義基類繼承依賴項屬性的派生類來明確地重寫
以上語言很生硬,但卻說明了意圖.但我們總無法第一時間領會設計者的想法.暫且先知道有這個概念的存在

五.屬性元數據基本行為
屬性元數據基本行為為依賴屬性提供了3個功能,這也是本文剛提出來的問題.

默認屬性
屬性通知
屬性強制回調
先來看一看一個完整PropertyMetadata的構造函數,如果沒有為依賴屬性設置默認的PropertyMetadata的話,內部會為依賴屬性自動創建一個PropertyMetadata對象.

public PropertyMetadata(object defaultValue, PropertyChangedCallback propertyChangedCallback, CoerceValueCallback coerceValueCallback)
依賴屬性借用屬性元數據的概念來完成屬性默認值,屬性通知,強制回調等行為

1.屬性默認值

public static readonly DependencyProperty NameProperty =
    DependencyProperty.Register("Name", typeof(string), typeof(DPCustomPeople),
    new PropertyMetadata(string.Empty));
大部分人看到此處都會產生一個疑問,設置一個默認值為什麼要用PropertyMetadata,為什麼不直接在Register方法中直接注冊呢?如下代碼

public static readonly DependencyProperty NameProperty =
    DependencyProperty.Register("Name", typeof(string), typeof(DPCustomPeople),
    string.Empty);
當然這個疑問一直伴隨著我很久,解不開也是沒辦法,先放著.

注意點:當在PropertyMetadata給屬性賦默認值時,是無法檢測類型正確性的

如這樣的定義,因為vs中的dp代碼段默認值是0,這是值得注意的地方

public static readonly DependencyProperty NameProperty =
    DependencyProperty.Register("Name", typeof(string), typeof(DPCustomPeople),
    new UipropertyMetadata(0));
2.屬性默認值恢復操作

當屬性賦值以後可以通過DependencyObject的ClearValue方法恢復默認值,如下代碼

public string Name
{
    get { return (string)GetValue(NameProperty); }
    set { SetValue(NameProperty, value); }
}

public static readonly DependencyProperty NameProperty =
    DependencyProperty.Register("Name", typeof(string), typeof(DPCustomPeople),
    new UIPropertyMetadata(string.Empty));

public void DPPropertyClearOperator()
{
    Console.WriteLine("Name:" + this.Name);
    this.Name="Terry";
    Console.WriteLine("ChangedName:" + this.Name);
    this.ClearValue(NameProperty);
    Console.WriteLine("Name:" + this.Name);
}
輸出結果

 

注意點:區分默認賦值與默認值

默認賦值一般在構造函數中進行,但這卻不是默認值(在依賴屬性出現之前這的確是),特別是在派生類重寫屬性的時候

public class Student : DPCustomPeople
{
    public Student()
    {
        this.Name = "Sky";
    }

    public void TestSubDefaultDpValue()
    {
        Console.WriteLine("Clear Before:"+this.Name);
        this.ClearValue(Student.NameProperty);
        Console.WriteLine("Clear After:" + this.Name);
    }
}
輸出結果

 

3.屬性變更通知

這項功能是最常用的.當屬性值發生變化時,會觸發PropertyChangedCallback回調

public bool IsBoy
{
    get { return (bool)GetValue(IsBoyProperty); }
    set { SetValue(IsBoyProperty, value); }
}

public static readonly DependencyProperty IsBoyProperty =
    DependencyProperty.Register("IsBoy", typeof(bool), typeof(Student),
    new UIPropertyMetadata(false,new PropertyChangedCallback(IsBoyPropertyChangedCallback)));

public static void IsBoyPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    Student st = d as Student;
    if (st.IsBoy)
    {
        Console.WriteLine("Hello,Boy");
    }
    else
    {
        Console.WriteLine("Hello,Girl");
    }
}

public void TestPropertyChangedCallback()
{
    this.IsBoy = false;
    this.IsBoy = true;    this.IsBoy = true;
}
可以通過DependencyPropertyChangedEventArgs來查看舊值和新值
輸出結果

 

注意點:

(1).通過上面的輸出結果,你是否已經看出依賴屬性的默認值是不會觸發屬性變更通知的

(2).手動觸發屬性變更通知

如果你希望默認值也能觸發一次屬性變更(其實有時候真的需要),你就不等不手動進行觸發了

private void RaiseIsBoyPropertyChangedCallback()
{
    IsBoyPropertyChangedCallback(this,new DependencyPropertyChangedEventArgs
        (Student.IsBoyProperty, Student.IsBoyProperty.DefaultMetadata.DefaultValue, null));
}
(3).當有屬性變更通知時,一定要保證屬性默認值類型的正確性

我們知道值類型都有是默認值的,引用類型則沒有(即可以賦值為null),一個類型是否有默認類型可以用default關鍵字查看.如下圖

 

我們將上面定義的依賴屬性默認值改寫null,在沒有PropertyChangedCallback的時候可以很好的運行,但在有屬性變更通知的時候災難發生了,程序將出現異常,說類型不匹配.

public static readonly DependencyProperty IsBoyProperty =
    DependencyProperty.Register("IsBoy", typeof(bool), typeof(Student),
    new UIPropertyMetadata(null,new PropertyChangedCallback(IsBoyPropertyChangedCallback)));
再來看看引用類型,默認值為null則相安無事

public IList LovedGirl
{
    get { return (IList)GetValue(LovedGirlProperty); }
    set { SetValue(LovedGirlProperty, value); }
}

public static readonly DependencyProperty LovedGirlProperty =
    DependencyProperty.Register("LovedGirl", typeof(IList), typeof(Student),
    new UIPropertyMetadata(null, new PropertyChangedCallback(LovedGirlChangedCallback)));

public static void LovedGirlChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    Student st = d as Student;
    foreach (var item in e.NewValue as IList)
    {
        Console.WriteLine(item);
    }
}

public void TestReferenceDpType()
{
    List<string> list = new List<string>();
    list.Add("girl 1");
    list.Add("girl 2");
    this.LovedGirl = list;
}
4.強制屬性回調

首先默認值還是不會觸發回調方法.

強制回調方法即不管屬性值有無發生變化,都會進入回調方法

public int Score
{
    get { return (int)GetValue(ScoreProperty); }
    set { SetValue(ScoreProperty, value); }
}

public static readonly DependencyProperty ScoreProperty =
    DependencyProperty.Register("Score", typeof(int), typeof(Student),
    new UIPropertyMetadata(0,null,new CoerceValueCallback(ScoreCoerceValueCallback)));

public static object ScoreCoerceValueCallback(DependencyObject d, object baseValue)
{
    Console.WriteLine(baseValue);
    return baseValue;
}

public void TestCoerceValueCallback()
{
    this.Score = 0;
    this.Score = 0;
    this.Score = 0;
}
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved