none
How to have only one checkbox checked in a checked listview

    Question

  •  

    I have a listview with checkboxes on my windows form. I would like only one item in the listview to be selected at a time. Therefore I have set the multiselect property to false. An item can be selected by clicking on the item or on the checkbox. I then want the checkboxes in the listview to act as radiobuttons, where only one checkbox can be checked and that no checkboxes can be unchecked.

     

    So far, when an item is selected the appropriate checkbox is checked and all the checkboxes are unchecked. When the last item is selected, any checkbox above this can be checked and the selected item will become the checked item and the previous checked item is unchecked. However, if the first item is selected, any items below this that are checked do not select the checked item and all items can be checked (but I only want one item to be checked at a time).

     

    Here is my code for the listviews selected index changed event:

     

    private void listView1_SelectedIndexChanged(object sender, EventArgs e)

    {

    foreach (ListViewItem lvi in listView1.Items)

    { lvi.Checked = false; }

    if (listView1.SelectedItems.Count > 0)

    {

    string listItem = listView1.SelectedItems[0].Text;

    labelThreshold.Text = listItem;

    listView1.SelectedItems[0].Checked = true;

    }

    }

    And here is my code for the listviews Item checked event:

    private void listView1_ItemChecked(object sender, ItemCheckedEventArgs e)
    {
    if (listView1.SelectedItems.Count > 0)
    {
    listView1.CheckedItems[0].Selected = true;
    }
    }

    How can I add and improve my code so that it performs how I want it to?

     

    Lucy

    Thursday, November 22, 2007 11:08 AM

Answers

  • When I did this a while back, one of the things I realized that because the SelectedIndexChanged event was raising the ItemChecked event by changing the Checked status of an item and vice versa so I needed a way to exit my event handler without doing anything. I think I did something like this....

     

    // use these 2 to exit event handlers without doing anything

    private bool _doCheck = true;

    private bool _doSelect = true;

     

    private void listView1_SelectedIndexChanged(object sender, EventArgs e)

    {

    if (!_doSelect)

    return;

    //suppress the ItemChecked Event

    _doCheck = false;

    foreach (ListViewItem lvi in listView1.Items)

    { lvi.Checked = false; }

    if (listView1.SelectedItems.Count > 0)

    {

    string listItem = listView1.SelectedItems[0].Text;

    labelThreshold.Text = listItem;

    listView1.SelectedItems[0].Checked = true;

    }

    _doCheck = true;

    }

    private void listView1_ItemChecked(object sender, ItemCheckedEventArgs e)

    {

    if (!_doCheck)

    return;

    _doCheck = false;

    _doSelect = false;

    foreach (ListViewItem lvi in listView1.Items)

    { // clear all checked items except the one we are working with

    if (lvi != e.Item)

    lvi.Checked = false;

    }

    _doCheck = true;

    listView1.SelectedItems.Clear();

    e.Item.Selected = e.Item.Checked;

    _doSelect = true;

    }

    Thursday, November 22, 2007 2:39 PM
  • in the ItemChecked event handler you could add the line

    e.Item.Checked=true;

    just before the

    _doCheck = true;

     

    that should do it I think.

    Thursday, November 22, 2007 4:01 PM
  • I think you now want to set the listView1.FocusedItem to the specific Item in the listbox at the same time that you set tit Selected and Checked states of the item that you are selecting.

    for example, use the line

    this.listView1.FocusedItem = e.Item;

    at the same time that you set e.Item.Checked = true in the ItemChecked event handler.

    Friday, November 23, 2007 2:29 PM

