Layout

In diesem Thema wird das layoutsystem Windows Presentation Foundation (WPF) beschrieben. Das Verstehen, wie und wann Layoutberechnungen auftreten, ist wichtige für die Erstellung von Benutzeroberflächen in WPF.

Dieses Thema enthält folgende Abschnitte:

Umgebende Felder für Elemente

Wenn Sie an das Layout in WPF denken, ist es wichtig, die umgebenden Felder zu verstehen, die alle Elemente umschließen. Man kann sich jedes FrameworkElement, das vom Layoutsystem verwendet wird, als Rechteck vorstellen, das in das Layout eingepasst wird. Die LayoutInformation-Klasse gibt die Grenzen der Layout-Zuordnung eines Elements zurück, also den Slot. Die Größe des Rechtecks richtet sich nach der Berechnung des verfügbaren Bildschirmbereichs, der Größe aller Einschränkungen, layoutspezifischen Eigenschaften (z. B. Rand und Abstand) und dem individuellen Verhalten des übergeordneten Elements Panel. Durch Verarbeitung dieser Daten kann das Layoutsystem die Position aller untergeordneten Elemente eines bestimmten Panel berechnen. Es ist zu bedenken, dass die Größeneigenschaften für das übergeordnete Element definiert sind, z. B. eine Border-Klasse, die untergeordneten Elemente beeinflusst.

Die folgende Abbildung zeigt ein einfaches Layout.

Screenshot that shows a typical grid, no bounding box superimposed.

Dieses Layout kann mithilfe der folgenden XAML erreicht 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>

Ein einzelnes TextBlock Element wird in einem Grid. Während der Text nur die linke obere Ecke der ersten Spalte füllt, ist der zugeordnete Bereich für TextBlock tatsächlich viel größer. Das Begrenzungsrahmen eines beliebigen FrameworkElement kann mit der GetLayoutSlot-Methode abgerufen werden. Die folgende Abbildung zeigt den Begrenzungsrahmen für das TextBlock-Element.

Screenshot that shows that the TextBlock bounding box is now visible.

Wie im gelben Rechteck gezeigt, ist der zugewiesene Bereich des TextBlock-Elements tatsächlich viel größer, als es aussieht. Zusätzliche Elemente werden dem Grid-Element zugewiesen. Diese Zuweisung kann sich je nach Typ und Größe der Elemente, die hinzugefügt werden, verkleinern oder erweitern.

Der Layout-Slot des TextBlock wird mit Hilfe der GetLayoutSlot-Methode in ein Path übersetzt. Diese Technik kann für das Anzeigen des umgebenden Felds eines Elements nützlich sein.

private void getLayoutSlot1(object sender, System.Windows.RoutedEventArgs e)
{
    RectangleGeometry myRectangleGeometry = new RectangleGeometry();
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1);
    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();
}
Private Sub getLayoutSlot1(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim myRectangleGeometry As New RectangleGeometry
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1)
    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

Das Layoutsystem

Im einfachsten Fall ist Layout ein rekursives System, um ein Element in der Größe anzupassen, zu positionieren und zu zeichnen. Genauer gesagt, beschreibt Layout den Prozess der Vermessung und Anordnung der Member einer Panel-Elementsammlung Children. Layout ist ein intensiver Vorgang. Je größer die Children-Sammlung, desto höher die Anzahl von Berechnungen, die vorgenommen werden müssen. Komplexität kann auch basierend auf dem Layout-Verhalten eingeführt werden, das im Panel-Element der Sammlung definiert ist. Ein relativ einfaches Panel, wie z. B. Canvas, kann eine deutlich bessere Leistung haben als ein komplexeres Panel, wie z. B. Grid.

Immer wenn ein untergeordnetes UIElement seine Position ändert, kann es einen neuen Durchlauf des Layoutsystems auslösen. Daher ist es wichtig, die Ereignisse zu verstehen, die das Layoutsystem aufrufen können, da unnötige Aufrufe zu schlechter Anwendungsleistung führen können. Im Folgenden wird der Prozess beschrieben, der auftritt, wenn das Layoutsystem aufgerufen wird.

  1. Der Layoutprozess eines untergeordneten UIElement beginnt mit der Messung von dessen Kerneigenschaften.

  2. Größenanpassungseigenschaften, die definiert FrameworkElement sind, werden ausgewertet, z. B. Width, Height und Margin.

  3. Panel-spezifische Logik wird angewendet, z Dock . B. Richtung oder StapelungOrientation.

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

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

  6. Der Prozess wird erneut aufgerufen, wenn der Sammlung weitere Children hinzugefügt werden, ein LayoutTransform angewendet wird oder die UpdateLayout-Methode aufgerufen wird.

Dieser Prozess und wie er aufgerufen wird, werden in den folgenden Abschnitten ausführlicher definiert.

Messen und Anordnen von untergeordneten Elementen

Das Layoutsystem führt zwei Durchläufe für jeden Member in einer Children-Sammlung durch: einen Messdurchlauf und einen Anordnungsdurchlauf. Jedes untergeordnetes Panel bietet seine eigenen MeasureOverride- und ArrangeOverride-Methoden, um sein eigenes spezifisches Layout-Verhalten zu erreichen.

