附加屬性概觀 (WPF .NET)

附加屬性是可延伸的應用程式標記語言 (XAML) 概念。 附加屬性可讓任何衍生自 DependencyObject 的 XAML 元素上設定額外的屬性/值組,即使元素在其物件模型中未定義這些額外的屬性也一樣。 額外的屬性可全域存取。 附加屬性通常定義為沒有傳統屬性包裝函式的特殊相依性屬性形式。

重要

.NET 7 和 .NET 6 的桌面指南檔正在建置中。

必要條件

本文假設您具備相依性屬性的基本知識,而且您已閱讀 相依性屬性概觀 。 若要遵循本文中的範例,如果您熟悉 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 附加屬性支援相依性屬性概念,例如屬性中繼資料,包括中繼資料的預設值。

附加屬性使用模型

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

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

父定義附加屬性的範例

WPF 定義附加屬性的一般案例是父元素支援子專案集合,而父元素會根據每個子項目所報告的資料實作行為。

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

程式碼中的附加屬性

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

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

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.Dock只有 在 子專案 DockPanel 上設定的值會影響轉譯,而且不論您在將子專案加入 至 DockPanel 之前或之後設定值,轉譯都會相同。

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

附加屬性中繼資料

附加屬性的中繼資料通常與相依性屬性不同。 註冊附加屬性時,請使用 FrameworkPropertyMetadata 來指定屬性的特性,例如屬性是否會影響轉譯或測量。 當您藉由覆寫附加屬性中繼資料來指定預設值時,該值會變成覆寫類別實例上隱含附加屬性的預設值。 如果未設定附加屬性值,當您使用 存取子搭配您指定中繼資料的類別實例來查詢 Get<property name> 屬性時,就會報告預設值。

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

自訂附加屬性

建立附加屬性的時機

建立附加屬性在:

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

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

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

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

如何建立附加屬性

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

藉由宣告 public static readonly 類型的 DependencyProperty 欄位,將您的附加屬性定義為定義類別中的相依性。 然後,將 方法的 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) ,其中:

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

注意

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

Set 存取子

存取 set 子方法簽章為 public static void Set<property name>(DependencyObject target, object value) ,其中:

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

附加屬性屬性

WPF 定義數個 .NET 屬性,這些屬性會將附加屬性的相關資訊提供給反映進程,以及提供給反映和屬性資訊的取用者,例如設計工具。 設計工具會使用 WPF 定義的 .NET 屬性來限制屬性視窗中顯示的屬性,以避免具有所有附加屬性的全域清單壓倒性使用者。 您可以考慮將這些屬性套用至您自己的自訂附加屬性。 這些參考頁面會說明 .NET 屬性的用途和語法:

深入了解

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

另請參閱