Odd behavior when toggling CheckedListBox item's checked state via MouseClick when clicking on the same selection

Ahmad Mageed picture Ahmad Mageed · Nov 3, 2010 · Viewed 9.1k times · Source

The WinForms CheckedListBox control has 2 default behaviors when clicking with a mouse:

  1. In order to check/uncheck an item you're required to click an item twice. The first click selects the item, and the second toggles the check state.
  2. In addition, one subsequent click of the same item will toggle that item's checked state.

As a convenience feature I needed to allow users to toggle the selection in one click. I have achieved this, so now default behavior #1 above is achieved in one click. The problem is behavior #2 no longer works correctly when clicking the same (i.e., currently selected) item. It works fine when jumping between items, which is desired, but it requires up to 4 clicks on the same item.

My workaround for this is to call the toggling logic twice if the user selects the same item repeatedly. So on to my questions:

  1. This works, but why? What's the real underlying issue?
  2. Is there a better way to achieve this so I can get it working like default behavior #2 without calling the method twice and keeping track of my last selection?

Oddly enough debugging the code reveals that the checked state has changed but it doesn't appear on the UI side till it's called twice. I thought it might be threading related but it's not a re-entrant event being triggered that might need BeginInvoke usage.

Here's my code:

using System.Linq;
using System.Windows.Forms;

namespace ToggleCheckedListBoxSelection
{
    public partial class Form1 : Form
    {
        // default value of -1 since first item index is always 0
        private int lastIndex = -1;

        public Form1()
        {
            InitializeComponent();
            CheckedListBox clb = new CheckedListBox();
            clb.Items.AddRange(Enumerable.Range(1, 10).Cast<object>().ToArray());
            clb.MouseClick += clb_MouseClick;
            this.Controls.Add(clb);
        }

        private void clb_MouseClick(object sender, MouseEventArgs e)
        {
            var clb = (CheckedListBox)sender;
            Toggle(clb);

            // call toggle method again if user is trying to toggle the same item they were last on
            // this solves the issue where calling it once leaves it unchecked
            // comment these 2 lines out to reproduce issue (use a single click, not a double click)
            if (lastIndex == clb.SelectedIndex)
                Toggle(clb);

            lastIndex = clb.SelectedIndex;
        }

        private void Toggle(CheckedListBox clb)
        {
            clb.SetItemChecked(clb.SelectedIndex, !clb.GetItemChecked(clb.SelectedIndex));
        }
    }
}

To reproduce my problem comment out the lines mentioned in the code comments and follow these steps:

  1. Click the item at index 2 - state changes to checked.
  2. With the current item selected, click it again - state does not change. Expected: unchecked. Click it a few times and it finally switches.

Thanks for reading!

Answer

Jeff Ogata picture Jeff Ogata · Nov 3, 2010

As a convenience feature I needed to allow users to toggle the selection in one click.

I'm not sure what's happening with the code, but setting CheckOnClick to true will do this:

CheckOnClick indicates whether the check box should be toggled whenever an item is selected. The default behavior is to change the selection on the first click, and then have the user click again to apply the check mark. In some instances, however, you might prefer have the item checked as soon as it is clicked.