public class CustomClass
public static readonly DependencyProperty CustomProperty = DependencyProperty.RegisterAttached(
"Custom",
typeof(double),
typeof(CustomClass),
new PropertyMetadata());
[AttachedPropertyBrowsableForType(typeof(Control))]
public static double GetCustom(DependencyObject obj)
=> (double)obj.GetValue(CustomProperty);
public static void SetCustom(DependencyObject obj, double value)
=> obj.SetValue(CustomProperty, value);
可以看到與相依屬性非常像,附加屬性透過DependencyProperty.RegisterAttached方法來註冊,傳入的參數也一模一樣:
-
“Custom":附加屬性的名稱
-
typeof(double):附加屬性的類型
-
typeof(CustomAttachedProperty):擁有附加屬性的類別
-
new PropertyMetadata():附加屬性的其它設定,與
與相依屬性最大的不同在於:相依屬性必須要定義在擁有它的類別中,而附加屬性由於是要附加到其他控件上的,所以要額外定義一個類別來裝載附加屬性,就像上面我們用CustomClass來裝載Custom這個附加屬性。
RegisterAttached註冊完附加屬性後,同樣會回傳一個DependencyProperty屬性,這個屬性同樣有
命名規範
,同樣是
屬性名稱+Property
。
與相依屬性不同的是:由於附加屬性不是定義在控件類別中,因此若要對附加屬性取值或賦值,就要透過靜態方法GetCustom, SetCustom來操作。而這兩個靜態方法同樣有
命名規範
,必須是
Get+屬性名稱 / Set + 數性名稱
。這些名稱都必須完全相符,在XAML中才能正確執行附加屬性。
此外在GetCustom上面有一個AttachedPropertyBrowsableForType的修飾詞,可以用來指定甚麼類型的控件才可以使用這個附加屬性。
附加屬性實戰演練
雖然現在已經了解附加屬性要怎麼定義了,但是附加屬性實際上要怎麼使用呢?接下來讓我們實作一個附加屬性來看看吧。假如我們現在想要判斷TextBox中使用者有沒有輸入文字,然而預設的TextBox是沒有HasText這個屬性讓我們判斷的,這個時候就是使用附加屬性的好時機,我們可以為TextBox實作一個HasText的附加屬性來幫助我們判斷。
1. 實作TextBoxServices類別
首先,我們要先建立一個類別來定義HasText附加屬性,這裡我們把當稱之為
HasText Services。
namespace WpfAttachedProperty
public class HasTextServices
public class HasTextServices
public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached(
"IsEnabled",
typeof(bool),
typeof(HasTextServices),
new PropertyMetadata(default(bool)));
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static bool GetIsEnabled(DependencyObject obj)
=> (bool)obj.GetValue(IsEnabledProperty);
public static void SetIsEnabled(DependencyObject obj, bool value)
=> obj.SetValue(IsEnabledProperty, value);
public static readonly DependencyProperty HasTextProperty = DependencyProperty.RegisterAttached(
"HasText",
typeof(bool),
typeof(HasTextServices),
new PropertyMetadata(default(bool)));
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static bool GetHasText(DependencyObject obj)
=> (bool)obj.GetValue(HasTextProperty);
public static void SetHasText(DependencyObject obj, bool value)
=> obj.SetValue(HasTextProperty, value);
3. 定義OnIsEnabledChanged
定義好附加屬性後,接下來就要讓附加屬性可以正常運作,實現我們想要的功能。實現的方法通常是透過附加屬性改變的回呼(OnPropertyChanged)來達成,在這個回呼裡面註冊事件,由事件驅動的方式來執行我們想要的命令。
具體來說,當某個TextBox控件的IsEnabled附加屬性被使用者設定成true時,我們就要註冊這個TextBox的TextChanged事件。
public class HasTextServices
public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached(
"IsEnabled",
typeof(bool),
typeof(HasTextServices),
new PropertyMetadata(default(bool), OnIsEnabledChanged));
private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
var tb = d as TextBox;
if ((bool)e.NewValue)
tb.TextChanged += Tb_TextChanged;
tb.TextChanged -= Tb_TextChanged;
private static void Tb_TextChanged(object sender, TextChangedEventArgs e)
在上面的代碼中,我們透過PropertyMetadata定義IsEnabledProperty的屬性改變回呼OnIsEnabledChanged。裡面先把傳入的相依物件d轉型成TextBox(因為我們確定這個附加屬性一定只有TextBox才可以使用),在透過事件參數e.NewValue判斷IsEnabled屬性是否為true,若是,則註冊Tb_TextChanged方法給TextBox.TextChanged事件;若否,則取消註冊。
4. 定義Tb_TextChanged
接下來每當TextChanged時,我們就要判斷現在TextBox.Text是否為空,並把結果設定給HasText附加屬性。注意到要設定控件的附加屬性必須要透過當初定義的Set方法來設定。
private static void Tb_TextChanged(object sender, TextChangedEventArgs e)
var tb = sender as TextBox;
SetHasText(tb, !string.IsNullOrEmpty(tb.Text));
這樣就大功告成啦!我們可以用下面的代碼來測試一下:
<!--MainWindow.cs-->
<Window x:Class="WpfDataBinding.MainWindow"
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ap="clr-namespace:WpfAttachedProperty"
Height="300"
Width="300">
<StackPanel>
<TextBox Name="textBox"
ap:HasTextServices.IsEnabled="True" />
<Label Content="{Binding ElementName=textBox, Path=(ap:HasTextServices.HasText)}" />
</StackPanel>
</Window>
注意到我們把附加屬性定義在WpfAttachedProperty這個命名空間,因此需要透過xmlns把命名空間引用進來,並稱作ap。之後在設定TextBox.(ap:HasTextServices.IsEnabled)附加屬性為true,並讓Label控件的內容綁定到TextBox的HasText附加屬性,這樣就可以將HasText即時顯示出來。特別注意到綁定的Path,如果要綁定到附加屬性,必須使用
()
把附加屬性包起來。
執行之後,試著在TextBox中輸入一些文字,然後觀察一下Label的改變吧!
附加屬性是WPF提供的非常強大的功能,它的妙用遠遠不只本文中示範的如此而已。熟悉使用附加屬性可以大量簡化XAML與C#的代碼,並且由於附加屬性隨貼隨用的特性,寫出來的代碼泛用性高,可以大幅度的提高代碼的質量。趕快趁現在好好練習自己寫出一個好用的附加屬性吧。
近期迴響