Das Layoutsystem

Aktualisiert: November 2007

In diesem Thema wird das Windows Presentation Foundation (WPF)-Layoutsystem beschrieben. Wenn Sie Benutzeroberflächen mit einer ansprechenden Darstellung und hoher Leistungsfähigkeit erstellen möchten, ist es wichtig, dass Sie verstehen, wie und wann Layoutberechnungen auftreten.

Dieses Thema enthält folgende Abschnitte.

Das Layoutsystem

Der Begriff "Layout" umfasst den Vorgang des Messens und Anordnens der Member der Children-Auflistung eines Panel-Elements und das anschließende Zeichnen dieser Member auf dem Bildschirm. Dies ist ein ressourcenintensiver Vorgang. Je größer die Children-Auflistung ist, desto mehr Rechenvorgänge werden durchgeführt. Auch das Layoutverhalten, das vom Panel-Element (dem Besitzer der Auflistung) definiert wird, kann zu höherer Komplexität führen. Ein relativ einfaches Layout, wie z. B. Canvas, kann ein sehr gutes Leistungsverhalten haben, wenn ein komplexeres Panel-Element, wie z. B. Grid, nicht erforderlich ist.

Jedes Mal, wenn ein untergeordnetes UIElement seine Position ändert, kann ein neuer Durchlauf des Layoutsystems ausgelöst werden. Sie sollten wissen, durch welche Ereignisse das Layoutsystem aufgerufen werden kann, da unnötige Aufrufe die Leistung der Anwendung verschlechtern können.

Im einfachsten Fall ist das Layout ein rekursives System, das dafür sorgt, dass die Größe eines Elements angepasst, das Element positioniert und auf dem Bildschirm gezeichnet wird. Das Layoutsystem führt für jedes Member der untergeordneten Children-Auflistung zwei Durchläufe durch: eine Maßübergabe und eine Anordnungsübergabe. Jedes untergeordnete Panel-Element bietet eine eigene MeasureOverride-Methode und ArrangeOverride-Methode für das eigene spezifische Layoutverhalten. Dabei handelt es sich um eine Reihe von Ereignissen, die auftreten, wenn das Layoutsystem aufgerufen wird.

  1. Ein untergeordnetes UIElement startet den Layoutprozess, indem es zunächst seine Kerneigenschaften messen lässt.

  2. Die in FrameworkElement definierten Größeneigenschaften werden ausgewertet, z. B. Width, Height und Margin.

  3. Die für das Panel-Element spezifische Logik wird angewendet, z. B. die Dock-Richtung oder die Stapel-Orientation.

  4. Der Inhalt wird angeordnet, nachdem alle untergeordneten Elemente gemessen wurden.

  5. Die Children-Auflistung wird auf dem Bildschirm gezeichnet.

  6. Der Prozess wird erneut aufgerufen, wenn der Auflistung zusätzliche Children hinzugefügt werden, eine LayoutTransform angewendet oder die UpdateLayout-Methode aufgerufen wird.

Dieser Prozess und die Mittel, mit denen er aufgerufen wird, werden in den folgenden Abschnitten genauer definiert.

Umgebende Felder für Elemente

Wenn Sie ein Anwendungslayout in Windows Presentation Foundation (WPF) erstellen möchten, ist es wichtig, dass Sie Kenntnisse über das umgebende Feld haben, das alle Elemente umgibt. Diese Abstraktion hilft Ihnen, das Verhalten des Layoutsystems zu verstehen. Jedes vom Layoutsystem verwendete FrameworkElement kann als Rechteck gesehen werden, das in eine Layoutpartition eingefügt wird. Eine Klasse (LayoutInformation) wird verfügbar gemacht, die die geometrischen Grenzen der Layoutreservierung oder des Layoutplatzes zurückgeben kann. Die Größe dieses Rechtecks wird vom System festgelegt, indem der verfügbare Bildschirmplatz, die Größe eventuell vorhandener Einschränkungen, Layout-spezifische Eigenschaften wie Margin und Padding sowie das individuelle Verhalten des übergeordneten Panel-Elements berechnet werden. Durch Verarbeitung dieser Daten ist das System in der Lage, die Position aller untergeordneten Elemente für ein bestimmtes Panel zu berechnen. Sie sollten berücksichtigen, dass die auf dem übergeordneten Element definierten Größenanpassungsmerkmale (wie z. B. Border) die untergeordneten Elemente beeinflussen.

