Demystifying WPF/Silverlight layout properties

What is the difference between ActualSize, RenderSize, LayoutSlot?

There are quite a few properties in WPF and Silverlight for layout, and it is confusing. It helps me to think of them in two separate categories – layout input properties (control layout), and layout output properties (set after the layout occurs)

Layout input properties 

· Width/Height – specify the width/height of a control as a double. For autosize in XAML say “auto”, for autosize in C#/VB say double.NaN.

· MinWidth/MinHeight – specify the minimum size of a control.

· MaxWidth/MaxHeight – specify the maximum size of a control.

· HorizontalAlignment/VerticalAlignment – the combination of these two properties specify the positioning within the parent (e.g. TopLeft, MiddleCenter,Stretch)

· Margin – specifies the exterior spacing between the element and adjacent elements. (e.g. the distance between elements in a stack panel can be configured by margin).

· Padding – specifies the interior spacing within the current element (e.g. the space from the top of the text box to the top of the character within the text box).

· Panel specific layout inputs – additionally each layout panel may have attached properties that control layout.

 Some examples include:

· Grid – Grid.Row, Grid.Column, Grid.RowSpan, Grid.ColumnSpan

· Canvas – Canvas.Top, Canvas.Bottom, Canvas.Left, Canvas.Right

· DockPanel – DockPanel.Dock

Layout output properties

· RenderSize- Technically, the output of the call to Arrange – think of it as “ArrangeSize”. Its purpose is to tell you how big to draw a background. If elements have a funky implementation of ArrangeOverride this property can return unexpected results.

· ActualHeight/ActualWidth - same as RenderSize. The reason these exist is so you can databind to them.

· LayoutInformation.LayoutSlot – this is the area in which an element can layout. So for example if you’re in a grid, this would be the element’s current grid cell.

· LayoutInformation.GetLayoutClip - clipping is used to “cut off” elements that extend past areas they don’t belong. E.g. within a grid if a button is larger than the current cell and it has a column span set to 1, the grid will clip it to be the size of the current cell.

 

Side note about clipping: If you are writing a custom layout it is interesting to note that the protected virtual GetLayoutClip() on FrameworkElement is what controls this – and it usually kicks in if the size in arrange is larger than the size in measure. If you’re experiencing cut off buttons in your custom layout try returning null from GetLayoutClip() and see if that is your issue.

Common layout input properties in practice

 

ActualSize, depending on perspective may vary.

All of these output properties are relative to the element’s own coordinate system – if you want to figure it out relative to anything else you need to apply the transform from that object to the other object. 

Example: Button in a grid that scales everything by 2x. 

When you ask a 100px button it will say it's 100px wide, but it will render as 200px on the screen.

<Window x:Class="WpfApplication100.Window1"

   xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

   Title="Window1" Height="353" Width="398">

    <Grid>

    

        <Grid.RenderTransform>

            <ScaleTransform ScaleX="2"></ScaleTransform>

        </Grid.RenderTransform>

        <Button Name="button1"  Width="100" Height="75"

               HorizontalAlignment="Left"

               Margin="10,10,0,0"

               VerticalAlignment="Top"

               Click="Button_Click">Hi</Button>

       

    </Grid>

</Window>

 

Kinda like the observer principle in physics, the button itself has no idea that eventually its size will double. It’s as if when you apply the render transform to the grid, you’re looking at the grid through a 2x magnifying glass.

 

To get the "actual" actual width we need to transform the coordinate system into the Window’s coord system. (Reality is all relative?)

              

private void Button_Click(object sender, RoutedEventArgs e) {

           

            // get a magical formula that transforms button coords to window coords

         Transform button1ToWindowTransform = button1.TransformToAncestor(this) as Transform;

           

            // create a local coordinate rect. if you don't pass in a point it's x=0, y=0

            Rect button1LocalCoordRect = new Rect(button1.RenderSize);

            Rect button1InWindowCoords = button1ToWindowTransform.TransformBounds(button1LocalCoordRect);

            MessageBox.Show(

                "Button1 thinks ActualWidth is: " + button1.ActualWidth + "\r\n" +

                "Button1 in window coords: " + button1InWindowCoords.ToString());

        }