18.8 Grid 

Der sicherlich flexibelste Container der WPF wird durch Grid beschrieben. Im ersten Moment erinnert dieser Container sehr an das zuvor besprochene UniformGrid, er ist aber mit weitaus mehr Eigenschaften ausgestattet. Um nur ein Beispiel zu nennen: Sie können eine Komponente zellübergreifend darstellen. Das ist eine Fähigkeit, die Sie im UniformGrid vergeblich suchen.
18.8.1 Struktur eines »Grid« festlegen 

Grundsätzlich wird der Bereich im Grid, wie schon der Name verrät, in Zeilen und Spalten aufgeteilt. Dazu sind innerhalb des <Grid>-Tags in der XAML-Datei zwei Bereiche zu definieren: einen Bereich, der alle Zeilen beschreibt, und einen Bereich, der alle Spalten beschreibt. Innerhalb der beiden Bereiche definieren Sie die Zeilen und Spalten.
<Grid> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> </Grid>
Grid.RowDefinitions grenzt den Bereich der Zeilen ein, Grid.ColumnDefinitions den Bereich der Spalten. Innerhalb dieser beiden Bereiche wird mit RowDefinition eine Zeile und mit ColumnDefinition eine Spalte beschrieben. Bei dieser Aufteilung werden alle Zellen in gleicher Größe dargestellt. Zur Laufzeit führt eine Veränderung der Fenstergröße dazu, dass sich die Spalten und Zeilen im gleichen Verhältnis vergrößern oder verkleinern.
Die Breite jeder einzelnen Spalte können Sie mit der Eigenschaft Width anpassen. Analog kann die Höhe jeder Zeile mit Height festgelegt werden. Sie können die Angabe in Pixel machen, aber es bieten sich noch andere Möglichkeiten, beispielsweise Auto. Mit dieser Einstellung wird die Spaltenbreite beziehungsweise die Zeilenhöhe anhand des breitesten beziehungsweise höchsten Controls bestimmt.
Die Komponenten, die in den Zellen positioniert werden sollen, müssen als eigenständiger Bereich parallel neben Grid.RowDefinitions und Grid.ColumnDefinitions innerhalb von Grid eingetragen sein. Um eine Komponente eindeutig einer Zelle im Grid zuzuordnen, verwenden Sie die Attached Propertys Grid.Column und Grid.Row. Beiden übergeben Sie jeweils den Spalten- bzw. Spaltenindex, die 0-basiert sind. Vergessen Sie die Angabe von Zeilen-oder Spaltenindex, wird dieser Index automatisch mit 0 festgelegt.
Im folgenden Codefragment wird ein Grid in jeweils zwei Zeilen und Spalten aufgeteilt. Die Spaltenbreite soll sich mit Auto automatisch anpassen. In drei der vier Zellen wird jeweils ein Button platziert. Button1 hat eine Breite von 200 Pixel. Der in der Zeile darunter befindliche Button3 weist nur eine Breite von 100 Pixel auf. Weil die Spalte sich an der Breite des größten Elements orientiert, wird Button3 zentriert in seiner Zelle angezeigt. Button2 andererseits weist eine Breite auf, die in der Summe mit der Breite von Button1 größer ist als die Fensterbreite von 300. Daher wird Button2 nicht mehr vollständig angezeigt, wie in Abbildung 18.18 zu sehen ist.
<Window ... Height="200" Width="300"> <Grid> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"></ColumnDefinition> <ColumnDefinition Width="Auto"></ColumnDefinition> </Grid.ColumnDefinitions> <Button Grid.Column="0" Grid.Row="0" Width="200">Button1</Button> <Button Grid.Column="1" Grid.Row="0" Width="150">Button2</Button> <Button Grid.Column="0" Grid.Row="1" Width="100">Button3</Button> </Grid> </Window>
Abbildung 18.18 Grid-Steuerelemente mit automatischer Spaltenbreite
Vergrößern Sie die Breite des Fensters über die Summe der Breiten der Schaltflächen Button1 und Button2 hinaus, verbleibt im rechten Fensterbereich ein ungenutzter Bereich. Einen ähnlichen Effekt können Sie auch bei der Festlegung der Höhen beobachten. Daher sollten Sie bei zumindest einer Zeile und einer Spalte des Grid das Wildcard-Zeichen * benutzen. Diese Zeile oder Spalte füllt dann den verbleibenden Platz aus.
<Window ... Height="200" Width="400"> <Grid> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"></ColumnDefinition> <ColumnDefinition Width="*"></ColumnDefinition> </Grid.ColumnDefinitions> ... </Grid> </Window>
In Abbildung 18.19 sehen Sie das Fenster zur Laufzeit. Beachten Sie bitte, dass die Wildcard nur dann ihre Stärken ausspielen kann, wenn die Breite des Fensters die Gesamtbreite der beiden Schaltflächen überschreitet.
Abbildung 18.19 Fenster mit Einsatz einer Wildcard
Die Wildcard * kann auch Verhältnisse bezüglich Höhe und Breite bilden. Geben Sie beispielsweise 3* an, bedeutet dies, dass die Höhe beziehungsweise Breite dem dreifachen Wert einer mit * gekennzeichneten Spalte oder Reihe entspricht. Angenommen, Sie hätten drei Spalten mit den Breiten *, 2* und 3* spezifiziert, dann wird die Gesamtbreite des Fensters in sechs gleich große Einheiten aufgeteilt. Dabei wird die erste Spalte eine Einheit breit, die zweite zwei Einheiten und die dritte drei Einheiten (siehe Abbildung 18.20). Da für den Zahlenwert der Typ double erlaubt ist, können Sie eine sehr feine Zellenstruktur erzielen.
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="3*"></ColumnDefinition> <ColumnDefinition Width="2*"></ColumnDefinition> <ColumnDefinition Width="*"></ColumnDefinition> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition></RowDefinition> </Grid.RowDefinitions> </Grid>
Die Wildcard * lässt sich mit absoluten Mindestmaßen kombinieren. Mit MinWidth="50" und Width="*" erreichen Sie beispielsweise, dass die entsprechende Spalte 50 Pixel nicht unterschreitet. Reicht der Platz aus, verbreitert sich die Spalte.
Abbildung 18.20 Die Aufteilung der Spalten mit *
18.8.2 »ColumnSpan« und »RowSpan« 