Beachten Sie z. B. das folgende einfache Layoutszenario.

Ein typisches Raster, ohne überlagerndes umgebendes Feld.

Dieses Layout kann mit dem folgenden Extensible Application Markup Language (XAML)-Code erstellt werden.

<Grid Name="myGrid" Background="LightSteelBlue" Height="150">
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="250"/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>
  <TextBlock Name="txt1" Margin="5" FontSize="16" FontFamily="Verdana" Grid.Column="0" Grid.Row="0">Hello World!</TextBlock>
  <Button Click="getLayoutSlot1" Width="125" Height="25" Grid.Column="0" Grid.Row="1">Show Bounding Box</Button>
  <TextBlock Name="txt2" Grid.Column="1" Grid.Row="2"/>
</Grid>

Das einzelne TextBlock-Element wird in einem Grid gehostet, und während der Text nur die linke obere Ecke der Spalte ausfüllt, in der er platziert wurde, ist der zugewiesene Platz für den TextBlock eigentlich viel größer. Das umgebende Feld für ein FrameworkElement kann mit der GetLayoutSlot-Methode abgerufen werden. Wenn Sie diese Methode verwenden, wird das umgebende Feld des TextBlock-Elements überlagert (dies ist möglich, da der TextBlock in einem Grid gehostet wird, das als Panel-Element die gemeinsame Nutzung von Layoutkoordinaten erlaubt).

Das umgebende Feld des TextBlocks ist jetzt sichtbar.

Die umgebende weiße Linie zeigt, dass die dem TextBlock-Element zugewiesene Partition tatsächlich viel größer ist als der Bereich, den es ausfüllt. Wenn dem Grid zusätzliche Elemente hinzugefügt werden, könnte diese Zuordnung abhängig von dem Typ und der Größe der hinzugefügten Elemente kleiner oder größer werden.

Der Layoutplatz für den TextBlock wird zurückgegeben und in einen Path übersetzt, wobei mit der GetLayoutSlot-Methode eine Technik verwendet wird, die beim Anzeigen des umgebenden Felds eines Elements nützlich sein kann.

Private Sub getLayoutSlot1(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim myRectangleGeometry As New RectangleGeometry
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1)
    Dim myGeometryDrawing As New GeometryDrawing
    Dim myPath As New Path
    myPath.Data = myRectangleGeometry
    myPath.Stroke = Brushes.LightGoldenrodYellow
    myPath.StrokeThickness = 5
    Grid.SetColumn(myPath, 0)
    Grid.SetRow(myPath, 0)
    myGrid.Children.Add(myPath)
    txt2.Text = "LayoutSlot is equal to " + LayoutInformation.GetLayoutSlot(txt1).ToString()
End Sub
private void getLayoutSlot1(object sender, System.Windows.RoutedEventArgs e)
{
    RectangleGeometry myRectangleGeometry = new RectangleGeometry();
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1);
    GeometryDrawing myGeometryDrawing = new GeometryDrawing();
    Path myPath = new Path();
    myPath.Data = myRectangleGeometry;
    myPath.Stroke = Brushes.LightGoldenrodYellow;
    myPath.StrokeThickness = 5;
    Grid.SetColumn(myPath, 0);
    Grid.SetRow(myPath, 0);
    myGrid.Children.Add(myPath);
    txt2.Text = "LayoutSlot is equal to " + LayoutInformation.GetLayoutSlot(txt1).ToString();
}

Messen und Anordnen von untergeordneten Elementen

Wenn der Inhalt eines Window-Objekts gerendert wird, wird das Layoutsystem automatisch aufgerufen. Um Inhalt anzuzeigen, muss der Content des Fensters ein Stamm-Panel-Element definieren, das zur Definition eines Frameworks dient, mit dem Children auf dem Bildschirm angeordnet werden. Unter Panel-Elemente und benutzerdefiniertes Layoutverhalten finden Sie eine Liste der verfügbaren Panel-Elemente sowie Informationen zum Erstellen von benutzerdefinierten Layoutelementen.

Das erste Element des Layouts ist die Maßübergabe, bei der jedes Member der Children-Auflistung ausgewertet wird. Der Prozess beginnt mit einem Aufruf an die Measure-Methode. Diese Methode wird innerhalb der Implementierung des übergeordneten Panel-Elements aufgerufen. Sie muss nicht explizit aufgerufen werden, um das Layout durchzuführen.

