WPF Proportional Panel

January 16, 2009 at 12:59 PMAlec Bryte

Standard Layout

Windows Presentation Framework provides a great flexibility when it comes to the layout. There are several Panel controls a developer may use to position elements, and they include:

  • Canvas - for absolute positioning of elements using (X,Y) coordinates. Useful for drawing custom graphics
  • Grid - most commonly used layout control. Allows dividing area into rows and cells and automatic alignment of controls placed inside the cells.
  • StackPanel - ideal for placing several controls next to each other, horizontally or vertically
  • DockPanel - aligns elements to a particular edge and allows an element to stretch and fill the remainder of the space

Custom Layout

All WPF panels are derived from Panel public class, which means one can create his own custom panel implementation.

In my case, I needed to create a special type of a panel that would resize the child elements based on a given percentage value. For example, assume two elements are in the panel positioned horizontally and the first one has a value of 20%, while the second one is 80%. The width of the first element would be 20% of the total available width, and the remainder would be the 80% of the width taken by the second element.

Here is an example with 4 elements:

ProportionalPanelDemo

Notice how the elements have their widths proportional to their percentage values.

To achieve this behavior we will create a class ProportionalPanel derived from the original Panel class. There we need to override 2 important methods that take care of the layout logic: ArrangeOverride() and MeasureOverride(). Basically, when a panel renders elements, it first asks each child for its current size. It then uses the values to properly position the elements.

        protected override Size MeasureOverride(Size constraint)
        {
            [...]

            double totalProportion = 0.0;
            foreach (UIElement child in this.Children)
            {
                 totalProportion+= GetProportion(child);
            }
            foreach (UIElement child in this.Children)
            {
                if (Orientation == Orientation.Horizontal)
                {
                    childWidth = constraint.Width * (GetProportion(child) / totalProportion);
                    childHeight = constraint.Height;
                }
    [...]

Note that GetProportion() is implemented as an attached property. This allows setting Proportion values on elements inside our panel. Since we want to allow just about any element (TextBox, Grid, ComboBox, Rectangle) to be placed inside the panel, even a custom element, such elements won't have a Proportion property by default. Attached Properties is a new and a very powerful feature in WPF that allows extending of existing classes with custom properties.

Using Proportion attached property, we can set the value as follows:

<ctl:ProportionalPanel Orientation="Horizontal" >
    <TextBlock Text="Apples" ctl:ProportionalPanel.Proportion=".20" />
    <TextBlock Text="Oranges" ctl:ProportionalPanel.Proportion=".15" />
    [....]
</ctl:ProportionalPanel>

Complete Solution

Click below to download a working Visual Studio 2008 solution of the WPF Proportional Panel.
ProportionalPanelDemo.zip (27.24 kb)

Posted in: .NET | WPF

Tags: