Udostępnij za pośrednictwem


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());

        }

Comments

  • Anonymous
    September 07, 2009
    Nice to see you again. Your posts remains as good as always :)

  • Anonymous
    September 09, 2009
    Welcome back! Could you put DesiredSize into perspective too? I assume it's an Layout Output property since it's set during Measure. I've observed that it can "lie" to you if UpdateLayout() has not been run recently. Any tips for making measurement and doing programmatic layout of controls before they are actually visible? John

  • Anonymous
    September 09, 2009
    Thanks! DesiredSize is a layout output, yes.  I believe it is a side effect of calling Measure().  If you find you're out of date you can call Measure() manually before calling DesiredSize.   I think you may be able to find out if the DesiredSize needs a refresh via the IsMeasureValid flag. It's best if you're in a situation where you can work with layout within the layouting events. UpdateLayout() does a lot more work than you probably want it to -- so if you can avoid that it's usually a good idea.