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.