#1,194 – DesiredSize of Child Elements Includes Margins

After the measure phase, during which a custom element calls the Measure method on each of its child elements, each child element will set its DesiredSize property to indicate how much space it wants.

The DesiredSize of a child element accounts for any margins that have been set on that child.  Assume that we have the following XAML (MyElement is a custom element with a single child).

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <loc:MyElement x:Name="Left">
            <loc:MyElement.Child>
                <Label Content="Doowahditty"/>
            </loc:MyElement.Child>
        </loc:MyElement>

        <loc:MyElement Grid.Column="1" x:Name="Right">
            <loc:MyElement.Child>
                <Label Content="Doowahditty" Margin="10"/>
            </loc:MyElement.Child>
        </loc:MyElement>
    </Grid>

We can see that the child element uses a margin.
1194-001

We can look at the value of the DesiredSize property after Measure has been called on the child element.  We can see that in the second case, the desired size is larger, indicating that the child wants to add a margin.

1194-002

 

 

Advertisement

#1,193 – MeasureOverride and Margins

During the measure phase, the MeasureOverride method is called on an element, indicating the size available to the control.  If a Margin has been set on the control, the available size passed in to MeasureOverride will have already been adjusted for that margin.

Below, we include two instances of MyElement in a Grid, setting a margin of 15 on the second element.

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <loc:MyElement/>
        <loc:MyElement Grid.Column="1" Margin="15"/>
    </Grid>

At run-time, we see that the second element is smaller.
1193-001
If we instrument MyElement to report the value of the Size parameter that is passed to it, we see that MeasureOverride gets a smaller size passed in for the second instance.  The Size has been adjusted for a uniform margin of 15, subtracting 30 from both the width and the height.  (We see the same values passed in to ArrangeOverride).

1193-002

 

#802 – Transforms Do Not Affect an Element’s Margins

When you apply a 2D transform to a user interface element, the transform impacts only the element and everything inside of the element.  It therefore does not change the element’s margins.

In the example below, we have a Label element that has a margin of 5 separating it from its parent Border.  Notice that when we scale the second instance of the Label, it still retains the same margin.

    <StackPanel>
        <Border BorderBrush="Black" BorderThickness="3" HorizontalAlignment="Center">
            <Label Background="LightCoral" Content="Scale Me" Margin="5"/>
        </Border>
        <Border BorderBrush="Black" BorderThickness="3" HorizontalAlignment="Center">
            <Label Background="LightCoral" Content="Scale Me" Margin="5">
                <Label.LayoutTransform>
                    <ScaleTransform ScaleX="1.5"/>
                </Label.LayoutTransform>
            </Label>
        </Border>
    </StackPanel>

802-001

#426 – Layout Panels Can Also Have Margins

You typically set margins on elements contained within a layout panel to create space between different elements and to create space between an element and the edge of the containing panel.

Here’s a Window containing a Grid (beige background), which contains some controls.  No margins have been set.

We can then specify a Margin value for the child elements within the Grid.  (Here, we set all margins to 5).  This creates space between each control and the cell in the Grid that it’s contained in.

We can also specify a Margin value for the Grid itself.  Doing so will create space between the Grid and the edges of its container, the Window.  In the example below, we specify a Margin of 10 for the Grid.  The background of the Window is set to blue, so that we can see the edges of the Grid.

 

 

#421 – HorizontalAlignment, Margins and Column Widths in a Grid

When working with elements in a Grid, you use the row and column height/width, alignment properties and margins to determine the size and location of each element.  In certain cases, some of the properties might be ignored, depending on the value of other properties.

In the example below, the first two columns have their Width property set to Auto, so that they auto-size to their content.  The third and fourth columns use * sizing.  The 2nd and 4th columns specify a margin of 10 on both the left and right, while the 1st and 3rd use no margins.  Elements in the 1st row set HorizontalAlignment to Center, and Left, Right and Stretch for the following rows.

Notice the following:

  • HorizontalAlignment of Stretch is the same as Center for auto-sized columns
  • Margins don’t apply for *-sized columns when HorizontalAlignment is Center
  • Element are aligned within their margins

#387 – Set Background Color to See How Layout Works