Zuerst werden systemeigene Größeneigenschaften für das UIElement ausgewertet, z. B. Clip und Visibility. Dies generiert den Wert constraintSize, der an MeasureCore übergeben wird.

Danach werden auf FrameworkElement definierte Frameworkeigenschaften verarbeitet, was sich auf den Wert von constraintSize auswirkt. Diese Eigenschaften beschreiben normalerweise die Größenanpassungsmerkmale für das zugrunde liegende UIElement, z. B. Height, Width, Margin und Style. Jede dieser Eigenschaften kann den Platz ändern, der zum Anzeigen des Elements nötig ist. MeasureOverride wird dann mit constraintSize als Parameter aufgerufen.

Tipp

Es gibt einen Unterschied zwischen den Eigenschaften von Height, Width, ActualHeight und ActualWidth. Die ActualHeight-Eigenschaft ist beispielsweise ein berechneter Wert, der auf anderen Höheneingaben und dem Layoutsystem beruht. Der Wert wird von dem Layoutsystem selbst auf Grundlage des tatsächlichen Renderingdurchlaufs festgelegt und kann aus diesem Grund etwas kleiner sein als der Wert, der für Eigenschaften festgelegt wird, welche die Grundlage für die Eingabeänderung sind, wie z. B. Height.

Da es sich bei ActualHeight um einen berechneten Wert handelt, sollten Sie daran denken, dass es mehrere oder inkrementelle gemeldete Änderungen dieses Werts geben kann, die das Ergebnis verschiedener Vorgänge des Layoutsystems sind. Unter Umständen meldet das Layoutsystem den erforderlichen Platz für untergeordnete Elemente, Einschränkungen durch das übergeordnete Element usw.

Das Ziel der Maßübergabe ist letztendlich, dass das untergeordnete Element seine DesiredSize festlegt. Dies geschieht während des MeasureCore-Aufrufs. Dieser Wert wird von Measure zur Verwendung während des Anordnungsprozesses für den Inhalt gespeichert.

Der Anordnungsprozess beginnt mit einem Aufruf an die Arrange-Methode. Während der Anordnungsübergabe generiert das übergeordnete Panel-Element ein Rechteck, das die Grenzen des untergeordneten Elements darstellt. Dieser Wert wird zur Verarbeitung an die ArrangeCore-Methode übergeben.

Die ArrangeCore-Methode wertet die DesiredSize des untergeordneten Elements sowie alle zusätzliche Ränder aus, die die gerenderte Größe des Elements beeinflussen könnten, und generiert eine arrangeSize, die als ein Parameter an das ArrangeOverride-Element des Panels übergeben wird. ArrangeOverride generiert die finalSize des untergeordneten Elements, und am Schluss führt die ArrangeCore-Methode eine abschließende Evaluierung von Offseteigenschaften, z. B. Margin und Alignment, durch und platziert das untergeordnete Element innerhalb seines Layoutplatzes. Das untergeordnete Element muss nicht (und wird häufig nicht) den gesamten zugeordneten Platz ausfüllen. Dem übergeordneten Panel-Element wird dann die Steuerung zurückgegeben, und der Layoutprozess ist abgeschlossen.

Panel-Elemente und benutzerdefiniertes Layoutverhalten

Windows Presentation Foundation (WPF) enthält eine abgeleitete Suite von Panel-Elementen, die das Erstellen von vielen komplexen Layouts ermöglicht. Gängige Szenarien, zum Beispiel das Stapeln von Elementen, können problemlos durch Verwenden des StackPanel-Elements erzielt werden, während ein Canvas komplexere, frei fließende Layouts ermöglicht.

Die folgende Tabelle bietet einen Überblick über die verfügbaren Layoutelemente.

Panel-Name

Beschreibung

Canvas

Definiert einen Bereich, in dem Sie untergeordnete Elemente explizit mithilfe von Koordinaten positionieren können, die relativ zum Canvas-Bereich sind.

DockPanel

Definiert einen Bereich, in dem Sie untergeordnete Elemente entweder horizontal oder vertikal relativ zueinander anordnen können.

Grid

Definiert einen flexiblen Rasterbereich, der aus Spalten und Zeilen besteht.

StackPanel

Ordnet untergeordnete Elemente in einer einzelnen Zeile an, die horizontal oder vertikal ausgerichtet sein kann.

VirtualizingPanel

