Priorität von Abhängigkeitseigenschaftswerten (WPF.NET)

Die Funktionsweise des WPF-Eigenschaftensystems (Windows Presentation Foundation) wirkt sich auf den Wert einer Abhängigkeitseigenschaft aus. In diesem Artikel wird erläutert, wie die Priorität verschiedener eigenschaftsbasierter Eingaben innerhalb des WPF-Eigenschaftensystems den effektiven Wert einer Abhängigkeitseigenschaft bestimmt.

Wichtig

Die Dokumentation zum Desktopleitfaden für .NET 6 und .NET 5 (einschließlich .NET Core 3.1) wird derzeit erstellt.

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.

Das WPF-Eigenschaftensystem

Das WPF-Eigenschaftensystem nutzt eine Vielzahl von Faktoren, um den Wert von Abhängigkeitseigenschaften zu bestimmen, darunter die Eigenschaftenüberprüfung in Echtzeit, die späte Bindung und die Benachrichtigung über Eigenschaftenänderungen bei zugehörigen Eigenschaften. Obwohl die Reihenfolge und die Logik, die zur Bestimmung der Werte von Abhängigkeitseigenschaften verwendet werden, komplex sind, kann das Erlernen der Logik Ihnen dabei helfen, unnötige Eigenschafteneinstellungen zu vermeiden. Darüber hinaus können Sie herausfinden, warum der Versuch, eine Abhängigkeitseigenschaft festzulegen, nicht zum erwarteten Wert geführt hat.

Festlegung von Abhängigkeitseigenschaften an verschiedenen Stellen

Das folgende XAML-Beispiel zeigt, wie drei verschiedene Set-Vorgänge für die Background-Eigenschaft der Schaltfläche deren Wert beeinflussen können.

<StackPanel>
    <StackPanel.Resources>
        <ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
            <Border Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" 
                    BorderBrush="{TemplateBinding BorderBrush}">
                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
            </Border>
        </ControlTemplate>
    </StackPanel.Resources>

    <Button Template="{StaticResource ButtonTemplate}" Background="Red">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Setter Property="Background" Value="Blue"/>
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="Yellow" />
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
        Which color do you expect?
    </Button>
</StackPanel>

Im Beispiel wird die Eigenschaft Background lokal auf Red festgelegt. Allerdings versucht der im Bereich der Schaltfläche deklarierte implizite Stil, die Eigenschaft Background auf Blue festzulegen. Und wenn sich die Maus über der Schaltfläche befindet, versucht der Trigger im impliziten Stil, die Eigenschaft Background auf Yellow festzulegen. Mit Ausnahme von Koersion und Animation hat ein lokal festgelegter Eigenschaftswert die höchste Priorität, d. h. die Schaltfläche wird Rot angezeigt – auch bei einem MouseOver. Wenn Sie jedoch den lokal festgelegten Wert aus der Schaltfläche entfernen, erhält sie ihren Background-Wert aus dem Stil. Innerhalb eines Stils haben Trigger Vorrang, sodass die Schaltfläche bei einem MouseOver in Gelb und ansonsten in Blau dargestellt wird. Das Beispiel ersetzt den Standardwert ControlTemplate der Schaltfläche, da die Standardvorlage einen hartcodierten MouseOver-Wert Background aufweist.

Rangfolgeliste für Abhängigkeitseigenschaften

