Layout |
This article discusses the layout process of GUI controls.
This topic contains the following sections:
Each UIControl has a Width, and a Height property which define the control size. The default value is NaN, which means that the control automatically determines its size. For example: The size of an Image control is determined by the drawn image. The size of a TextBlock is determined by the text. If a control has child controls, like a Panel or a ContentControl, the size is usually determined by the child controls.
A control also has the properties MinWidth, MinHeight, MaxWidth and MaxHeight. They default to 0 for the minimums and NaN for the maximums, which means that there are no size restrictions. These properties limit the actual control size.
There a several mechanisms that influence the position of a control.
Parent Controls: GUI controls are organized in a tree. The control at the root is the UIScreen. The UIScreen contains other child controls, like Windows, Panels, Buttons. Each control can in turn contain other controls. A parent control defines how it positions child elements. Most other controls allow the children to position themselves freely within the parent control space using the properties described below.
Panels are special controls – their whole purpose is to arrange child elements using a specific layout strategy. Current panel controls are the Canvas, which allows free positioning of the children, and the StackPanel, which arranges child controls in a horizontal or vertical stack.
Margin and Padding: Margin and Padding are two UI control properties. Each is a 4-dimensional vector specifying the left, top, right, and bottom values. The margin is added around a control. Per default, it is (0, 0, 0, 0). In a panel (e.g. a StackPanel) a margin can be added to the controls to create some empty space between them. The padding is usually the space between the outer border of a control and contained child controls. However, different types of controls may interpret the Padding property differently.
HorizontalAlignment and VerticalAlignment: Using the properties HorizontalAlignment and VerticalAlignment a control can determine its alignment in the area of the parent control.
X and Y: Each control has an X and a Y property. These two properties are only relevant for children of a UIScreen or a Canvas panel. X and Y determine the position of the control’s top left corner on the screen or in the canvas. – X and Y should only be used for controls that are left/top aligned.
GUI controls are organized in a tree. The control at the root is the UIScreen. The screen contains child controls, which in turn contain other controls, and so on. The layout process consists of two traversals of the whole control tree: The measure pass and the arrange pass. First all controls are measured and determine their desired own size. In the second pass all controls are told where on the screen they should be rendered.
The layout process is started by calling UpdateLayout on the root control. It is not necessary to call this manually. The UIScreen will automatically call UpdateLayout when needed.
In the measure pass the method Measure is called on each control with the available size for the control. Each control can determine how big it wants to be. The measure results are stored in the DesiredWidth and DesiredHeight properties.
Controls can override the method OnMeasure. This method should call Measure of child controls and then return the desired size. A control can ignore the availabeSize parameter and return any value. Some controls will want to adapt to the available size (e.g. a TextBlock with text wrapping will wrap the text at the bounds defined by the available size).
The results of the measure pass are cached until a UI control property with the AffectsMeasure flag is modified or InvalidateMeasure is called manually. The flag IsMeasureValid indicates whether the current measure results are valid.
In the arrange pass the method Arrange is called on each control. The method tells a control the rectangle on screen that the control can use. Each control subtracts its own margin and sets its ActualX, ActualY, ActualWidth and ActualHeight properties. This properties define the rectangle relative to the screen that this control can use. A UI control also has an ActualBounds property to get the rectangle defined by these properties.
Often a control will get exactly its desired size to arrange into. If it gets more space, it is allowed to fill the whole space. It can also happen that the actual size is less than the desired size when there is not enough space on the screen or in a window. In this case a well-behaved control only draws within the actual bounds. A misbehaving control may draw outside the allowed bounds and hope that the parent control automatically clips the drawing. Some controls like the ContentControls have a ClipContent property. If this is set to true, they will clip the content automatically during the render process.
If the desired size of a child control is smaller than the actual size of the parent control, then the properties HorizontalAlignment and VerticalAlignment are used to determine the relative position of the child control in the parent control.
Arrange pass results are cached until a UI control property with the AffectsMeasure flag or a AffectsArrange flag is modified or InvalidateMeasure or InvalidateArrange. is called. The flag IsArrangeValid indicates whether the current actual positions are valid.