Übersicht über XAML-Ressourcen (WPF .NET)

Eine Ressource ist ein Objekt, das an unterschiedlichen Stellen in Ihrer App wiederverwendet werden kann. Beispiele für Ressourcen sind Pinsel und Stile. In dieser Übersicht wird beschrieben, wie Ressourcen in XAML (Extensible Application Markup Language) verwendet werden. Sie können auch mithilfe von Code Ressourcen erstellen und auf diese zugreifen.

Hinweis

Die in diesem Artikel beschriebenen XAML-Ressourcen unterscheiden sich von App-Ressourcen, die in der Regel Dateien sind, die einer App hinzugefügt werden, z. B. Inhalte, Daten oder eingebettete Dateien.

Wichtig

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

Verwenden von Ressourcen in XAML

Im folgenden Beispiel wird ein SolidColorBrush-Element als Ressource für das Stammelement einer Seite definiert. Anschließend verweist das Beispiel auf die Ressource und verwendet sie, um Eigenschaften mehrerer untergeordneter Elemente festzulegen, einschließlich eines Ellipse-, eines TextBlock- und eines Button-Elements.

<Window x:Class="resources.ResExample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ResExample" Height="400" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="#05E0E9"/>
        <Style TargetType="Border">
            <Setter Property="Background" Value="#4E1A3D" />
            <Setter Property="BorderThickness" Value="5" />
            <Setter Property="BorderBrush">
                <Setter.Value>
                    <LinearGradientBrush>
                        <GradientStop Offset="0.0" Color="#4E1A3D"/>
                        <GradientStop Offset="1.0" Color="Salmon"/>
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="TextBlock" x:Key="TitleText">
            <Setter Property="FontSize" Value="18"/>
            <Setter Property="Foreground" Value="#4E87D4"/>
            <Setter Property="FontFamily" Value="Trebuchet MS"/>
            <Setter Property="Margin" Value="0,10,10,10"/>
        </Style>
        <Style TargetType="TextBlock" x:Key="Label">
            <Setter Property="HorizontalAlignment" Value="Right"/>
            <Setter Property="FontSize" Value="13"/>
            <Setter Property="Foreground" Value="{StaticResource MyBrush}"/>
            <Setter Property="FontFamily" Value="Arial"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="Margin" Value="0,3,10,0"/>
        </Style>
    </Window.Resources>

    <Border>
        <StackPanel>
            <TextBlock Style="{StaticResource TitleText}">Title</TextBlock>
            <TextBlock Style="{StaticResource Label}">Label</TextBlock>
            <TextBlock HorizontalAlignment="Right" FontSize="36" Foreground="{StaticResource MyBrush}" Text="Text" Margin="20" />
            <Button HorizontalAlignment="Left" Height="30" Background="{StaticResource MyBrush}" Margin="40">Button</Button>
            <Ellipse HorizontalAlignment="Center" Width="100" Height="100" Fill="{StaticResource MyBrush}" Margin="10" />
        </StackPanel>
    </Border>

</Window>

Jedes Element auf Frameworkebene (FrameworkElement oder FrameworkContentElement) verfügt über eine Resources-Eigenschaft. Hierbei handelt es sich um einen ResourceDictionary-Typ, der definierte Ressourcen enthält. Sie können Ressourcen für beliebige Elemente definieren, z. B.für ein Button-Element. Ressourcen werden jedoch am häufigsten für das Stammelement definiert. In diesem Beispiel ist es eine Window.

Jeder Ressource in einem Ressourcenverzeichnis muss ein eindeutiger Schlüssel zugewiesen werden. Wenn Sie Ressourcen in Markup definieren, weisen Sie den eindeutigen Schlüssel durch die x:Key-Anweisung zu. In der Regel ist der Schlüssel eine Zeichenfolge. Sie können jedoch den Schlüssel auch als einen anderen Objekttyp definieren, indem Sie die entsprechenden Markuperweiterungen verwenden. Nichtzeichenfolgeschlüssel für Ressourcen werden von bestimmten Funktionsbereichen in WPF verwendet, insbesondere für Stile, Komponentenressourcen und Datenformatierung.

