共用方式為


附加屬性概觀

附加屬性是透過 XAML 所定義的概觀。 附加屬性為一種全域屬性,任何相依物件皆可設定。 在 Windows Presentation Foundation (WPF) 中,附加屬性通常被定義為一種特殊形式的相依性屬性,它沒有傳統屬性的「包裝函式」。

必要條件

本文假設您已透過 Windows Presentation Foundation (WPF) 類別的現有相依屬性消費者角度了解何謂相依屬性,並已閱讀完相依屬性概觀。 要遵循本文中的範例,您也應了解何謂 XAML,並清楚如何撰寫 WPF 應用程式。

為何要使用附加屬性

附加屬性的其中一個用途為允許不同的子元素為父元素定義的屬性指定唯一的值。 上述情境的具體應用為讓子元素通知父元素,它們在使用者介面 (UI) 中的呈現形式。 DockPanel.Dock 屬性即為其中一例。 DockPanel.Dock 屬性之所以為附加屬性,是因為其用途為設定 DockPanel 所包含的元素,而非為了設定 DockPanel 本身而設計。 類別 DockPanel 會定義名為 DockProperty 的靜態 DependencyProperty 欄位,然後提供 GetDockSetDock 方法做為附加屬性的公用存取子。

XAML 中的附加屬性

在 XAML 中,您可以使用 AttachedPropertyProvider.<屬性名稱> 語法來設定附加屬性。

以下為如何在 XAML 中設定 DockPanel.Dock 的範例:

<DockPanel>
    <TextBox DockPanel.Dock="Top">Enter text</TextBox>
</DockPanel>

做法有點類似靜態屬性;您應一律參考擁有並註冊附加屬性的 DockPanel 類型,而非參考依名稱指定的任何執行個體。

此外,因為 XAML 中的附加屬性是您在標記中設定的屬性,所以只有設定作業才會有任何相關性。 雖然有一些間接機制可比較值 (例如樣式中的觸發程序),但是您無法在 XAML 中直接取得屬性 (如需詳細資訊,請參閱設定樣式和範本)。

WPF 中的附加屬性實作

在 Windows Presentation Foundation (WPF) 中,WPF 類型中大部分與 UI 相關的附加屬性都是以相依屬性的方式實作。 附加屬性的概念為 XAML,而相依屬性的概念為 WPF。 因為 WPF 附加屬性為相依性屬性,故支援相依性屬性概念,例如屬性中繼資料,以及該屬性中繼資料的預設值。

擁有類型如何使用附加屬性

雖然可在任何物件上設定附加屬性,但是這不自動表示設定屬性就會產生明確結果,或者另一個物件將使用值。 一般而言,會使用附加屬性,讓來自各種可能類別階層或邏輯關聯性的物件都可以報告可定義附加屬性之類型的通用資訊。 可定義附加屬性的類型通常會遵循下列其中一個模型︰

  • 設計可定義附加屬性的類型,因此它可以是設定附加屬性值之項目的父項目。 類型接著會透過內部邏輯針對某個物件樹狀結構逐一查看其子物件,並取得值,然後以某種方式處理這些值。

  • 可定義附加屬性的類型將會用作各種可能父項目和內容模組的子項目。

  • 可定義附加屬性的類型代表服務。 其他類型設定附加屬性的值。 然後,在服務內容中評估可設定屬性的項目時,會透過服務類別的內部邏輯取得附加屬性值。

父代已定義的附加屬性範例

WPF 定義是附加屬性最常見的情境,為父元素支援子元素的集合,亦為行為實作,該行為的具體細節會以每個子元素為單位個別報告。

DockPanel.Dock 的附加屬性由 DockPanel 定義,DockPanel 的類別層級程式碼是轉譯邏輯 (尤其是 MeasureOverrideArrangeOverride) 的一部分。 DockPanel 執行個體會一律檢查自己所有的即時子元素是否均已將值設定為 DockPanel.Dock。 如果是這樣,這些值會變成套用至該特定子項目之轉譯邏輯的輸入。 巢狀 DockPanel 執行個體會各自處理自己的即時子元素集合,但該行為是 DockPanel 如何處理 DockPanel.Dock 值的專屬實作方式。 理論上,可能會有附加屬性影響直屬父代以外的項目。 就算 DockPanel.Dock 附加屬性被設定在沒有 DockPanel 父元素作用的元素上,也不會產生任何錯誤或例外狀況。 這只代表全域屬性值已設定,但目前沒有任何 DockPanel 父元素能取用這項資訊。

