Check only one ListViewItem at time
Asked Answered
D

6

5

I'm developing a smart device project using the Compact Framework.

I have a ListView with several checkable ListViewItems: the property CheckBoxes is true. I need to check only one ListViewItem at time, so I subscribed the ItemCheck event:

// I need to know the last item checked
private ListViewItem lastItemChecked;

private void listView_ItemCheck(object sender, ItemCheckEventArgs e)
{
    if (lastItemChecked != null && lastItemChecked.Checked)
    {
        /* I need to do the following to prevent infinite recursion:
        i.e. subscribe and then unsubscribe the ItemCheck event. */
        listView.ItemCheck -= listView_ItemCheck;
        lastItemChecked.Checked = false;
        listView.ItemCheck += listView_ItemCheck;
    }

    lastItemChecked = listView.Items[e.Index];
}

Is there a better way to prevent the infinite recursion and thus the Stack Overflow?

Dysphemia answered 13/12, 2012 at 11:9 Comment(4)
social.msdn.microsoft.com/Forums/en/winforms/thread/…Diminution
@BaliC why that could be better?Dysphemia
What do you mean? I googled it and that was the first result. Same questions and examples by the looks of it.Diminution
@BaliC I need a better example (and an explanation of why is better), not just another google example...Dysphemia
S
14

Well, I think what might be better than juggling with the EventHandlers is checking if the lastItemCheck is a current item from the EventArgs. Like this:

// I need to know the last item checked
private ListViewItem lastItemChecked;

private void listView_ItemCheck(object sender, ItemCheckEventArgs e)
{
    // if we have the lastItem set as checked, and it is different
    // item than the one that fired the event, uncheck it
    if (lastItemChecked != null && lastItemChecked.Checked
        && lastItemChecked != listView.Items[e.Index] )
    {
        // uncheck the last item and store the new one
        lastItemChecked.Checked = false;
    }

    // store current item
    lastItemChecked = listView.Items[e.Index];
}

I think that you'll agree, that re-assigning EventHandlers is a bit worse than simply checking the reference of the stored object.

Stillwell answered 13/12, 2012 at 12:7 Comment(1)
You might consider storing the lastItemChecked only if listView.Items[e.Index].Checked is true. Then the condition would be even simplier: if (lastItemChecked != null && lastItemChecked != listView.Items[e.Index]) { ... }Stillwell
S
3

Here's a simpler approach that can be reused for multiple lists.

private void listView_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            var listView = sender as ListView;
            if (listView != null && e.NewValue == CheckState.Checked)
            {
                foreach (ListViewItem checkedItem in listView.CheckedItems)
                {
                    checkedItem.Checked = false;
                }
            }
        }

The ItemCheck event fires before the list item state changes, so no additional tests are required.

ItemCheck - Indicates that an item is about to have its checked state changed. The value is not updated until after the event occurs.

Shend answered 8/7, 2016 at 2:20 Comment(2)
Old thread but... this clearly results in StackOverflow, that was the whole point of the previous approaches. Not sure how this has positive votes.Proceed
Thanks @Proceed I did have this working locally at the time but you're right it seems what I've posted here wasn't complete, that's embarrassing, I've added in the NewValue check which should solve the issue and avoid the unnecessary extra tracking of last item checked.Shend
D
2

M Bak you forgot to disable Item check event before assign new value. And your code will uncheck last checked value too, because you use checked event. Also CheckedItems.Count will be changed after Checked value changed. Fixed solution is:

private void LstFirstPageBanner_ItemCheck(object sender, ItemCheckEventArgs e)
    {
        var listView = sender as ListView;
        if (listView != null)
        {
            var checkedCount = listView.CheckedItems.Count;
            listView.ItemCheck -= LstFirstPageBanner_ItemCheck;
            for (var i = 0; i < checkedCount; i++)
            {
                listView.CheckedItems[i].Checked = false;
            }
            listView.ItemCheck += LstFirstPageBanner_ItemCheck;
        }
    } 
Distaste answered 18/7, 2017 at 9:24 Comment(0)
H
0

So many code and tricks...

listView.SelectionMode = System.Windows.Controls.SelectionMode.Single;
Hazan answered 14/8, 2013 at 12:35 Comment(1)
I think this is only for WPFJodijodie
C
0
private void listView1_ItemChecked(object sender, ItemCheckedEventArgs e)
    {
        listView1.ItemChecked -= listView1_ItemChecked;
        foreach (var item in listView1.CheckedItems)
        {
            if (e.Item != item)
            {
                ((ListViewItem)item).Checked = false;

            }

        }
        listView1.ItemChecked += listView1_ItemChecked;
    }
Carrico answered 3/11, 2014 at 21:23 Comment(0)
J
0

This is what I use and it seems to work fine!

   private void TestListView_ItemChecked(object sender, ItemCheckedEventArgs e)
        {
            ListViewItem checkedItem = e.Item as ListViewItem;
            if (checkedItem.Checked)
            {
                foreach (ListViewItem item in productsListView.Items)
                {
                    if (item.Checked && !item.Equals(checkedItem))
                    {
                        item.Checked = false;
                    }
                }
            }
        }
Junie answered 13/4, 2023 at 20:56 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Hom

© 2022 - 2024 — McMap. All rights reserved.