Sie können eine definierte Ressource mit der Syntax der Ressourcenmarkuperweiterung verwenden, die den Schlüsselnamen der Ressource angibt. Verwenden Sie beispielsweise die Ressource als Wert einer Eigenschaft für ein anderes Element.

<Button Background="{StaticResource MyBrush}"/>
<Ellipse Fill="{StaticResource MyBrush}"/>

Wenn im vorherigen Beispiel das XAML-Ladeprogramm den Wert {StaticResource MyBrush} für die Background-Eigenschaft für Button verarbeitet, überprüft die Ressourcennachschlagelogik zuerst das Ressourcenverzeichnis auf das Button-Element. Wenn Button nicht über eine Definition des Ressourcenschlüssels MyBrush (in diesem Beispiel ist dies nicht der Fall, die Ressourcensammlung ist leer), überprüft die Nachschlagefunktion als nächstes das übergeordnete Element von Button. Wenn die Ressource nicht auf dem übergeordneten Element definiert ist, wird die logische Struktur des Objekts nach oben überprüft, bis sie gefunden wurde.

Wenn Sie eine Ressource für das Window-Stammelement definieren, können alle Elemente in der logischen Struktur des Page-Elements darauf zugreifen. Und Sie können dieselbe Ressource wiederverwenden, um den Wert einer beliebigen Eigenschaft festzulegen, die denselben Typ akzeptiert, den die Ressource darstellt. Im vorherigen Beispiel legt die gleiche MyBrush-Ressource zwei verschiedene Eigenschaften fest: Button.Background und Ellipse.Fill.

Statische und dynamische Ressourcen

Auf eine Ressource kann als statische oder dynamische Ressource verwiesen werden. Verweise werden über die StaticResource-Markuperweiterung oder die DynamicResource-Markuperweiterung erstellt. Eine Markuperweiterung ist eine XAML-Funktion, mit der Sie einen Objektverweis angeben können, indem die Markuperweiterung die Attributzeichenfolge verarbeitet und das Objekt an das XAML-Ladeprogramm zurückgibt. Weitere Inforationen zum Verhalten von Markuperweiterungen finden Sie unter Markuperweiterungen und WPF-XAML.

Wenn Sie eine Markuperweiterung verwenden, geben Sie in der Regel einen oder mehrere Parameter im Zeichenfolgenformat an, die von dieser speziellen Markuperweiterung verarbeitet werden. Die StaticResource-Markuperweiterung verarbeitet einen Schlüssel, indem sie nach dem Wert für diesen Schlüssel in allen verfügbaren Ressourcenverzeichnissen sucht. Die Verarbeitung erfolgt beim Laden, d. h., wenn der Ladeprozess den Eigenschaftswert zuweisen muss. Die DynamicResource-Markuperweiterung verarbeitet stattdessen einen Schlüssel, indem sie einen Ausdruck erstellt. Dieser Ausdruck wird nicht ausgewertet, bis die App ausgeführt wird. Zu diesem Zeitpunkt wird der Ausdruck ausgewertet und ergibt einen Wert.

Wenn Sie auf eine Ressource verweisen, sind bei der Wahl zwischen einem statischen und einem dynamischen Ressourcenverweis folgende Aspekte zu beachten:

  • Wenn Sie den allgemeinen Entwurf festlegen, wie Sie die Ressourcen für Ihre Anwendung erstellen (pro Seite, in der App, in „losem“ XAML oder in einer reinen Ressourcenassembly), sollten Sie Folgendes berücksichtigen:

  • Die Funktionalität der App. Ist die Aktualisierung von Ressourcen in Echtzeit Bestandteil Ihrer Anwendungsanforderungen?

  • Das Suchverhalten des jeweiligen Ressourcenverweistyps.

  • Jeweiliger Eigenschafts- oder Ressourcentyp sowie dessen natives Verhalten.

Statische Ressourcen

