Search code examples
c#wpfwrappaneluniformgrid

WPF 2 columns layout container


How can I create layout container that I can use as the ItemsPanel of an ItemsControl that stacks the items in two columns and uses the right column for the remaining items that didn't have enough space in the left column (something like a uniform grid mixed with wrap panel).

For example:

four items with the pretty much same height will show as the following:

four items where two has significally smaller width will show like this:


Solution

  • Try WrapPanel with Vertical Orientation

    <WrapPanel Orientation="Vertical" Height="200">
        <Border BorderBrush="Red" BorderThickness="2" Height="100" Width="100"/>
        <Border BorderBrush="Red" BorderThickness="2" Height="100"  Width="100"/>
        <Border BorderBrush="Red" BorderThickness="2" Height="100"  Width="100"/>
        <Border BorderBrush="Red" BorderThickness="2" Height="100"  Width="100"/>
    </WrapPanel>
    

    output enter image description here

    <WrapPanel Orientation="Vertical" Height="200">
        <Border BorderBrush="Red" BorderThickness="2" Height="100" Width="100" />
        <Border BorderBrush="Red" BorderThickness="2" Height="50"  Width="100" />
        <Border BorderBrush="Red" BorderThickness="2" Height="50"  Width="100" />
        <Border BorderBrush="Red" BorderThickness="2" Height="200"  Width="100" />
    </WrapPanel>
    

    Output

    enter image description here

    Edit: As your comment says wrappanel doesnt fit your requirement .Lets create our panel according to your requrement.

    Below is a Custom Panel that sets the controls Vertically and when the Height of children goes more than Panel Height it moves the children to next column exactly same as WrapPanel with Vertical orientation but It has a property Columns which user can specify in xaml and the Panel wont create more than that Columns value

    CustomPanel.cs

        public class CustomPanel : Panel
    {
        //TODO :Create as Attached Property
        public int Columns { get; set; }
    
        // Default public constructor 
        public CustomPanel(): base()
        {
        }
    
        protected override Size MeasureOverride(Size availableSize)
        {
            Size size = ArrangeAndMeasure(availableSize, true);
            if (double.IsInfinity(availableSize.Height) || double.IsInfinity(availableSize.Width))
                return size;
            else
                return availableSize;
        }
    
        protected override Size ArrangeOverride(Size finalSize)
        {
            return ArrangeAndMeasure(finalSize, false);
        }
    
        Size ArrangeAndMeasure(Size finalSize,bool isMeasure)
        {
            //if columns not specified set it value 1.
            Columns = Columns == 0 ? 1 : Columns;
            Size size = new Size(0, 0);
            double maxWidth = 0.0;
            int colCount = 1;
    
            foreach (UIElement child in InternalChildren)
            {
                if ((size.Height + child.DesiredSize.Height > finalSize.Height) && Columns >= colCount)
                {
                    //if all height consumed move to next column
                    size.Width += maxWidth;
                    size.Height = 0.0;
                    colCount++;
                }
    
                if (isMeasure)
                    child.Measure(finalSize);
                else
                    child.Arrange(new Rect(new Point(size.Width, size.Height), child.DesiredSize));
    
                size.Height += child.DesiredSize.Height;
    
                if (maxWidth < child.DesiredSize.Width)
                    maxWidth = child.DesiredSize.Width;
            }
            return size; 
        }
    }
    

    xaml

     <local:CustomPanel Height="200" Columns="2">
        <Border BorderBrush="LimeGreen" BorderThickness="2" Height="200" Width="100"  />
        <Border BorderBrush="Red" BorderThickness="2" Height="50"  Width="100"  />
        <Border BorderBrush="Black" BorderThickness="2" Height="50"  Width="100"  />
        <Border BorderBrush="Blue" BorderThickness="2" Height="50"  Width="100"  />
        <Border BorderBrush="Yellow" BorderThickness="2" Height="50"  Width="100"  />
    </local:CustomPanel>
    

    output

    enter image description here

    xaml

     <local:CustomPanel Height="200" Columns="2">
        <Border BorderBrush="Red" BorderThickness="2" Height="50"  Width="100"  />
        <Border BorderBrush="Black" BorderThickness="2" Height="50"  Width="100"  />
        <Border BorderBrush="Blue" BorderThickness="2" Height="50"  Width="100"  />
        <Border BorderBrush="Yellow" BorderThickness="2" Height="50"  Width="100"  />
    </local:CustomPanel>
    

    Output

    enter image description here

    xaml

    <local:CustomPanel Height="200" Columns="2">
        <Border BorderBrush="Red" BorderThickness="2" Height="50"  Width="100"  />
        <Border BorderBrush="Black" BorderThickness="2" Height="50"  Width="100"  />
        <Border BorderBrush="Blue" BorderThickness="2" Height="50"  Width="100"  />
        <Border BorderBrush="Yellow" BorderThickness="2" Height="200"  Width="100"  />
    </local:CustomPanel>
    

    Output enter image description here