程式碼中的附加屬性

WPF 中的附加屬性通常不採用可容易取得/設定存取的 CLR「包裝函式」法。 這是因為對於已設定屬性的執行個體,其附加屬性不一定屬於 CLR 命名空間的一部分。 不過,XAML 處理器必須可以在剖析 XAML 時設定這些值。 若要支援有效的附加屬性使用方式,附加屬性的擁有者類型必須在取得屬性名稱設定屬性名稱表單中實作專用存取子方法。 這些專用存取子方法也適用於取得或設定程式碼中的附加屬性。 從程式碼觀點,附加屬性類似具有方法存取子而非屬性存取子的支援欄位,而且該支援欄位可以存在於任何物件,而不需要特別進行定義。

下列範例示範如何在程式碼中設定附加屬性。 在此範例中,myCheckBoxCheckBox 類別的執行個體。

DockPanel myDockPanel = new DockPanel();
CheckBox myCheckBox = new CheckBox();
myCheckBox.Content = "Hello";
myDockPanel.Children.Add(myCheckBox);
DockPanel.SetDock(myCheckBox, Dock.Top);
Dim myDockPanel As New DockPanel()
Dim myCheckBox As New CheckBox()
myCheckBox.Content = "Hello"
myDockPanel.Children.Add(myCheckBox)
DockPanel.SetDock(myCheckBox, Dock.Top)

與 XAML 情況類似,如果沒有在第四行程式碼之前將 myCheckBox 新增為 myDockPanel 的子元素,則第五行程式碼將不會產生例外狀況,但該屬性值也不會與 DockPanel 父元素互動,因此不會執行任何動作。 只有當子元素設定好 DockPanel.Dock 值,再加上 DockPanel 父元素的存在,才會在轉譯的應用程式中產生有效行為。 (在此情況下,您可以設定附加屬性,然後將其附加至樹狀結構。或者,您可以將其附加至樹狀結構,然後設定附加屬性。任一個動作順序都會提供相同的結果)。

附加屬性中繼資料

登錄屬性時,會設定 FrameworkPropertyMetadata 來指定該屬性的特性,例如屬性是否會影響轉譯、測量等。 附加屬性的中繼資料一般與相依性屬性並無不同。 如果您在附加屬性中繼資料的覆寫中指定預設值,該值會變成覆寫類別執行個體上的隱含附加屬性預設值。 具體而言,如果某個處理序透過該屬性的 Get 方法存取子來查詢附加屬性值,並指定已指定中繼資料之類別的執行個體,則會報告預設值,否則不會設定該附加屬性的值。

如果您想要啟用屬性的屬性值繼承,則應該使用附加屬性,而不是使用非附加相依性屬性。 如需詳細資訊,請參閱屬性值繼承

自訂附加屬性

該何時建立附加屬性

非定義類別的類別需要有可用的屬性設定機制時,您可以建立附加屬性。 最常見的案例是配置。 現有版面配置屬性的範例包括 DockPanel.DockPanel.ZIndexCanvas.Top。 在這裡啟用的情節是本身為配置控制項目之子項目的項目可以個別表達其配置父項目的配置需求,且各會設定父代定義為附加屬性的屬性值。

另一個使用附加屬性的情節是類別代表一項服務,而且想要類別能夠更緊密地整合服務。

另一種情境是接收 Visual Studio WPF Designer 支援,例如屬性視窗編輯。 如需詳細資訊,請參閱控制項撰寫概觀

如前所述,如果您想要使用屬性值繼承,則應該註冊為附加屬性。

如何建立附加屬性

如果您的類別是嚴格定義附加屬性,目的為用於其他類型,則該類別不需衍生自 DependencyObject。 然而,如果您遵循整體 WPF 模型,且希望您的附加屬性也成為相依性屬性,您的類別就必須衍生自 DependencyObject

