20 Konzepte der WPF
In diesem Kapitel lernen Sie mit Ressourcen, Styles, Triggern, Templates und Commands die grundlegenden Konzepte der WPF kennen.
20.1 Ressourcen 

Unter der WPF müssen wir zwei Gruppen von Ressourcen unterscheiden:
- binäre Ressourcen
- logische Ressourcen
Bei den binären Ressourcen kann es sich um Videos, Musik oder auch Bilder handeln, für die binäre Datenströme (Streams) notwendig sind. Im Grunde genommen handelt es sich dabei um nicht ausführbare Dateien, die mit einer Anwendung ausgeliefert werden müssen. Binäre Daten wurden schon von der ersten Version des .NET Frameworks unterstützt und stellen somit keine Besonderheit im Zusammenhang mit der WPF dar.
Ganz anders verhält es sich mit logischen Ressourcen, einem mit der WPF eingeführten neuen Konzept. Dabei handelt es sich um .NET-Objekte, die an mehreren Stellen einer WPF-Anwendung genutzt werden können. Ressourcen werden in einem mit Resources gekennzeichneten Abschnitt eines WPF-Elements angegeben. Resources ist als Eigenschaft in der Klasse FrameworkElement definiert, sodass nahezu jedes Control einen Resources-Abschnitt beschreiben kann.
Die später in diesem Kapitel behandelten Styles und Templates setzen logische Ressourcen voraus. Styles fassen Eigenschaften zusammen, die auf mehrere Steuerelemente angewendet werden können. Damit lässt sich das Layout vereinheitlichen. Templates gehen noch einen Schritt weiter und ermöglichen es, Steuerelemente vollständig umzugestalten. Sie können mit Templates zum Beispiel runde Schaltflächen definieren.
20.1.1 Wo logische Ressourcen definiert werden können 

Logische Ressourcen werden in der Regel im XAML-Code definiert. Alles, was Sie im XAML-Code festlegen, können Sie aber auch mit C#-Code erreichen. Allerdings ist der Weg über XAML der besser lesbare und normalerweise auch derjenige, der zu bevorzugen ist.
Ressourcen lassen sich nahezu jedem Objekt hinzufügen und müssen nicht an einer zentralen Stelle verwaltet werden. Dazu bieten sich drei Möglichkeiten an:
- Die logische Ressource wird anwendungsweit definiert. Das geschieht in der Datei App.xaml.
- Die logische Ressource wird in dem Window definiert, in dem sie benötigt wird. Die Ressource kann dabei sowohl auf Fensterebene als auch innerhalb einer Komponente definiert werden. Zu beachten ist hier die Regel, dass die Ressource bekannt sein muss, bevor sie benutzt wird.
- Logische Ressourcen können auch in einer externen Datei definiert sein.
Um eine Ressource anwendungsweit bereitzustellen, definieren Sie die Ressource in der Datei App.xaml, in der ein Abschnitt für die Ressourcen bereits vordefiniert ist.
<Application x:Class="WpfApplication.App" xmlns=http://... xmlns:x=http://... StartupUri="MainWindow.xaml"> <Application.Resources> </Application.Resources> </Application>
Auf die Ressourcen, die hier definiert sind, haben alle Elemente Zugriff: sowohl alle Window- als auch alle Steuerelemente.
Sie können einen Ressourcenbereich auch im Window festlegen. Allerdings müssen Sie diesen Bereich manuell angeben:
<Window x:Class="WpfApplication.MainWindow" xmlns="http://..." xmlns:x="http://..." Title="MainWindow" Height="300" Width="350"> <Window.Resources> <!-- windoweigene Ressourcen --> </Window.Resources> </Window>
In gleicher Weise lässt sich auch ein Abschnitt für Ressourcen beispielsweise in einem Button oder einem Grid definieren:
<Grid> <Grid.Resources> <!-- Ressourcen des Grids --> </Grid.Resources> <Button> <Button.Resources> <!-- Ressourcen der Schaltfläche --> </Button.Resources> </Button> </Grid>
Logische Ressourcen lassen sich in eine oder mehrere Ressourcendateien auslagern. Damit erhält man eine bessere Strukturierung der Anwendung, der XAML-Code wird übersichtlicher, und zudem können Ressourcendateien zur Laufzeit je nach Bedarf nachgeladen werden. Separate Ressourcendateien fügen Sie einer WPF-Anwendung unter dem etwas unglücklich übersetzten Begriff Ressourcenwörterbuch hinzu. Die XAML-Grundstruktur sieht etwas anders aus, sie wird mit dem Wurzelelement ResourceDictionary beschrieben.
<ResourceDictionary xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> </ResourceDictionary>
Innerhalb des Elements tragen Sie die Ressourcen ein, die in der Anwendung verwendet werden sollen.
Bei so vielen unterschiedlichen Ebenen, in denen Ressourcen bereitgestellt werden können, stellt sich unweigerlich die Frage, nach welchem Muster die Suche nach einer Ressource abläuft. Im Grunde genommen ist der Ablauf sehr einfach: Die Suche beginnt immer beim aktuellen Element, beispielsweise einem Button. Wird die Ressource hier nicht gefunden, setzt sich die Suche im nächsten übergeordneten Element fort. Hier könnte es sich beispielsweise um einen Grid-Container handeln. Wenn die Suche auch hier nicht zum Erfolg führt, wird unter den Window-Ressourcen gesucht, danach gegebenenfalls noch in den Application-Ressourcen.
20.1.2 Definition logischer Ressourcen 

