Startseite Inhaltsverzeichnis << >>
Kapitel 4 - Komponenten
4.1
Übersicht
Die
meisten bekannten Standardkontrollelemente sind bereits in XAML bzw.
WinFX verfügbar. Auf einige wie den Treeview müssen
Sie noch etwas warten. Die meisten Kontrollelemente können im
Gegensatz zu den Windows-Controls beliebig ineinander verschachtelt
werden. So kann ein Button beispielsweise einen Text und ein Bild oder
eine Listbox enthalten. Damit mehrere Elemente verwendet werden
können, müssen diese wiederum in eine Container-Komponente
wie ein FlowPanel eingefügt werden. Ein Button darf nämlich
nur ein untergeordnetes Element besitzen.
<Button>
<FlowPanel>
<Text>Hallo</Text>
<Image Source="xxx.jpg" />
</FlowPanel>
</Button>
Komponente |
Kurzbeschreibung |
Button |
Nach dem Klick auf eine Schaltfläche führt diese üblicherweise eine Aktion aus. |
CheckBox |
Markierungsfelder
dienen zum (De)Aktivieren einer unabhängigen Einstellung. Sie
können weiterhin einen dritten Zustand (undefiniert) einnehmen. |
ComboBox |
Ein
Kombinationsfeld ist eine Mischung aus einem Textfeld und einer
ListBox. Die Liste kann über einen Pfeil aufgeklappt und ein Wert
ausgewählt werden. Die Liste kann editiert werden. |
ContextMenu |
Ein
Kontextmenü wird nach dem Klicken mit der rechten Maustaste auf
ein bestimmtes Element angezeigt. Je nach Element können die
Menüpunkte unterschiedlich sein. Das Menü ist also vom
Kontext abhängig. |
HorizontalScrollBar |
Laufbalken dienen dazu, einen Teil eines großen Bereichs auszuwählen und auf dem Bildschirm anzuzeigen. |
HorizontalSlider |
Über
Slider können Sie Elemente in der Breite (Vertical) oder Höhe
(Horizontal) ändern, so das ein Element mehr Platz einnehmen kann. |
Hyperlink |
Über Hyperlinks können Sie in einer
NavigationApplication auf andere Dateien über eine URI-Angabe
verzweigen. Diese können im gleichen oder einem neuen Fenster
angezeigt werden. |
Image |
|
Label |
Statische Textfelder dienen der Beschriftung von anderen Elementen und können nicht editiert werden. |
ListBox |
|
Menu |
|
PageBar |
|
PageViewer |
|
RadioButton |
|
RadioButtonList |
|
RepeatButton |
Solange auf diese Schaltfläche geklickt wird, werden
Click-Ereignisse ausgelöst. |
ScrollBar |
Stellt eine abstrakte Basisklasse für vertikale und
horizontale SrollBars bereit. |
ScrollViewer |
Vereint eine vertikale und horizontale ScrollBar und stellt
darüber einen scrollbaren Bereich zur Verfügung. |
Slider |
|
StatusBar |
|
TabControl |
|
TextBox |
|
Thumb |
|
ToggleButton |
|
ToolTip |
Ein ToolTip ist eigentlich keine richtige Komponente sondern eine Eigenschaft
einer visuellen Komponente. Wird der Mauszeiger auf eine Komponente bewegt,
der ein ToolTip zugrordnet ist (z.B. ein Text), wird dieser angezeigt. |
VerticalScrollBar |
Stellt eine vertikale ScrollBar zur Verfügung. Die von
der Position
des Scrollbalkens abhängige Funktionalität muss manuell
programmiert
werden. |
VerticalSlider |
|
ZoomControl |
Zeigt eine ComboBox mit Zoomfaktoren an.
Über die Eigenschaft Zoom kann der ausgewählte Faktor (1.0 ==
100%) ermittelt werden. |
4.1.1 Button
Schaltflächen werden durch Buttons beschrieben. Damit später
auf den Klick reagiert werden kann muss eine Schaltfläche mit
einer ID versehen werden und einen Eventhandler für das
Click-Ereignis erhalten, siehe Kapitel 3. Damit Schaltflächen aus
mehreren Elementen zusammengesetzt werden können, muss darin eine
Containerkomponente wie z.B. ein FlowPanel eingefügt werden. Die
einfachste Form eines Buttons besitzt nur einen Text, der durch die
Eigenschaft Content zugewiesen wird. Alernativ können Sie den Text
auch als Inhalt des Elements oder als Unterelement <Text>
angeben. Auch nur die Anzeige eines Bildes ist möglich. Der letzte
Button wurde nur über seine Eigenschaften in der Darstellung
geändert.
Beispiel: Button.xaml
<Window xmlns="http://schemas.microsoft.com/2003/xaml" xmlns:def="Definition">
<FlowPanel>
<Button>
<FlowPanel>
<Image Source="../images/Kreis.gif" />
<Text>Klick mich 1</Text>
</FlowPanel>
</Button>
<Button Content="Klick mich 2" />
<Button>
Klick mich 3
</Button>
<Button>
<Text>Klick mich 4</Text>
</Button>
<Button>
<Image Source="../images/Kreis.gif" />
</Button>
<Button Width="200" Height="80" FontSize="20"
Background="LightBlue" Foreground="Blue"
BorderBrush="Gray" BorderThickness="3"
Cursor="Hand"
Margin="20,20,20,20"
Opacity="0.5">
Klick Klack
</Button>
</FlowPanel>
</Window>
4.1.2 CheckBox
Über eine CheckBox können Sie eine Einstellung (de)aktivieren
oder sie besitzt einen mitteleren Zustand (z.B.: der markierte Text ist
nicht vollständig fett, sondern normal gesetzt mit einigen fett
markieren Worten). Die Eigenschaft CheckState enthält den
aktuellen Zustand der CheckBox der über die Aufzählung
CheckState definiert wird (Checked - 0, Unchecked - 1, Indeterminate -
2). Standardmäßig besitzt die CheckBox nur zwei
Zustände. Um den undefinierten Zustand zu aktivieren, setzen Sie
die Eigenschaft ThreeState auf den Wert true. Um den Zustand mit einer
Voreinstellung zu versehen, setzen Sie die Eigenschaft CheckState der
CheckBox auf einen der drei Zustandswerte.
Auf das Ändern des Zustands kann über das Ereignis
CheckStateChange reagiert werden.
Beispiel: CheckBox.xaml
<Window xmlns="http://schemas.microsoft.com/2003/xaml" xmlns:def="Definition">
<GridPanel>
<CheckBox Content="Frau" CheckState="Unchecked" />
<CheckBox CheckState="Indeterminate">
Herr
</CheckBox>
<CheckBox BorderThickness="3"
CheckState="Checked"
ID="KindCheck"
Margin="0,20,0,0">
<FlowPanel>
<Image Source="../images/Kreis.gif" />
<Text>Kind</Text>
</FlowPanel>
</CheckBox>
<!-- funktioniert nicht im IE wegen Code
<CheckBox CheckStateChanged="StatusCheck" ID="CheckBox1"
Content="Markiere mich" ThreeState="true" />
<x:Code>
<![CDATA[
void StatusCheck(object sender, CheckStateChangedEventArgs e)
{
if((sender as CheckBox).CheckState == CheckState.Checked)
MessageBox.Show("Hallo");
}
]]>
</x:Code>
-->
</GridPanel>
</Window>
4.1.3 ComboBox
Eine ComboBox erlaubt die Auswahl eines Wertes aus einer aufklappbaren
Liste. Standardmäßig ist die Liste nicht editerbar. Durch
das Setzen der Eigenschaft IsEditable auf True, können Sie im
Eingabebereich Text eingeben. Die einzelnen Listeneinträge werden
in ListItem-Objekte gekapselt. Diese können nun Text enthalten
oder auch Bilder oder andere zusammengesetzte Objekte.
Standardmäßig ist kein Eintrag selektiert. Um eine
Vorauswahl zu treffen, müssen Sie der Eigenschaft SelectedIndex
einen Wert zwischen 0 und n-1 zuweisen. Über das Ereignis
SelectionChanged reagieren Sie auf die Auswahl eines Eintrages.
Beachten Sie in der Ereignisbehandlung, den Eintrag auf den Typ
ListItem zu prüfen. Wenn Sie beispielsweise einen Text in das
Eingabefeld eingeben, wird ein anderer Typ geliefert und es gibt eine
Exception. Um einen Eintrag hinzuzufügen, reagieren Sie auf das
Ereignis KeyDown, erzeugen ein neues ListItem-Objekt und fügen es
der Liste hinzu.
Beispiel: ComboBox.xaml
<FlowPanel xmlns="http://schemas.microsoft.com/2003/xaml" xmlns:def="Definition">
<GridPanel>
<ComboBox>
<ListItem>Auswahl 1</ListItem>
<ListItem>Auswahl 2</ListItem>
<ListItem>Auswahl 3</ListItem>
</ComboBox>
<ComboBox MaxDropDownHeight="100" SelectedIndex="1" Height="50">
<ListItem>Auswahl 1</ListItem>
<ListItem>Auswahl 2</ListItem>
<ListItem><Image Source="../images/Kreis.gif" /></ListItem>
</ComboBox>
<!-- Code wird im IE nicht unterstützt
<ComboBox MaxDropDownHeight="100" IsEditable="true" ID="CB1" KeyDown="CB1KeyDown"
SelectionChanged="CB1SelectionChanged">
<ListItem>Auswahl 1</ListItem>
<ListItem>Auswahl 2</ListItem>
<ListItem>Auswahl 3</ListItem>
</ComboBox>
<x:Code>
<![CDATA[
void CB1SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox cb = (sender as ComboBox);
if(cb.SelectedItem == typeof(ListItem))
{
ListItem li = cb.SelectedItem as ListItem;
String s = li.Content.ToString();
MessageBox.Show(s);
}
}
]]>
</x:Code>
-->
<!-- in Windows.xaml.cs einfügen, da KeyEventArgs momentan nicht gefunden wird
void CB1KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
ComboBox cb = (sender as ComboBox);
String NewItemText = cb.Text;
ListItem li = new ListItem();
li.Content = NewItemText;
cb.Items.Add(li);
}
}
-->
</GridPanel>
</FlowPanel>
4.1.4 ContextMenu
Besitzt ein Kontrollelement eine Eigenschaft ContextMenu kann ihm ein
Kontextmenü zugewiesen werden, dass beim Klicken mit der rechten
Maustaste über dem Element angezeigt wird. Dazu wird die
Compound-Eigenschaft Element.ContextMenu verwendet und darunter ein
<ContextMenu>-Element eingefügt. Die Menupunkte werden durch
<MenuItem>-Elemente definiert. Durch Verschachtelung werden auf
einfache Weise Untermenüs erzeugt. Die Eigenschaft Header kann zum
Erzeugen des Menutexts verwendet werden. Alternativ können Sie
auch einen Text und ein Bild verwenden. In diesem Fall muss die
Compound-Eigenschaft Header genutzt werden und die Elemente darin in
ein Container-Element untergebracht werden, im Beispiel durch ein
<FlowPanel>-Element. Das Klicken auf einen Menüpunkt wird
durch das Click-Ereignis abgefangen. Wenn Sie die Eigenschaft Header
immer vom Typ String ist, können Sie diese mit Header.ToString()
auslesen. Bei verschachtelten Menüinhalten kann ein Menüpunkt
über seine Eigenschaft ID identifiziert werden.
Beispiel: ContextMenu.xaml
<!-- Im Beispiel sind die Ereignisse entfernt worden, der korrekte Code ist auskommentiert -->
<FlowPanel xmlns="http://schemas.microsoft.com/2003/xaml" xmlns:def="Definition">
<GridPanel>
<TextBox>
<TextBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Item 1">
<MenuItem Header="Item 1.1" Click="MenuItemClick" />
<MenuItem Header="Item 1.2" Click="MenuItemClick" />
</MenuItem>
<MenuItem Header="Item 2" Click="MenuItemClick" />
<MenuItem Click="MenuItemClick" ID="TextBild">
<MenuItem.Header>
<FlowPanel>
<Text>Item 3</Text>
<Image Source="../images/Kreis.gif" />
</FlowPanel>
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
<x:Code>
<![CDATA[
void MenuItemClick(object sender, RoutedEventArgs e)
{
if((sender as MenuItem).Header is String)
MessageBox.Show((sender as MenuItem).Header.ToString());
else
MessageBox.Show((sender as MenuItem).ID.ToString());
}
]]>
</x:Code>
</GridPanel>
</FlowPanel>
4.1.5 ScrollBar, HorizontalScrollBar
und VerticalScrollBar, ScrollViewer
Eine ScrollBar dient als abstrakte Basisklasse für horizontale und
vertikale ScrollBars. Außerdem werden ScrollBars im
ScrollViewer-Element verwendet. Eine ScrollBar also nicht direkt
verwendet. Laut Dokumentation befinden sich diese Kontrollelemente im
Namespace System.Windows.Comtrols.Primitives. Sie lassen sich in XAML
verwenden, allerdings kann noch nicht auf das Event ValueChanged
korrekt reagiert werden. Dies tritt ein, wenn sich die Position des
Sliders auf der ScrollBar verändert.
Über die Eigenschaft Value wird auf den Wert des Scrollbalkens
zugegriffen, der sich zwischen den Werten der Eigenschaften Minimum und
Maximum befinden kann.
Das Kontrollelement ScrollViewer bietet bereits etwas mehr
Funktionalität. Es kapselt einen Bereich, der bei Bedarf gescrollt
werden kann. Standardmäßig sind die ScrollBars nur dann
sichtbar, wenn der gekapselte Bereich größer als der
sichtbare Bereich des ScrollViewers ist. Sie können die
Sichtbarkeit der ScrollBars über die Werte Auto, Hidden und
Visible der Aufzählung ScrollBarVisibility steuern. Weisen Sie die
Werte den Eigenschaften VerticalScrollBarVisibility bzw.
HorizontalScrollBarVisibility zu. Über die Eigenschaften Width und
Height legen Sie die Größe des ScrollViewers fest.
Im Beispiel wird ein Rechteck mit der Größe 500x500 und
einem Gradienten als Füllmuster erstellt. Daran lässt sich
besser das Scrolling erkennen. Da die Größe des
ScrollViewers nur 200x200 beträgt, kann der restliche Bereich des
Rechteckts nur durch Scrollen mit den Scrollbalken in den sichtbaren
Bereich geholt werden.
Beispiel: ScrollBar.xaml
<Grid xmlns="http://schemas.microsoft.com/2003/xaml" xmlns:def="Definition">
<FlowPanel>
<ScrollViewer Width="200" Height="200" VerticalScrollBarVisibility="Auto">
<Rectangle Width="500" Height="500" Fill="RadialGradient Blue Yellow" />
</ScrollViewer>
</FlowPanel>
</Grid>
4.1.6 Hyperlink
Ein Hyperlink-Kontrollelement erlaubt das Verzweigen der Anwendung auf
eine andere URL. Diese kann eine lokale Datei, ein Verweis auf Daten im
Internet oder Intranet oder eine andere XAML-Datei sein (was
natürlich auch eine Datei etc. ist). Hyperlinks werden nur dann
von einer Avalon-Anwendung unterstützt, wenn es sich um eine
NavigationApplication handelt. Ansonsten bleibt das Klicken auf den
Link wirkungslos, da die neue Seite nicht angezeigt werden kann.
Mit dem Attrivut NavigateUri legen Sie die URL fest, zu der verzweigt
werden soll. Dies kann ein relativer Pfad, eine HTTP-Adresse oder eine
lokale Datei sein. Über die Eigenschaft Target könnten Sie
noch das Ausgabefenster festlegen. Standardmäßig ist es das
Fenster der laufenden Anwendung. Damit kein neues Fenster beim Klick
auf den Hyperlink geöffnet wird, muss ein Grid-Kontrollelement
(aber kein Window / NAvigationWindow) verwendet werden. Klicken Sie nun
auf den Hyperlink, wird die neue Seite im gleichen Fenster
geöffnet und die Navigationsschaltflächen werden mit Leben
gefüllt. Momentan müssen Sie auf den unteren Pfeil klicken.
Dann wird eine Liste der bisherigen Navigationsziele angezeigt.
Beispiel: Hyperlink1.xaml
<NavigationWindow ID="MainWin" xmlns="http://schemas.microsoft.com/2003/xaml" xmlns:def="Definition">
<FlowPanel FlowOrientation="Vertical">
<HyperLink NavigateUri="Hyperlink2.xaml">HyperLink</HyperLink>
<HyperLink NavigateUri="http://www.del-net.com/avalon/index.html">Avalon Tutorial</HyperLink>
<HyperLink NavigateUri="file:///E:/avalon/examples/Hyperlink2.xaml">HyperLink</HyperLink>
</FlowPanel>
</NavigationWindow>
Beispiel: Hyperlink2.xaml
<Grid xmlns="http://schemas.microsoft.com/2003/xaml" xmlns:def="Definition">
<FlowPanel>
<HyperLink NavigateUri="Hyperlink1.xaml">Zurueck</HyperLink>
</FlowPanel>
</Grid>
4.1.x RepeatButton
Solange auf diese Schaltfläche geklickt wird, werden in
definierten Abständen Click-Ereignisse ausgelöst. Damit
eignet sich der Schalter zum Beispiel zum Einstellen von Werten in
einem bestimmten Bereich, z.B. einem digitalen Wecker.
Über die Eigenschaft Delay stellen Sie die Zeit in Millisekunden
ein, nach der die Schaltfläche beginnt, wiederholt das
Click-Ereignis auszulösen. Die Zeit zwischen den einzelnen Klicks
wird dann über die Eigenschaft Interval, ebenfalls in
Millisekunden, festgelegt. Ohne Code ist die Verwendung des
RepeatButton nicht möglich, so dass Sie das Beispiel nur im Visual
Studio zum Laufen bekommen.
Beispiel: RepeatButton.xaml
<Window xmlns="http://schemas.microsoft.com/2003/xaml" xmlns:def="Definition">
<FlowPanel FlowOrientation="Vertical">
<RepeatButton Delay="100" Interval="50" Width="100" Height="30" Click="Erhoehe" />
<def:Code>
<![CDATA[
void Erhoehe(object sender, RoutedEventArgs e)
{
Int32 Num = Convert.ToInt32(tbZahl.TextContent);
tbZahl.TextContent = ((Num + 1).ToString());
}
]]>
</def:Code>
<Text ID="tbZahl">0</Text>
</FlowPanel>
</Window>
4.1.x ToolTip
Ein ToolTip wird in der Regel angezeigt, wenn die Maus auf eine Komponente
bewegt wird und dort einen Moment verweilt. Er dient dabei der Erläuterung
der Funktionsweise der Komponente oder als Hinweis zur Verwendung. So können
Sie über einen ToolTip z.B. einen Hinweis anbringen, dass in ein Eingabefeld
nur Zahlen eingegeben werden dürfen. Ein ToolTip kann als Attribut von Komponenten
oder als verschachtelte Eigenschaft eingesetzt werden. Mit der letzteren Methode
können Sie in einem ToolTip neben Text auch Bilder oder andere Objekte anzeigen.
4.1.x ZoomControl
Im PageViewer wird automatisch dieses Kontrollelement verwendet, um den
Zoom des Dokuments festzulegen. Es kann aber auch als einzelnes Element
genutzt werden. Der Zugriff auf den Zoomfaktor erfolgt über die
Eigenschaft Zoom vom Typ double oder den selektierten Wert in Textform.
Das Beispiel verwendet das Control, um die Schriftgröße der
Schaltfläche zu ändern.
Beispiel: ZoomControl.xaml
<FlowPanel xmlns="http://schemas.microsoft.com/2003/xaml" xmlns:def="Definition">
<GridPanel>
<Text TextContent="Zoom" />
<ZoomControl />
<!--
<ZoomControl SelectionChanged="ZCSelectionChanged" />
<x:Code>
<![CDATA[
void ZCSelectionChanged(object sender, SelectionChangedEventArgs e)
{
ZoomControl cb = (sender as ZoomControl);
String s = (cb.SelectedItem as ContentControl).Content.ToString();
ZoomButton.FontSize = new FontSize(15 * cb.Zoom);
/*
switch(s)
{
case "75%": ZoomButton.FontSize = new FontSize(7.5); break;
case "100%": ZoomButton.FontSize = new FontSize(10); break;
case "200%": ZoomButton.FontSize = new FontSize(20); break;
}
*/
}
]]>
</x:Code>
-->
<Button ID="ZoomButton">
Hallo
</Button>
</GridPanel>
</FlowPanel>
4.2 Standardeigenschaften
ActiveElement
AnimationEffects
Background
BorderBrush
BorderThickness
Command
CommandBindings
CommandLinks
Content
ContentStyle
ContentStyleSelector
Context
CultureInfo
Cursor
DataContext
DesiredSize
DoesAnimate
FlowDirection
Focusable
FocusVisualStyle
FontFamily
FontSize
FontStretch
FontStyle
FontWeight
ForeCursor
Foreground
ForwardCommandsTo
HasContent
Height
HitTestBounds
HorizontalAlignment
HorizontalLayoutAlignment
ID
ImageEffect
InputBindings
InputScope
LogicalChildren
Margin
MaxHeight
MaxWidth
MinHeight
MinWidth
Opacity
OpacityMask
Padding
Parent
PersistId
RenderBounds
RenderSize
RenderTransform
Resources
StoryBoards
Style
StyleParent
TabIndex
Tag
TextDecorations
TextTrimming
ToolTip
VerticalAlignment
VerticalLayoutAlignment
Visibility
Width
4.2 Ressourcen
Bestimmte Eigenschaften von Komponenten können zentral in einem
übergeordneten Element im Ressourcen-Bereich
innerhalb des Logical Trees definiert werden. Dabei wird ein Name
für die Eigenschaft vergeben, der später referenziert wird.
<FlowPanel.Resources>
<SolidColorBrush def:Name="MyNiceBrush" Color="LightGreen"/>
</FlowPanel.Resources>
...
<Rectangle Width="100" Height="100" Fill="{MyNiceBrush}" />
4.3 Stile
definieren und anwenden
Um ein einheitliches Erscheinungsbild vom Elementen sicherzustellen,
können diese mit einem Stil versehen werden. Die Stile werden
im Ressourcen-Bereich der Anwendung oder eines Elements definiert.
Änderungen in einem Stil können sich so einfach auf
zahlreiche Elemente auswirken. Der Ressouce-Abschnitt eines Elements
wird über die Compound-Eigenschaft Resources begonnen. Ein
Stil wird darin über das Element <style>
definiert.
Im folgenden Beispiel wird ein Stil für eine
Schaltfläche definiert. Diese wird in einer Schrift von 24pt
in der Schriftart Verdane mit einem hellblauen Hintergrund angezeigt.
Der Name des Stils wird durch das Attribut Name festgelegt. Um einen
Stil anzuwenden, wird er in geschweiften Klammern als Wert des
Attributs Style der betreffenden Komponenten hinzugewiesen.
Beispiel:
Style1.xaml
<Window xmlns="http://schemas.microsoft.com/2003/xaml" xmlns:def="Definition">
<DockPanel.Resources>
<Style def:Name="NiceButton">
<Button FontSize="24" FontFamily="Verdana" Background="LightBlue" />
</Style>
</DockPanel.Resources>
...
<Button DockPanel.Dock="Top" Style="{NiceButton}">
Klick mich
</Button>
...
</Window>
Standardstil
Das Beispiel definiert einen benannten Stil, der nur dann zur Anwendung
kommt, wenn er explizit von einem Element referenziert wird. Wenn Sie
die Angabe eines Namen für einen Stil weglassen in dem Sie
kein Attribut Name angeben, wird der Stil für alle Elemente
angewant, die dem aktuellen Element untergeordnet sind.
Sie können in einem Stil nur solche Eigenschaften
vordefinieren, die vom Typ DependencyProperty sind. Außerdem
darf nicht das Element <style> selbst in einem Stil
verwendet werden, um keine zirkulären Abhängigkeiten
zu erzeugen.
Stile können auf Basis eines anderen Stils definiert werden.
Dazu beziehen Sie sich mit dem Attribut BasedOn auf den Namen des
anderen Stils.
Beispiel:
Style2.xaml
<DockPanel.Resources>
<Style def:Name="NiceButton">
<Button FontSize="24" FontFamily="Verdana" Background="LightBlue" />
</Style>
<Style def:Name="MuchNicerButton" BasedOn="{NiceButton}">
<Button FontSize="24" FontFamily="Verdana" Foreground="Red" />
</Style>
</DockPanel.Resources>
Trigger
Über Trigger kann der Stil eines Elements abhängig
vom Wert einer Eigenschaft gesetzt werden. Die Eigenschaft wird dabei
permanent überwacht. Zur Definition eines Triggers wird die
Eigenschaft VisualTriggers des Elements <Style> verwendet.
Darunter wird als Unterlement ein PropertyTrigger (oder ein
MultiPropertyTrigger, siehe später...) definiert. Trigger
können nur innerhalb von Stilen definiert werden.
Hat die Eigenschaft IsMouseOver den Wert true, wird die Eigenschaft
Background auf den Wert Yellow gesetzt.
Beispiel:
Style3.xaml
<Style def:Name="MuchNicerButton" BasedOn="{NiceButton}">
<Button FontSize="24" FontFamily="Verdana" Foreground="Red" />
<Style.VisualTriggers>
<PropertyTrigger Property="IsMouseOver" Value="true">
<Set PropertyPath="Background" Value="Yellow"/>
</PropertyTrigger>
</Style.VisualTriggers>
</Style>
Die Bedingungen eines Triggers basieren auf den Eigenschaftswerten des
Elements. Die Eigenschaften mit dem Präfix Is bieten neben den
Standardeigenschaften Informationen zum Zustand eines Elements. Einige
werden in der folgenden Tabelle gezeigt.
Eigenschaft |
Beschreibung |
IsEnabled
|
true, wenn das Element
auf Benutzereingaben reagiert
|
IsFocused |
true, wenn das Element
den Fokus besitzt
|
IsMouseOver
|
true, wenn sich die Maus
über dem Element befindet.
|
IsPressed
|
true, wenn ein Button
geklickt wird. |
Trigger
mit mehreren Bedingungen
Sie können auch mehrere Trigger pro Element definieren.
Interessant wird es, wenn Sie mehrere Bedingungen zum Auslösen
des Triggers definieren möchten. Dazu wird ein
<MultiPropertyTrigger>-Element statt eines einfachen
<PropertyTrigger>-Elements verwendet. Über die
Compound-Eigenschaft <MultiPropertyTrigger.Conditions>
werden die Bedingungen definiert. Dazu werden ein oder mehrere
<Condition>-Elemente verwendet. Treffen alle Bedingungen
zu, wird die Eigenschaft des darauffolgenden
<Set>-Elements verändert.
Das folgende Beispiel zeigt, dass Sie auch "normale" Eigenschaften wie
die Farbe der Vordergrundfarbe für eine
Bedingungsprüfung einsetzen können. Die zweite
Schaltfläche wird grau angezeigt, wenn die Vordergrundfarbe
Rot ist und sich der Mauszeiger über der Schaltfläche
befindet. Weiterhin zeigt das Beispiel, dass auch mehrere Eigenschaften
über einen Trigger verändert werden können.
Die Schriftbreite wird zusätzlich auf Bold gesetzt.
Beispiel:
Style4.xaml
<Style.VisualTriggers>
<MultiPropertyTrigger>
<MultiPropertyTrigger.Conditions>
<Condition Property="IsMouseOver" Value="true" />
<Condition Property="Foreground" Value="Red" />
</MultiPropertyTrigger.Conditions>
<Set PropertyPath="Background" Value="Gray" />
<Set PropertyPath="FontWeight" Value="Bold" />
</MultiPropertyTrigger>
</Style.VisualTriggers>
Nach dem die Bedingung eines Triggers nicht mehr zutrifft, wird wieder
der vorherige Wert der Eigenschaft wiederhergestellt.
Der
VisualTree
Jedes Kontrollelement besitzt eine Standarddarstellung, die vom
Betriebssystem oder wie im Falle von Windows XP vom ausgewählten
Thema abhängt. Über den Visual Tree eines solchen Elements
können Sie seine Darstellung selbst vornehmen, d.h. den
Standard-Stil überschreiben. Das Überschreiben der
Darstellung kann für alle Elemente erfolgen, die von der Klasse
Control abgeleitet sind.
<XXX.Resources>
<Style def:Name="MeinStil">
<Element />
<Style.VisualTree>
<!-- Darstellung des Elements -->
</Style.VisualTree>
</Style>
</XXX.Resources>
<Element Style="{MeinStil}" ... />
Die Größe des Buttons wird mit 200x20 fetst vorgegeben.
Dadurch ändern er auch seine Breite nicht, wenn er in einem
DockPanel am Rand ausgerichtet wird. Sie können allerdings seine
Ausmasse durch das Überschreiben der Werte von Width und Height in
der Definition des <Button>-Elements ändern. Durch die
Angabe eines Canvas-Elements wird eine Zeichenfläche festgelegt,
auf der der Button selbst gezeichnet wird. Im Beispiel ist dies
ein hellblau gefülltes Rechteck mit einem blauen Rand. Das Canvas
und das Rechteck nehmen dabei immer die volle
Darstellungsgröße des Buttons mit 100% ein.
<Button Width="200" Height="20" />
<Style.VisualTree>
<Canvas Width="100%" Height="100%">
<Rectangle RadiusX="5" RadiusY="5" Width="100%" Height="100%" Stroke="Blue" Fill="LightBlue" />
</Canvas>
</Style.VisualTree>
Auf der Schaltfläche fehlt aber noch die Beschriftung. Dazu wird
ein <ContentPresenter>-Element verwendet. Dieses Element zeigt
innerhalb des Visual Trees den Inhalt des betreffenden Elements an.
Dazu wird das Attribut ContentControl.Content mit einem Wert belegt.
Dieser Wert wird häufig durch einen Alias festgelegt. Dieser Alias
bezieht sich auf das Originalelement und im Beispiel auf dessen
aktuellen Wert der Eigenschaft Content, die gleichbedeutend mit der
Beschriftung ist. Sie könnten auch einen festen Wert eintragen.
Die Syntax The *Alias() ist dabei ein Property Aliasing. Der Wert der
über Target angegebenen Eigenschaft wird damit zurückgegeben.
<ContentPresenter ContentControl.Content="*Alias(Target=Content)" />
<ContentPresenter ContentControl.Content="Ein fester Wert" />
Damit der angezeigte Text ausgerichtet ist, wird der
<ContentPresenter> in ein FlowPanel-Element eingeschlossen.
<FlowPanel HorizontalAlignment="center" VerticalAlignment="center" Width="100%" Height="100%">
<ContentPresenter ContentControl.Content="*Alias(Target=Content)" />
</FlowPanel>
Um eine Interaktion mit dem Button hervorzuheben, können
Referenzen auf bestimmte Eigenschaften hergestellt werden. Die Anzeige
des Rahmens der Schaltfläche soll beim Überfahren mit der
Maus geändert werden. Außerdem soll beim Festlegen der
Eigenschaft Background dieser Wert übernommen werden. Letzteres
erfolgt über Fill="*Alias(Target=Background)". Im Rechteck wird
eine Style-ID mit dem Namen ActiveStyle definiert. Ein PropertyTrigger
prüft dann die Eigenschaft IsMouseOver. Befindet sich die Maus
über der Schaltfläche, wird als Ziel der Stil ActiveStyle
verwendet und dort die Eigenschaften Fill und Opacity geändert.
<Style def:Name="SpecialButton">
<Button Width="200" Height="20" />
<Style.VisualTree>
<Canvas Width="100%" Height="100%">
<Rectangle def:StyleID="ActiveStyle" RadiusX="5" RadiusY="5" Width="100%" Height="100%"
Stroke="Blue" Fill="*Alias(Target=Background)" />
<FlowPanel HorizontalAlignment="center" VerticalAlignment="center" Width="100%" Height="100%">
<ContentPresenter ContentControl.Content="*Alias(Target=Content)" />
</FlowPanel>
</Canvas>
</Style.VisualTree>
<Style.VisualTriggers>
<PropertyTrigger Property="IsMouseOver" Value="true">
<Set Target="ActiveStyle" PropertyPath="Fill" Value="LightYellow" />
<Set Target="ActiveStyle" PropertyPath="Opacity" Value=".5" />
</PropertyTrigger>
</Style.VisualTriggers>
</Style>
Sollen Attribute der Beschriftung der Schaltfläche, also der
Content geändert werden, wird wieder ein Stil definiert und auf
das Element <ContentPresenter> angewendet. In diesem Fall gilt
die Definition für alle <ContentPresenter>-Elemente.
<Style def:Name="SpecialButtonText">
<ContentPresenter />
<Style.VisualTree>
<Text FontFamily="Verdana" Foreground="Red" FontWeight="Bold" FontSize="14pt">
<Text.TextContent>
<Bind/>
</Text.TextContent>
</Text>
</Style.VisualTree>
</Style>
Auslesen des VisualTrees einer
Komponente
using System.Text;
...
void Klack(object sender, RoutedEventArgs e)
{
Button b = sender as Button;
StringBuilder sb1 = new StringBuilder();
VisualCollection vc = VisualOperations.GetChildren(b);
if (vc.Count == 0)
{
sb1.Append("Keine Kinder");
sb1.Append(b.GetType());
}
else
{
sb1.Append(b.GetType());
sb1.Append(Environment.NewLine);
WriteToVisualTreeInfo(sb1, vc, 1);
}
tb1.Text = sb1.ToString();
}
void GetVisualTreeInfo(StringBuilder sb, VisualCollection vc, int level)
{
foreach (Visual v in vc)
{
sb.Append(new string(' ', level * 2));
sb.Append(v.GetType());
sb.Append(Environment.NewLine);
VisualCollection vc = VisualOperations.GetChildren(v);
if (vc.Count > 0)
{
GetVisualTreeInfo(sb, vc, level + 1);
}
}
4.4
Layoutkontrolle
In Windows-Anwendungen wird das Layout in der Regel durch die
pixelgenaue Positionierung der Komponenten realisiert. Weitere
Einstellungen sind über Anker und das Docking an eine
bestimmte Himmelsrichtung (oder den gesamten Inhalt) möglich.
Wird eine Anwendung verkleinert oder vergrößert hat
man bisher wenig Einfluß auf die Anordnung der
Kontrollelemente. In der Regel werden Sie bei einem zu schmalen Fenster
nicht mehr angezeigt. Avalon übernimmt das Konzept
der Layoutmanager Java's, in dem es verschiedene Paneltypen definiert,
die eine automatische Ausrichtung der Kontrollelemente vornehmen, z.B.
anhand der gewählten Schriftart und -größe
einer Schaltfläche. Die Panel dienen weiterhin dazu, eine
bestimmte Grundanordnung der Kontrollelemente zu schaffen.
Dazu können Panels weitere Panels enthalten. Die folgende
Abbildung zeigt die Aufteilung einer Anwendung in mehrere Bereiche
mittels Dock- und FlowPanel.
Alle Panels stammen aus dem Namespace System.Windows.Controls.
Panel |
Beschreibung |
BulletPanel |
Definieren das Layout
zweier zusammengehöriger Elemente, in der Regel ein Symbol
oder Element und ein beschreibender Text. |
Canvas |
Die Positionierung der
Kontrollelemente erfolgt genau anhand der angegebenen Koordinaten
für die Eigenschaften Width und Height. |
DockPanel |
Die
maximal 5 Kindelemente können oben, unten, links und rechts
sowie mittig angeordnet werden. Dazu werden bei den Elementen Werte der
Auszählung Dock angegeben, z.B. DockPanel.Dock="Top",
DockPanel.Dock="Bottom", DockPanel.Dock="Left",
DockPanel.Dock="Right" oder DockPanel.Dock="Fill". |
FixedPanel |
Erlaubt die exakte
Positionierung von Elementen unabhängig vom
Ausgabegerät. |
FlowPanel |
Die enthaltenen Elemente
werden hintereinander angeordnet. Begonnen wird links oben. |
Grid |
Die Elemente werden
tabellenartig in Zeilen und Spalten angeordnet. Sie können
auch mehrere Zellen einnehmen.
|
GridPanel |
<deprecated>,
verwenden Sie stattdessen Grid oder Table |
StackPanel |
>> ToDo
<< |
Table |
|
TabPanel |
Dient der Konfiguration
der Arbeitsfläche eines Tabs in einem TabControl. |
TextPanel |
Kann mehrzeiligen Text
mit Formatierungen und mehreren Spalten anzeigen. |
Canvas
Durch die manuelle Angabe der Position können sich Elemente
theretisch auch überlappen. Standardmäßg
wird das
zuletzt angegebene Element an
oberster Stelle angezeigt. Alle Unterelemente von Canvas
erhalten
zusätzliche Attribute Canvas.xxx zur Festlegung der Position
innerhalb des Canvas. Ansonsten werden sie einfach
übereinander
angezeigt.
<Window>
<Canvas Background="LightBlue">
<Button Canvas.Left="10" Canvas.Top="10">Hallo</Button>
</Canvas>
</Window>
Als SourceCode würde dies so aussehen:
Window win = new Window ();
Canvas can = new Canvas ();
can.Background = Brushes.LightBlue;
Button btn = new Button();
btn.Content = "Hallo";
Canvas.SetTop(btn, new System.Windows.Length(10));
Canvas.SetLeft(btn, new System.Windows.Length(10));
can.Children.Add (btn);
win.Content= can;
DockPanel
Dieses Panel erlaubt die Positionierung der enthaltenen Elemente oben,
unten, links, rechts oder mittig (ausfüllend). Dazu wird bei
jedem
Element als Attribut die Eigenschaft DockPanel.Dock mit einem Wert
belegt. Geben Sie keine Attribute an, werden die Elemente in der
Reihenfolge Top, Bottom, Left, Right, Fill angeordnet. Die Fill-Angabe
muss immer die letzte sein, da sonst dieses Element alle folgenden
verdeckt.
Öffne
Beispiel:
LayoutDockPanel.xaml
<Window Width="300" Height="200" xmlns="http://schemas.microsoft.com/2003/xaml">
<DockPanel DockPanel.Dock="Fill">
<FlowPanel DockPanel.Dock="Top" Background="LightGreen">
<Button>Neu</Button>
<Button>Öffnen</Button>
</FlowPanel>
<FlowPanel DockPanel.Dock="Bottom" Background="LightSteelBlue">
<Button>Dies ist die Statusleiste</Button>
</FlowPanel>
<TextPanel DockPanel.Dock="Fill" Background="LightYellow">
Halllllllo
</TextPanel>
</DockPanel>
</Window>
Als SourceCode wird die Position folgendemaßen gesetzt:
FlowPanel fp = new FlowPanel();
DockPanel.SetDock(fp, Dock.Top);
FlowPanel
Wenn Sie in ein FlowPanel Kontrollelemente einfügen, werden
diese
hintereinander von oben links beginnend angeordnet. Reicht der Platz
nicht aus, wird eine Zeile darunter fortgefahren. Die
Elemente
können Größenangaben wie Width und Height
verwenden.
Eine spezielle Angabe für die Position im FlowPanel gibt es
nicht.
=>
<Window Width="250" Height="100">
<FlowPanel>
<Button>Neu</Button>
<Button>Öffnen</Button>
<Button>Schließen</Button>
<Button>Beenden</Button>
</FlowPanel>
</Window>
TextPanel
Zur Anzeige von Text dient das TextPanel. Es wird in diesem mehrzeilgen
Textfeld die Verwendung mehrerer Schriftarten und -stile
unterstützt. Für die Anzeige von einfach formatierten
Text
eignet sich das Text-Element besser. Die Formatierung des Textes
erfolgt auf ähnliche Art und Weise wie durch HTML nur mit
anderen
Tags. Die Elemente für die Formatierung werden über
Klassen
aus dem Namespace System.Windows.Documents bereitgestellt. Dazu widmet
sich ein eigenes Kapitel.
<Window Width="300" Height="120">
<TextPanel DockPanel.Dock="Fill" ColumnCount="2" ColumnGap="10"
ColumnRuleBrush="Red" ColumnRuleWidth="3">
<Block FontSize="14">
Dies ist ein Text mit unterschiedlichen <Italic>Schriftstilen</Italic> und
<Inline FontFamily="Courier New">Schriftarten</Inline>.
Dies ist ein Text mit unterschiedlichen <Italic>Schriftstilen</Italic> und
<Inline FontFamily="Courier New">Schriftarten</Inline>.
</Block>
</TextPanel>
</Window>
Grid
Über ein Grid können Sie eine Tabellenstruktur
erzeugen.
Dazu wird der von Panel eingenommene Bereich in Zeilen und Spalten
aufgeteilt. Zuerst wird ein Grid-Element definiert. Im Unterschied zu
Tabellen können Elemente in einem Grid mehrere Zellen in
Anspruch
nehmen.
Eigenschaft |
Beschreibung |
Height
|
Legt die Höhe
fest |
Width |
Legt die Breite fest |
ShowGridlines |
Zeigt/verbirgt die
Gitternetzlinien (true oder false) |
Background |
Left die
Hintergrundfarbe fest |
Als Unterelemente des Grids werden nun die Spalten- und
Zeilendefinitionen angegeben. Für jede Spalte wird ein
<ColumnDefinition />-Element eingefügt.
Jede Zeile wird durch ein <RowDefinition />-Element
erzeugt. Im gezeigten Beispiel sind dies also 3 Spalten mit 2 Zeilen.
Beide Elemente besitzen keine wesentlichen Eigenschaften. Jetzt werden
die Kindelemente hinzugefügt. In den Kindelementen
können Sie jetzt Attribute des Grids verwenden, um die
Position innerhalb der Zeilen und Spalten sowie die Abstände
vom Rand der Zelle festzulegen.
Eigenschaft |
Beschreibung |
Column
|
Gibt die Spalte an, von
0 beginnend. |
Row |
Gibt die Zeile an, von 0
beginnend.
|
Top, Bottom, Left, Right
|
Definiert den Abstand
zum oberen, unteren, linken und unteren Rand zur aktuellen Zelle in
Pixeln.
|
ColumnSpan, RowSpan |
Geben Sie an, wieviel
Zellen das Element einnehmen soll.
|
Beispiel
öffnen: LayoutGrid.xaml
<Grid xmlns="http://schemas.microsoft.com/2003/xaml"
Width="300" Height="200" ShowGridLines="true" Background="LightGreen">
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<RowDefinition />
<RowDefinition />
<Rectangle Grid.Column="0" Grid.Row="0" Grid.Left="5" Grid.Top="5"
Grid.Bottom="5" Grid.Right="5" Fill="LightYellow">
</Rectangle>
<Rectangle Grid.Column="1" Grid.Row="0" Grid.Left="5" Grid.Top="5"
Grid.Bottom="5" Grid.Right="5" Fill="LightYellow">
</Rectangle>
<Rectangle Grid.Column="2" Grid.Row="0" Grid.Left="5" Grid.Top="5"
Grid.Bottom="5" Grid.Right="5" Fill="LightYellow">
</Rectangle>
<Rectangle Grid.Column="0" Grid.Row="1" Grid.Left="5" Grid.Top="5"
Grid.Bottom="5" Grid.Right="5" Fill="LightYellow">
</Rectangle>
<Rectangle Grid.Column="1" Grid.Row="1" Grid.Left="5" Grid.Top="5"
Grid.Bottom="5" Grid.Right="5" Grid.ColumnSpan="2" Fill="LightYellow">
</Rectangle>
<Text Grid.Column="0" Grid.Row="0" Grid.Left="40" Grid.Top="40">0, 0</Text>
<Text Grid.Column="1" Grid.Row="0" Grid.Left="40" Grid.Top="40">1, 0</Text>
<Text Grid.Column="2" Grid.Row="0" Grid.Left="40" Grid.Top="40">2, 0</Text>
<Text Grid.Column="0" Grid.Row="1" Grid.Left="40" Grid.Top="40">0, 1</Text>
<Text Grid.Column="1" Grid.Row="1" Grid.Left="40" Grid.Top="40">1, 1</Text>
<Text Grid.Column="2" Grid.Row="1" Grid.Left="40" Grid.Top="40">2, 1</Text>
</Grid>
Erstellen eines Grids mit zwei Rechtecken in C#.
public partial class Window1 : Window
{
System.Windows.Controls.Grid grid1;
System.Windows.Controls.ColumnDefinition col1;
System.Windows.Controls.ColumnDefinition col2;
System.Windows.Controls.RowDefinition row1;
System.Windows.Shapes.Rectangle rect1;
System.Windows.Shapes.Rectangle rect2;
protected override void OnLoading(EventArgs args)
{
grid1 = new Grid();
grid1.Width = new Length(200);
grid1.Height = new Length(100);
grid1.ShowGridLines = true;
grid1.Background = new SolidColorBrush(Colors.LightBlue);
col1 = new ColumnDefinition();
col2 = new ColumnDefinition();
grid1.ColumnDefinitions.Add(col1);
grid1.ColumnDefinitions.Add(col2);
row1 = new RowDefinition();
grid1.RowDefinitions.Add(row1);
rect1 = new Rectangle();
rect1.Fill = new SolidColorBrush(Colors.LightSeaGreen);
Grid.SetColumn(rect1, 0);
Grid.SetRow(rect1, 1);
Grid.SetTop(rect1, new Spacing(10));
rect2 = new Rectangle();
rect2.Fill = new SolidColorBrush(Colors.LightSeaGreen);
Grid.SetColumn(rect2, 1);
Grid.SetRow(rect2, 0);
Grid.SetTop(rect2, new Spacing(10));
grid1.Children.Add(rect1);
grid1.Children.Add(rect2);
this.Content = grid1;
base.OnLoading(args);
}
}
FixedPanel
Die Elemente des Bereichs können exakt positioniert werden,
unabhängig vom verwendeten Ausgabegerät.
>> ToDo <<
StackPanel
Sie können immer nur ein StackPanel verwenden. Ansonsten
erhalten
Sie eine Exception. In der aktuellen Version des CTP von Avalon
erhalten Sie eine Exception "StackPanel can only be used in the new
layout model". Das neue Layoutmodell ist noch nicht implementiert.
>> TODo <<
BulletPanel
Dieses Layout dient der Anzeige von jeweils zwei Elementen,
einem
Symbol und einem Text. Typischerweise wird es eingesetzt um
Aufzählungen oder Auswahllisten zu implementieren.
Über die
Eigenschaften Height und Width legen Sie die Ausmasse des Panels fest.
Mit der Eigenschaft Margin definieren Sie den Abstand der enthaltenen
Elementen (nach allen Seiten).
Beispiel
öffnen: LayoutBulletPanel.xaml
<TextPanel xmlns="http://schemas.microsoft.com/2003/xaml">
<BulletPanel Height="25" Width="150" Margin="10">
<RadioButton/><Text>A RadioButton Bullet</Text>
</BulletPanel>
<BulletPanel Height="25" Width="150" Margin="10">
<CheckBox/>
<Text>A CheckBox Bullet</Text>
</BulletPanel>
</TextPanel>
TabPanel
Ein TabControl besteht aus Tabs und jeder Tab besitzt ein Panel, auf
dem dessen spezifische Elemente angeordnet werden. Ein TabPanel kann
für solch ein Panel Eigenschaften festlegen, z.B. die
Hintergrundfarbe oder den Cursor.
Beispiel
öffnen: LayoutTabPanel.xaml
<Grid xmlns="http://schemas.microsoft.com/2003/xaml" Width="300" Height="200">
<TabControl TabStripPlacement="Top">
<TabItem Header="Hallo Tab 1">
<TabPanel Background="LightYellow" Cursor="Hand">
<Text>Dies ist der erste Tab</Text>
</TabPanel>
</TabItem>
<TabItem Header="Hallo Tab 2">
<TabPanel Background="LightBlue">
<Text>Dies ist der erste Tab</Text>
</TabPanel>
</TabItem>
</TabControl>
</Grid>
Table
Um Daten tabellarisch auszugeben, kann dieses Element verwendet werden.
Die Funktionsweise ist ähnliche dem Aufbau einer HTML-Tabelle.
In
ein <Table>-Element können Sie jeweils ein
<TableHeader>, <TableBody> und
<TableFooter>-Element
einfügen. Das Element <TableRow> beginnt eine
neue Zeile.
Über <TableCell> wird eine neue Zelle in einer
Zeile
begonnen. Dieses Element enthält dann auch den konkreten
Inhalt.
Ein <Table>-Element muss innerhalb eines
<TextPanel>-Elements verwendet werden. Mit einem Grid
sind Sie da
beispielsweise unabhängiger. Über ein
<TableColumn>-Element können Sie zu Beginn die
Spaltenbreiten festlegen.
<TextPanel>
<Table>
<TableColumn />
<TableHeader>
<TableRow>
<TableCell>...</TableCell>
<TableCell>...</TableCell>
</TableRow>
</TableHeader>
<TableBody>
...
</TableBody>
<TableFooter>
</TableFooter>
</Table>
</TextPanel>
Beispiel
öffnen: LayoutTable.xaml
<TextPanel xmlns="http://schemas.microsoft.com/2003/xaml" xmlns:def="Definition">
<Table CellSpacing="3">
<TableColumn Width="150"/>
<TableColumn Width="100"/>
<TableHeader>
<TableRow>
<TableCell ColumnSpan="2">
<Text FontSize="18pt" FontWeight="Bold">
Einkaufsliste
<Text.TextDecorations>
<TextDecorations>
<TextDecoration Location="Underline" />
</TextDecorations>
</Text.TextDecorations>
</Text></TableCell>
</TableRow>
<TableRow Background="LightGray">
<TableCell><Text>Artikel</Text></TableCell>
<TableCell><Text>Preis</Text></TableCell>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell><Text>Brot</Text></TableCell>
<TableCell><Text>3,00 Euro</Text></TableCell>
</TableRow>
<TableRow>
<TableCell><Text>Butter</Text></TableCell>
<TableCell><Text>2,00 Euro</Text></TableCell>
</TableRow>
</TableBody>
<TableFooter>
<TableRow Background="LightGray">
<TableCell><Text>Summe</Text></TableCell>
<TableCell><Text>5.00 Euro</Text></TableCell>
</TableRow>
</TableFooter>
</Table>
</TextPanel>