quadroid
quadroid

Reputation: 8950

ItemsControl (WrapPanel) Grouping should split GroupItems

I have a ItemsControl with a WrapPanel as ItemsHost and multiple Groupings. Got this going so far with the following Templates:

<GroupStyle.ContainerStyle>
   <Style TargetType="GroupItem">
      <Setter Property="Template">
         <Setter.Value>
            <ControlTemplate TargetType="GroupItem">
               <Grid>
                  <Grid.RowDefinitions>
                     <RowDefinition Height="Auto" />
                     <RowDefinition Height="*" />
                  </Grid.RowDefinitions>
                  <ContentPresenter Grid.Row="0" x:Name="PART_Header" Content="{TemplateBinding Content}" />
                  <ItemsPresenter Grid.Row="1" />
               </Grid>
            </ControlTemplate> 
         </Setter.Value>
      </Setter>
   </Style>
</GroupStyle.ContainerStyle>
<GroupStyle.Panel>
   <ItemsPanelTemplate>
      <WrapPanel IsItemsHost="True" Orientation="Vertical" />
   </ItemsPanelTemplate>
</GroupStyle.Panel>

Now I have the Problem that every Group does start a new Column why I want it to Continue right under the last GroupItem and Wrap in the Middle of the GroupItem instead of at the beginning.

It should look like the Windows 8 Apps overview (not the start page, if you go down to the overview)

Is that possible?

Upvotes: 2

Views: 1030

Answers (2)

Liero
Liero

Reputation: 27348

You can't do that kind of wrapping directly, since groups are in one panel and items in the each group in their own panel.

You have two options:

  • Use combination of viewmodel and Datatemplates to simulate grouping. ItemsSource won't be grouped CollectionView, just plain collection, but the items will contain group name. In DataTemplate you will show Group Header for the first item in each group.

  • Create your own control, which will arrange children into groups out of the box. Quite a lof of work, but much better reusability. I believe this is how WinRT GridView and ListView grouping works. Maybe you can find simmilar 3rd party controls in WPF

Upvotes: 1

quadroid
quadroid

Reputation: 8950

I solved this in the ViewModel instead. I add a GroupItem into the ObservableCollection that is styled like a (Expandable) GroupHeader. Than I added a seperate DataTemplate for the GroupHeader that sets a IsCollapsed property on the Group. All Items do now have a reference to the parent Group and bind the Visibility to the IsCollapsed property of the Parent Group.

Sadly i was not able to achive this using the CollectionViewSource.

This is the XAML:

<ItemsControl ItemsSource="{Binding Items}">
    <ItemsControl.DataContext>
        <local:ViewModel/>  
   </ItemsControl.DataContext>
   <ItemsControl.Resources>
       <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
       <DataTemplate DataType="{x:Type local:GroupViewModel}">
           <StackPanel>
               <CheckBox IsChecked="{Binding IsExtended}" />
               <!--Restyle to look like a extender-->
           </StackPanel>
       </DataTemplate>
       <DataTemplate DataType="{x:Type local:ItemViewModel}">
           <TextBlock Text="{Binding Name}" 
               Visibility="{Binding Group.IsExtended, Converter={StaticResource BooleanToVisibilityConverter}}"/>
       </DataTemplate>
   </ItemsControl.Resources>
</ItemsControl>

This is the ViewModel:

public class ViewModel
{
    public ViewModel()
    {
        //Some Test data
        GroupViewModel group1 = new GroupViewModel("Group1");
        GroupViewModel group2 = new GroupViewModel("Group2");

        this.Items = new ObservableCollection<object>(new[]
        {
            new ItemViewModel("Item1", group1),
            new ItemViewModel("Item2", group1),
            new ItemViewModel("Item3", group2),
            new ItemViewModel("Item4", group2)
        });

        string groupName = string.Empty;
        foreach (ItemViewModel item in this.Items.ToArray())
        {
            //Insert Group headers
            if (item.Group.Name != groupName)
            {
                groupName = item.Group.Name;
                this.Items.Insert(this.Items.IndexOf(item), item.Group);
            }
        }
    }

    public ObservableCollection<object> Items { get; }
}


public class GroupViewModel : ViewModelBase
{
    private bool isExtended = true;

    public GroupViewModel(string name)
    {
        this.Name = name;
    }

    public string Name { get; }

    public bool IsExtended
    {
        get { return this.isExtended; }
        set { this.SetProperty(ref this.isExtended, value); }
    }
}

public class ItemViewModel
{
    public ItemViewModel(string name, GroupViewModel group)
    {
        this.Name = name;
        this.Group = group;
    }

    public string Name { get; }
    public GroupViewModel Group { get; }
}

Upvotes: 2

Related Questions