共用方式為


附加屬性概觀 (WPF .NET)

附加屬性是一種 Extensible Application Markup Language (XAML) 概念。 附加屬性可讓任何衍生自 DependencyObject 的 XAML 元素上設定額外的屬性/值組,即使元素在其物件模型中未定義這些額外的屬性也一樣。 額外的屬性可全域存取。 附加屬性一般會定義為沒有傳統屬性「包裝函式」的特殊形式相依性屬性。

必要條件

本文假設您具備相依性屬性的基本知識,而且您已閱讀相依性屬性概觀。 若要遵循本文中的範例,且您熟悉 XAML,並知道如何撰寫 Windows Presentation Foundation (WPF) 應用程式,則這會有幫助。

為何使用附加屬性

附加屬性可讓子元素指定父元素中定義之屬性的唯一值。 常見的情況是子元素,用來指定其父元素在 UI 中轉譯的方式。 例如,DockPanel.Dock 是附加屬性,因為其是在 DockPanel 的子元素上設定,而不是在 DockPanel 本身設定。 類別 DockPanel 會定義名為 DockProperty 的靜態 DependencyProperty 欄位,然後提供 GetDockSetDock 方法做為附加屬性的公用存取子。

XAML 中的附加屬性

在 XAML 中,您可以使用語法 <attached property provider type>.<property name> 來設定附加屬性,其中附加屬性提供者是定義附加屬性的類別。 下列範例示範 DockPanel 子元素如何設定 DockPanel.Dock 屬性值。

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

使用方式類似於靜態屬性,您參考擁有和登錄附加屬性的類型 (例如 DockPanel),而不是執行個體名稱。

當您使用 XAML 屬性指定附加屬性時,僅適用設定動作。 雖然有一些間接機制 (例如樣式中的觸發程式) 可用來比較值,但您無法透過 XAML 直接取得屬性值。

WPF 中的附加屬性

附加屬性是 XAML 概念,而相依性屬性是 WPF 概念。 在 WPF 中,WPF 類型上大部分與 UI 相關的附加屬性都會實作為相依性屬性。 實作為相依性屬性的 WPF 附加屬性支援相依性屬性概念,例如屬性中繼資料,包括中繼資料的預設值。

附加屬性使用模型

雖然任何物件都可以設定附加屬性值,但這並不意味著設定值將產生有形的結果或該值將被另一個物件使用。 附加屬性的主要目的是為各種類別階層和邏輯關聯性的物件提供方法,以將通用資訊報告給定義附加屬性的類型。 附加屬性使用方式通常遵循下列其中一個模型:

  • 定義附加屬性的類型是設定附加屬性值之元素的父代。 父類型會透過內部邏輯針對物件樹狀結構逐一查看其子物件並取得值,然後以某種方式處理這些值。
  • 可定義附加屬性的類型會用作各種可能父元素和內容模型的子元素。
  • 可定義附加屬性的類型代表服務。 其他類型設定附加屬性的值。 然後,在服務內容中評估可設定屬性的項目時,會透過服務類別的內部邏輯取得附加屬性值。

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

WPF 定義附加屬性的典型情況是當父元素支援子元素集合,而父元素會根據每個子元素所報告的資料實作行為。

DockPanel 定義 DockPanel.Dock 附加屬性。 DockPanel 具有類別層級程式碼,特別是 MeasureOverrideArrangeOverride,這是其轉譯邏輯的一部分。 DockPanel 執行個體會檢查其任何立即子元素是否已設定 DockPanel.Dock 的值。 如果是,這些值會變成套用至每個子元素之轉譯邏輯的輸入。 雖然從理論上講,附加屬性可以影響超出直接父代的元素,但巢狀 DockPanel 執行個體的已定義行為只會與其立即子元素集合互動。 因此,如果您在沒有 DockPanel 父代的元素上設定 DockPanel.Dock,則不會引發任何錯誤或例外狀況,而且您已建立不會由任何 DockPanel 取用的全域屬性值。

程式碼中的附加屬性

