Posts Tagged ‘ StackPanel ’

[LeftRightStackPanel] – How to create a custom WPF Container

Well today I needed a stackPanel which aligns my Buttons in a way such that it looks good 🙂

So I decided to extend StackPanel and implement my own functionality. If in vertical mode all even buttons should be on the left and all odd buttons (the second, fourth, sixth… button) should be at the right of the previous button.

If the orientation is the to vertical it looks like this:

LeftRightStackPanel-Vertical

If the Orientation is set to horizontal it looks like this

LeftRightStackPanel-Horizontal

First of all it is necessary to create a new class and extend StackPanel

public class LeftRightStackPanel : StackPanel
{
}

Now we have to implement the MeasureOverride and ArrangeOverride Methode.
The first will be called when it repaints the graphical user interface. In this method we have to measure the size which we need to place all controls.
In the ArrangeOverride method we have to position the controls (like we measured before).

Here you see a short example. It is this LeftRightStackPanel (but only the vertical view)

public class LeftRightStackPanel : StackPanel
{
  private readonly Size _infinite = new Size(Double.MaxValue,Double.MaxValue);

  protected override Size MeasureOverride(Size constraint)
  {
    Size resultSize = new Size(0, 0);
    //Measure the size we need for our modified StackPanel
    foreach (UIElement child in Children)
    {
      //Call the measure method of the childs
      //with the parameter we can specify the maximal size (the childs can use)
      //In our case we don't care about the size of the childs. We just arrange them in the StackPanel.
      child.Measure(_infinite);
      //Get the Width of the widest child
      resultSize.Width = Math.Max(resultSize.Width, child.DesiredSize.Width);
      //Add the height of each child to the total height
      resultSize.Height += child.DesiredSize.Height;
    }
    //Multiply the width by 2 because we have 2 buttons side by side
    resultSize.Width = resultSize.Width * 2;
    return resultSize;
  }

  protected override Size ArrangeOverride(Size finalSize)
  {
    double x = 0, y = 0;
    int count = 1;
    //Arrange the childs in the modified StackPanel
    foreach (UIElement child in Children)
    {
      //Arrange the child at x,y (the first will be at 0,0)
      child.Arrange(new Rect(x, y, child.DesiredSize.Width, child.DesiredSize.Height));
      //Determine the x value for the next child (even = left, odd = right)
      x = count % 2 == 0 ? 0 : child.DesiredSize.Width;
      //Determine the y value for the next child (below the acutal child)
      y += child.DesiredSize.Height;
      count++;
    }
    return finalSize;
  }
}

Allright the orientation = vertical works. Now we have to implement the functionality for the horizontal orientation.
So we have to check if Orientation is set to vertical or horizontal and then behave in a different way.
Here you see how it can be done.

public class LeftRightStackPanel : StackPanel
{
  private readonly Size _infinite = new Size(Double.MaxValue,Double.MaxValue);

  protected override Size ArrangeOverride(Size finalSize)
  {
    if (Orientation == Orientation.Vertical)
    {
      double x = 0, y = 0;
      int count = 1;

      foreach (UIElement child in Children)
      {
         child.Arrange(new Rect(x, y, child.DesiredSize.Width, child.DesiredSize.Height));

        x = count % 2 == 0 ? 0 : child.DesiredSize.Width;
        y += child.DesiredSize.Height;
        count++;
      }
    }
    else
    {
      double x = 0, y = 0;
      int count = 1;

      foreach (UIElement child in Children)
      {
        child.Arrange(new Rect(x, y, child.DesiredSize.Width, child.DesiredSize.Height));
        x += child.DesiredSize.Width;
        y = count % 2 == 0 ? 0 : child.DesiredSize.Height;
        count++;
      }
    }
    return finalSize;
  }

  protected override Size MeasureOverride(Size constraint)
  {
    if (Orientation == Orientation.Vertical)
    {
      Size resultSize = new Size(0, 0);

      foreach (UIElement child in Children)
      {
        child.Measure(_infinite);
        resultSize.Width = Math.Max(resultSize.Width, child.DesiredSize.Width);
        resultSize.Height += child.DesiredSize.Height;
      }

      resultSize.Width = resultSize.Width * 2;

      return resultSize;
    }
    else
    {
      Size resultSize = new Size(0, 0);

      foreach (UIElement child in Children)
      {
         child.Measure(_infinite);
         resultSize.Width += child.DesiredSize.Width;
         resultSize.Height = Math.Max(child.DesiredSize.Height, resultSize.Height);
       }

       resultSize.Height = resultSize.Height * 2;

       return resultSize;
    }
  }
}

This is the final sourcecode. You can use this control in the same way as you use the StackPanel.