如何自訂WPF的DependencyProperty相依屬性
撰文:吳俊毅
時間:2010/02/16
在WPF當中若沒有相依屬性幾乎無法做任何事情,因為您可能要重複定義許多共同的物件屬性,這不僅增加Pg許多無意義的工作且程式設計較沒有彈性。要實做WPF的相依屬性我們得在一個class中先宣告一個DependencyProperty物件,再定義一個屬性,這個屬性做為屬性變更告知之用,而透過DependencyProperty的連結,比較重要的是在相依屬性裡並不是使用我們在.NET應用程式裡慣用的get;set;的方式,而是使用在DependencyObject物件裡定義的SetValue()與GetValue()方法,如下程式:
class ColorTextBlock: TextBlock
{
public static readonly DependencyProperty BackgroundColorProperty;
public bool ChangedBackgroundColor
{
get
{
return (bool)GetValue(BackgroundColorProperty);
}
set { SetValue(BackgroundColorProperty, value); }
}
}
筆者希望透過ChangedBackgroundColor屬性被變更為true時,將自身物件的Background改為Blue,因此必須註冊BackgroundColorProperty這個DependencyProperty,以便告訴WPF的子系統有這個相依的事件必須在這個屬性遭到變更時觸發!怎麼做呢?首先我們必須在constructor裡放置FrameworkPropertyMetadata物件存放與DependencyProperty相關的屬性資料並設定其DefaultValue屬性與PropertyChangedCallback的事件處理常式,最後註冊相依屬性ChangedBackgroundColor,程式碼如下:
static ColorTextBlock()
{
FrameworkPropertyMetadata meta = new FrameworkPropertyMetadata();
meta.DefaultValue = false;
meta.Inherits = true;
meta.PropertyChangedCallback += OnBackgroundColorPropertyChanged;
BackgroundColorProperty = DependencyProperty.Register("ChangedBackgroundColor", typeof(bool), typeof(ColorTextBlock), meta);
}
如程式,要註冊DependencyProperty只要執行DependencyProperty.Register方法,傳入(引數1
:相依屬性名稱,引數2:為觸發該相依屬性的屬性type,引數3:本身class的型別,引數4:PropertyMetadata物件)。OnBackgroundColorPropertyChanged的事件的實作如下:
static void OnBackgroundColorPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
ColorTextBlock ctb = obj as ColorTextBlock;
ctb.Background = Brushes.Blue;
}
這裡所傳入的DependencyObject即是ColorTextBlock物件,而這個事件只有當屬性ChangedBackgroundColor被變更時會觸發,所以筆者只是將背景設為Blue而已,要測試此程式我們可以在Grid_Load裡加入如下:
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
ColorTextBlock ctb = new ColorTextBlock();
ctb.Text = "abcdefghijk";
ctb.HorizontalAlignment = HorizontalAlignment.Left;
ctb.VerticalAlignment = VerticalAlignment.Top;
ctb.ChangedBackgroundColor = true;
Grid1.Children.Add(ctb);
}
程式的行結果如下:
若將ctb.ChangedBackgroundColor = true 這行拿掉底色就不會變成藍色,這點讀者可以自行測試看看。
而將ctb.ChangedBackgroundColor設定為false底色也不會改變這是因為我們對這個DependencyProperty的DefaultValue屬性給的是false,所以您再塞false進去當然對於系統而言屬性不算是被變更!所以不會觸發OnBackgroundColorPropertyChanged 這個事件。
時間:2010/02/16
在WPF當中若沒有相依屬性幾乎無法做任何事情,因為您可能要重複定義許多共同的物件屬性,這不僅增加Pg許多無意義的工作且程式設計較沒有彈性。要實做WPF的相依屬性我們得在一個class中先宣告一個DependencyProperty物件,再定義一個屬性,這個屬性做為屬性變更告知之用,而透過DependencyProperty的連結,比較重要的是在相依屬性裡並不是使用我們在.NET應用程式裡慣用的get;set;的方式,而是使用在DependencyObject物件裡定義的SetValue()與GetValue()方法,如下程式:
class ColorTextBlock: TextBlock
{
public static readonly DependencyProperty BackgroundColorProperty;
public bool ChangedBackgroundColor
{
get
{
return (bool)GetValue(BackgroundColorProperty);
}
set { SetValue(BackgroundColorProperty, value); }
}
}
筆者希望透過ChangedBackgroundColor屬性被變更為true時,將自身物件的Background改為Blue,因此必須註冊BackgroundColorProperty這個DependencyProperty,以便告訴WPF的子系統有這個相依的事件必須在這個屬性遭到變更時觸發!怎麼做呢?首先我們必須在constructor裡放置FrameworkPropertyMetadata物件存放與DependencyProperty相關的屬性資料並設定其DefaultValue屬性與PropertyChangedCallback的事件處理常式,最後註冊相依屬性ChangedBackgroundColor,程式碼如下:
static ColorTextBlock()
{
FrameworkPropertyMetadata meta = new FrameworkPropertyMetadata();
meta.DefaultValue = false;
meta.Inherits = true;
meta.PropertyChangedCallback += OnBackgroundColorPropertyChanged;
BackgroundColorProperty = DependencyProperty.Register("ChangedBackgroundColor", typeof(bool), typeof(ColorTextBlock), meta);
}
如程式,要註冊DependencyProperty只要執行DependencyProperty.Register方法,傳入(引數1
:相依屬性名稱,引數2:為觸發該相依屬性的屬性type,引數3:本身class的型別,引數4:PropertyMetadata物件)。OnBackgroundColorPropertyChanged的事件的實作如下:
static void OnBackgroundColorPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
ColorTextBlock ctb = obj as ColorTextBlock;
ctb.Background = Brushes.Blue;
}
這裡所傳入的DependencyObject即是ColorTextBlock物件,而這個事件只有當屬性ChangedBackgroundColor被變更時會觸發,所以筆者只是將背景設為Blue而已,要測試此程式我們可以在Grid_Load裡加入如下:
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
ColorTextBlock ctb = new ColorTextBlock();
ctb.Text = "abcdefghijk";
ctb.HorizontalAlignment = HorizontalAlignment.Left;
ctb.VerticalAlignment = VerticalAlignment.Top;
ctb.ChangedBackgroundColor = true;
Grid1.Children.Add(ctb);
}
程式的行結果如下:
若將ctb.ChangedBackgroundColor = true 這行拿掉底色就不會變成藍色,這點讀者可以自行測試看看。
而將ctb.ChangedBackgroundColor設定為false底色也不會改變這是因為我們對這個DependencyProperty的DefaultValue屬性給的是false,所以您再塞false進去當然對於系統而言屬性不算是被變更!所以不會觸發OnBackgroundColorPropertyChanged 這個事件。
自訂特別的控制項,這樣的方法蠻實用的,只是很不解的是只要繼承任何控制項,原本在Xmal可以參考到的事件,全部都無法正常參考
回覆刪除例如這篇文章自訂的ColorTextBlock控制項,在一個視窗加入兩個控制項,一個是自訂的,一個是原本的
在code behind的地方,會發現ColorTextBlock_IsEnabledChanged的參考是0,TextBlock_IsEnabledChanged是1,不過程式可以正常運作,事件也可以正常引發
平常這樣就算了,在Xmal分別把程式碼改成這樣,但是code behind不改
只要一編譯,TextBlock_IsEnabledChanged_Error這個會報錯誤,ColorTextBlock_IsEnabledChanged_Error會過關
如果只修改TextBlock_IsEnabledChanged_Error的錯誤,一執行,引發ColorTextBlock_IsEnabledChanged_Error事件就會錯誤
<local:ColorTextBlock IsEnabledChanged="ColorTextBlock_IsEnabledChanged"/>
刪除<TextBlock IsEnabledChanged="TextBlock_IsEnabledChanged"/>