22.2 Ressourcen
Wenn Sie alle Daten fest im Code zu verankern, ist eine Anwendung unflexibel und der Quelltext oft schwer lesbar (zum Beispiel eine Inline-Bitmap). Für einige Daten ist es oft besser, statt der Daten im Code einen eindeutigen Schlüssel (Key) zu verwenden und die eigentlichen Daten an einer anderen Stelle zu speichern. Solche externen (bezüglich der Stelle im Code) Daten werden Ressourcen genannt und sind später austauschbar, ohne die Stelle im Code ändern zu müssen, da dort der Schlüssel und nicht die Daten selbst verwendet werden. Typische Ressourcen sind Füllmuster, geometrische Objekte oder Stile.
Eine enorme Arbeitserleichterung ergibt sich, wenn dieselben Daten mehrfach verwendet werden. Im Code steht für diese immer derselbe Schlüssel. Eine Änderung eines einzigen Datums in der Ressource wirkt sich automatisch auf alle Stellen aus, die es über den Schlüssel referenzieren. Das macht ein einheitliches Design einfach, denn alle Elemente beziehen sich auf dieselben Daten, und Änderungen erfolgen nur an einer Stelle. An die Stelle einer Programmierung tritt eine Konfiguration.
Ressourcen lassen sich nahezu jedem Objekt hinzufügen und müssen nicht an einer zentralen Stelle verwaltet werden. Um eine Ressource anwendungsweit bereitzustellen, definieren Sie sie in der Datei Application.xaml, in der bereits ein Abschnitt für Ressourcen steht.
<Application x:Class="WpfApplication4.App" ... StartupUri="Window1.xaml"> <Application.Resources> </Application.Resources> </Application>
Auf die hier definierten Ressourcen können alle Elemente der Anwendung zugreifen, sowohl alle Fenster als auch alle Steuerelemente. Ein in Window explizit angegebener Ressourcenbereich ist dagegen nur innerhalb des Fensters gültig.
<Window x:Class="WpfApplication4.Window1" ... <Window.Resources> <!-- fenstereigene Ressourcen --> </Window.Resources> ... </Window>
Analog sind Ressourcenabschnitte beispielsweise in einem Button oder Grid möglich:
<Grid> <Grid.Resources> <!-- Ressourcen des Grid --> </Grid.Resources> <Button> <Button.Resources> <!-- Ressourcen des Button --> </Button.Resources> </Button> </Grid>
Die Suche nach den Daten zu einem Ressourcenschlüssel erfolgt vom Speziellen zum Allgemeinen. Zuerst wird in der Komponente gesucht, die den Schlüssel verwendet, zum Beispiel Button. Hat sie keinen Ressourcenabschnitt oder ist dort kein passender Schlüssel, setzt sich die Suche in der übergeordneten Komponente fort, zum Beispiel Grid. Die Analyse übergeordneter Komponenten kann bis zur Ebene der Anwendung selbst erfolgen. Ist auch dort kein passender Schlüssel, findet sich immer etwas Passendes in den System-Ressourcen. Wird ein passender Schlüssel auf einer Ebene gefunden, wird der zugehörige Wert verwendet und nicht weitergesucht. Mit den bisherigen Beispielen ergibt sich eine maximale Suchkette Button, Grid, Window, Application, Laufzeitumgebung bzw. Betriebssystem.
22.2.1 Definition
Nachdem wir geklärt haben, wo Ressourcen definiert werden, widmen wir uns nun dem Wie. Im folgenden Beispiel sind zwei Ressourcen im Ressourcenbereich des Window definiert. Eine Ressource beschreibt einen blauen Farbton, die zweite einen gelben. Vier Schaltflächen sind im Fenster, zwei greifen auf beide Ressourcen zu, einer nutzt nur eine Ressource, und der letzte nutzt keine (siehe Abbildung 22.1).
'...\WPFKonzepte\Ressourcen\Farben.xaml |
<Window x:Class="Farben" ...> <Window.Resources> <SolidColorBrush x:Key="blau">CornflowerBlue</SolidColorBrush> <SolidColorBrush x:Key="gelb" Color="Yellow" /> </Window.Resources> <StackPanel> <Button BorderBrush="{StaticResource gelb}" Background="{StaticResource blau}" Height="40">Button1</Button> <Button BorderBrush="{StaticResource gelb}" Background="{StaticResource blau}" Height="40">Button2</Button> <Button Background="{StaticResource blau}" Height="40">Button3</Button> <Button Height="40">Button3</Button> </StackPanel> </Window>
Abbildung 22.1 Farben als Ressourcen
Jede Ressource muss einen eindeutigen Schlüssel haben, unter dem sie aufrufbar ist. In unserem Beispiel lauten die beiden Schlüssel:
x:Key="blau"
x:Key="gelb"
Ressourcenschlüssel hängen von der Groß-/Kleinschreibung ab. Die gewünschte Ressource wird in geschweiften Klammern angegeben, der Namensbestandteil Ressource ist optional (siehe Abschnitt 19.3.6, »Indirektion und Markup-Erweiterungen«):
Background="{StaticResource blau}"
Die Elementschreibweise ist für komplexe Eigenschaften nötig (siehe Abschnitt 19.3.4, »Attribute«).
<Button Height="40"> <Button.Background> <StaticResource ResourceKey="blau" /> </Button.Background> Button3 </Button>
22.2.2 Zeitpunkt der Wertbestimmung
Der Wert einer Ressource kann zu zwei Zeitpunkten bestimmt werden: einmalig beim Laden der Anwendung oder dynamisch nach Bedarf.
Statische Ressourcen
Wird eine Ressource statisch an eine Eigenschaft gebunden, wird der Wert der Ressource nur ein einziges Mal ausgewertet und der Eigenschaft zugewiesen. Ändert sich danach eine Ressource, wird der neue Wert nicht mehr berücksichtigt.
Eine solche Anbindung erfolgt mit StaticResource. Diese Markup-Erweiterung wird zusammen mit dem eindeutigen Schlüssel der Ressource in geschweiften Klammern der Eigenschaft übergeben. Existiert die Ressource nicht, wird eine Ausnahme ausgelöst.
Dynamische Ressourcen
Um Änderungen der Ressource zur Laufzeit zu erfassen, müssen Sie sie dynamisch an die Eigenschaft binden. Dynamische Ressourcen werden von WPF automatisch auf Änderungen hin geprüft. Das kostet Rechenzeit. Extrem viele dynamische Bindungen machen eine Anwendung träge.
Der Wert einer dynamischen Ressource muss erst dann bekannt sein, wenn er gebraucht wird. Fehlende Werte dynamischer Ressourcen werden einfach ignoriert. Wird während der Laufzeit ein Wert erzeugt (oder geändert), aktualisiert WPF die Eigenschaft automatisch.
Die Festlegung dynamischer Ressourcen kann im Programmcode erfolgen. Im folgenden Beispiel wird der Hintergrund einer Schaltfläche beim Klicken auf diese durch die Erzeugung einer dynamischen Ressource im Code des Ereignishandlers geändert (siehe Abbildung 22.2). Die Ressource wird im Window-Objekt abgelegt.
'...\WPFKonzepte\Ressourcen\Dynamisch.xaml |
<Window x:Class="Dynamisch" ... > <StackPanel> <Button Background="{DynamicResource hintergrund}" Click="Klick" Name="button1" Height="40">Button1</Button> </StackPanel> </Window>
'...\WPFKonzepte\Ressourcen\Dynamisch.xaml.vb |
Partial Public Class Dynamisch Private Sub Klick(ByVal sender As Object, ByVal e As RoutedEventArgs) Dim col As New LinearGradientBrush(Colors.Yellow, Colors.Blue, 0) Me.Resources.Remove("hintergrund") ' nötig ab dem zweiten Klick Me.Resources.Add("hintergrund", col) End Sub End Class
Abbildung 22.2 Dynamische Ressourcenbindung
22.2.3 Abrufen von Systemressourcen
Bisher haben wir nur selbst definierte Ressourcen verwendet. Aber auch das Betriebssystem stellt Ressourcen zur Verfügung. WPF macht einen Teil davon über die klassengebundenen schreibgeschützten Eigenschaften dreier Klassen zugänglich:
- SystemColors: abstrakte Farben wie MenuColor
- SystemFonts: abstrakte Font-Informationen wie MenuFontFamily, IconFontSize
- SystemParameters: Verschiedenes wie IconWidth, VirtualScreenTop, IsTabletPC
Auf jede der Ressourcen können Sie auf zwei Arten zugreifen. Eigenschaften ohne das Suffix Key liefern direkt einen Wert, während die Variante mit Key eine Referenz auf den eigentlichen Wert darstellt. Zum Beispiel gibt SystemParameters.CaptionWidth die Breite der Titelleiste als Double an, und SystemParameters.CaptionWidthKey ist eine Referenz vom Typ RessourceKey. Bei einer statischen Bindung brauchen Sie den Wert direkt (CaptionKey), bei einer dynamischen den Schlüssel (CaptionWidthKey).
Sehen wir uns zuerst eine statische Ressourcenabfrage an. Beachten Sie, dass für den Zugriff auf die Systemressourcen die Markup-Erweiterung x:Static vorgeschrieben ist.
<Label Content="{x:Static SystemParameters.CaptionWidth}" />
Und nun der dynamische Zugriff.
<Label Content="{DynamicResource {x:Static SystemParameters.CaptionWidthKey}}" />
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.