Click here to Skip to main content
15,888,579 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
See more:
I have a listView, first item is "Select All". I can select and deselect all the items in the listView using the ItemChecked event - easily when a user clicks on the "Select All" checkbox. My problem comes in when a user has clicked "Select All" and then de-selects one of the items in the list...I want to un-check the "Select All" item only but it does the ItemChecked event again
and everything get's messed up. Some items are clicked some aren't. The only code I have is the following (I've tossed everything else - so frustrating):

Form Load event:
C#
listView1.Items.Add("SELECT ALL");
listView1.Items.Add("ITEM 1");
listView1.Items.Add("ITEM 2");
listView1.Items.Add("ITEM 3");
listView1.Items.Add("ITEM 4");


Function called (works fine, very easy):
C#
public static bool SetListItemsCheckAll(ListView l, ItemCheckedEventArgs e)
{
    try
    {
        if (e.Item.Text == "SELECT ALL")
        {
            if (e.Item.Checked)
            {
                foreach (ListViewItem li in l.Items)
                {
                    li.Checked = true;
                }
            }
            else if (!e.Item.Checked)
            {
                foreach (ListViewItem li in l.Items)
                {
                    li.Checked = false;
                }
            }
        }

        return true;
    }
    catch (Exception exc)
    {
        string ex;
        ex = exc.Message.ToString();
        MessageBox.Show(ex, "Error - List Selection All", MessageBoxButtons.OK);
        return false;
    }
}


C#
private void listView1_ItemChecked(object sender, ItemCheckedEventArgs e)
{
    ret = SetListItemsCheckAll(listView1, e);
}


Any help would be greatly appreciated. Thanks.
Posted
Comments
lmilano 10-Nov-15 13:28pm    
Richard...awesome answer...couldn't figure it out. Works perfectly, thanks for all your help.

Add a field to guard against reentrancy:
C#
private bool _inListView1ItemChecked;

private void listView1_ItemChecked(object sender, ItemCheckedEventArgs e)
{
    if (!_inListView1ItemChecked)
    {
        _inListView1ItemChecked = true;
        try
        {
            SetListItemsCheckAll(listView1, e);
        }
        finally
        {
            _inListView1ItemChecked = false;
        }
    }
}


You might also want to update your method so that it doesn't try to change the state of the current item:
C#
public static void SetListItemsCheckAll(ListView l, ItemCheckedEventArgs e)
{
    if (e.Item.Text == "SELECT ALL")
    {
        // Select / de-select all items:
        foreach (ListViewItem li in l.Items)
        {
            if (li != e.Item)
            {
                li.Checked = e.Item.Checked;
            }
        }
    }
    else if (!e.Item.Checked)
    {
        // De-select the "Select All" item:
        foreach (ListViewItem li in l.Items)
        {
            if (li.Text == "SELECT ALL")
            {
                li.Checked = false;
            }
        }
    }
}
 
Share this answer
 
Comments
Matt T Heffron 10-Nov-15 16:19pm    
+5
Another way to go about this ... without using a bool variable to prevent recursion ... is to use the ListView 'ItemCheck event which is a 'preview Event ... it's called before 'ItemChecked.

By "dis-connecting" the ItemCheck EventHandler when the 'SelectAll CheckBox has its CheckState changed, we can then go ahead and set all the CheckBoxes to that same state; then, we re-connect the ItemCheck EventHandler when we're done, so "normal" use is restored for all the other CheckBoxes.
C#
private void listView1_ItemCheck(object sender, ItemCheckEventArgs e)
{
    // note we assume "select all" is the first ListViewItem
    if (e.Index == 0)
    {
        bool isCheckAll = e.NewValue == CheckState.Checked; 
        
        listView1.SuspendLayout();

        // let the user know what' next
        listView1.Items[0].Text =  isCheckAll 
            ? "Deselect All"
            : "Select All";

        // disconnect the EventHandler
        listView1.ItemCheck -= listView1_ItemCheck;

        // set CheckState of all but 0th. item
        for (int i = 1; i < listView1.Items.Count; i++)
        {
            listView1.Items[i].Checked = isCheckAll;
        }

        // re-connect the EventHandler
        listView1.ItemCheck += listView1_ItemCheck;

        listView1.ResumeLayout();
    }
}
Notes:

1. there's nothing wrong doing this another way using a variable to prevent recursion !

2. the WinForm ListView is an old creature, and, you may notice, is inconsistent in structure and syntax compared to other Controls. Many other WinForm Controls have an equivalent to the 'ItemCheck Event of the ListView whose name starts with 'Before..., and those Event Handlers often have a built-in way to allow you to cancel the complementary 'After... Event Handler.

3. for production code: I would choose to keep track of the state of all the ListView Items (not the 'Select/DeSelect item) so that any time they were all either checked, or unchecked, the Text of the 'Select/DeSelect ListViewItem would reflect what its next CheckState will do. But, that's getting 'fancy: you implement that, your client/employer should pay you more :)
 
Share this answer
 
v2
Comments
Matt T Heffron 10-Nov-15 16:19pm    
+5
BillWoodruff 10-Nov-15 20:30pm    
Thanks, Matt !

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900