Welcome to Pete Brown's 10rem.net

First time here? If you are a developer or are interested in Microsoft tools and technology, please consider subscribing to the latest posts.

You may also be interested in my blog archives, the articles section, or some of my lab projects such as the C64 emulator written in Silverlight.

(hide this)

Creating a Silverlight Control that Stays Square

Pete Brown - 09 January 2009

I had a dumb design problem today. I wanted an ellipse to be inside a grid, but centered vertically and horizontally, and with a 1:1 aspect ratio. I want a circle, centered in a cell. I could get it sized correctly, but the minute I set VerticalAlignment, it would disappear unless I set it to UniformToFill rather than Uniform. Checking it out, the size was undefined. No other panels/containers seemed to help. I verified that this behavior also happens in WPF, so I assume it is a by-design thing.

Problem is, at design time, I don’t know what the width and height will be, and didn’t want to have to write code to handle resizing it with some hard-coded behavior that walks the tree looking for a named element in a particular template. It needed to be designer-friendly.

So I wrote a little content control (a control that can hold one other element in Silverlight) that will stay square and make its contents fill the container. I haven’t tested it outside of my particular scenario, but thought you might be interested. The code is dead simple:

// This container will maintain a square aspect ratio
public class SquareContainer : ContentControl
{
    private Size CalculateMaxSize(Size availableSize)
    {
        double maxDimension = Math.Min(availableSize.Height, 
                                        availableSize.Width);

        Size maxSize = new Size(maxDimension, maxDimension);

        return maxSize;
    }

    protected override Size MeasureOverride(Size availableSize)
    {
        Size maxSize = CalculateMaxSize(availableSize);

        if (Content != null && Content is FrameworkElement)
        {
            ((FrameworkElement)Content).Measure(maxSize);
        }

        return maxSize;
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        Size maxSize = CalculateMaxSize(finalSize);

        if (Content != null && Content is FrameworkElement)
        {
            ((FrameworkElement)Content).Arrange(   
                            new Rect(new Point(0, 0), maxSize));
        }

        return maxSize;
    }
}

The markup is also pretty simple. Just point the xmlns:local to the assembly where this control resides.

<local:SquareContainer x:Name="OverlayContainer"
                       VerticalAlignment="Center"
                       Margin="14">
    <Ellipse Stroke="Black"
             StrokeThickness="1"
             Stretch="Uniform"
             >
        <Ellipse.Fill>
            <RadialGradientBrush>
                <GradientStop Offset="0" Color="#BBFFFFFF" />
                <GradientStop Offset="1" Color="#00FFFFFF" />
            </RadialGradientBrush>
        </Ellipse.Fill>
    </Ellipse>
</local:SquareContainer>

Hope this helps solve a problem for you some day :)

UPDATE: Just updated the code so the control worked a little better. Also, I need to caution you against nesting the SquareContainer. If you do, and you cause a bunch of changes (by resizing the browser a lot, for example, you end up with what is likely a stack overflow or similar and the browser will just die. I’ll work to fix that, but wanted to make sure you were aware of the limitation.

   
posted by Pete Brown on Friday, January 9, 2009
filed under:    

3 comments for “Creating a Silverlight Control that Stays Square”

  1. Mrltensays:
    This square container is very handy! We're using it to put ellipses in the background of piecharts and circular gauges.

    To make the SquareContainer work in WPF, remove the ArrangeOverride method (or else the content won't be visible).
  2. Daansays:
    The original suggestion, as mentioned above, doesn't work directly in WPF. However, for me, removing ArrangeOverride broke caused the container to no longer be square.

    To get it to display a square in WPF I adden a call to base.ArrangeOverride at the beginning of the original ArrangeOverride.

Comment on this Post

Remember me

3 trackbacks for “Creating a Silverlight Control that Stays Square”

  1. Christopher Steensays:
    Code Camps First New Hampshire Code Camp on February 28th [Via: cbowen ] Link Collections Interesting...
  2. Community Blogssays:
    In this issue: Michael Washington, Pete Brown(2), Shawn Wildermuth(2), Jesse Liberty, Mike Snow, Terence