Description:
Grouping ListView items dynamically can be confusing at first, the reason is that you can't simply "tell" the item what's his group's header is. You have to find the proper group from the group-collection and add the item to that existing group. And if no matching group is found, you have to create a new group. Okay, let's get started:
I'm going to skip form-designing. The form that I'm going to use for demonstration looks like this:
Drop-down list items:
- Size: Small, Medium, Large
- Color: Red, Yellow, Green, Blue, Violet
- Group: By name, By size, By color
Our grouping function:
private void GroupItem(ListViewItem item)
{
// This flag will
tell us if proper group already exists
bool
group_exists = false;
// Check each
group if it fits to the item
foreach (ListViewGroup group in
this.listView.Groups)
{
// Compare group's header to selected subitem's text
if
(group.Header == item.SubItems[this.groupBox.SelectedIndex].Text)
{
// Add item to
the group.
//
Alternative is: group.Items.Add(item);
item.Group = group;
group_exists = true;
break;
}
}
// Create new
group if no proper group was found
if
(!group_exists)
{
// Create group and specify its header by
// getting
selected subitem's text
ListViewGroup
group = new ListViewGroup(item.SubItems[this.groupBox.SelectedIndex].Text);
// We need to
add the group to the ListView first
this.listView.Groups.Add(group);
item.Group = group;
}
}
Make sure some items are selected from the start:
this.sizeBox.SelectedIndex = 0;
this.colorBox.SelectedIndex = 0;
this.groupBox.SelectedIndex = 0;
Add an event handler to the SelectedIndexChanged event, which occurs when different type of grouping is selected :
this.groupBox.SelectedIndexChanged += (object o, EventArgs
e) =>
{
// Clear group
collection
this.listView.Groups.Clear();
// Loop through
all existing items to group them properly
foreach (ListViewItem item in
this.listView.Items)
{
GroupItem(item);
}
};
Event handler for a click on the "Add" button:
this.add.Click += (object
o, EventArgs e) =>
{
// Display messagebox if
textbox with name is empty
if (this.nameBox.Text == "")
{
MessageBox.Show("No
name was specified.", "Name
missing!",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
else
{
// ListViewItem has a useful constructor that
// allows us
to add collection of subitems
// in form of
a string array
ListViewItem
item = new ListViewItem(new string[] {
this.nameBox.Text,
// Unbox the
selected item by using a cast
(string)this.sizeBox.SelectedItem,
(string)this.colorBox.SelectedItem
});
// Call our
item-grouping function
GroupItem(item);
// Finally,
add the item to the ListView
this.listView.Items.Add(item);
}
};
For more complex grouping use enumeration (for types of grouping) and a switch statement that assigns proper checking actions to a boolean delegate. Use the boolean delegate in the if statement that checks if existing group matches the condition.
For example, let's group items either by first letter or by name lenght:
Our delegate and its variable:
delegate bool GroupMatch(ListViewItem
item, ListViewGroup group);
GroupMatch groupMatch;
Enumeration and its variable with "ByLetter" as initial value:
enum Grouping {
ByLetter, ByLenght };
Grouping grouping = Grouping.ByLetter;
Switch statement that assigns proper checking actions to our delegate
variable
switch (grouping)
{
case Grouping.ByLetter:
groupMatch
= (ListViewItem item, ListViewGroup group) =>
{
return
item.Text.ToUpper()[0] == group.Header[0];
};
break;
case Grouping.ByLenght:
groupMatch
= (ListViewItem item, ListViewGroup group) =>
{
return
item.Text.Length == group.Header.Length;
};
break;
}
The body of our new GroupItem function:
bool group_exists = false;
foreach (ListViewGroup
group in this.listView.Groups)
{
if (groupMatch(item, group))
{
item.Group
= group;
group_exists = true;
break;
}
}
if (!group_exists)
{
ListViewGroup group;
switch
(grouping)
{
case Grouping.ByLetter:
group = new
ListViewGroup(item.Name.ToUpper()[0].ToString());
break;
case Grouping.ByLenght:
group = new
ListViewGroup(item.Name.Length.ToString());
break;
default:
// even though
this will never execute
// we
have add default case, otherwise
// the
program won't compile, because group
// supposedly
isn't assigned in all cases
return;
}
this.listView.Groups.Add(group);
item.Group = group;
}
If you want to sort items by a value that isn't in the collection of subitems, one way to do it is to assign the value to ListViewItem.Tag. Tag's type is Sytem.Object (keyword object), which means that you can add whole collection of data to the item without adding any subitems. But be sure to box and unbox your data properly.
Thanks for reading, code safely!