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.

PanelDemo

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>