WPF 中的附加屬性沒有一般的 CLR getset 包裝函式方法,因為屬性可能從 CLR 命名空間外部設定。 若要允許 XAML 處理器在剖析 XAML 時設定這些值,定義附加屬性的類別必須以 Get<property name>Set<property name> 的形式實作專用存取子方法。

您也可以使用專用存取子方法來取得和設定程式碼中的附加屬性,如下列範例所示。 在此範例中,myTextBoxTextBox 類別的執行個體。

DockPanel myDockPanel = new();
TextBox myTextBox = new();
myTextBox.Text = "Enter text";

// Add child element to the DockPanel.
myDockPanel.Children.Add(myTextBox);

// Set the attached property value.
DockPanel.SetDock(myTextBox, Dock.Top);
Dim myDockPanel As DockPanel = New DockPanel()
Dim myTextBox As TextBox = New TextBox()
myTextBox.Text = "Enter text"

' Add child element to the DockPanel.
myDockPanel.Children.Add(myTextBox)

' Set the attached property value.
DockPanel.SetDock(myTextBox, Dock.Top)

如果您未將 myTextBox 新增為 myDockPanel 的子元素,則呼叫 SetDock 不會引發例外狀況或有任何效果。 只有在 DockPanel 的子元素上設定的 DockPanel.Dock 值才會影響轉譯,並且無論您在將子元素新增至 DockPanel 之前還是之後設定該值,轉譯都會相同。

從程式碼的觀點來看,附加屬性就像具有方法存取子而非屬性存取子的支援欄位,而且可以在任何物件上設定,而不需要先在這些物件上定義。

附加屬性中繼資料

附加屬性的中繼資料通常與相依性屬性並無不同。 登錄附加屬性時,請使用 FrameworkPropertyMetadata 來指定屬性的特性,例如屬性是否會影響轉譯或量值。 透過覆寫附加屬性中繼資料來指定預設值時,該值會變成覆寫類別執行個體所隱含附加屬性的預設值。 如果未以其他方式設定附加屬性值,則在使用 Get<property name> 存取子和指定中繼資料的類別的執行個體來查詢該屬性時,會報告預設值。

若要在屬性上啟用屬性值繼承,請使用附加屬性,而不是非附加相依性屬性。 如需詳細資訊,請參閱屬性值繼承

自訂附加屬性

何時建立附加屬性

建立附加屬性在以下情況下很有用:

  • 您需要定義類別以外的類別可用的屬性設定機制。 常見的案例是 UI 版面配置,例如 DockPanel.DockPanel.ZIndexCanvas.Top 都是現有版面配置屬性的範例。 在版面配置案例中,版面配置控制元素的子元素能夠表達其版面配置父代的版面配置需求,以及為父代所定義的附加屬性設定值。

  • 其中一個類別代表服務,而且您希望其他類別更透明地整合服務。

  • 您想要 Visual Studio WPF 設計工具支援,例如能夠透過 [屬性] 視窗編輯屬性。 如需詳細資訊,請參閱控制項製作概觀

  • 您想要使用屬性值繼承。

如何建立附加屬性

如果您的類別只定義附加屬性供其他類型使用,則您的類別不需要衍生自 DependencyObject。 否則,請遵循 WPF 模型,讓附加屬性也是相依性屬性,方法是從 DependencyObject 衍生類別。

藉由宣告 DependencyProperty 類型的 public static readonly 欄位,將您的附加屬性定義為定義類別中的相依性。 然後,將方法 RegisterAttached 的傳回值指派給欄位,也稱為相依性屬性識別碼。 依照 WPF 屬性命名慣例,藉由命名識別碼欄位 <property name>Property,將欄位與其所代表的屬性區別開來。 此外,提供靜態 Get<property name>Set<property name> 存取子方法,讓屬性系統存取附加屬性。

下列範例示範如何使用 RegisterAttached 方法登錄相依性屬性,以及如何定義存取子方法。 在此範例中,附加屬性的名稱是 HasFish,因此識別碼欄位名為 HasFishProperty,而存取子方法的名稱為 GetHasFishSetHasFish