藉由宣告public static readonly類型欄位DependencyProperty,您可將附加屬性定義為相依性屬性。 您可以使用 RegisterAttached 方法的傳回值來定義此欄位。 欄位名稱必須符合附加 Property 字串的附加屬性名稱,以遵循命名識別欄位與其所代表屬性的已建立 WPF 模式。 附加屬性提供者也必須提供靜態取得屬性名稱設定屬性名稱方法作為附加屬性的存取子,要是不這麼做,會導致屬性系統無法使用您的附加屬性。

注意

如果您省略附加屬性的取得存取子步驟,則屬性的資料繫結將無法在設計工具中運作,例如 Visual Studio 和 Blend for Visual Studio。

Get 存取子

取得屬性名稱存取子的簽章必須為:

public static object GetPropertyName(object target)

  • target 物件可以指定為實作中的更特定類型。 例如,DockPanel.GetDock 方法會輸入 UIElement 為參數,因為只打算將附加屬性設定在 UIElement 執行個體上。

  • 傳回值可以指定為實作中的更特定類型。 例如,GetDock 方法會輸入 Dock 為參數,因為該值只能設定為列舉。

Set 存取子

設定屬性名稱存取子的簽章必須為:

public static void SetPropertyName(object target, object value)

  • target 物件可以指定為實作中的更特定類型。 例如,SetDock 方法會輸入 UIElement 為參數,因為只打算將附加屬性設定在 UIElement 執行個體上。

  • target 物件可以指定為實作中的更特定類型。 例如,GetDock 方法會輸入 Dock 為參數,因為該值只能設定為列舉。 請記住,當這個方法在標記的附加屬性使用方式中遇到附加屬性時,其值是來自 XAML 載入器的輸入。 該輸入是指定為標記中 XAML 屬性值的值。 因此,您使用的類型必須要有類型轉換、值序列化程式或標記延伸支援,因此,可以從屬性值 (這最後就是一個字串) 建立適當的類型。

以下範例說明如何使用 RegisterAttached 方法註冊相依性屬性,以及如何取得屬性名稱設定屬性名稱存取子。 在此範例中,附加屬性名稱為 IsBubbleSource。 因此,存取子必須命名為 GetIsBubbleSourceSetIsBubbleSource

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);
}
Public Shared ReadOnly IsBubbleSourceProperty As DependencyProperty = DependencyProperty.RegisterAttached("IsBubbleSource", GetType(Boolean), GetType(AquariumObject), New FrameworkPropertyMetadata(False, FrameworkPropertyMetadataOptions.AffectsRender))
Public Shared Sub SetIsBubbleSource(ByVal element As UIElement, ByVal value As Boolean)
    element.SetValue(IsBubbleSourceProperty, value)
End Sub
Public Shared Function GetIsBubbleSource(ByVal element As UIElement) As Boolean
    Return CType(element.GetValue(IsBubbleSourceProperty), Boolean)
End Function

附加屬性 (property) 的屬性 (attribute)

WPF 可定義數個 .NET 屬性,這些屬性的用途為將附加屬性的相關資訊提供給反映程序以及反映資訊和屬性資訊的一般使用者,例如設計師。 因為附加屬性的類型為無限制範圍,所以設計人員需要方法來避免使用 XAML 的特定技術實作中所定義之所有附加屬性的全域清單,讓使用者無所適從。 WPF 為附加屬性定義的 .NET 屬性可用於限定指定附加屬性在屬性視窗顯示的範圍。 您也可以考慮針對您自己的自訂附加屬性套用這些屬性。 .NET 屬性的用途和語法請見對應的參考頁面:

深入了解附加屬性

  • 如需建立附加屬性的詳細資訊,請參閱註冊附加屬性

  • 如需相依性屬性和附加屬性的更進階使用方式情節,請參閱自訂相依性屬性

  • 您也可以將屬性註冊為附加屬性和相依性屬性,但仍公開「包裝函式」實作。 在此情況下,可以在該項目上設定屬性,或透過 XAML 附加屬性語法的任何項目上設定屬性。 FrameworkElement.FlowDirection 這個屬性範例顯示了在適當情境下的標準使用方式和附加使用方式。

另請參閱