All replies

  • When I did this a while back, one of the things I realized that because the SelectedIndexChanged event was raising the ItemChecked event by changing the Checked status of an item and vice versa so I needed a way to exit my event handler without doing anything. I think I did something like this....

     

    // use these 2 to exit event handlers without doing anything

    private bool _doCheck = true;

    private bool _doSelect = true;

     

    private void listView1_SelectedIndexChanged(object sender, EventArgs e)

    {

    if (!_doSelect)

    return;

    //suppress the ItemChecked Event

    _doCheck = false;

    foreach (ListViewItem lvi in listView1.Items)

    { lvi.Checked = false; }

    if (listView1.SelectedItems.Count > 0)

    {

    string listItem = listView1.SelectedItems[0].Text;

    labelThreshold.Text = listItem;

    listView1.SelectedItems[0].Checked = true;

    }

    _doCheck = true;

    }

    private void listView1_ItemChecked(object sender, ItemCheckedEventArgs e)

    {

    if (!_doCheck)

    return;

    _doCheck = false;

    _doSelect = false;

    foreach (ListViewItem lvi in listView1.Items)

    { // clear all checked items except the one we are working with

    if (lvi != e.Item)

    lvi.Checked = false;

    }

    _doCheck = true;

    listView1.SelectedItems.Clear();

    e.Item.Selected = e.Item.Checked;

    _doSelect = true;

    }

    Thursday, November 22, 2007 2:39 PM
  • Thank you! That now works perfectly. I had to change it slightly as I came accross another problem. In my form load event I select an item in the listview. This did not work with the following code section in my SelectedIndexChanged event:

     

    foreach (ListViewItem lvi in listView1.Items)

    { lvi.Checked = false; }

     

    This has now been resolved using the boolean tip that you provided.

     

    I now have another query. How can I make sure that there is always one checked checkbox? I.e - You cannot uncheck the currently selected item (Which is checked).

     

    Lucy

    Thursday, November 22, 2007 3:44 PM
  • in the ItemChecked event handler you could add the line

    e.Item.Checked=true;

    just before the

    _doCheck = true;

     

    that should do it I think.

    Thursday, November 22, 2007 4:01 PM
  • Thank you for your help!

     

    I have just put that code (before reading your post) in the If statement as follows:

     

    foreach (ListViewItem lvi in listView1.Items)

    {

    if (lvi != e.Item)

    { lvi.Checked = false; }

    else

    { e.Item.Checked = true; }

    }

     

    And this works fine. I'm sure it will work just the same in the place where you suggested. Thank you.

     

    Lucy

    Thursday, November 22, 2007 4:12 PM
  • I have no come accross another problem. When my form loads, a specific item in the listview is selected and checked. The items backcolour is blue to show that it is selected and the check box is checked. But the first item in the listbox has a dotted line around the item as though that item is selected but it is not checked. If for example, the selected item when the form loads is item3 then the dotted line is around item1. Therefore when the down arrow is pressed on the keyboard, the user should expected item4 to be selected and checked but instead item2 is selected and checked, as though the up arrow has been pressed. The dotted line around the first item when the form loads stays when different chech boxes are checked. When the user selects the actual item in the listview the dotted line appears around the selected item.

     

    Can anyone help me to solve this problem?

     

    Lucy

    Friday, November 23, 2007 10:09 AM
  • I think you now want to set the listView1.FocusedItem to the specific Item in the listbox at the same time that you set tit Selected and Checked states of the item that you are selecting.

    for example, use the line

    this.listView1.FocusedItem = e.Item;

    at the same time that you set e.Item.Checked = true in the ItemChecked event handler.

    Friday, November 23, 2007 2:29 PM
  •  

    I have a listview with checkboxes on my windows form. I would like only one item in the listview to be selected at a time. Therefore I have set the multiselect property to false. An item can be selected by clicking on the item or on the checkbox. I then want the checkboxes in the listview to act as radiobuttons, where only one checkbox can be checked and that no checkboxes can be unchecked.

     

    So far, when an item is selected the appropriate checkbox is checked and all the checkboxes are unchecked. When the last item is selected, any checkbox above this can be checked and the selected item will become the checked item and the previous checked item is unchecked. However, if the first item is selected, any items below this that are checked do not select the checked item and all items can be checked (but I only want one item to be checked at a time).

     

    Here is my code for the listviews selected index changed event:

     

    [...]

    How can I add and improve my code so that it performs how I want it to?

     

    Lucy


    I know the question was asked a while, but I did not find an answer on the Internet for that.
    So, I'll post my solution here:

    When I want to select only one box, I used the "ItemCheck":

    foreach (ListViewItem item in listView1.Items)
    {
        item.Checked = false;
    }
    listView1.Items [e.Index]. Checked = true; 

    But this causes an StackOverflowException error. This error occurs because the function calls itself every time you change the value "Checked" of an item and never returns.

    What you should do is:
    - Say to the function you are working and not finished the job yet, so she will direct, returning to the original code.

    ItemCheck()->ItemCheck()->return;

    Then, the code looks like this:

    bool isChecking;
    private void listView1_ItemCheck (object sender, ItemCheckEventArgs e)
    {
        if (!isChecking)
        {
            isChecking = true;
            foreach (ListViewItem item in listView1.Items)
            {
                item.Checked = false;
            }
            listView1.Items [e.Index]. Checked = true;
            e.NewValue = CheckState.Checked;
            isChecking = false;
        }
    }

    This code works as a "RadioButton", only one box will be marked forever.

    For only check with MouseDoubleClick on a Item, we put a lock.
    So always use to unlock:

    // Releases the lock
    canCheck = true;
    // Now we check the item
    item.Checked = true;

    The code with a lock:

    bool isChecking;
    bool canCheck;
    private void listView1_ItemCheck(object sender, ItemCheckEventArgs e)
    {
        if (!isChecking && canCheck)
        {
            isChecking = true;
            foreach (ListViewItem item in listView1.Items)
            {
                item.Checked = false;
            }
            listView1.Items[e.Index].Checked = true;
            e.NewValue = CheckState.Checked;
            canCheck = false;
            isChecking = false;
        }
        else
        {
            if (isChecking)
            {
                e.NewValue = CheckState.Unchecked;
            }
            else
            {
                e.NewValue = e.CurrentValue;
            }
        }
    }
    
    private void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
    {
        // We can't unlock the lock if the value doesnt change.
        if (!listView1.GetItemAt(e.X, e.Y).Checked)
        {
            canCheck = true;
            listView1.GetItemAt(e.X, e.Y).Checked = true;
        }
    }

    akyryz
    Monday, October 19, 2009 9:51 AM