How can I toggle the checkbox of an item in a CheckedListbox
if CheckOnClick
is True
, but SelectionMode
is None
..?
If I set SelectionMode
to One
it works as expected, but I would like to prevent items in the list from being selected. The only purpose of the CheckedListbox is to use the checkboxes; having items selected is not desired.
I tried a bit of code with the various Click
and Mouse
events, but none of them seem to report the item in the list that was clicked. If I could determine that, it would be a simple matter to toggle the checkbox of the clicked item.
CodePudding user response:
The MouseClick
event will tell you when the control was clicked and where. Determine whether the click was on an item and which one, then toggle it:
Private Sub CheckedListBox1_MouseClick(sender As Object, e As MouseEventArgs) Handles CheckedListBox1.MouseClick
Dim clickedIndex = CheckedListBox1.IndexFromPoint(e.X, e.Y)
If clickedIndex <> ListBox.NoMatches Then
CheckedListBox1.SetItemChecked(clickedIndex, Not CheckedListBox1.GetItemChecked(clickedIndex))
End If
End Sub
CodePudding user response:
After a little testing, I would say that you need to consider “how” you want to achieve the non-selection mode you describe. As you have noted, if you set the CheckedListBox
es SelectionMode
to None
… then basically the check boxes become unusable.
The user cannot check or un-check any check box, as you already know… and this is why you want “your” code to change the check boxes checked state. So, you have now inherited the job of changing the check boxes check state because you set its “SelectionMode” to “None" … ? …
In addition, when the selection mode is set to “None” … then many “typical” properties of the CheckedListBox
will lose functionality and become useless. Example, the checked list boxes SelectedItem
property will always be null
and its SelectedIndex
property will most likely always be -1. Meaning, most “selection” type events will need to find what item was clicked by using the mouse location as shown in another answer.
The main point here is that when you decided to set the checked list boxes selection mode to “None”, then you basically open the door for more coding/responsibility on your part as far as “managing” the checked list box. I am just saying that the out-of-the-box .Net CheckedListBox
is not feature rich and is a basic control. I am guessing there “may” be a third-party CheckedListBox
Control that may have this “non-selected” functionality built-in.
So… I suggest another approach… however… it also has some drawbacks… basically you have to create a new Class MyCheckedListBox
type “Control” that inherits from the CheckedListBox
and then override its draw method to paint the cell the way we want.
I tend to avoid creating new controls. However, this will allow us to “keep” the CheckedListBoxes
“selection functionality” by keeping its SelectionMode
set to One
. In addition to removing the job of “your” code having to manage each check box’s check state… we can also use all the checked list boxes “selection” events and use them as we typically would using the list boxes “selection” properties.
Below is a crude example of how to override the CheckedListBox
’s Draw method to keep the “selected” items back color to the same color of the non-selected items.
class CheckedListBox_NoSelect : CheckedListBox {
protected override void OnDrawItem(DrawItemEventArgs e) {
DrawItemEventArgs new_e_Args = new DrawItemEventArgs
(e.Graphics,
e.Font,
new Rectangle(e.Bounds.Location, e.Bounds.Size),
e.Index,
(e.State & DrawItemState.Focus) == DrawItemState.Focus ? DrawItemState.Focus : DrawItemState.None,
this.ForeColor,
this.BackColor);
base.OnDrawItem(new_e_Args);
}
}
The code above is a simplified version of this SO question…
The CheckedListBox
on the left is a regular CheckedListBox
and uses the approach from user18387401‘s answer. The CheckedListBox
on the right is our new control class CheckedListBox_NoSelect
above.
For each control, the SelectedIndexChanged
event is wired up to demonstrate that the checked list box on the left with its SelectionMode
set to None
will always have its SelectedItem
set to null
and its SelectedIndex
will always be set to -1. However, it is not difficult to figure out “which” item was selected using user18387401‘s approach. This index is also displayed in its SelectedIndexChanged
event.
private void Form1_Load(object sender, EventArgs e) {
checkedListBox1.SelectionMode = SelectionMode.None;
checkedListBox1.Items.Add("Item 1");
checkedListBox1.Items.Add("Item 2");
checkedListBox1.Items.Add("Item 3");
checkedListBox1.Items.Add("Item 4");
checkedListBox1.Items.Add("Item 5");
checkedListBox1.CheckOnClick = true;
// Leave default selection mode to "One"
checkedListBox_NoSelect1.Items.Add("Item 1");
checkedListBox_NoSelect1.Items.Add("Item 2");
checkedListBox_NoSelect1.Items.Add("Item 3");
checkedListBox_NoSelect1.Items.Add("Item 4");
checkedListBox_NoSelect1.Items.Add("Item 5");
checkedListBox_NoSelect1.CheckOnClick = true;
}
private void checkedListBox1_MouseClick(object sender, MouseEventArgs e) {
int clickedIndex = checkedListBox1.IndexFromPoint(e.X, e.Y);
if (clickedIndex != -1) {
checkedListBox1.SetItemChecked(clickedIndex, !checkedListBox1.GetItemChecked(clickedIndex));
Debug.WriteLine("LEFT: MouseClick Selected Index: " clickedIndex);
}
}
private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e) {
Debug.WriteLine(" LEFT -> Item: " (checkedListBox1.SelectedItem == null ? "Null" : checkedListBox1.SelectedItem));
Debug.WriteLine(" LEFT -> Index: " checkedListBox1.SelectedIndex);
}
private void checkedListBox_NoSelect1_SelectedIndexChanged(object sender, EventArgs e) {
Debug.WriteLine("RIGHT -> Item: " (checkedListBox_NoSelect1.SelectedItem == null ? "Null" : checkedListBox_NoSelect1.SelectedItem));
Debug.WriteLine("RIGHT -> Index: " checkedListBox_NoSelect1.SelectedIndex);
}
I hope this makes sense and helps. Sorry that I did this in C#. If you can not convert the code to a VB version, then let me know and I will add a VB version.