附加屬性概觀
更新:2007 年 11 月
附加屬性是可延伸標記語言 (XAML) 所定義的概念。附加屬性主要是當做一種全域屬性使用,可在任何物件上設定。在 Windows Presentation Foundation (WPF) 中,附加屬性通常會定義為特殊形式的相依性屬性,這種屬性沒有傳統的屬性「包裝函式」。
這個主題包含下列章節。
- 必要條件
- 為什麼要使用附加屬性
- XAML 中的附加屬性
- 擁有者型別使用附加屬性的方式
- 程式碼中的附加屬性
- 附加屬性中繼資料
- 自訂附加屬性
- 進一步了解附加屬性
- 相關主題
必要條件
本主題假設您已了解相依性屬性,知道如何使用 Windows Presentation Foundation (WPF) 類別上的現有相依性屬性,而且也閱讀過相依性屬性概觀。為了能夠跟隨本主題中的範例,您也應該了解可延伸標記語言 (XAML),並知道如何撰寫 WPF 應用程式。
為什麼要使用附加屬性
附加屬性的其中一個用途,就是針對父項目中定義的屬性,讓不同的子項目能夠指定該屬性的唯一值。一種特定的應用方式,就是讓子項目告知父項目其本身在使用者介面 (UI) 中的呈現方式。其中一個例子就是 DockPanel.Dock 屬性。DockPanel.Dock 屬性會建立做為附加屬性,因為它是設計在 DockPanel 所包含的項目上設定,而不是在 DockPanel 本身上設定。DockPanel 類別會定義名為 DockProperty 的靜態 DependencyProperty 欄位,然後提供 GetDock 和 SetDock 方法做為附加屬性的公用存取子。
XAML 中的附加屬性
在 XAML 中,附加屬性是使用語法 AttachedPropertyProvider.PropertyName 設定的。
下列範例顯示如何在 XAML 中設定 DockPanel.Dock:
<DockPanel>
<CheckBox DockPanel.Dock="Top">Hello</CheckBox>
</DockPanel>
請注意,其用法有點類似於靜態屬性,但參考的是擁有和註冊附加屬性的 DockPanel 型別,而不是參考由名稱指定的任何執行個體。
此外,由於 XAML 中的附加屬性 (Property) 是在標記中設定的屬性 (Attribute),因此只有設定作業才具關聯性。您無法直接取得 XAML 中的屬性 (Property),不過,您可以利用一些間接機制來比較值,例如樣式中的觸發程序 (如需詳細資訊,請參閱設定樣式和範本)。
WPF 中的附加屬性實作
在 Windows Presentation Foundation (WPF) 中,大多數存在於 WPF 型別上的附加屬性都會實作為相依性屬性。附加屬性是 XAML 概念,而相依性屬性是 WPF 概念。由於 WPF 附加屬性是相依性屬性,因此支援相依性屬性的概念,例如屬性中繼資料以及來自於屬性中繼資料的預設值。
擁有者型別使用附加屬性的方式
雖然附加屬性可在任何物件上設定,但這並不表示設定了屬性就會產生明確的結果,也不表示會有另一個物件使用該值。一般來說,使用附加屬性的目的,是要供來自各種不同的類別階層架構或邏輯關聯性的物件,都能將通用資訊報告給擁有者型別。定義附加屬性的型別通常遵循下列其中一種模型:
定義附加屬性的型別會設計成父項目,做為會設定附加屬性值之項目的父項目。此型別接著會透過內部邏輯逐一查看子項目、取得值,然後針對這些值以某種方式運作。
定義附加屬性的型別會當做各種可能的父項目和內容模型的子項目使用。
定義附加屬性的型別代表服務。其他型別會設定附加屬性的值。接著,當設定屬性的項目在服務的內容中被評估時,服務類別的內部邏輯會取得附加屬性值。
父項目定義的附加屬性範例
WPF 定義附加屬性最常見的典型案例是當父項目支援子項目集合並且會實作行為,其中每個子項目都會個別報告該行為的特性。
DockPanel 會定義 DockPanel.Dock 附加屬性,而且 DockPanel 有類別層級程式碼做為其轉譯邏輯的一部分 (具體來說,就是 MeasureOverride 和 ArrangeOverride)。DockPanel 執行個體一定會檢查它是否有任何直接子項目設定了 DockPanel.Dock 的值。若有,這些值就會成為套用到該特定子項目之轉譯邏輯的輸入。巢狀 DockPanel 執行個體會處理自己目前的子項目集合,不過該行為是實作特定的。理論上,附加屬性可以影響目前父項目以外的項目。如果在項目上設定 DockPanel.Dock 附加屬性,而該項目沒有對其執行動作的 DockPanel 父項目,則不會引發錯誤或例外狀況。這只是表示設定了全域屬性值,但目前沒有 DockPanel 父項目可使用該資訊。
程式碼中的附加屬性
WPF 中的附加屬性沒有典型的 CLR「包裝函式」方法可提供方便的取得/設定存取。這是因為附加屬性不一定是已設定屬性之執行個體的 CLR 命名空間的一部分。不過,當 XAML 進行處理時,XAML 讀取器必須能夠設定這些值。若要為有效的附加屬性,附加屬性的擁有者型別必須以 GetPropertyName 和 SetPropertyName 的形式實作專屬的存取子方法。當在程式碼中取得或設定附加屬性時,也必須使用這些專屬的存取子方法。從程式碼的角度來說,附加屬性類似於有方法存取子而非屬性存取子的支援欄位,而支援欄位可以存在於任何物件上,而無須特別定義。
下列範例顯示如何在程式碼中設定附加屬性。在這個範例中,myCheckBox 是 CheckBox 類別的執行個體。
DockPanel myDockPanel = new DockPanel();
CheckBox myCheckBox = new CheckBox();
myCheckBox.Content = "Hello";
myDockPanel.Children.Add(myCheckBox);
DockPanel.SetDock(myCheckBox, Dock.Top);
如同 XAML 的情況,如果第三行程式碼沒有把 myCheckBox 加入為 myDockPanel 的子項目,第四行程式碼就不會引發例外狀況,但屬性值不會與 DockPanel 父項目互動,因此將不會有任何作用。只有在子項目上有設定 DockPanel.Dock 值再加上有 DockPanel 父項目存在,才會在轉譯的應用程式中產生有效的行為。
附加屬性中繼資料
註冊屬性時,會設定 FrameworkPropertyMetadata 來指定屬性的特性,例如屬性是否會影響轉譯、測量等。附加屬性的中繼資料與相依性屬性的中繼資料一般並無不同。如果您覆寫附加屬性中繼資料時指定預設值,該值會成為覆寫類別之執行個體上的隱含附加屬性預設值。具體來說,如果某個處理序透過附加屬性的 Get 方法存取子查詢該屬性的值,就會回報預設值,其中指出您所指定之中繼資料的類別執行個體,以及未設定之附加屬性的值。
如果您要在屬性上啟用屬性值繼承,請使用附加屬性,而不要使用非附加的相依性屬性。如需詳細資訊,請參閱屬性值繼承。
自訂附加屬性
建立附加屬性的時機
當類別要以定義類別以外的方式來設定屬性時,就可以建立附加屬性。最常見的情形就是配置。現有配置屬性的範例包括 DockPanel.Dock、Panel.ZIndex 和 Canvas.Top。上述情形是指用來控制配置之項目的子項目,能夠各自將配置需求表達給其配置父項目,針對父項目定義為附加屬性的屬性,各子項目也會設定屬性值。
另一個使用附加屬性的情形是當類別代表服務,而您要其他類別能夠與該服務更緊密整合時。
另外還有一個情形是要獲得 Visual Studio 2008 WPF 設計工具 支援,例如編輯 [屬性] 視窗。如需詳細資訊,請參閱控制項撰寫概觀。
如前所述,若要使用屬性值繼承機制,請註冊成附加屬性。
如何建立附加屬性
如果類別定義附加屬性僅供在其他型別上使用,那麼該類別就無須從 DependencyObject 衍生。不過,如果您依循完整 WPF 模型,要附加屬性也當做相依性屬性使用,那麼就需要從 DependencyObject 衍生。
請宣告型別 DependencyProperty 的 publicstaticreadonly 欄位,以將附加屬性定義為相依性屬性。您可以使用 RegisterAttached 方法的傳回值來定義此欄位。欄位名稱必須與附加屬性名稱相符,後面附加字串 Property,以遵循 WPF 辨識欄位與其所代表屬性的命名模式。附加屬性提供者也必須提供靜態 GetPropertyName 和 SetPropertyName 方法做為存取子,以存取附加屬性;如果沒有提供,屬性系統將無法使用該附加屬性。
Get 存取子
GetPropertyName 存取子的簽章必須是:
public static object GetPropertyName(object target)
target 物件可指定為實作中的特定型別。例如,DockPanel.GetDock 方法會將參數的型別設為 UIElement,因為附加屬性只能在 UIElement 執行個體上設定。
Set 存取子
SetPropertyName 存取子的簽章必須是:
public static void SetPropertyName(object target, object value)
target 物件可指定為實作中的特定型別。例如,SetDock 方法會將它的型別設為 UIElement,因為附加屬性只能在 UIElement 執行個體上設定。
value 物件可指定為實作中的特定型別。例如,SetDock 方法會將該值的型別設為 Dock,因為它只能設為該列舉型別。請注意,此方法的值是當 XAML 載入器在標記中遇到做為附加屬性 (Property) 使用的附加屬性 (Property) 時,所產生的輸入。此輸入是在標記中指定為 XAML 屬性值 (Attribute) 的值。因此,您所使用的型別必須有型別轉換、值序列化程式或標記延伸支援,以從屬性 (Attribute) 值 (其最終只是字串) 建立適當的型別。
下列範例顯示相依性屬性註冊作業 (使用 RegisterAttached 方法),以及 GetPropertyName 和 SetPropertyName 存取子。在這個範例中,附加屬性名稱為 IsBubbleSource。因此,存取子必須命名為 GetIsBubbleSource 和 SetIsBubbleSource。
public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached(
"IsBubbleSource",
typeof(Boolean),
typeof(AquariumObject),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender)
);
public static void SetIsBubbleSource(UIElement element, Boolean value)
{
element.SetValue(IsBubbleSourceProperty, value);
}
public static Boolean GetIsBubbleSource(UIElement element)
{
return (Boolean)element.GetValue(IsBubbleSourceProperty);
}
附加屬性 (Property) 的屬性 (Attribute)
WPF 定義了數個 .NET Framework 屬性 (Attribute),目的是要將附加屬性的相關資訊提供給反映 (Reflection) 處理序,以及反映和屬性資訊的一般使用者 (例如設計人員)。由於附加屬性的型別沒有範圍限制,因此設計人員需要方法來避免使用者查看全域清單時,看到數量驚人之使用 XAML 特定技術所定義的所有附加屬性。WPF 針對附加屬性所定義的 .NET Framework 屬性 (Attribute) 可以限制屬性視窗的顯示範圍,只顯示特定的附加屬性。您也可以考慮將這些屬性 (Attribute) 套用到您的自訂附加屬性 (Property)。有關 .NET Framework 屬性 (Attribute) 的用途和語法說明,請參閱相關參考頁面:
進一步了解附加屬性
如需建立附加屬性的詳細資訊,請參閱 HOW TO:註冊附加屬性。
如需相依性屬性和附加屬性的進階使用案例,請參閱自訂相依性屬性。
您也可以將屬性註冊為附加屬性以及相依性屬性,但仍公開「包裝函式」實作。在此情況下,屬性可在該項目上設定,也可透過 XAML 附加屬性語法,在任何項目上設定。FrameworkElement.FlowDirection 就是一個有標準和附加使用案例的屬性。