The Layout System
This topic describes the Windows Presentation Foundation (WPF) layout system. Understanding how and when layout calculations occur is essential in crafting great looking, high performance user interfaces.
This topic contains the following sections.
The term "layout" describes the process of measuring and arranging the members of a Panel element's Children collection then drawing them onscreen. This is an intensive process, and the larger the Children collection, the greater the number of calculations made. Complexity can also be introduced based upon the layout behavior defined by the Panel element that owns the collection. A relatively simple layout such as Canvas can yield excellent performance when a more complex Panel such as Grid is not required.
Each time a child UIElement changes its position it has the potential to trigger a new pass by the layout system. As such, it's important to understand the events that can invoke the layout system, as unnecessary invocation can lead to poor application performance.
At its simplest, layout is a recursive system that leads to an element being sized, positioned, and drawn onscreen. The layout system completes two passes for each member of the Children collection, a measure pass and an arrange pass. Each child Panel provides its own MeasureOverride and ArrangeOverride methods in order to achieve its own specific layout behavior. This is the series of events that occurs whenever the layout system is invoked.
A child UIElement begins layout process by first having its core properties measured.
Content is arranged after all children have been measured.
Children collection is drawn to the screen.
This process, and the means by which it is invoked, are defined in greater detail in the sections that follow.
When thinking about application layout in Windows Presentation Foundation (WPF), it is important to understand the bounding box that surrounds all elements. This abstraction assists in comprehending the behavior of the layout system. Each FrameworkElement consumed by the layout system can be thought of as a rectangle that is slotted into a layout partition. A class, LayoutInformation, is exposed that can return the geometric boundaries of an element's layout allocation, or slot. The size of that rectangle is determined by the system, by calculating the available screen space, the size of any constraints, layout specific properties like margin and padding, and the individual behavior of the parent Panel element. Processing this data, the system is able to calculate the position of all the children of a given Panel. It is important to remember that sizing characteristics defined on the parent element (such as a Border) affect its children.
As an example, consider the following simple layout scenario.
This layout can be achieved using the following Extensible Application Markup Language (XAML).
<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>
The lone TextBlock element is hosted within a Grid, and while the text fills only the upper left corner of the column in which it has been placed, the allocated space for the TextBlock is actually much larger. The bounding box of any FrameworkElement can be retrieved using the GetLayoutSlot method. Using this method, the bounding box of the TextBlock element is superimposed (this is possible because the TextBlock is hosted within a Grid, a Panel element that permits sharing of layout coordinates).
As is now apparent by the white line that surrounds it, the partition allocated to the TextBlock element is actually much larger than the space it fills. As additional elements are added to the Grid, this allocation could shrink or expand, depending on the type and size of elements that are added.
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
When the content of a Window object is rendered, the layout system is automatically invoked. In order to display content, the Content of the window must define a root Panel which serves to define a framework by which Children are organized on the screen. See Panel Elements and Custom Layout Behaviors for a list of available Panel elements and information on creating custom layout elements.
The first pass of layout is the measure pass, wherein each member of the Children collection is evaluated. The process begins with a call to the Measure method. This method is called within the implementation of the parent Panel element, and does not need to be called explicitly for layout to occur.
Secondly, framework properties defined on FrameworkElement are processed, affecting the value of constraintSize. These properties tend to describe the sizing characteristics of the underlying UIElement, such as its Height, Width, Margin, and Style. Each of these properties can alter the space necessary to display the element. MeasureOverride is then called with constraintSize as a parameter.
There is a difference between the properties of Height and Width and ActualHeight and ActualWidth. For example, the ActualHeight property is a calculated value based on other height inputs and the layout system. The value is set by the layout system itself, based on an actual rendering pass, and may therefore lag slightly behind the set value of properties such as Height that are the basis of the input change.
Because ActualHeight is a calculated value, you should be aware that there could be multiple or incremental reported changes to it as a result of various operations by the layout system. The layout system may be calculating required measure space for child elements, constraints by the parent element, and so on.
The ultimate goal of the measure pass is for the child to determine its DesiredSize, which happens during the MeasureCore call. This value is stored by Measure for use during the content arrange process.
The arrange process begins with a call to the Arrange method. During the arrange pass, the parent Panel element generates a rectangle that represents the bounds of the child. This value is passed to the ArrangeCore method for processing.
The ArrangeCore method evaluates the DesiredSize of the child and evaluates any additional margins that may affect the rendered size of the element and generates an arrangeSize, which is passed on to the ArrangeOverride of the Panel as a parameter. ArrangeOverride generates the finalSize of the child, and finally the ArrangeCore method does a final evaluation of offset properties such as margin and alignment and places the child within its layout slot. The child does not need to (and frequently won't) fill the entire allocated space. Control is then returned to the parent Panel, and the layout process is complete.
The Windows Presentation Foundation (WPF) includes a derived suite of Panel elements that enable many complex layouts. Common scenarios, such as stacking elements can easily be achieved using the StackPanel element, while more complex and free flowing layouts are possible using a Canvas.
The following table summarizes the available layout elements.
Defines an area within which you can explicitly position child elements by coordinates relative to the Canvas area.
Defines an area within which you can arrange child elements either horizontally or vertically, relative to each other.
Defines a flexible grid area consisting of columns and rows.
Arranges child elements into a single line that can be oriented horizontally or vertically.
Provides a framework for Panel elements that "virtualize" their child data collection. This is an abstract class.
Positions child elements in sequential position from left to right, breaking content to the next line at the edge of the containing box. Subsequent ordering happens sequentially from top to bottom or right to left, depending on the value of the Orientation property.
For code samples showing usage for each of these elements, see Layout Samples.
For scenarios that require application layout that is not possible using any of the predefined Panel elements, custom layout behaviors can be achieved by inheriting from Panel and overriding the MeasureOverride and ArrangeOverride methods. For an example, see Custom Radial Panel Sample.
Layout is a recursive process. Each child element in a Children collection gets processed during each invocation of the system. As a result, triggering the system should be avoided when it is not necessary. The following tips can help you achieve higher performance.
Dependency properties whose values can cause the layout system to be initialized are marked with public flags. AffectsMeasure and AffectsArrange provide useful clues as to which property value changes will force a recursive update by the layout system. In general, any property that can affect the size of an element's bounding box should set the AffectsMeasure flag to true. For more information, please see Dependency Properties Overview.
A LayoutTransform can be a very useful way to affect the content of a user interface (UI). However, if the effect of the transform does not need to impact the position of other elements, it is best to use a RenderTransform instead, as RenderTransform does not invoke the layout system. LayoutTransform applies its transformation and forces a recursive layout update to account for the new position of the affected element.
Avoid unnecessary calls to UpdateLayout. This method forces a recursive layout update, and is frequently not necessary. Unless you're certain that a full update is required, rely on the layout system to call this method for you.
When dealing with a large Children collection, consider using the VirtualizingStackPanel rather than a regular StackPanel. By "virtualizing" the child collection, the VirtualizingStackPanel only keeps objects in memory that are currently within the parent's ViewPort. As a result, performance is substantially improved in most scenarios.
Understanding how elements are measured and arranged is the first step in understanding layout as a system. For a deeper understanding of the available Panel elements, see Panels Overview. To better understand the various positioning properties that can affect layout, see Alignment, Margins, and Padding Overview. For an example of a custom Panel element, see Custom Radial Panel Sample. When you're ready to put it all together in a light-weight application, see Getting Started with Windows Presentation Foundation.