Stellt ein Framework für Panel-Elemente bereit, die die Auflistung ihrer untergeordneten Daten virtualisieren. Dies ist eine abstrakte Klasse.

WrapPanel

Positioniert untergeordnete Elemente sequenziell von links nach rechts und umbricht den Inhalt am Rand des enthaltenden Felds auf die nächste Zeile. Je nach dem Wert der Orientation-Eigenschaft erfolgt die weitere Anordnung nacheinander von oben nach unten oder von rechts nach links.

Codebeispiele, in denen die Verwendung jedes dieser Elemente veranschaulicht wird, finden Sie unter Layoutbeispiele.

Für Szenarien, die ein Anwendungslayout erfordern, welches durch Verwendung der vordefinierten Panel-Elemente nicht erstellt werden kann, können benutzerdefinierte Layoutverhalten erzielt werden, indem Elemente von Panel erben und die MeasureOverride-Methode und die ArrangeOverride-Methode überschrieben werden. Ein Beispiel finden Sie unter Beispiel für einen benutzerdefinierten radial angeordneten Bereich.

Überlegungen zur Leistungsfähigkeit des Layouts

Das Layout ist ein rekursiver Prozess. Bei jedem Aufruf des Systems werden alle untergeordneten Elemente in einer Children-Auflistung verarbeitet. Das System sollte also nur dann ausgelöst werden, wenn es notwendig ist. Die folgenden Tipps können Ihnen helfen, eine bessere Leistung zu erzielen.

Abhängigkeitseigenschaften, deren Werte das Initialisieren des Layoutsystems verursachen können, werden mit öffentlichen Flags markiert. AffectsMeasure und AffectsArrange bieten nützliche Hinweise darauf, welche Änderungen der Eigenschaftenwerte eine rekursive Aktualisierung durch das Layoutsystem erzwingen. Im Allgemeinen kann jede Eigenschaft, die die Größe des umgebenden Felds eines Elements beeinflussen kann, das AffectsMeasure-Flag auf true setzen. Weitere Informationen finden Sie unter Übersicht über Abhängigkeitseigenschaften.

LayoutTransform kann sehr nützlich sein, um den Inhalt einer Benutzeroberfläche (user interface, UI) zu beeinflussen. Wenn die Auswirkung der Transformation jedoch nicht die Position anderer Elemente beeinflussen muss, sollte stattdessen RenderTransform verwendet werden, da RenderTransform nicht das Layoutsystem aufruft. LayoutTransform wendet seine Transformation an und erzwingt eine rekursive Layoutaktualisierung, um die neue Position des betroffenen Elements zu berücksichtigen.

Vermeiden Sie unnötige Aufrufe an UpdateLayout. Diese Methode erzwingt eine rekursive Layoutaktualisierung und ist häufig nicht notwendig. Wenn Sie nicht sicher sind, ob eine vollständige Aktualisierung notwendig ist, verlassen Sie sich auf das Layoutsystem, das diese Methode für Sie aufruft.

Bei einer großen Children-Auflistung sollten Sie in Erwägung ziehen, das VirtualizingStackPanel-Element anstelle des regulären StackPanel-Elements zu verwenden. Durch das "Virtualisieren" der Auflistung untergeordneter Elemente behält VirtualizingStackPanel nur die Objekte im Speicher, die sich derzeit im ViewPort des übergeordneten Elements befinden. In den meisten Szenarien verbessert dies die Leistung erheblich.

Weitere Informationen

Wenn Sie verstehen, wie Elemente gemessen und angeordnet werden, beginnen Sie, das Layout als System zu verstehen. Weitere Informationen zu den verfügbaren Panel-Elementen finden Sie unter Übersicht über Panel-Elemente. Informationen zu den verschiedenen Positionierungseigenschaften, die das Layout beeinflussen können, finden Sie unter Übersicht über Alignment, Margin und Padding. Ein Beispiel für ein benutzerdefiniertes Panel-Element finden Sie unter Beispiel für einen benutzerdefinierten radial angeordneten Bereich. Wenn Sie bereit sind, alles in einer Lightweightanwendung zusammenzufügen, lesen Sie Erste Schritte mit Windows Presentation Foundation.

Siehe auch

Konzepte

Übersicht über Panel-Elemente

Übersicht über Alignment, Margin und Padding

Pixelausrichtung in WPF-Anwendungen

XAMLPad

Optimieren der Leistung: Layout und Entwurf

Referenz

FrameworkElement

UIElement