Statische Ressourcenverweise funktionieren optimal unter folgenden Bedingungen:

  • Der App-Entwurf konzentriert die meisten seiner Ressourcen in Ressourcenverzeichnissen auf Seiten- oder Anwendungsebene.

    Statische Ressourcenverweise werden nicht basierend auf Laufzeitverhalten wie dem erneuten Laden einer Seite erneut ausgewertet. Es kann also ein gewisser Leistungsvorteil entstehen, wenn Sie eine große Anzahl dynamischer Ressourcenverweise vermeiden, wenn diese aufgrund Ihres Ressourcen- und App-Entwurfs nicht erforderlich sind.

  • Sie legen den Wert einer Eigenschaft fest, die nicht für einen DependencyObject oder ein Freezable-Element ist.

  • Sie erstellen ein Ressourcenwörterbuch, das in eine DLL kompiliert wird, die für Apps freigegeben ist.

  • Sie erstellen ein Design für ein benutzerdefiniertes Steuerelement und definieren Ressourcen, die in den Designs verwendet werden.

    In diesem Fall möchten Sie in der Regel nicht das Verhalten der dynamischen Ressourcenreferenz. Verwenden Sie stattdessen das Verhalten statischer Ressourcenreferenzen, sodass das Nachschlageverhalten vorhersagbar und für das Design selbst enthalten ist. Bei einem dynamischen Ressourcenverweis wird sogar ein Verweis innerhalb eines Designs erst zur Laufzeit ausgewertet. Daher besteht die Möglichkeit, dass bei der Anwendung des Designs ein Schlüssel, auf den das Design zu verweisen versucht, von einem lokalen Element neu definiert wird, und das lokale Element statt des Designs bei der Suche gefunden wird. In diesem Fall wird Ihr Design sich nicht wie erwartet verhalten.

  • Ressourcen werden verwendet, um die große Anzahl von Abhängigkeitseigenschaften festzulegen. Abhängigkeitseigenschaften können auf das effektive Zwischenspeichern von Werten des Eigenschaftensystems zugreifen. Wenn Sie einer Abhängigkeitseigenschaft einen Wert bereitstellen, der zur Ladezeit ausgewertet werden kann, ist keine Neuauswertung des Ausdrucks von der Abhängigkeitseigenschaft erforderlich. Sie kann den letzten effektiven Wert zurückgeben. Mit diesem Verfahren kann ein Leistungsvorteil erzielt werden.

  • Sie möchten die zugrunde liegende Ressource für alle Consumer ändern oder für jeden einzelnen Consumer separate Instanzen mit Schreibzugriff unter Verwendung der x: Shared-Attribute verwalten.

Suchverhalten von statischen Ressourcen

Im Folgenden wird der Suchvorgang beschrieben, der automatisch erfolgt, wenn von einer Eigenschaft oder einem Element auf eine statische Ressource verwiesen wird:

  1. Der Suchprozess prüft den angeforderten Schlüssel im Ressourcenverzeichnis, das durch das Element definiert wird, das die Eigenschaft festlegt.

  2. Der Suchprozess durchläuft anschließend die logische Struktur aufwärts zum übergeordneten Element und dessen Ressourcenverzeichnis. Dieser Vorgang wird fortgesetzt, bis das Stammelement erreicht wird.

  3. App-Ressourcen werden überprüft. App-Ressourcen sind die Ressourcen im Ressourcenverzeichnis, die vom Application-Objekt für Ihre WPF-Anwendung definiert werden.

Statische Ressourcenverweise innerhalb eines Ressourcenverzeichnisses müssen auf eine Ressource verweisen, die bereits vor dem Ressourcenverweis lexikalisch definiert worden ist. Vorwärtsverweise können von einem statischen Ressourcenverweis nicht aufgelöst werden. Gestalten Sie daher Ihre Ressourcenverzeichnisstruktur so, dass die Ressourcen am oder nahe dem Anfang des jeweiligen Ressourcenverzeichnisses definiert werden.