Die folgende Liste ist die endgültige Rangfolge, die das Eigenschaftensystem bei der Zuweisung von Laufzeitwerten zu Abhängigkeitseigenschaften verwendet. Die oberste Priorität ist zuerst aufgeführt.

  1. Eigenschaftensystemkoersion: Weitere Informationen zur Koersion finden Sie unter Koersion und Animationen.

  2. Aktive Animationen oder Animationen mit einem Halteverhalten: Um eine praktische Wirkung zu haben, muss ein Animationswert Vorrang vor dem Basiswert (ohne Animation) haben, auch wenn der Basiswert lokal festgelegt wurde. Weitere Informationen finden Sie unter Koersion und Animationen.

  3. Lokale Werte: Sie können einen lokalen Wert über eine Wrapper-Eigenschaft festlegen, was dem Festlegen eines Attributs oder Werts in der XAML entspricht, oder durch Aufrufen der SetValue-API unter Verwendung einer Eigenschaft einer bestimmten Instanz. Ein lokaler Wert, der über eine Bindung oder Ressource festgelegt wird, hat die gleiche Priorität wie ein Wert, der direkt festgelegt wird.

  4. TemplatedParent-Vorlageneigenschaftswerte: Ein Element verfügt über ein TemplatedParent-Element, wenn es über eine Vorlage (ControlTemplate oder DataTemplate) erstellt wurde. Weitere Informationen finden Sie unter TemplatedParent. Innerhalb der durch TemplatedParent angegebenen Vorlage gilt die folgende Rangfolge:

    1. Trigger

    2. Eigenschaftensätze, in der Regel über XAML-Attribute

  5. Implizite Stile: Gilt nur für die Style-Eigenschaft. Der Style-Wert ist eine beliebige Stilressource mit einem TargetType-Wert, der dem Elementtyp entspricht. Die Stilressource muss innerhalb der Seite oder Anwendung vorhanden sein. Die Suche nach einer impliziten Stilressource erstreckt sich nicht auf Stilressourcen in Designs.

  6. Formattrigger: Ein Formattrigger ist ein Trigger innerhalb eines expliziten oder impliziten Stils. Der Stil muss innerhalb der Seite oder Anwendung vorhanden sein. Trigger in Standardstilen weisen eine niedrigere Priorität auf.

  7. Vorlagentrigger: Ein Vorlagentrigger ist ein Trigger aus einer direkt angewendeten Vorlage oder aus einer Vorlage innerhalb eines Stils. Der Stil muss innerhalb der Seite oder Anwendung vorhanden sein.

  8. Style Setter-Werte: Ein Style Setter-Wert ist ein Wert, der durch Setter innerhalb eines Stils angewendet wird. Der Stil muss innerhalb der Seite oder Anwendung vorhanden sein.

  9. Standardstile, auch bekannt als Designstile: Weitere Informationen finden Sie unter Standardstile (Designstile). Innerhalb eines Standardstils gilt die folgende Rangfolge:

    1. Aktive Trigger

    2. Setter

  10. Vererbung: Einige Abhängigkeitseigenschaften eines untergeordneten Elements erben ihren Wert vom übergeordneten Element. Es ist also nicht unbedingt notwendig, für jedes Element in der Anwendung Eigenschaftswerte festzulegen. Weitere Informationen finden Sie unter Vererbung von Eigenschaftswerten.

  11. Standardwert aus Metadaten der Abhängigkeitseigenschaft: Für eine Abhängigkeitseigenschaft kann bei der Registrierung dieser Eigenschaft im Eigenschaftensystem ein Standardwert festgelegt werden. Abgeleitete Klassen, die eine Abhängigkeitseigenschaft erben, können die Metadaten der Abhängigkeitseigenschaft (einschließlich des Standardwerts) pro Typ außer Kraft setzen. Weitere Informationen finden Sie unter Metadaten für Abhängigkeitseigenschaften. Bei einer geerbten Eigenschaft hat der Standardwert eines übergeordneten Elements Vorrang vor dem Standardwert eines untergeordneten Elements. Wenn also keine vererbbare Eigenschaft festgelegt ist, wird anstelle des Standardwerts des untergeordneten Elements der Standardwert des Stamm- oder übergeordneten Elements verwendet.

TemplatedParent