Logische Ressourcen lassen sich mit Cascading Style Sheets (CSS) vergleichen, die ebenfalls zentral außerhalb ihres Einsatzgebietes definiert werden. Da Sie beliebig viele Ressourcen festlegen können, muss jede einzelne über einen eindeutigen Schlüssel identifizierbar sein. Dieser wird mit dem Key-Attribut beschrieben, das einem Namespace zugeordnet ist, der per Vorgabe durch das Präfix x beschrieben wird.
<SolidColorBrush x:Key="btnBackground">Red</SolidColorBrush>
Elemente, die auf Ressourcen zugreifen, verwenden die Markup-Erweiterungen StaticResource oder DynamicResource. Der Name verrät bereits den Unterschied zwischen diesen beiden Ressourcen. Mit StaticResource erfolgt der Zugriff statisch. Der Wert wird nur einmal ermittelt und zugewiesen und bleibt erhalten, solange das bindende Objekt existiert. Änderungen an der Ressource werden vom Objekt nicht wahrgenommen.
Ganz anders die Ressourcen, die mit DynamicResource eingebunden werden. Eine Änderung der Ressource wird dazu führen, dass das Objekt den neuen Ressourcenwert zur Kenntnis nimmt und darauf reagiert. Dieses Verhalten setzt voraus, dass die bindende Eigenschaft als Abhängigkeitseigenschaft (Dependency Property) implementiert ist. Nur eine abhängige Eigenschaft kann von einer Änderung der Ressource Notiz nehmen und entsprechend reagieren. Eine herkömmliche Eigenschaft ist dazu nicht in der Lage. Das bedeutet im Umkehrschluss auch, dass eine herkömmliche Eigenschaft nur statisch an eine Ressource gebunden werden kann.
Das folgende Programm zeigt die Nutzung von Ressourcen. In einem Grid sind zwei Schaltflächen platziert, die ihre Hintergrundfarbe aus einer Ressource beziehen, die einen Farbverlauf beschreibt und als Window-Ressource definiert ist. Das Fenster bezieht seine Ressourceninformationen aus der Datei App.xaml. Diese steht somit anwendungsweit allen Objekten der Anwendung gleichermaßen zur Verfügung
// --------------------------------------------------------- // Beispiel: ...\Kapitel 20\LogischeRessourcen // --------------------------------------------------------- <Window x:Class="LogischeRessourcen.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Logische Ressourcen" Height="350" Width="525" Background="{StaticResource wndBackground}"> <Window.Resources> <LinearGradientBrush x:Key="btnBackground" StartPoint="0,0" EndPoint="1,1"> <GradientStop Offset="0.0" Color="White" /> <GradientStop Offset="1.0" Color="Blue" /> </LinearGradientBrush> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="60" /> <RowDefinition Height="60" /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Button Grid.Row="0" Grid.Column="1" Width="150" Height="40" Background="{StaticResource btnBackground}">Button1</Button> <Button Grid.Row="1" Grid.Column="1" Width="150" Height="40" Background="{StaticResource btnBackground}">Button2</Button> </Grid> </Window>
Hier folgt jetzt die Datei App.xaml, in der die Ressource wndBackground zur Verfügung gestellt wird:
<Application x:Class="LogischeRessourcen.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml"> <Application.Resources> <LinearGradientBrush x:Key="wndBackground" StartPoint="0,0" EndPoint="1,1"> <GradientStop Offset="0.0" Color="White" /> <GradientStop Offset="1.0" Color="Gray" /> </LinearGradientBrush> </Application.Resources> </Application>
Abbildung 20.1 Die Ausgabe des Beispiels »LogischeRessourcen«
Jeder Ressource muss ein eindeutiger Schlüssel zugeordnet sein, unter der sie abrufbar ist. In unserem Beispiel lauten die beiden Schlüssel wie folgt:
x:Key="wndBackground"
und
x:Key="btnBackground"
Beim Zugriff auf eine Ressource wird die Markup-Erweiterung StaticResource verwendet. Dahinter folgt der eindeutige Bezeichner der Ressource, bei dem Sie die Groß-/Kleinschreibung berücksichtigen müssen, zum Beispiel:
Background="{StaticResource backgroundBlue}
Es gibt darüber hinaus auch noch eine zweite Schreibweise, um auf eine Ressource zuzugreifen:
<Button Height="40"> <Button.Background> <StaticResource ResourceKey="btnBackground" /> </Button.Background>Button3 </Button>
Wenn Sie die Element-Schreibweise einsetzen, müssen Sie beachten, dass die gewünschte Ressource dem Attribut ResourceKey angegeben wird.
20.1.3 Statische und dynamische Ressourcen 

