« April 2009 | Main | July 2009 »

May 22, 2009

Simulating SharedSizeGroup in Silverlight

There are several features of WPF that are not present in Silverlight, some of which are pretty difficult to work-around or live without.  One such feature is the SharedSizeGroup property that WPF has for column and row definitions of a layout grid.  This feature made many advanced layout scenarios entirely declarative.  Consider, for instance, the situation where an outer grid defines a set of control groupings for a form.  Within each of the out grid's cells are text boxes with left aligned labels.

SharedSizeGroup in WPF

In this case, the inner grids can use a SharedSizeGroup property on the column containing the labels and the text will be aligned automagically.

We wanted to be able to accomplish basically the same thing in Silverlight, and maybe even see if we could go about making it a little more general.  In particular we wanted to divorce the functionality from the Grid so that it could be used in non-grid situations as well.

What we came up with was a combination of two classes: SharedSizePanel and SharedSizeGroup.  SharedSizePanel is a simple, single element container which has the optional properties WidthGroup and HeightGroup.  All panels associated with a particular group will be given the size of the panel with the greatest content size in that dimension.

<local:SharedSizePanel WidthGroup="{StaticResource Horizontal}" >
  <Border Background="Coral">
    <local:ResizeBox Width="30" Height="30" />
  </Border>
</local:SharedSizePanel>

A SharedSizeGroup is a simple class that can easily be instantiated in the resources section:

<UserControl.Resources>
  <local:SharedSizeGroup x:Key="Horizontal" />
</UserControl.Resources>

We wanted to allow widths and heights to be synchronized to the same value, which would allow such fanciful things as the width and height of a particular control to be synced.  That capability would add quite a bit of complexity to what is presently a very simple implementation so we held off.  

There were a few challenges that were encountered in building this control, including:

  • Custom Measure and Arrange logic doesn't seem to work as expected as a descendant of ContentControl.  It worked as Panel descendant, but there doesn't seem to be a way to limit the panel to a single child, so fortunately it was found to work as a ContentPresenter.
  • The group cannot and must not force measurement of the panels, so it must store what the measurement was when it happened.  This is true for several reasons including the fact that the "available size" is only known when passed as an argument to the measure override.
  • As a content control, the VisualTreeHelper class must be used to get the actual element that the Content becomes.  Content of type object and you can't Measure an object.

In the end, the solution seems relatively simple and elegant, so fortunately I consider that all of the above worked out favorably. 

I haven't seen any indications that SharedSizeGroup will be introduced in Silverlight 3, and I'm not ready yet to dispose of my Silverlight 2 development environment to find out.  Anyone have version 3 installed that wouldn't mind verifying?

Download the code for these classesDownload the WPF SharedSizeGroup sample.