Bei einer Komponente, die sich über mehrere Spalten oder Zeilen aufspannen soll, geben Sie Grid.ColumnSpan oder/und Grid.RowSpan an. Die beiden Eigenschaften Grid.Column und Grid.Row dienen in dem Fall dazu, die linke obere Zelle für das Element zu reservieren.
<Grid> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <Button Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="2" Grid.RowSpan="2">Button1</Button> </Grid>
Abbildung 18.21 Button, der über mehrere Zellen aufspannt ist
18.8.3 Spalten- und Zeilenbreite mit »GridSplitter« ändern 

Möchten Sie dem Anwender erlauben, ähnlich wie bei einem Excel-Tabellenblatt die Zeilenhöhe oder Spaltenbreite mit der Maus zu verändern, kommt das Element GridSplitter ins Spiel. Für einen GridSplitter sollten Sie eine eigene Spalte beziehungsweise Reihe bereitstellen, die auch über eine ausreichende Höhe oder Breite verfügt. Zudem sollten Sie einen nicht zu kleinen Randabstand mit der Eigenschaft Margin festlegen. Ansonsten könnte es sein, das der Splitter zur Laufzeit so weit gezogen wird, dass er nicht bedient werden kann. Diese Komponente hat also einen Haken, wenn Sie beim Design nicht genügend aufpassen.
Eine Eigenschaft sollte an dieser Stelle noch erwähnt werden: ShowPreview. Setzen Sie diese auf true, wird beim Greifen und Ziehen mit der Maus der Splitter in seiner ursprünglichen Lage weiterhin angezeigt. Erst beim Loslassen der Maus wird der neue Zustand endgültig eingenommen und werden die Komponenten, die sich in den Zellen befinden, an die neue Zellengröße angepasst.
Im folgenden Beispielcode ist ein vertikaler und ein horizontaler GridSplitter definiert. Beachten Sie, wie mit den Eigenschaften Column, Row, ColumnSpan und RowSpan die vertikale und horizontale Ausrichtung und Größe festgelegt wird:
<Grid> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition Height="15"></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition Width="20"></ColumnDefinition> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <Button Grid.Column="0" Grid.Row="0">Button1</Button> <Button Grid.Column="2" Grid.Row="1">Button2</Button> <Button Grid.Column="3" Grid.Row="3">Button3</Button> <Button Grid.Column="4" Grid.Row="4">Button4</Button> <GridSplitter ShowsPreview="True" Grid.Column="1" Grid.Row="0" Grid.RowSpan="3" HorizontalAlignment="Center" Margin="5,5,5,5" Width="3" />’ <GridSplitter ShowsPreview="True" Grid.Row="2" Grid.ColumnSpan="3" HorizontalAlignment="Stretch" Margin="5,5,5,5" Height="3" /> </Grid>
Abbildung 18.22 Ein vertikaler und ein horizontaler GridSplitter in Visual Studio 2010