When building a Silverlight control, in some situations, you need the visual elements of the control to scale according to the size of the control. Sometimes, this is easy because you can simply leave the height and width dimensions unspecified for the visual elements and they will fill to consume the available space in the parent container. But in more complex situations, you need to be able to specify the dimensions of the visual elements in your control.
The color picker control illustrated above has different parts, each of which should maintain the appropriate relative size. But how do we maintain the aspect ratio and relative sizes of the visual elements in the control when the control is resized? Furthermore, the font size used for displaying the selected color is set to 11px, how do we make the font size grow and shrink with the control?
One possibility is to use a ScaleTransform on the root element of you control. When the control size changes, a scale transform is applied to “fill” the control’s boundaries. In fact, this is how the ViewBox control (from the Silverlight Toolkit) works. I’ve distilled a large portion of the logic from the ViewBox down to something that works for my scenario. Hopefully you will find this useful as well!
Step 1: Obtain a reference to the root element and apply a Scale Transform object to the root element.
public override void OnApplyTemplate()
{
m_rootElement = GetTemplateChild("RootElement") as Panel;
m_rootElementScale = new ScaleTransform();
m_rootElement.RenderTransform = m_rootElementScale;
base.OnApplyTemplate();
}
Step 2: In the MeasureOverride call for the control, determine the root element’s desired size and then find the appropriate scale factor to make the control fill the available size.
protected override Size MeasureOverride(Size availableSize)
{
Size size = new Size();
if (m_rootElement != null)
{
m_rootElement.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Size desiredSize = m_rootElement.DesiredSize;
Size scaleSize = CalculateScaleSize(availableSize, desiredSize);
size.Width = scaleSize.Width * desiredSize.Width;
size.Height = scaleSize.Height * desiredSize.Height;
}
return size;
}
private Size CalculateScaleSize(Size availableSize, Size desiredSize)
{
double scaleWidth = 1;
double scaleHeight = 1;
if (!double.IsPositiveInfinity(availableSize.Width) &&
!double.IsPositiveInfinity(availableSize.Height))
{
scaleWidth = desiredSize.Width == 0 ? 0 : (availableSize.Width / desiredSize.Width);
scaleHeight = desiredSize.Height == 0 ? 0 : (availableSize.Height / desiredSize.Height);
}
return new Size(scaleWidth, scaleHeight);
}
Step 3: In the ArrangeOverride call, use the calculated scale size to adjust the render transformation on the root element and the position the root element.
protected override Size ArrangeOverride(Size finalSize)
{
if (m_rootElement != null)
{
Size desiredSize = m_rootElement.DesiredSize;
Size scaleSize = CalculateScaleSize(finalSize, desiredSize);
m_rootElementScale.ScaleX = scaleSize.Width;
m_rootElementScale.ScaleY = scaleSize.Height;
Rect originalPosition = new Rect(0, 0, desiredSize.Width, desiredSize.Height);
m_rootElement.Arrange(originalPosition);
finalSize.Width = scaleSize.Width * desiredSize.Width;
finalSize.Height = scaleSize.Height * desiredSize.Height;
}
return finalSize;
}
With this logic in place, we can now resize the control with the correct scaling behavior in place. As you can see in the screenshot below, the control now scales gracefully!