Statische Ressourcen
Im letzten Beispiel wurden statische Ressourcen verwendet. Wird eine Ressource statisch an eine Eigenschaft gebunden, wird der Wert der Ressource nur ein einziges Mal ausgewertet und der Eigenschaft zugewiesen. Ändert sich die Ressource zur Laufzeit, wird der neue Wert nicht berücksichtigt.
Die Anbindung an eine Ressource erfolgt mit StaticResource. Diese Markup-Erweiterung wird zusammen mit dem Bezeichner der Ressource in geschweiften Klammern der Eigenschaft übergeben. Alternativ bietet sich auch die Element-Schreibweise an. Die angegebene Ressource muss natürlich existieren, da ansonsten eine Exception ausgelöst wird.
Dynamische Ressourcen
Ändert sich eine Ressource zur Laufzeit, können Sie eine dynamische Ressource definieren, damit das Element, das die Ressource beansprucht, den neuen Wert berücksichtigt. Dynamische Ressourcen haben gegenüber den statischen Ressourcen nicht nur in der Aktualisierbarkeit einen Vorteil. Während eine statische Ressource sofort verfügbar sein muss, kann eine dynamische Ressource auch zu einem späteren Zeitpunkt bereitgestellt werden. Das könnte beispielsweise dann der Fall sein, wenn die dynamische Ressource im Programmcode definiert wird.
Das folgende Beispiel zeigt den parallelen Einsatz statischer und dynamischer Ressourcen. Das Fenster enthält drei Textboxen und eine Schaltfläche. Die Hintergrundfarben der Textboxen sind auf drei verschiedene Arten an Ressourcen gebunden. Die obere TextBox ist dynamisch an eine Ressource gebunden, die deklarativ vom Window-Objekt bereitgestellt wird. Die mittlere TextBox bezieht ebenfalls dynamisch den Ressourcenwert. Dieser wird jedoch erst in dem Moment zur Verfügung gestellt, wenn auf die Schaltfläche geklickt wird. Die untere TextBox benutzt ebenfalls die vom Window-Objekt bereitgestellte Ressource, bindet diese allerdings statisch.
// --------------------------------------------------------- // Beispiel: ...\Kapitel 20\DynamischeRessourcen // --------------------------------------------------------- <Window x:Class="DynamischeRessourcen.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="229" Width="346"> <Window.Resources> <SolidColorBrush x:Key="txtBackground" Color="AntiqueWhite" /> </Window.Resources> <StackPanel> <TextBox Height="30" Margin="5" Text="Dynamische Ressource" FontSize="16" Background="{StaticResource txtBackground}"> </TextBox> <TextBox Height="30" Margin="5" FontSize="16" Text="Dynamische Ressource (zur Laufzeit)" Background="{DynamicResource newRessource}"> </TextBox> <TextBox Height="30" Margin="5" Text="Statische Ressource" FontSize="16" Background="{DynamicResource txtBackground}"> </TextBox> <Button Background="LightGray" Name="button1" FontSize="16" Height="35" Width="120" Click="button1_Click" Margin="15"> Button1 </Button> </StackPanel> </Window>
In der Codedatei wird die Ressource txtBackground verändert und mit newRessource eine neue Ressource eingeführt, die von der mittleren TextBox benutzt wird.
private void button1_Click(object sender, RoutedEventArgs e) {
LinearGradientBrush linearColor =
new LinearGradientBrush(Colors.Yellow, Colors.Blue, 0);
this.Resources["txtBackground"] = linearColor;
SolidColorBrush solidColor = new SolidColorBrush(Colors.LightPink);
if(this.Resources["newRessource"] == null)
this.Resources.Add("newRessource", solidColor);
}
Die Klasse Window erbt von der Basis FrameworkElement die Eigenschaft Resources. Diese Eigenschaft gibt die Referenz auf eine Collection vom Typ ResourceDictionary zurück, in der die definierten Ressourcen durch ein Schlüssel/Wert-Paar verwaltet werden. Mit
if(this.Resources["newRessource"] == null) this.Resources.Add("newRessource", solidColor);
wird eine neue Ressource an die Auflistung übergeben. Dabei wird zuerst der Schlüssel als Zeichenfolge genannt, danach die Ressource, die in unserem Beispiel durch ein zuvor definiertes SolidColorBrush-Objekt beschrieben wird. Zuvor muss auf jeden Fall geprüft werden, ob die Ressource bereits existiert. Versäumen Sie diese Überprüfung, wird ein zweites Klicken auf die Schaltfläche zu einer Ausnahme führen. Sie können eine neue Ressource auch direkt mit
this.Resources["newRessource"] = solidColor;
festlegen. Existiert die Ressource noch nicht, wird sie automatisch der Auflistung hinzugefügt. Sollte sie jedoch bereits vorhanden sein, wird ihr der neue Wert zugewiesen, wie die folgende Anweisung im Ereignishandler unseres Beispiels zeigt:
this.Resources["txtBackground"] = linearColor;
In Abbildung 20.2 sehen Sie das Fenster zur Laufzeit.
Abbildung 20.2 Die Ausgabe des Beispiels »DynamischeRessourcen«
20.1.4 Ressourcen in Ressourcendateien 