Während des Messdurchlaufs wird jedes Member der Children-Sammlung bewertet. Der Prozess beginnt mit einem Aufruf der Measure-Methode. Diese Methode wird in der Implementierung des übergeordneten Panel-Elements aufgerufen und muss nicht explizit aufgerufen werden, damit das Layout auftritt.

Zunächst werden die Eigenschaften der nativen Größe der UIElement bewertet, wie z. B. Clip und Visibility. Dies erzeugt einen Wert mit dem Namen constraintSize, der an MeasureCore übertragen wird.

Dann werden Frameworkeigenschaften, die auf FrameworkElement definiert sind, verarbeitet, was den Wert von constraintSize betrifft. Diese Eigenschaften beschreiben in der Regel die Größeneigenschaften der zugrunde liegenden UIElement, z. B. deren Height, Width, Marginund Style. Jede dieser Eigenschaften kann den Bereich ändern, der zum Anzeigen des Elements nötig ist. MeasureOverride wird dann als constraintSize Parameter aufgerufen.

Hinweis

Es gibt einen Unterschied zwischen den Eigenschaften und Height und Width und ActualHeightund ActualWidth. Die ActualHeight-Eigenschaft ist z. B. ein berechneter Wert, der auf anderen Größeneingaben und dem Layoutsystem basiert. Der Wert wird vom Layoutsystem selbst festgelegt, basierend auf einem tatsächlichen Renderingdurchlauf, und kann daher etwas hinter den festgelegten Wert von Eigenschaften zurückfallen, die die Grundlagen der Eingabeänderung sind, z. B. Height.

Da ActualHeight ein berechneter Wert ist, sollten Sie bedenken, dass möglicherweise mehrere oder inkrementelle gemeldete Änderungen dafür auftreten, aufgrund verschiedener Vorgänge des Layoutsystems. Das Layoutsystem berechnet möglicherweise den Bereich für untergeordnete Elemente, Einschränkungen durch übergeordnete Elemente usw.

Das ultimative Ziel des Maßnahmendurchlaufs ist, dass das Untergeordnete seine DesiredSize bestimmt, das während des MeasureCore-Aufrufs auftritt. Der DesiredSize-Wert wird zur Verwendung während des Durchlaufs der Inhaltsvermittlung von Measure gespeichert.

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

Die ArrangeCore-Methode wertet die DesiredSize des untergeordneten Elements aus und bewertet alle zusätzlichen Ränder, die sich auf die gerenderte Größe des Elements auswirken können. ArrangeCore generiert einen arrangeSize, der an die ArrangeOverride Methode des Panel als Parameter übergeben wird. ArrangeOverride generiert das finalSize Kind. Abschließend führt die ArrangeCore-Methode eine letzte Auswertung von Offset-Eigenschaften durch, z. B. Rand und Anordnung, und fügt das untergeordnete Element in den Layoutslot ein. Das untergeordnete Element muss nicht den gesamten zugewiesenen Bereich füllen (und tut dies häufig auch nicht). Das Steuerelement wird daraufhin an das übergeordnete Panel-Element zurückgegeben, und der Layoutvorgang wird abgeschlossen.

Panel-Elemente und benutzerdefiniertes Layoutverhalten

WPF enthält eine Gruppe von Elementen, die sich von Panel ableiten. Diese Panel-Elemente ermöglichen viele komplexe Layouts. Beispielsweise kann das Stapeln von Elementen einfach mithilfe des StackPanel-Elements erreicht werden, wobei komplexere und freie Layouts mithilfe eines Canvas möglich sind.

Die folgende Tabelle fasst die verfügbaren Layout-PanelElemente zusammen.

Panelname Beschreibung
Canvas Definiert einen Bereich, in dem Sie mithilfe von Koordinaten, die relativ zum Canvas-Bereich sind, explizit untergeordnete Elemente positionieren können.
DockPanel Definiert einen Bereich, in dem Sie untergeordnete Elemente entweder horizontal oder vertikal relativ zueinander anordnen können.
Grid Definiert einen flexiblen Rasterbereich mit Spalten und Zeilen.
StackPanel Ordnet untergeordnete Elemente in einer einzelnen Zeile an, die horizontal oder vertikal ausgerichtet werden kann.
VirtualizingPanel Stellt ein Framework für Panel-Elemente bereit, die die Auflistung ihrer untergeordneten Daten virtualisieren. Dies ist eine abstrakte Klasse.
WrapPanel Ordnet untergeordnete Elemente nacheinander von links nach rechts und umbricht den Inhalt in die nächste Zeile am Rand des enthaltenden Felds. Die nachfolgende Sortierung erfolgt je nach dem Wert der Orientation-Eigenschaft von oben nach unten bzw. von rechts nach links.

Für Anwendungen, die ein Layout benötigen, das mit den vordefinierten Panel-Elementen nicht möglich ist, können Sie benutzerdefinierte Layout-Verhaltensweisen erreichen, indem Sie von den Panel erben und die MeasureOverride- und ArrangeOverride-Methoden überschreiben.