Die TemplatedParent-Rangfolge gilt nicht für Eigenschaften von Elementen, die direkt im Standardanwendungsmarkup deklariert werden. Das TemplatedParent-Konzept besteht nur für untergeordnete Elemente innerhalb einer visuellen Struktur, die durch die Anwendung einer Vorlage entstanden sind. Wenn das Eigenschaftensystem die durch TemplatedParent angegebene Vorlage nach den Eigenschaftswerten eines Elements durchsucht, wird nach der Vorlage gesucht, die das Element erstellt hat. Die Eigenschaftswerte aus der Vorlage TemplatedParent verhalten sich im Allgemeinen so, als seien sie lokal festgelegte Werte für das Element, jedoch mit geringerer Priorität als die tatsächlichen lokalen Werte, da Vorlagen potenziell gemeinsam genutzt werden. Weitere Informationen finden Sie unter TemplatedParent.

Die Style-Eigenschaft

Mit Ausnahme der Eigenschaft Style gilt für alle Abhängigkeitseigenschaften die gleiche Rangfolge. Die Eigenschaft Style ist insofern einzigartig, als sie selbst nicht formatiert werden kann. Eine Koersion oder Animation der Style-Eigenschaft wird nicht empfohlen (und die Animation der Style-Eigenschaft würde eine benutzerdefinierte Animationsklasse erfordern). Folglich gelten nicht alle Rangfolgenelemente. Es gibt nur drei Möglichkeiten zum Festlegen der Style-Eigenschaft:

  • Expliziter Stil. Die Style-Eigenschaft eines Elements wird direkt festgelegt. Der Wert der Eigenschaft Style verhält sich wie ein lokaler Wert und hat die gleiche Priorität wie Element 3 in der Rangfolgenliste. In den meisten Szenarien werden explizite Stile nicht inline definiert, sondern stattdessen explizit als Ressource referenziert, z. B. Style="{StaticResource myResourceKey}".

  • Impliziter Stil. Die Style-Eigenschaft eines Elements wird nicht direkt festgelegt. Stattdessen wird ein Stil angewendet, wenn er auf einer bestimmten Ebene innerhalb der Seite oder Anwendung vorhanden ist und einen Ressourcenschlüssel aufweist, der mit dem Elementtyp übereinstimmt, auf den der Stil angewendet wird (zum Beispiel <Style TargetType="x:Type Button">). Der Typ muss genau übereinstimmen, daher wird zum Beispiel <Style TargetType="x:Type Button"> nicht auf den Typ MyButton angewendet, auch wenn MyButton von Button abgeleitet ist. Der Wert der Eigenschaft Style hat die gleiche Priorität wie Element 5 in der Rangfolgenliste. Ein impliziter Stilwert lässt sich ermitteln, indem man die DependencyPropertyHelper.GetValueSource-Methode aufruft, die Style-Eigenschaft übergibt und in den Ergebnissen nach ImplicitStyleReference sucht.

  • Standardstil, auch bekannt als Designstil. Die Style-Eigenschaft eines Elements wird nicht direkt festgelegt. Stattdessen wird sie per Laufzeitdesignauswertung durch die WPF-Präsentations-Engine ermittelt. Vor der Laufzeit lautet der Wert der Style-Eigenschaft null. Der Wert der Eigenschaft Style hat die gleiche Priorität wie Element 9 in der Rangfolgenliste.

Standardstile (Designstile)

Jedes Steuerelement im Lieferumfang von WPF umfasst einen Standardstil, der je nach Design variieren kann. Aus diesem Grund wird der Standardstil gelegentlich auch als Designstil bezeichnet.