Statische Ressourcensuche kann auf Designs oder auf Systemressourcen ausgeweitet werden. Diese Suche wird aber nur aus dem Grund unterstützt, dass das XAML-Ladeprogramm die Anforderung verzögert. Die Verzögerung ist erforderlich, damit das Laufzeitdesign zum Zeitpunkt des Ladens der Seite ordnungsgemäß auf die App angewendet wird. Statische Ressourcenverweise auf Schlüssel, von denen bekannt ist, dass sie nur in Designs oder als Systemressourcen vorhanden sind, sind jedoch nicht zu empfehlen, da solche Verweise nicht erneut ausgewertet werden, wenn das Design vom Benutzer in Echtzeit geändert wird. Dynamische Ressourcenverweise sind zuverlässiger, wenn Sie Designs oder Systemressourcen abfragen. Eine Ausnahme bilden Abfragen, bei denen ein Designelement selbst andere Ressourcen abfragt. Diese Verweise sind aus den oben genannten Gründen als statische Ressourcenverweise zu definieren.

Falls statistische Verweise nicht gefunden werden, sind verschiedene Ausnahmeverhaltensmuster möglich. Falls die Ressource verzögert wurde, tritt die Ausnahme zur Laufzeit ein. Falls die Ressource nicht verzögert wurde, tritt die Ausnahme zur Ladezeit ein.

Dynamische Ressourcen

Dynamische Ressourcen funktionieren am besten, wenn Folgendes gilt:

  • Der Wert der Ressource, einschließlich der Systemressourcen oder Ressourcen, die anderweitig vom Benutzer festgelegt werden können, hängt von Bedingungen ab, die erst zur Laufzeit bekannt sind. Beispielsweise können Sie Setterwerte erstellen, die sich auf Systemeigenschaften beziehen, wie sie durch SystemColors, SystemFonts oder SystemParameters offengelegt werden. Diese Werte sind wirklich dynamisch, da sie letztlich von der Laufzeitumgebung des Benutzers und dem Betriebssystem stammen. Möglicherweise haben Sie außerdem Designs auf der Anwendungsebene, die sich ändern können, und deren Veränderungen auch vom Ressourcenzugriff auf der Seitenebene erfasst werden müssen.

  • Sie erstellen Designstile für ein benutzerdefiniertes Steuerelement oder verweisen darauf.

  • Sie beabsichtigen, den Inhalt eines ResourceDictionary-Elements während der Lebensdauer einer App anzupassen.

  • Sie haben eine komplizierte Ressourcenstruktur mit gegenseitigen Abhängigkeiten, die möglicherweise Vorwärtsverweise erfordert. Im Gegensatz zu statischen Ressourcenverweisen, unterstützen dynamische Ressourcenverweise Vorwärtsverweise, da die Ressource erst zur Laufzeit ausgewertet werden muss, und Vorwärtsverweise daher kein relevantes Konzept sind.

  • Sie verweisen auf eine Ressource, die aus der Perspektive eines kompilieren oder Arbeitssatzes groß ist, und möglicherweise beim Laden der Seite nicht sofort verwendet wird. Statische Ressourcenverweise werden immer aus XAML geladen, wenn die Seite geladen wird. Ein dynamischer Ressourcenverweis wird jedoch erst geladen, wenn er verwendet wird.

  • Sie erstellen einen Stil, bei dem Setterwerte von anderen Werten stammen könnten, die durch Designs oder andere Benutzereinstellungen beeinflusst werden.

  • Sie ordnen Ressourcen Elementen zu, die in der logischen Struktur während der Lebensdauer der App neu zugeordnet werden können. Beim erneuten Zuordnen der Elemente wird möglicherweise auch der Ressourcensuchbereich neu definiert. Wenn Sie wollen, dass die Ressource für ein neu zugeordnetes Element entsprechend dem neuen Suchbereich neu ausgewertet wird, verwenden Sie immer einen dynamischen Ressourcenverweis.

Suchverhalten von dynamischen Ressourcen

Das Verhalten der Ressourcensuche für einen dynamischen Ressourcenverweis weist Parallelen zum Nachschlageverhalten in Ihrem Code auf, wenn Sie FindResource oder SetResourceReference aufrufen:

  1. Die Suche prüft den angeforderten Schlüssel im Ressourcenverzeichnis, das durch das Element definiert wird, das die Eigenschaft festlegt:

  2. Die Suche durchläuft die logische Struktur aufwärts bis zum übergeordneten Element und dessen Ressourcenverzeichnis. Dieser Vorgang wird fortgesetzt, bis das Stammelement erreicht wird.

  3. App-Ressourcen werden überprüft. App-Ressourcen sind die Ressourcen im Ressourcenverzeichnis, die vom Application-Objekt für Ihre WPF-Anwendung definiert werden.

  4. Das Ressourcenverzeichnis des Designs wird für das derzeit aktive Design überprüft. Falls das Design zur Laufzeit geändert wird, wird der Wert neu ausgewertet.

  5. Systemressourcen werden geprüft.

