Search code examples
wpfitemscontrolitemspaneluniformgrid

Can ItemsControl group the bound collection?


I'm trying to determine if there's a way to use an ItemsControl to group items in the ItemsSource into individual ItemsPanels. Specifically, I'm trying to create a view such that a collection of say, 8 items can be listed in grid-type fashion, like a UniformGrid, but with even results, and not empty cells.

While a UniformGrid with 8 items in the source would produce results like the following:

-------------------------------------
- +++++++++ - +++++++++ - +++++++++ -
-------------------------------------
- +++++++++ - +++++++++ - +++++++++ -
-------------------------------------
- +++++++++ - +++++++++ - ooooooooo -
-------------------------------------

(the last cell being empty)

I'm trying to produce results like so:

-------------------------------------
- +++++++++ - +++++++++ - +++++++++ -
-------------------------------------
- +++++++++ - +++++++++ - +++++++++ -
-------------------------------------
- +++++++++++++++ - +++++++++++++++ -
-------------------------------------

If I programmatically break it up, I can get the display easily by nesting like so:

<StackPanel Orientation="Horizontal">
   <UniformGrid>
      item 1
      item 2
      item 3
   </UniformGrid>
   <UniformGrid>
      item 4
      item 5
      item 6
   </UniformGrid>
   <UniformGrid>
      item 7
      item 8
   </UniformGrid>
</StackPanel>

But I'd like to acheive the results I want just through Xaml.


Solution

  • I found the way to do it by nesting an new ItemsControl in the DataTemplate of the one bound to the collection, and using a ValueConverter to convert the collection into an array of arrays.

    <ItemsControl ItemsSource="{Binding MyCollection, Converter={StaticResource ArraySplitConverter}, ConverterParameter=3}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <ItemsControl ItemsSource="{Binding}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <UniformGrid IsItemsHost="True" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Label Content="{Binding Title} />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    

    And the ValueConverter:

    public class ArraySplitConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            int param = System.Convert.ToInt16(parameter);
            object[] coll = (object[])value;
            ArrayList outer = new ArrayList();
            ArrayList inner = new ArrayList();
    
            for (int i = 0; i < coll.Length; i++)
            {
                inner.Add(coll[i]);
                if (((i + 1) % param == 0) || (i == coll.Length - 1)) { outer.Add(inner); inner = new ArrayList(); }
            }
    
            return outer;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }