Creating a Silverlight Layout Panel
On our current project, we came across the need to build some custom layout panels in Silverlight to achieve some complex fluid layouts that we could not get from the built in controls. I was amazed to see how easy it was to create your own. This panel that I’m going to show is nothing fancy, but it shows how simple it really is. It will layout it’s children in a diagonal direction from upper left to lower right.
The first thing we need to do is create a new class and inherit from Panel. Panel is the base class for the built-in layout controls such as Grid and StackPanel.
public class DiagonalStackPanel : Panel { }
The next thing is to override the MeasureOverride() method. In this method you should iterate through the Children collection and call the Measure() method on each child. After Measure() is called, the DesiredSize property of the child will be populated with the size in which the child wants to be rendered.
protected override Size MeasureOverride(Size availableSize) { double totalWidth = 0; double totalHeight = 0; foreach (var child in Children) { child.Measure(availableSize); totalWidth += child.DesiredSize.Width; totalHeight += child.DesiredSize.Height; } return new Size(totalWidth, totalHeight); }
You can see that we’re adding up all the widths and heights of each child and returning the size (desired) for our Panel.
Next we override the ArrangeOverride() method. This is where we actually layout the children in the diagonal pattern. We do that by calculating the running total sizes of the child controls and calling Arrange() on each child with the appropriate sizing Rect object.
protected override Size ArrangeOverride(Size finalSize) { double runningWidth = 0; double runningHeight = 0; foreach (var child in Children) { var width = child.DesiredSize.Width; var height = child.DesiredSize.Width; var finalRect = new Rect(runningWidth, runningHeight, width, height); child.Arrange(finalRect); runningWidth += width; runningHeight += height; } return new Size(runningWidth, runningHeight); }
Now we can use our panel like so:
<UserControl x:Class="LayoutPanelExample.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:LayoutPanelExample"> <Grid x:Name="LayoutRoot" Background="White"> <c:DiagonalStackPanel> <Button Content="Button"/> <Button Content="Button"/> <Button Content="Button"/> <Button Content="Button"/> <Button Content="Button"/> <Button Content="Button"/> </c:DiagonalStackPanel> </Grid> </UserControl>
and we’ll get something that looks like:
You could extend this by added Dependency Properties to the panel so that you can customize how each child reacts in the layout. This is how the Grid works. It looks at the properties set on the child elements and figures out what row and column to place them in.