Es sind verschiedene Ausnahmeverhaltensmuster möglich (sofern sie auftreten):

  • Wenn eine Ressource von einem FindResource-Aufruf angefordert und nicht gefunden wurde, wird eine Ausnahme ausgelöst.

  • Wenn eine Ressource von einem TryFindResource-Aufruf angefordert und nicht gefunden wurde, wird keine Ausnahme ausgelöst, und der zurückgegebene Wert ist null. Wenn die festzulegende Eigenschaft null nicht akzeptiert, ist es dennoch möglich, dass eine tiefere Ausnahme ausgelöst wird. Dies hängt von der jeweiligen Eigenschaft ab, die festgelegt wird.

  • Wenn eine Ressource von einem dynamischen Ressourcenverweis in XAML angefordert und nicht gefunden wurde, hängt das Verhalten vom allgemeinen Eigenschaftensystem ab. Das allgemeine Verhalten ist so, als ob auf der Ebene, auf der die Ressource vorhanden ist, kein Vorgang zum Festlegen von Eigenschaften stattfindet. Wenn Sie z. B. versuchen, den Hintergrund eines einzelnen Schaltflächenelements mit einer Ressource festzulegen, die nicht ausgewertet werden konnte, wird als Ergebnis kein Wert festgelegt. Ein effektiver Wert kann aber dennoch von anderen Teilnehmern im Eigenschaftssystem und in der Wertrangfolge bereitgestellt werden. Beispielsweise kann der Wert für den Hintergrund von einem lokal definierten Schaltflächenstil oder vom Designstil zurückgegeben werden. Für Eigenschaften, die nicht von Designstilen definiert werden, kann nach einer fehlgeschlagenen Auswertung der Standardwert aus den Eigenschaftenmetadaten als effektiver Wert übernommen werden.

Beschränkungen

Die Verwendung dynamischer Verweise ist mit einigen wichtigen Beschränkungen verbunden. Mindestens eine der folgenden Bedingungen muss erfüllt sein:

Da die festzulegende Eigenschaft eine DependencyProperty- oder eine Freezable-Eigenschaft sein muss, können die meisten Eigenschaftsänderungen an die Benutzeroberfläche weitergeleitet werden, da Eigenschaftsänderungen (die Werte der geänderten dynamischen Ressourcen) vom Eigenschaftensystem anerkannt werden. Die meisten Steuerelemente enthalten eine Logik, die ein anderes Layout eines Steuerelements erzwingt, wenn eine DependencyProperty geändert wird, und diese Eigenschaft sich auf das Layout auswirken könnte. Allerdings ist nicht für alle Eigenschaften, die eine DynamicResource-Markuperweiterung als Wert aufweisen, garantiert, dass sie Echtzeitaktualisierungen in der Benutzeroberfläche bereitstellen. Diese Funktionalität kann dennoch variieren, abhängig sowohl von der Eigenschaft als auch vom Typ, der die Eigenschaft besitzt oder sogar von der logischen Struktur Ihrer App.

Stile, DataTemplates und implizite Schlüssel

Obwohl alle Elemente in einem ResourceDictionary-Element über einen Schlüssel verfügen müssen, bedeutet das nicht, dass alle Ressourcen über einen expliziten x:Keyverfügen müssen. Manche Objekttypen unterstützen einen impliziten Schlüssel, wenn sie als Ressourcen definiert sind, wobei der Schlüsselwert an den Wert einer anderen Eigenschaft gebunden ist. Dieser Schlüsseltyp wird als impliziter Schlüssel bezeichnet, während ein x:Key-Attribut ein expliziter Schlüssel ist. Sie können jegliche implizite Schlüssel durch die Angabe eines expliziten Schlüssels überschreiben.