ControlTemplate ist ein wichtiges Element innerhalb des Standardstils für ein Steuerelement. ControlTemplate ist ein Setterwert für die Eigenschaft Template des Stils. Wenn die Standardstile keine Vorlage enthalten würden, hätte ein Steuerelement ohne eine benutzerdefinierte Vorlage als Teil eines benutzerdefinierten Stils kein visuelles Erscheinungsbild. Eine Vorlage definiert nicht nur das visuelle Erscheinungsbild eines Steuerelements, sondern auch die Verbindungen zwischen den Eigenschaften in der visuellen Struktur der Vorlage und der entsprechenden Steuerelementklasse. Jedes Steuerelement macht einen Satz von Eigenschaften verfügbar, die die visuelle Darstellung des Steuerelements beeinflussen können, ohne die Vorlage zu ersetzen. Betrachten Sie zum Beispiel das Standardaussehen eines Thumb-Steuerelements, bei dem es sich um eine ScrollBar-Komponente handelt.

Ein Thumb-Steuerelement umfasst bestimmte anpassbare Eigenschaften. Die Standardvorlage eines Thumb-Steuerelements erzeugt eine Basis- bzw. visuelle Struktur mit mehreren geschachtelten Border-Komponenten, um ein abgeschrägtes Aussehen zu erzeugen. Innerhalb der Vorlage werden Eigenschaften, die über die Klasse Thumb anpassbar sein sollen, durch TemplateBinding verfügbar gemacht. Die Standardvorlage für das Steuerelement Thumb hat verschiedene Rahmeneigenschaften, die eine Vorlagenbindung mit Eigenschaften wie Background oder BorderThickness gemeinsam haben. Wenn jedoch Werte für Eigenschaften oder visuelle Anordnungen in der Vorlage hartcodiert oder an Werte gebunden sind, die direkt aus dem Design stammen, können Sie diese Werte nur ändern, indem Sie die gesamte Vorlage ersetzen. Wenn eine Eigenschaft aus einer übergeordneten Vorlage stammt und nicht durch eine TemplateBinding verfügbar gemacht wird, kann der Eigenschaftswert in der Regel nicht durch Stile geändert werden, da es keine einfache Möglichkeit gibt, ihn anzusteuern. Diese Eigenschaft kann jedoch weiterhin durch die Vererbung von Eigenschaftswerten in der angewendeten Vorlage oder durch einen Standardwert beeinflusst werden.

Standardstile geben einen TargetType in ihren Definitionen an. Bei der Laufzeitdesignauswertung wird der TargetType eines Standardstils mit der DefaultStyleKey-Eigenschaft eines Steuerelements abgeglichen. Im Gegensatz dazu wird beim Suchverhalten für implizite Stile der tatsächliche Typ des Steuerelements verwendet. Der Wert von DefaultStyleKey wird an abgeleitete Klassen vererbt, sodass abgeleitete Elemente, die andernfalls möglicherweise keinem Stil zugeordnet werden, ein Standardaussehen erhalten. Wenn Sie zum Beispiel MyButton von Button ableiten, erbt MyButton die Standardvorlage von Button. Abgeleitete Klassen können den Standardwert von DefaultStyleKey in den Metadaten der Abhängigkeitseigenschaft überschreiben. Wenn Sie also eine andere visuelle Darstellung für MyButton benötigen, können Sie die Metadaten der Abhängigkeitseigenschaft für DefaultStyleKey auf MyButton außer Kraft setzen und dann den relevanten Standardstil einschließlich einer Vorlage definieren, die Sie mit Ihrem Steuerelement MyButton paketieren. Weitere Informationen finden Sie unter Übersicht über das Erstellen von Steuerelementen.

Dynamische Ressource

Dynamische Ressourcenverweise und Bindungsvorgänge berücksichtigen die Rangfolge des Standorts, an dem sie festgelegt werden. Beispielsweise hat eine dynamische Ressource, die auf einen lokalen Wert angewendet wird, die gleiche Priorität wie Element 3 in der Rangfolgenliste. Ein weiteres Beispiel: Eine dynamische Ressourcenbindung, die auf einen Eigenschaftssetter innerhalb eines Standardstils angewendet wird, hat die gleiche Priorität wie Element 9 in der Rangfolgenliste. Da dynamische Ressourcenverweise und Bindungen Werte aus dem Laufzeitzustand der Anwendung beziehen müssen, erstreckt sich der Prozess zur Bestimmung der Priorität von Eigenschaftswerten für jedwede Eigenschaft bis zur Laufzeit.

