Laden von XAML und Abhängigkeitseigenschaften (WPF .NET)

Die WPF-Implementierung (Windows Presentation Foundation) des XAML-Prozessors (Extensible Application Markup Language) ist so konzipiert, dass Abhängigkeitseigenschaften berücksichtigt werden. Daher verwendet der XAML-Prozessor Methoden des WPF-Eigenschaftensystems, um XAML zu laden und Attribute von Abhängigkeitseigenschaften zu verarbeiten, und umgeht die Wrapper von Abhängigkeitseigenschaften vollständig, indem er Methoden des WPF-Eigenschaftensystems wie GetValue und SetValue verwendet. Wenn Sie also dem Eigenschaftenwrapper Ihrer benutzerdefinierten Abhängigkeitseigenschaft benutzerdefinierte Logik hinzufügen, wird diese nicht vom XAML-Prozessor aufgerufen, wenn ein Eigenschaftswert in XAML festgelegt ist.

Wichtig

Der Desktopleitfaden zu .NET 7 und .NET 6 ist in Bearbeitung.

Voraussetzungen

Im Artikel wird davon ausgegangen, dass sie grundlegende Kenntnisse über Abhängigkeitseigenschaften haben und eine Übersicht über Abhängigkeitseigenschaften gelesen haben. Um den Beispielen in diesem Artikel zu folgen, ist es hilfreich, wenn Sie mit Extensible Application Markup Language (XAML) vertraut sind und wissen, wie WPF-Anwendungen geschrieben werden.

Leistung des WPF-XAML-Ladeprogramms

Für den WPF XAML-Prozessor entsteht weniger Rechenaufwand, wenn er zum Festlegen des Werts einer Abhängigkeitseigenschaft direkt SetValue aufruft, anstatt den Eigenschaftenwrapper einer Abhängigkeitseigenschaft zu verwenden.

Wenn der XAML-Prozessor den Eigenschaftenwrapper verwenden würde, müsste er das gesamte Objektmodell des zugrunde liegenden Codes nur auf der Grundlage der im Markup angegebenen Typ- und Memberbeziehungen ableiten. Obwohl der Typ aus dem Markup durch eine Kombination von xmlns und Assemblyattributen identifiziert werden kann, würde die Identifizierung der Member, die Bestimmung, welche Member als Attribut festgelegt werden können, und die Auflösung der unterstützten Eigenschaftswerttypen eine umfangreiche Reflexion unter Verwendung von PropertyInfo erfordern.

Das Eigenschaftensystem von WPF verwaltet eine Speichertabelle mit Abhängigkeitseigenschaften, die für einen bestimmten abgeleiteten DependencyObject-Typ implementiert werden. Der XAML-Prozessor verwendet diese Tabelle, um den Bezeichner der Abhängigkeitseigenschaft für eine Abhängigkeitseigenschaft abzuleiten. Beispielsweise lautet der Bezeichner der Abhängigkeitseigenschaft für eine Abhängigkeitseigenschaft mit dem Namen ABC üblicherweise ABCProperty. Der XAML-Prozessor kann den Wert jeder Abhängigkeitseigenschaft effizient festlegen, indem er die SetValue-Methode des enthaltenen Typs unter Verwendung des Bezeichners der Abhängigkeitseigenschaft aufruft.

Weitere Informationen zu Wrappern für Abhängigkeitseigenschaften finden Sie unter Benutzerdefinierte Abhängigkeitseigenschaften (WPF .NET).

Auswirkungen auf benutzerdefinierte Abhängigkeitseigenschaften

Der WPF-XAML-Prozessor umgeht die Eigenschaftenwrapper und ruft SetValue direkt auf, um den Wert einer Abhängigkeitseigenschaft festzulegen. Vermeiden Sie es deshalb, zusätzliche Logik in den set-Accessor Ihrer benutzerdefinierten Abhängigkeitseigenschaft einzufügen, da diese Logik nicht ausgeführt wird, wenn ein Eigenschaftswert in XAML festgelegt wird. Der set-Accessor sollte nur einen SetValue-Aufruf enthalten.

In ähnlicher Weise umgehen Aspekte des WPF-XAML-Prozessors, die Eigenschaftswerte abrufen, den Eigenschaftenwrapper und rufen GetValue direkt auf. Vermeiden Sie es daher, zusätzliche Logik in den get-Accessor Ihrer benutzerdefinierten Abhängigkeitseigenschaft einzufügen, da diese Logik nicht ausgeführt wird, wenn ein Eigenschaftswert in XAML gelesen wird. Der get-Accessor sollte nur einen GetValue-Aufruf enthalten.

Beispiel: Abhängigkeitseigenschaft mit Wrapper

Das folgende Beispiel zeigt eine empfohlene Definition einer Abhängigkeitseigenschaft mit Eigenschaftenwrappern. Der Bezeichner der Abhängigkeitseigenschaft wird als public static readonly-Feld gespeichert, und die get- und set-Accessoren enthalten keinen Code, der über die notwendigen Methoden des WPF-Eigenschaftensystems hinausgeht, die den Wert der Abhängigkeitseigenschaft unterstützen. Wenn Sie über Code verfügen, der ausgeführt werden muss, wenn sich der Wert Ihrer Abhängigkeitseigenschaft ändert, sollten Sie diesen Code in den PropertyChangedCallback für Ihre Abhängigkeitseigenschaft einfügen. Weitere Informationen finden Sie unter Rückrufe mit geänderten Eigenschaften.

// 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

Weitere Informationen