It’s sometimes hard to understand how a container is laying out its children.  You can use the Background property of each control, or of the panels, to get a better idea of where things are.

Suppose that we have a GUI that includes a handful of controls and two nested panels.

    <StackPanel Orientation="Vertical">
        <Label Content="Bob's Your Uncle" HorizontalAlignment="Right"/>
        <StackPanel Orientation="Horizontal">
            <Label Content="Paul"/>
            <Button Content="Ringo" Margin="10"/>
            <TextBox Text="George" VerticalContentAlignment="Bottom"/>
        </StackPanel>
        <TextBox Text="Herman was here.."/>
    </StackPanel>

The GUI would look like this:

To better see how things are being layed out, we can set the Background to a different color for each element.

    <StackPanel Orientation="Vertical" Background="Pink">

        <Label Content="Bob's Your Uncle" HorizontalAlignment="Right" Background="Lavender"/>

        <StackPanel Orientation="Horizontal" Background="LightBlue">
            <Label Content="Paul" Background="Red"/>
            <Button Content="Ringo" Margin="10" Background="Green"/>
            <TextBox Text="George" VerticalContentAlignment="Bottom" Background="Blue"/>
        </StackPanel>

        <TextBox Text="Herman was here.." Background="Orange"/>
    </StackPanel>

#386 – Layout = Panels + FrameworkElements + Alignment/Margins/Padding

Layout in WPF is the process by which the location and size of all user interface elements is determined.

A user interface is composed of an outer Window or Page which contains a hierarchy of user interface elements.  The hierarchy can contain individual user interface elements or Panels, which in turn contain a collection of child FrameworkElements.

Panel is an abstract class that serves as a parent for specific layout panels, including Canvas, DockPanel, Grid, StackPanel and WrapPanel.

A panel will contain a collection of child FrameworkElement instances.  These can be individual controls that derive from FrameworkElement, directly or indirectly.  Because Panel is itself a child of the FrameworkElement class, a panel can contain other panels.

FrameworkElement child elements are position within a parent using properties related to alignment, margins and paddingThese properties include:

  • HorizontalAlignment, VerticalAlignment  and Margin  (from FrameworkElement)
  • HorizontalContentAlignment, VerticalContentAlignment and Padding  (from Control)

#333 – Margin and Padding Overview

A control’s Margin property specifies the spacing between the edges of the control and its container.  A control’s Padding property specifies the spacing between the edges of the control and its interior content.

In the example below, we specify the Margin.  Because the HorzontalAlignment is Stretch (the default), the left/right Padding values are ignored and the Button is stretched to reach the desired Margin values.

    <StackPanel VerticalAlignment="Top" Background="Goldenrod">
        <Button Content="I'm a Button" Margin="5,10,50,10" Padding="15,5,15,5"/>
    </StackPanel>


If we now set HorizontalAlignment to Center, the control will size to fit its content. The Padding values will be honored exactly and the control will be centered horizontally within the available space after the specified left and right Margin values have been accounted for.

This becomes more clear when we draw lines for the specified Margin values.

#326 – Specifying a Symmetrical Margin Using Two Arguments

You can set the Margin property on a control so that it has some extra room around its edges, within its container.

You can specify a value for the Margin property in XAML using either one, two or four integer values.  Using one value indicates the margin for each of the four sides of the control.  Using four values indicates the left/top/right/bottom margin values individually.

When you use two values in specifying the Margin, you are specifying a value to be used for the left and right margins, and a value to be used for the top and bottom margins.  So a value of 10,20 is identical to 10,20,10,20.

    
        
        
        
    

The value of 10,20 indicates that the left and right margins should both be 10 WPF units and top and bottom should both be 20.  Using the two-argument version results in symmetrical margins.

#325 – Specifying a Margin Value in Code

You can set the Margin property on a control so that it has some extra room around its edges, within its container.

You’ll typically set the values for Margin in XAML, but you can also set a Margin value in code.  The type of the Margin property is Thickness, which is a struct having fields for margins along the left, top, right and bottom of a control.

You can specify a value for Margin by creating a new instance of the Thickness structure.

            double left = 5.0;
            double top = 10.0;
            double right = 2.0;
            double bottom = 20.0;
            lblName.Margin = new Thickness(left, top, right, bottom);