Dynamische Ressourcenverweise sind technisch gesehen nicht Teil des Eigenschaftensystems und verfügen über ihre eigene Suchreihenfolge, die mit der Rangfolgenliste interagiert. Die Rangfolge der dynamischen Ressourcenverweise lautet im Wesentlichen: Element zu Seitenstamm, Anwendung, Design, System. Weitere Informationen finden Sie unter XAML-Ressourcen.

Obwohl dynamische Ressourcenverweise und Bindungen die Priorität des Standorts aufweisen, an dem sie festgelegt werden, wird der Wert zurückgestellt. Dies hat zur Folge, dass bei der Festlegung einer dynamischen Ressource oder Bindung auf einen lokalen Wert jede Änderung des lokalen Werts die dynamische Ressource oder Bindung vollständig ersetzt. Selbst wenn Sie die ClearValue-Methode aufrufen, um den lokal festgelegten Wert zu löschen, wird die dynamische Ressource oder Bindung nicht wiederhergestellt. Tatsächlich wird beim Aufruf von ClearValue für eine Eigenschaft mit einer dynamischen Ressource oder Bindung (ohne literalen lokalen Wert) die dynamische Ressource oder Bindung gelöscht.

SetCurrentValue

Die SetCurrentValue-Methode ist eine weitere Möglichkeit zum Festlegen einer Eigenschaft, aber sie ist nicht in der Rangfolgenliste enthalten. Mit SetCurrentValue können Sie den Wert einer Eigenschaft ändern, ohne die Quelle eines vorherigen Werts zu überschreiben. Wenn beispielsweise eine Eigenschaft durch einen Trigger festgelegt wird und Sie dann mit SetCurrentValue einen anderen Wert zuweisen, setzt die nächste Triggeraktion die Eigenschaft wieder auf den Triggerwert zurück. Sie können SetCurrentValue immer dann verwenden, wenn Sie einen Eigenschaftswert festlegen möchten, ohne diesem Wert die Priorität eines lokalen Werts zu geben. In ähnlicher Weise können Sie SetCurrentValue verwenden, um den Wert einer Eigenschaft zu ändern, ohne eine Bindung zu überschreiben.

Koersion und Animation

Koersion und Animation wirken beide auf einen Basiswert. Der Basiswert ist der Wert der Abhängigkeitseigenschaft mit der höchsten Priorität, der durch eine Auswertung der Rangfolgenliste von unten nach oben ermittelt wird, bis Element 2 erreicht ist.

Wenn in einer Animation nicht sowohl From- als auch To-Eigenschaftswerte für ein bestimmtes Verhalten angegeben sind, oder wenn die Animation nach Abschluss gezielt auf den Basiswert zurückgesetzt wird, dann kann der Basiswert den animierten Wert beeinflussen. Um dies in der Praxis zu sehen, führen Sie die Beispielanwendung Target Values aus. Versuchen Sie, die lokalen Werte für die Höhe des Rechtecks im Beispiel so festzulegen, dass sich der ursprüngliche lokale Wert von jedem From-Wert unterscheidet. Die Beispielanimationen beginnen sofort, wobei der Wert From anstelle des Basiswerts verwendet wird. Durch die Angabe von Stop als FillBehavior wird bei Abschluss einer Animation ein Eigenschaftswert auf seinen Basiswert zurückgesetzt. Nach der Beendigung einer Animation wird die normale Rangfolge für die Bestimmung des Basiswerts verwendet.

