WPF: Bind Collection with Collection to a ListBox with groups

28,412

Solution 1

Well, I am not sure if this is what you want achieve but here is a way that you can try:

Assuming your classes are like these:

public class Group
{
    public string Name { get; set; }
    public List<Contact> Contacts { get; set; }
}

public class Contact
{
    public string Name { get; set; }
    public bool IsOnline { get; set; }
}

You can set ListBox.ItemTemplate as another ListBox bind to Contacts property, like:

<CollectionViewSource x:Key="groups" Source="{Binding}" >
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="Name" />
    </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

<DataTemplate x:Key="groupTemplate" DataType="Group">
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding Name}" />
    </StackPanel>
</DataTemplate>

<ListBox ItemsSource="{Binding Source={StaticResource groups}}">
    <ListBox.GroupStyle>
        <GroupStyle HeaderTemplate="{StaticResource groupTemplate}" />
    </ListBox.GroupStyle>
    <ListBox.ItemTemplate>
        <DataTemplate DataType="Contact">
            <ListBox ItemsSource="{Binding Contacts}">
                <ListBox.ItemTemplate>
                    <DataTemplate DataType="Contact">
                        <TextBlock Text="{Binding Name}" />
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

You have to style the inner listbox a little bit.

Edit: Another solution by using TreeView

<DataTemplate DataType="Contact">
   <TextBlock Text="{Binding Name}" />
</DataTemplate>

<TreeView ItemsSource="{Binding Source={StaticResource groups}}">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate DataType="Group" ItemsSource="{Binding Contacts}">
            <TextBlock Text="{Binding Name}" />
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

Solution 2

If you have a copy of Programming WPF, jump to Page 221. If not I'll summarize (in my inept sort of way)

First you don't need to manually group the Person objects.

If each Person object has a Group property,

People people = // retrieve the List<Person> somehow
ICollectionView view = CollectionViewSource.GetDefaultView(people);
view.GroupDescriptions.Add( new PropertyGroupDescription("Group") );

All controls that derive from ItemsControl can display grouped items, so

// the XAML
<ListBox ... ItemsSource={Binding}>
  <ListBox.GroupStyle>
    <x:Static Member="GroupStyle.Default" />
  </ListBox.GroupStyle>
</ListBox>

You can also define a custom GroupStyle and specify the GroupStyle.HeaderTemplate to define a new DataTemplate that shows custom attributes like 'some PropertyValue (Count)' - Bind the one TextBlock to {Binding SomeProperty} and the other to {Binding ItemCount}

HTH

Share:
28,412
Admin
Author by

Admin

Updated on July 17, 2022

Comments

  • Admin
    Admin almost 2 years

    sometimes WPF is too complex for me. I've got my "Window1" holding a collection of "Group"s. "Group" is a class with a collection of "Person"s. In the end this should be a contact list. What I simply want to do is to show the groups with its person in a ListBox, where the group name of the list groups equals the Name Property of my class "Groups".

    I've tried with a CollectionViewSource bound to the "Collection". The groups are shown correct, but the items of the list are equal to the group names. So each group has only one item: its group name.

    Many examples here show the grouping of items with only one collection. What I can do is to set the group name as Property of "Person". But then I can't count (and that is really neccessary): - how many persons are in each group - how many of that persons have the "Status" "Online".

    I use linq in the "Group" class to count that. Thanks for any advice helping me to get started.

  • Admin
    Admin almost 15 years
    Thanks, first. My Person object does NOT have a group property, yet. I'd like to avoid it. I did not understand how I can handle the count (only the logic, the fact with the TextBlock is clear). Here is some code, if it might help you. enum OnlineStatus { ONLINE, OFFLINE } class Person { // The header of each group should count // how many Person exists with OnlineStatus "Online". public OnlineStatus Status { get; set; } } class Group { public List<Person> Persons = new List<Person>(); } class Window1 { public List<Group> Groups = new List<Group>; }
  • Admin
    Admin almost 15 years
    This is quiet helpful and equal to my old solution, where I had an Expander per group and a ListBox for the Contacts. The problem here is, that you can select a person per list, but I supposed you can do it with one. I try to copy the UI of the Windows Live Messenger 2009 contact list. I think they used only one ListBox and an Expander as ControlTemplate for the group. But I also assume, they defined the group as property. But I really don't know, how they can count the online persons. By the way, you are right with your classes.
  • idursun
    idursun almost 15 years
    Why don't you use TreeView control for displaying hierarchical data?
  • Admin
    Admin almost 15 years
    You are absolutely right! I think, that would solve my problem for sure. I've got no answer for the "why". I haven't used the TreeView control a lot. But this would be the best for my problem. Thanks!
  • Admin
    Admin almost 15 years
    TreeView was really that what I needed.
  • Dom
    Dom about 11 years
    +1, {Binding ItemCount} was the answer I needed! THANK YOU!