XAML 載入和相依性屬性 (WPF .NET)

其 Extensible Application Markup Language (XAML) 處理器的 Windows Presentation Foundation (WPF) 實作原本就是相依性屬性感知。 因此,XAML 處理器會使用 WPF 屬性系統方法來載入 XAML 和處理相依性屬性 (property) 的屬性 (attribute),並使用 GetValueSetValue 等 WPF 屬性系統方法完全略過相依性屬性包裝函式。 因此,如果您將自訂邏輯新增至自訂相依性屬性的屬性包裝函式,在 XAML 中設定屬性值時,XAML 處理器將不會呼叫該屬性包裝函式。

必要條件

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

WPF XAML 載入器效能

WPF XAML 處理器直接呼叫 SetValue 來設定相依性屬性的值,而不是使用相依性屬性的屬性包裝函式,計算成本較低。

如果 XAML 處理器確實使用屬性包裝函式,它只需要根據標記中所指出的類型和成員關聯性來推斷支援程式碼的整個物件模型。 儘管可以透過使用 xmlns 和組件屬性的組合來從標記中識別類型,但識別成員、確定哪些成員可以設定為屬性以及解析支援的屬性值類型,將需要使用 PropertyInfo 進行廣泛的反射。

WPF 屬性系統會維護在指定 DependencyObject 衍生類型上實作之相依性屬性的儲存資料表。 XAML 處理器使用該資料表來推斷相依性屬性的相依性屬性識別碼。 例如,依慣例,名為 ABC 之相依性屬性的相依性屬性識別碼為 ABCProperty。 XAML 處理器可以使用相依性屬性識別碼,在其包含類型上呼叫 SetValue 方法,以有效率地設定任何相依性屬性的值。

如需相依性屬性包裝函式的詳細資訊,請參閱自訂相依性屬性

自訂相依性屬性的影響

WPF XAML 處理器會略過屬性包裝函式,並直接呼叫 SetValue 來設定相依性屬性值。 因此,請避免將任何額外的邏輯放在自訂相依性屬性的 set 存取子中,因為該邏輯不會在 XAML 中設定屬性值時執行。 存取子 set 應該只包含 SetValue 呼叫。

同樣地,取得屬性值的 WPF XAML 處理器層面會略過屬性包裝函式並直接呼叫 GetValue。 因此,也請避免將任何額外的邏輯放在自訂相依性屬性的存取子 get 中,因為該邏輯在 XAML 中讀取屬性值時不會執行。 存取子 get 應該只包含 GetValue 呼叫。

含包裝函式的相依性屬性範例

下列範例顯示具有屬性包裝函式的建議相依性屬性定義。 相依性屬性識別碼會儲存為 public static readonly 欄位,且 getset 存取子不包含支援相依性屬性值的必要 WPF 屬性系統方法以外的程式碼。 如果您有需要在相依性屬性的值變更時執行的程式碼,請考慮將該程式碼放入相依性屬性的 PropertyChangedCallback 中。 如需詳細資訊,請參閱屬性變更回呼

// Register a dependency property with the specified property name,
// property type, owner type, and property metadata. Store the dependency
// property identifier as a public static readonly member of the class.
public static readonly DependencyProperty AquariumGraphicProperty =
    DependencyProperty.Register(
      name: "AquariumGraphic",
      propertyType: typeof(Uri),
      ownerType: typeof(Aquarium),
      typeMetadata: new FrameworkPropertyMetadata(
          defaultValue: new Uri("http://www.contoso.com/aquarium-graphic.jpg"),
          flags: FrameworkPropertyMetadataOptions.AffectsRender,
          propertyChangedCallback: new PropertyChangedCallback(OnUriChanged))
    );

// Property wrapper with get & set accessors.
public Uri AquariumGraphic
{
    get => (Uri)GetValue(AquariumGraphicProperty);
    set => SetValue(AquariumGraphicProperty, value);
}

// Property-changed callback.
private static void OnUriChanged(DependencyObject dependencyObject, 
    DependencyPropertyChangedEventArgs e)
{
    // Some custom logic that runs on effective property value change.
    Uri newValue = (Uri)dependencyObject.GetValue(AquariumGraphicProperty);
    Debug.WriteLine($"OnUriChanged: {newValue}");
}
' Register a dependency property with the specified property name,
' property type, owner type, and property metadata. Store the dependency
' property identifier as a public static readonly member of the class.
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty =
    DependencyProperty.Register(
        name:="AquariumGraphic",
        propertyType:=GetType(Uri),
        ownerType:=GetType(Aquarium),
        typeMetadata:=New FrameworkPropertyMetadata(
            defaultValue:=New Uri("http://www.contoso.com/aquarium-graphic.jpg"),
            flags:=FrameworkPropertyMetadataOptions.AffectsRender,
            propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnUriChanged)))

' Property wrapper with get & set accessors.
Public Property AquariumGraphic As Uri
    Get
        Return CType(GetValue(AquariumGraphicProperty), Uri)
    End Get
    Set
        SetValue(AquariumGraphicProperty, Value)
    End Set
End Property

' Property-changed callback.
Private Shared Sub OnUriChanged(dependencyObject As DependencyObject,
                                e As DependencyPropertyChangedEventArgs)
    ' Some custom logic that runs on effective property value change.
    Dim newValue As Uri = CType(dependencyObject.GetValue(AquariumGraphicProperty), Uri)
    Debug.WriteLine($"OnUriChanged: {newValue}")
End Sub

另請參閱