Auf eine einzelne Eigenschaft können mehrere Animationen angewendet werden, wobei jede Animation eine andere Priorität besitzt. Anstatt die Animation mit der höchsten Priorität anzuwenden, kann die WPF-Präsentations-Engine die Animationswerte kombinieren, je nachdem, wie die Animationen definiert wurden und welche Art von Werten animiert werden. Weitere Informationen finden Sie unter Übersicht über Animation.

Die Koersion befindet sich ganz oben in der Rangfolgenliste. Selbst eine ausgeführte Animation unterliegt der Wertkoersion. Einige vorhandene Abhängigkeitseigenschaften in WPF verfügen über eine integrierte Koersion. Für benutzerdefinierte Abhängigkeitseigenschaften können Sie das Koersionsverhalten definieren, indem Sie einen CoerceValueCallback schreiben, den Sie als Teil der Metadaten übergeben, wenn Sie eine Eigenschaft erstellen. Sie können das Koersionsverhalten vorhandener Eigenschaften auch überschreiben, indem Sie die Metadaten für diese Eigenschaft in einer abgeleiteten Klasse überschreiben. Die Koersion interagiert so mit dem Basiswert, dass die zum diesem Zeitpunkt vorliegenden Einschränkungen der Koersion angewendet werden, der Basiswert jedoch trotzdem erhalten bleibt. Dies führt dazu, dass beim späteren Aufheben der Einschränkungen die Koersion den Wert zurückgibt, der dem Basiswert am nächsten liegt, und eine Eigenschaft wird möglicherweise nicht mehr durch die Koersion beeinflusst, sobald alle Einschränkungen aufgehoben wurden. Weitere Informationen zum Koersionsverhalten finden Sie unter Rückrufe und Validierung von Abhängigkeitseigenschaften.

Triggerverhalten

Steuerelemente definieren das Triggerverhalten häufig als Teil ihres Standardstils. Das Festlegen lokaler Eigenschaften für Steuerelemente kann möglicherweise zu Konflikten mit diesen Triggern führen und verhindern, dass die Trigger auf benutzergesteuerte Ereignisse reagieren (entweder visuell oder per Verhalten). Ein üblicher Verwendungszweck eines Eigenschaftsauslösers ist die Steuerung von Zustandseigenschaften wie z. B. IsSelected oder IsEnabled. Wenn beispielsweise ein Button deaktiviert ist, legt ein Designstiltrigger (IsEnabled = false) den Wert Foreground standardmäßig so fest, dass Button abgeblendet dargestellt wird. Wenn Sie einen lokalen Foreground-Wert festgelegt haben, hat der lokale Eigenschaftswert mit höherer Priorität Vorrang vor dem Wert Foreground des Designstils, selbst wenn Button deaktiviert ist. Beim Festlegen von Eigenschaftswerten, die das Triggerverhalten auf Designebene für ein Steuerelement außer Kraft setzen, sollten Sie darauf achten, die vorgesehenen Benutzerfunktionen für dieses Steuerelement nicht übermäßig zu beeinträchtigen.

ClearValue

Die Methode ClearValue löscht jeden lokal angewendeten Wert einer Abhängigkeitseigenschaft für ein Element. Der Aufruf von ClearValue garantiert jedoch nicht, dass der während der Eigenschaftsregistrierung in den Metadaten festgelegte Standardwert der neue effektive Wert ist. Alle übrigen Elemente in der Rangfolgenliste sind weiterhin aktiv, und nur der lokal festgelegte Wert wird entfernt. Wenn Sie z. B. ClearValue für eine Eigenschaft aufrufen, die einen Designstil aufweist, wird der Designstilwert als neuer Wert angewendet, nicht der auf den Metadaten basierende Standardwert. Wenn Sie einen Eigenschaftswert auf den Standardwert der registrierten Metadaten festlegen möchten, rufen Sie den Standardwert der Metadaten ab, indem Sie die Metadaten der Abhängigkeitseigenschaft abfragen. Anschließend legen Sie den Eigenschaftswert lokal mit einem Aufruf von SetValue fest.

Weitere Informationen