Logische Ressourcen lassen sich in unabhängigen Dateien speichern. Das hat den Vorteil, dass Sie die darin definierten Ressourcen in mehreren Projekten nutzen können und dass zudem unterschiedliche Ressourcendateien zur Laufzeit nach Bedarf nachgeladen werden können.
Die Vorgehensweise soll im folgenden Beispiel demonstriert werden. Für externe Dateien, die Ressourcen beschreiben, gibt es eine eigene Vorlage, die Sie der Anwendung zuerst hinzufügen müssen. Die Vorlage wird Ressourcenwörterbuch genannt und stellt eine XAML-Datei dar. Hier können Sie alle Ressourcen beschreiben, die Sie auch projektübergreifend festlegen wollen. Über das Kontextmenü des Projekts können Sie die Vorlage auswählen (siehe Abbildung 20.3).
Im neu hinzugefügten Ressourcenwörterbuch sind bereits das Wurzelelement ResourceDictionary sowie alle erforderlichen Namespaces angegeben. Tragen Sie hier alle Ressourcen ein, die von der Anwendung verwendet werden sollen. Das dem Programm hinzugefügte Ressourcenwörterbuch soll backgrounds.xaml heißen.
Abbildung 20.3 Die Vorlage »Ressourcenwörterbuch«
// --------------------------------------------------------- // Beispiel: ...\Kapitel 20\ExterneRessourcen // --------------------------------------------------------- <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <LinearGradientBrush x:Key="background1" StartPoint="0,0" EndPoint="1,1"> <GradientStop Offset="0.0" Color="White" /> <GradientStop Offset="1.0" Color="Gray" /> </LinearGradientBrush> <LinearGradientBrush x:Key="background2" StartPoint="0,0" EndPoint="1,1"> <GradientStop Offset="0.0" Color="Gray" /> <GradientStop Offset="1.0" Color="Red" /> </LinearGradientBrush> </ResourceDictionary>
Externe Ressourcen müssen dem Programm bekannt gegeben werden. Wollen Sie die extern zur Verfügung gestellten Ressourcen anwendungsweit nutzen, eignet sich dazu die Datei App.xaml. Benötigen Sie die Ressourcen nur in einem Fenster, können Sie das Ressourcenwörterbuch im entsprechenden Fenster benennen. Geeignet sind dazu die Resource-Abschnitte, zum Beispiel:
<Window x:Class="ExterneRessourcendatei.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <ResourceDictionary Source="backgrounds.xaml" /> </Window.Resources> <StackPanel> <Button Background="{DynamicResource background1}" Height="50"></Button> <Button Background="{DynamicResource background2}" Height="50"></Button> </StackPanel> </Window>
Mehrere Ressourcenwörterbücher
Gespeicherte Ressourcen können auch aus mehreren Ressourcenwörterbüchern entnommen werden. Diese müssen dann im Abschnitt ResourceDictionary zusammengeführt werden. Das geschieht mit dem Eintrag ResourceDictionary.MergeDictionaries, entweder lokal im XAML-Code des Fensters oder in der Datei App.xaml.
<Window x:Class="ExterneRessourcendatei.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="images.xaml" /> <ResourceDictionary Source="backgrounds.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> ...
Innerhalb eines Ressourcenwörterbuches müssen die Ressourcen eindeutig benannt werden. Sollten sich in mehreren Wörterbüchern gleichnamige Ressourcen befinden, wird die Ressource abgerufen, die zuletzt genannt ist. Sie sollten daher darauf achten, dass die Schlüssel der Ressourcen auch nach dem Zusammenführen mehrerer Wörterbücher eindeutig sind.
20.1.5 Suche nach einer Ressource 

