09 Jan 2009

Creating a Silverlight Control that Stays Square

   

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.

Share |
posted by Pete Brown on Friday, January 09, 2009
filed under:    

Comment on this Post

Remember me

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

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