Ein besonders wichtiges Szenario für Ressourcen ist das Definieren eines Style. Tatsächlich ist ein Style fast immer als ein Eintrag in einem Ressourcenverzeichnis definiert, da Stile grundsätzlich zur Wiederverwendung vorgesehen sind. Weitere Informationen zu Stilen finden Sie unter Erstellen von Formaten und Vorlagen (WPF .NET).

Mit einem impliziten Schlüssel können Stile für Steuerelemente erstellt werden, und es kann damit auf sie verwiesen werden. Die Design-Stile, die die Standarddarstellung eines Steuerelements definieren, basieren auf diesen impliziten Schlüsseln. Aus Sicht der Anforderung ist der implizite Schlüssel der Type des Steuerelements selbst. Unter dem Gesichtspunkt der Definition der Ressourcen ist der implizite Schlüssel der TargetType des Stils. Wenn Sie daher Designs für benutzerdefinierte Steuerelemente oder Stile erstellen, die mit vorhandenen Design-Stilen interagieren, müssen Sie keine x:Key-Anweisung für diesen Style angeben. Und wenn Sie die Design-Stile verwenden möchten, müssen Sie gar keinen Stil angeben. Beispielsweise funktioniert die folgende Definition eines Stils, obwohl die Style-Ressource nicht über einen Schlüssel zu verfügen scheint:

<Style TargetType="Button">
    <Setter Property="Background" Value="#4E1A3D" />
    <Setter Property="Foreground" Value="White" />
    <Setter Property="BorderThickness" Value="5" />
    <Setter Property="BorderBrush">
        <Setter.Value>
            <LinearGradientBrush>
                <GradientStop Offset="0.0" Color="#4E1A3D"/>
                <GradientStop Offset="1.0" Color="Salmon"/>
            </LinearGradientBrush>
        </Setter.Value>
    </Setter>
</Style>

Dieser Stil besitzt aber tatsächlich einen Schlüssel: den impliziten Schlüssel: den System.Windows.Controls.Button-Typ. Im Markup können Sie einen TargetType direkt als den Typnamen angeben (optional können Sie {x:Type...} verwenden, um einen Type zurückzugeben.

Durch die von WPF verwendeten Standardmechanismen für Designstile wird dieser Stil als Laufzeitstil eines Button-Elements auf der Seite angewendet, auch wenn das Button-Element selbst nicht versucht, seine Style-Eigenschaft oder einen spezifischen Ressourcenverweis auf den Stil anzugeben. Ihr auf dieser Seite definierter Stil wird unter der Verwendung des selben Schlüssels in der Suchsequenz früher als der Stil des Designverzeichnisses gefunden. Sie könnten <Button>Hello</Button> an einer beliebigen Stelle auf der Seite anlegen, und der mit TargetType von Button definierte Stil würde auf diese Schaltfläche angewendet werden. Wenn Sie möchten, können Sie für den Stil dennoch aus Gründen der Übersichtlichkeit den Schlüssel mit demselben Typwert wie TargetType angeben. Das ist allerdings nur eine Option.

Implizite Schlüssel für Stile gelten nicht für ein Steuerelement, wenn OverridesDefaultStyletrue ist. (Beachten Sie auch, dass OverridesDefaultStyle als Teil des nativen Verhaltens für die Steuerelementklasse und nicht explizit für eine Instanz des Steuerelements festgelegt werden kann.) Um implizite Schlüssel für Szenarien mit abgeleiteten Klassen zu unterstützen, muss das Steuerelement DefaultStyleKey außer Kraft setzen (alle vorhandenen Steuerelemente, die mit WPF bereitgestellt werden, enthalten diese Außerkraftsetzung). Weitere Informationen zu Stilen, Designs und dem Steuerelemententwurf finden Sie unter Richtlinien zum Entwerfen formatierbarer Steuerelemente.

DataTemplate verfügt auch über einen impliziten Schlüssel. Der implizite Schlüssel für eine DataTemplate ist der DataType-Eigenschaftswert. DataType kann auch als der Name des Typs angegeben werden, anstatt {x:Type...} explizit zu verwenden. Weitere Informationen finden Sie unter Übersicht über Datenvorlagen.

Siehe auch