Ressourcen können an mehreren unterschiedlichen Stellen bereitgestellt werden. Wird in XAML mit einer Markup-Erweiterung oder mit der Methode FindResource nach einer bestimmten Ressource gesucht, werden die verschiedenen Bereiche in einer bestimmten Reihenfolge durchlaufen. Sobald die Suche erfolgreich verlaufen ist, wird sie beendet.
Es lassen sich drei Bereiche angeben, in denen die Suche der Reihe nach durchgeführt wird:
1. | Die Suche beginnt im Logical Tree. Hier wird zuerst die Resources-Eigenschaft des Elements geprüft, das die Suche initiiert hat. Anschließend wird der Logical Tree aufwärts untersucht. Dabei wird in jedem Element, das sich auf dem Pfad bis hin zum Wurzelelement befindet, nach dem entsprechenden Schlüssel gesucht. |
2. | Wird im Logical Tree der Schlüssel nicht gefunden, wird im Application-Objekt (entspricht dem Resource-Abschnitt in der Datei App.xaml) gesucht. |
3. | Dem Application-Objekt sind noch die System-Ressourcen übergeordnet. Zu den System-Ressourcen werden die in den Klassen SystemColors, SystemParameters und SystemFonts vordefinierten Schlüssel gerechnet. |
20.1.6 Ressourcen mit C#-Code 