public class Aquarium : UIElement
{
    // Register an attached dependency property with the specified
    // property name, property type, owner type, and property metadata.
    public static readonly DependencyProperty HasFishProperty = 
        DependencyProperty.RegisterAttached(
      "HasFish",
      typeof(bool),
      typeof(Aquarium),
      new FrameworkPropertyMetadata(defaultValue: false,
          flags: FrameworkPropertyMetadataOptions.AffectsRender)
    );

    // Declare a get accessor method.
    public static bool GetHasFish(UIElement target) =>
        (bool)target.GetValue(HasFishProperty);

    // Declare a set accessor method.
    public static void SetHasFish(UIElement target, bool value) =>
        target.SetValue(HasFishProperty, value);
}
Public Class Aquarium
    Inherits UIElement

    ' Register an attached dependency property with the specified
    ' property name, property type, owner type, and property metadata.
    Public Shared ReadOnly HasFishProperty As DependencyProperty =
        DependencyProperty.RegisterAttached("HasFish", GetType(Boolean), GetType(Aquarium),
            New FrameworkPropertyMetadata(defaultValue:=False,
                flags:=FrameworkPropertyMetadataOptions.AffectsRender))

    ' Declare a get accessor method.
    Public Shared Function GetHasFish(target As UIElement) As Boolean
        Return target.GetValue(HasFishProperty)
    End Function

    ' Declare a set accessor method.
    Public Shared Sub SetHasFish(target As UIElement, value As Boolean)
        target.SetValue(HasFishProperty, value)
    End Sub

End Class

Get 存取子

存取子 get 方法簽章為 public static object Get<property name>(DependencyObject target),其中:

  • target 是從中讀取附加屬性的 DependencyObject。 此 target 類型可以比 DependencyObject 更明確。 例如,DockPanel.GetDock 存取子方法會輸入 target 作為 UIElement,因為附加屬性是要在執行個體 UIElement 上設定。 UiElement 間接衍生自 DependencyObject
  • 傳回類型可以比 object 更明確。 例如,方法 GetDock 會將傳回的值輸入為 Dock,因為傳回值應該是 Dock 列舉。

注意

設計工具 (例如 Visual Studio 或 Blend for Visual Studio) 中的資料繫結支援需要附加屬性的存取子 get

Set 存取子

存取子 get 方法簽章為 public static object Get<property name>(DependencyObject target),其中:

  • target 是附加屬性寫入所在的 DependencyObject。 此 target 類型可以比 DependencyObject 更明確。 例如,SetDock 方法會輸入 target 作為 UIElement,因為附加屬性是要在執行個體 UIElement 上設定。 UiElement 間接衍生自 DependencyObject
  • target 類型可以比 DependencyObject 更明確。 例如,SetDock 方法需要值 Dock。 XAML 載入器必須能夠從表示附加屬性值的標記字串產生 value 類型。 因此,對於您使用的類型,必須有類型轉換、值序列化程式或標記延伸支援。

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

WPF 定義數個 .NET 屬性,以將附加屬性相關資訊提供給反映程序以及反映和屬性資訊的使用者 (例如設計工具)。 設計工具會使用 WPF 定義的 .NET 屬性來限制屬性視窗中顯示的屬性,以避免使用者因所有附加屬性的全域清單而感到不知所措。 您不妨對自己的自訂附加屬性套用這些屬性。 這些參考頁面會說明 .NET 屬性的用途和語法:

深入了解

  • 如需建立附加屬性的相關詳細資訊,請參閱登錄附加屬性
  • 如需相依性屬性和附加屬性的更進階使用的情況,請參閱自訂相依性屬性
  • 您可以將屬性登錄為附加屬性和相依性屬性,並包含傳統的屬性包裝函式。 如此一來,就可以使用屬性包裝函式在元素上設定屬性,也可以使用 XAML 附加屬性語法,在任何其他元素上設定屬性。 如需範例,請參閱 FrameworkElement.FlowDirection

另請參閱