- Controlling Size
- Controlling Position
- Applying 2D Transforms
- Applying 3D Transforms
- Further Exploration
- Summary
- Q&A
- Workshop
- Exercises
Controlling Position
This section doesn’t discuss positioning elements with (X,Y) coordinates, as you might expect. Parent panels define their own unique mechanisms for enabling children to position themselves, and those are discussed in the next hour. A few mechanisms are common to all FrameworkElement children, however, and that’s what this section examines. These mechanisms are related to alignment and a concept called flow direction.
Alignment
The HorizontalAlignment and VerticalAlignment properties enable an element to control what it does with any extra space that its parent panel gives it. Each property has a corresponding enumeration with the same name, giving the following options:
- HorizontalAlignment—Left, Center, Right, and Stretch
- VerticalAlignment—Top, Center, Bottom, and Stretch
Stretch is the default value for both properties, although various controls override the setting. The effects of HorizontalAlignment can easily be seen by placing a few Buttons in a StackPanel (a panel described further in the next hour) and marking them with each value from the enumeration:
<
StackPanel
>
<
Button
HorizontalAlignment
="Left"
Content
="Left"
Background
="Red"/>
<
Button
HorizontalAlignment
="Center"
Content
="Center"
Background
="Orange"/>
<
Button
HorizontalAlignment
="Right"
Content
="Right"
Background
="Green"/>
<
Button
HorizontalAlignment
="Stretch"
Content
="Stretch"
Background
="Blue"/>
</
StackPanel
>
Notice that an enumeration value such as HorizontalAlignment.Left is able to be specified in XAML as simply Left. This is thanks to a type converter that is able to handle any enumeration. The rendered result appears in Figure 3.2.
Figure 3.2 The effects of HorizontalAlignment on Buttons in a StackPanel
These two properties are useful only when a parent panel gives the child element more space than it needs. For example, adding VerticalAlignment values to elements in the StackPanel used in Figure 3.2 would make no difference, because each element is already given the exact amount of height it needs (no more, no less).
Content Alignment
In addition to HorizontalAlignment and VerticalAlignment properties, the Control class also has HorizontalContentAlignment and VerticalContentAlignment properties. These properties determine how a control’s content fills the space within the control. (Therefore, the relationship between alignment and content alignment is somewhat like the relationship between Margin and Padding.)
The content alignment properties are of the same enumeration types as the corresponding alignment properties, so they provide the same options. However, the default value for HorizontalContentAlignment is Left, and the default value for VerticalContentAlignment is Top. Some elements implicitly choose different defaults. Buttons, for example, center their content in both dimensions by default.
Figure 3.3 demonstrates the effects of HorizontalContentAlignment, simply by taking the previous XAML snippet and changing the property name as follows:
<
StackPanel
>
<
Button
Horizontal
Content
Alignment
="Left"
HorizontalAlignment
="Stretch"
Content
="Left"
Background
="Red"/>
<
Button
Horizontal
Content
Alignment
="Center"
HorizontalAlignment
="Stretch"
Content
="Center"
Background
="Orange"/>
<
Button
Horizontal
Content
Alignment
="Right"
HorizontalAlignment
="Stretch"
Content
="Right"
Background
="Green"/>
<
Button
Horizontal
Content
Alignment
="Stretch"
HorizontalAlignment
="Stretch"
Content
="Stretch"
Background
="Blue"/>
</
StackPanel
>
Figure 3.3 The effects of HorizontalContentAlignment on Buttons in a StackPanel
Each Button also has its HorizontalAlignment set to Stretch rather than its default of Left so the differences in HorizontalContentAlignment are visible. Without this, each Button would auto-size to its content and there would be no extra space for the content to move within.
In Figure 3.3, the Button with HorizontalContentAlignment="Stretch" might not appear as you expected. Its inner TextBlock is technically stretched, but it’s meaningless because TextBlock (which is not a Control) doesn’t have the same notion for stretching its inner text. For other types of content, Stretch can indeed have the intended effect.
FlowDirection
FlowDirection is a property on FrameworkElement (and several other classes) that can reverse the way an element’s inner content flows. It applies to some panels and their arrangement of children, and it also applies to the way content is aligned inside child controls. The property is of type FlowDirection, with two values: LeftToRight (FrameworkElement’s default) and RightToLeft.
The idea of FlowDirection is that it should be set to RightToLeft when the current culture corresponds to a language that is read from right to left. This reverses the meaning of left and right for settings such as content alignment. The following XAML demonstrates this, with Buttons that force their content alignment to Top and Left but then apply each of the two FlowDirection values:
<
StackPanel
>
<
Button
FlowDirection
="LeftToRight"
HorizontalContentAlignment
="Left"
Width
="320"
Background
="Red">
LeftToRight</
Button
>
<
Button
FlowDirection
="RightToLeft"
HorizontalContentAlignment
="Left"
Width
="320"
Background
="Orange">
RightToLeft</
Button
>
</
StackPanel
>
The result is shown in Figure 3.4.
Figure 3.4 The effects of FlowDirection on Buttons with Left content alignment
Notice that FlowDirection does not affect the flow of letters within these Buttons. English letters always flow left to right, and Arabic letters always flow right to left, for example. But FlowDirection reverses the notion of left and right for other pieces of the user interface, which typically need to match the flow direction of letters.
You must explicitly set FlowDirection to match the current culture, but fortunately you can do this on a single, top-level element. Windows doesn’t automatically change FlowDirection on your behalf in order for the behavior to be predictable and easily testable. The idea is that you should specify FlowDirection appropriately inside the .resw file for each distinct culture you support. For example, if you include a resource with the name Root.FlowDirection, then you can mark a Page’s root element with x:Uid="Root" to control FlowDirection on a per-culture basis. The resource just needs to be given the value of LeftToRight or RightToLeft.