Alles, was in XAML möglich ist, kann auch mit Programmcode erreicht werden. Dazu zählt auch die Suche nach einer Ressource und deren Anbindung an eine Eigenschaft.
Zuweisung einer statischen Ressource
Am einfachsten gestaltet sich der Zugriff auf eine statische Ressource, wenn sich die Ressource im aktuellen Fenster befindet und sie namentlich bekannt ist. Mit der Eigenschaft Resources rufen Sie das lokale Ressourcenwörterbuch ab und geben den Namen der gesuchten Ressource an. Angenommen, die Ressource background sei lokal definiert, würde die Anweisung wie folgt lauten:
this.button1.Background = (Brush)this.Resources["background"];
Die gefundene Ressource muss noch in den entsprechenden Typ umgewandelt werden, weil jeder Eintrag im Ressourcenwörterbuch vom Typ Object ist. Da die Hintergrundfarbe einer Schaltfläche vom Typ Brush ist, erfolgt die Konvertierung in der vorangehenden Anweisung in genau diesen Typ.
Befindet sich die Ressource nicht im aktuellen Fenster, eignet sich die gezeigte Anweisung nicht. Stattdessen muss die Ressource in der gesamten Hierarchie gesucht werden. Hierzu eignet sich die Methode FindResource, die jedes Steuerelement hat. Sie rufen die Methode auf dem Objekt, dem die Ressource zugewiesen werden soll, auf und übergeben als Argument den Bezeichner der Methode. Die gefundene Ressource muss natürlich ebenfalls in den Datentyp der entsprechenden Eigenschaft konvertiert werden.
this.button1.Background = (Brush)button1.FindResource("background");
Wird die angegebene Ressource nicht gefunden, ist eine Exception die Folge.
Alternativ bietet sich auch die Methode TryFindResource an. Im Gegensatz zu FindResource liefert diese eine null-Referenz, falls die Ressource nicht gefunden wird.
Zuweisung einer dynamischen Ressource
Mit den Methoden FindResource und TryFindResource wird die Markup-Erweiterung StaticResource mittels Code beschrieben. Eine Ressource dynamisch zu binden, ist mit diesen Methoden nicht möglich. Zur Anbindung an eine dynamische Ressource dient die Methode SetResourceReference der Steuerelemente. Die Methode hat zwei Parameter. Dem ersten wird die Abhängigkeitseigenschaft übergeben, die von der dynamischen Ressource profitieren soll, dem zweiten Parameter der Ressourcenbezeichner.
this.button1.SetResourceReference(Button.BackgroundProperty, "background");
Beachten Sie hierbei, dass die Angabe einer Abhängigkeitseigenschaft erfordert, diese direkt anzugeben, in unseren Fall demnach Button.BackgroundProperty.
20.1.7 Abrufen von Systemressourcen 