Überlegungen zur Layoutleistung

Layout ist ein rekursiver Prozess. Jedes untergeordnete Element in einer Children-Sammlung wird bei jedem Aufruf des Layoutsystems verarbeitet. Daher sollte das Auslösen des Layoutsystems vermieden werden, wenn es nicht erforderlich ist. Folgendes können Sie für eine bessere Leistung tun.

  • Achten Sie darauf, welche Änderungen des Eigenschaftswerts ein rekursives Update durch das Layoutsystem erzwingt.

    Abhängigkeitseigenschaften, deren Werte bewirken, dass das Layoutsystem initialisiert werden kann, werden mit öffentlichen Flags gekennzeichnet. AffectsMeasure und AffectsArrange zeigen an, welche Änderungen des Eigenschaftswerts ein rekursives Update durch das Layoutsystem erzwingt. Im Allgemeinen muss jede Eigenschaft, die die Größe des umgebenden Felds eines Elements beeinflussen kann, über ein AffectsMeasure-Flag verfügen, das auf TRUE festgelegt ist. Weitere Informationen finden Sie unter Übersicht über Abhängigkeitseigenschaften.

  • Wenn möglich, verwenden Sie anstelle RenderTransform einer LayoutTransform.

    Ein LayoutTransform kann eine sehr nützliche Methode sein, um den Inhalt einer Benutzeroberfläche (UI) zu beeinflussen. Wenn sich die Wirkung der Transformation jedoch nicht auf die Position anderer Elemente auswirken muss, ist es am besten, stattdessen ein RenderTransform zu verwenden, da RenderTransform das Layoutsystem nicht aufruft. LayoutTransform wendet die Umwandlung an und erzwingt ein rekursives Layoutupdate, um die neue Position des betroffenen Elements zu berücksichtigen.

  • Vermeiden Sie unnötige Aufrufe von UpdateLayout.

    Diese UpdateLayout-Methode erzwingt eine rekursive Aktualisierung des Layouts und ist häufig nicht notwendig. Wenn Sie sicher sind, dass eine vollständige Aktualisierung erforderlich ist, verlassen Sie sich auf das Layoutsystem, das diese Methode für Sie aufruft.

  • Wenn Sie mit einer großen Children-Sammlung arbeiten, sollten Sie einVirtualizingStackPanel anstelle eines normalen StackPanel verwenden.

    Durch das Virtualisieren der untergeordneten Sammlung behält VirtualizingStackPanel nur Objekte im Speicher, die sich derzeit innerhalb des ViewPort der übergeordneten Sammlung befindet. Daher wird die Leistung in den meisten Szenarios erheblich verbessert.

Subpixel-Rendering und Layoutglättung

Das WPF-Grafiksystem verwendet geräteunabhängige Einheiten, um die Unabhängigkeit von der Auflösung und vom Gerät zu aktivieren. Jedes geräteunabhängige Pixel skaliert automatisch mit der Einstellung des Systems punkte pro Zoll (dpi). Dadurch wird die ordnungsgemäße Skalierung von WPF-Anwendungen für unterschiedliche dpi-Einstellungen bereitgestellt, und die Anwendung ist automatisch bereit für dpi.

Jedoch kann diese dpi-Unabhängigkeit unregelmäßiges Edge-Rendering aufgrund von Antialiasing hervorrufen. Diese Artefakte, in der Regel verschwommene oder semitransparente Ränder, können auftreten, wenn die Position eines Rands in die Mitte eines Gerätepixels anstatt zwischen die Gerätepixel fällt. Das Layoutsystem bietet eine Möglichkeit, dafür eine Anpassung durch Layoutglättung vorzunehmen. Die Layoutglättung wird verwendet, wo Layoutsysteme nicht-integrale Pixelwerte während des Layoutdurchlaufs glättet.

Die Layoutglättung ist standardmäßig deaktiviert. Um die Layout-Rundung zu aktivieren, setzen Sie die UseLayoutRounding-Eigenschaft auf true auf beliebige FrameworkElement. Da es sich um eine Abhängigkeitseigenschaft handelt, wird der Wert an alle untergeordneten Elemente in der visuellen Struktur weitergegeben. Um die Layout-Rundung für die gesamte Benutzeroberfläche zu aktivieren, setzen Sie UseLayoutRounding auf true auf den Stammcontainer. Ein Beispiel finden Sie unter UseLayoutRounding.

Nächste Schritte

Das Verstehen, wie Elemente gemessen und angeordnet werden, ist der erste Schritt zum Verstehen von Layout. Weitere Informationen über verfügbare Panel-Elemente finden Sie unter Übersicht über Bereiche. Weitere Informationen über die verschiedenen Positionierungseigenschaften, die Layout beeinflussen können, finden Sie unter Übersicht über Alignment, Margin und Padding. Wenn Sie dafür bereit sind, alles in einer einfachen Anwendung zusammenzusetzen, gehen Sie unter Exemplarische Vorgehensweise: Meine erste WPF-Desktopanwendung.

Siehe auch