Bisher haben wir nur benutzerdefinierte Ressourcen verwendet. Sie können aber auch auf Ressourcen zugreifen, die vom System bereitgestellt werden. WPF enthält im Namespace System.Windows drei Klassen, mit denen sich bestimmte Eigenschaften des Systems auswerten lassen:
- SystemParameters
- SystemColors
- SystemFonts
SystemFonts beschreibt Eigenschaften, die die Systemressourcen für Schriftarten verfügbar machen, SystemColors beschreibt die vom System verwendeten Farben, und SystemParameters enthält Eigenschaften, die Sie zum Abfragen von Systemeinstellungen verwenden können. Die drei Klassen enthalten in ihren Eigenschaften immer die aktuellen Werte des Betriebssystems, die von den Einstellungen in der Systemsteuerung abhängen.
Wenn Sie sich die Dokumentation dieser Klassen ansehen, werden Sie feststellen, dass für jede Systemeigenschaft zwei Eigenschaften definiert sind, zum Beispiel
- CaptionHeight
- CaptionHeightKey
in der Klasse SystemParameters. Doch wozu braucht man zwei ähnliche Eigenschaften?
CaptionHeight ist vom Typ double und gibt die Höhe der Titelleiste in Pixel an. CaptionHeightKey hingegen ist vom Typ ResourceKey. Darüber wird der Name der Systemressource gekennzeichnet, die den Wert der Eigenschaft zurückliefert.
Möchten Sie auf eine Ressource statisch zugreifen, reicht die Angabe der Eigenschaft ohne das Suffix Key vollkommen aus. In diesem Fall reagiert die Anwendung nicht auf Änderungen an den Systemeinstellungen. Sehen wir uns eine statische Ressourcenabfrage an. Beachten Sie, dass für den Zugriff auf die Systemressourcen die Markup-Erweiterung x:Static vorgeschrieben ist.
<Label Content= "{StaticResource {x:Static SystemParameters.CaptionHeightKey}}" />
Die Angabe der Markup-Erweiterung StaticResource bewirkt, dass eine Suche nach der Ressource entlang der Hierarchie angestoßen wird. Das geht zulasten der Performance.
Anmerkung |
Mit der XAML-Markup-Erweiterung x:Static greift man auf die statischen Eigenschaften, Felder oder Konstanten einer Klasse oder Aufzählung zu. |
Besser ist es, direkt auf den Wert der Eigenschaft mit
<Label Content="{x:Static SystemParameters.CaptionHeight}" />
zuzugreifen. Im C#-Code können Sie den Wert der Eigenschaft mit
double height = SystemParameters.CaptionHeight;
ermitteln.
Bei einer Änderung des Werts in der Systemsteuerung nimmt die Anwendung zur Laufzeit keine Notiz von der Änderung. Soll sich die Anwendung der Änderung anpassen, müssen Sie die Ressource mit DynamicResource einbinden:
<Label Content="{DynamicResource {x:Static SystemParameters.CaptionHeightKey}}" />
Systemressourcen anpassen
Wie weiter oben schon erläutert wurde, läuft die Suche nach einer bestimmten Ressource nach einem vorgegebenen Schema ab. Sie beginnt im Logical Tree bei dem Element, auf dem die Markup-Erweiterung StaticResource oder DynamicResource verwendet oder die Methode FindResource aufgerufen wird. Wird die Ressource nicht gefunden, wird im Application-Objekt danach gesucht. Die letzte Ebene der Suche bilden die System-Ressourcen, in denen sich die Einstellungen des Betriebssystems finden.
Dieser Suchprozess gestattet, eine »höher liegende« Ressource durch eine tiefer liegende zu überschreiben, denn sobald die Suche erfolgreich war, wird sie beendet. Folglich lassen sich auch die vorgegebenen System-Ressourcen sehr leicht durch anwendungsspezifische ersetzen.
Damit ist es beispielsweise sehr einfach, die Hintergrundfarbe aller Fenster einer Anwendung festzulegen. Verantwortlich dafür ist die Ressource WindowBrushKey in der Klasse SystemColors. Wünschen Sie einen roten Hintergrund bei allen Fenstern der Anwendung, ergänzen Sie den Resources-Abschnitt der Datei App.xaml einfach wie folgt:
<Application.Resources> <SolidColorBrush Color="Red" x:Key="{x:Static SystemColors.WindowBrushKey}" /> </Application.Resources>
Einen Farbverlauf festzuschreiben ist mit der Ressource WindowBrushKey nicht möglich, da der durch diese Ressource beschriebene Wert vom Typ SolidColorBrush ist.