I am having some difficulty getting a CheckedListBox to behave the way I want it to. What I am trying to accomplish is getting a CheckedListBox with the first box checked on PageLoad. I want only one checkbox checked at any given time but I also don't want the user to uncheck the last checked box. I can do one or the other but I can't seem to do both.
Here is some code snippets that I have used to accomplish the task of having only one checkbox checked. The problem with these are the last selection can be unchecked where there are no checkboxes checked.
1st Snippet:
if(e.NewValue == CheckState.Checked)
{
// Uncheck the other items
for (int i = 0; i < defCheckedListBox.Items.Count; i++)
{
if (e.Index != i)
{
this.defCheckedListBox.SetItemChecked(i, false);
}
}
}
2nd Snippet
// Ensure that we are checking an item
if (e.NewValue != CheckState.Checked)
{
return;
}
// Get the items that are selected
CheckedListBox.CheckedIndexCollection selectedItems = this.defCheckedListBox.CheckedIndices;
// Check that we have at least 1 item selected
if (selectedItems.Count > 0)
{
// Uncheck the other item
this.defCheckedListBox.SetItemChecked(selectedItems[0], false);
}
Here is what I have used to prevent the last checked box to be "unchecked"
if (laborLevelDefCheckedListBox.CheckedItems.Count == 1)
{
if (e.CurrentValue == CheckState.Checked)
{
e.NewValue = CheckState.Checked;
}
}
I know this has got to be simple but I think because I've had a long week and I have looked at this too long it is just not coming to me. Any help with this is super appreciated! If I solve this over the weekend I will be sure to post my solution. BTW Happy Holidays to those here in the States :)
Chris makes a good point in the comments that this feels like you are re-inventing radio buttons but you are almost there with the code you have posted if you really want it to work with a CheckedListBox. I have adapted the code from your 1st Snippet which I think does the trick:
//remove the event handler so when we change the state of other items the event
//isn't fired again.
defCheckedListBox.ItemCheck -= defCheckedListBox_ItemCheck;
if (e.NewValue == CheckState.Checked)
{
// Uncheck the other items
for (int i = 0; i < defCheckedListBox.Items.Count; i++)
{
if (e.Index != i)
{
this.defCheckedListBox.SetItemChecked(i, false);
}
}
}
else
{
//the state was not checked.
//as only one item can ever be Checked inside the event
//handler the state of not checked is invalid for us; set the state back to Checked.
e.NewValue = CheckState.Checked;
}
//re-add the event handler
defCheckedListBox.ItemCheck += defCheckedListBox_ItemCheck;
Essentially the only new parts are the else where we reset the state if the state was not Checked and the removing and re-adding of the event to prevent it firing again when we manually set the state of other items (this could be handled with a global bool if you prefer).
// Use CheckBoxList Event
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e) {
// Stops DeInitialize Event
checkedListBox1.ItemCheck -= checkedListBox1_ItemCheck;
// Get the new value old value my be in Indeterminate State
checkedListBox1.SetItemCheckState(e.Index, e.NewValue);
// Start ReInitialize Event
checkedListBox1.ItemCheck += checkedListBox1_ItemCheck;
}
Related
I have a set of RadioButtons in a repeater. Once a RadioButton is checked, the related repetition is determined in a loop through its items and a parameter is saved to a Data Base. This triggers the loading of the next question with a new set of RadioButtons is. So far so good.
If the user navigates back to the previous question then he could post a new selection and mess up everything. To deal with this unwanted operation, I wrote this code for the RadioButton OnChecked event:
All repeaters Radio Buttons are first set back to unchecked and then the latest selection restored with ((RadioButton)sender).Checked = true.
Afterwards I determine the related parameter and overwrite the already existing record for that question with the new value.
protected void RB_Checked(object sender, EventArgs e)
{
for (int i = 0; i <= RPT_DisplayInquiry.Items.Count - 1; i++)
{
RadioButton rb = (RadioButton)RPT_DisplayInquiry.Items[i].FindControl("RB_Choice");
rb.Checked = false;
}
((RadioButton)sender).Checked = true;
for (int i = 0; i <= RPT_DisplayInquiry.Items.Count - 1; i++)
{
RadioButton rb = (RadioButton)RPT_DisplayInquiry.Items[i].FindControl("RB_Choice");
if (rb.Checked == true)
{
try
{
string Choice = Convert.ToString(i + 1);
Label lbl = (Label)RPT_DisplayInquiry.Items[i].FindControl("Lbl_Structure");
SaveSelection(lbl.Text, Choice, 0);
}
catch (Exception ex)
{
throw ex;
}
}
}
}
For some reason this doesn't work. If I navigate back again after the new selection is saved, I see that the reset had not worked and that 2 RadioButtons are selected. Therefore the saved value is not necessarily correct as it is just related to the first found checked RadioButton in the repeater.
If I eliminate the call of the Save Routine from that methode and call it afterwards from a separate click event then the whole thing works.
Does the reset of the RadioButton not work as they are never rendered?
What is going on?
I am working on a simple application (phonebook) in C# and in my project I have got a listview filled with contacts. I have been trying to implement the possibility to automatically (instantly) search through a listview using a textbox. I have managed to make it work, but not in the desired way. You will realise the actual problem if I give you an example. Let's say that I have got a contact named Bill Gates and when I try searching for it - it gets found and that part is OK. But, the problem is when I try to search for another contact. In that case, I have to clear the textbox before I type another name, but it is possible to remove only letter by letter. When I start removing the whole name, after removing a first letter it acts like I have just entered the name - it selects the item (and focuses as well) - actually there is no time to remove the whole name before it finds a contact again. I have to remove a first letter, then switch back to the textbox, remove another letter etc. Is there any solution for searching to be automatic - as it is now, but on the other hand for removing (clearing the textbox) without selecting contacts to be possible.
Take a look at the code:
private void txt_Search_TextChanged(object sender, System.EventArgs e)
{
if (txt_Search.Text != "")
{
foreach (ListViewItem item in listView1.Items)
{
if (item.Text.ToLower().Contains(txt_Search.Text.ToLower()))
{
item.Selected = true;
}
else
{
listView1.Items.Remove(item);
}
}
if (listView1.SelectedItems.Count == 1)
{
listView1.Focus();
}
}
else
{
LoadContacts();
RefreshAll();
}
}
There are some things wrong in your code, firstly when modifying a collection in a loop through it, we should not use foreach although in some case it seems to work but not really, it will surely be strange in future and confuse you. We should use a for loop instead and loop in the reverse order. The second wrong thing is you set the Selected to true which may cause your textBox lose focus to the listView. The solution is we have to use some other way to indicate that the item is selected, such as by using BackColor instead:
private void txt_Search_TextChanged(object sender, System.EventArgs e)
{
if (txt_Search.Text != "") {
for(int i = listView1.Items.Count - 1; i >= 0; i--) {
var item = listView1.Items[i];
if (item.Text.ToLower().Contains(txt_Search.Text.ToLower())) {
item.BackColor = SystemColors.Highlight;
item.ForeColor = SystemColors.HighlightText;
}
else {
listView1.Items.Remove(item);
}
}
if (listView1.SelectedItems.Count == 1) {
listView1.Focus();
}
}
else
LoadContacts();
RefreshAll();
}
}
Also after user focusing the ListView, all the BackColor and ForeColor should be reset, we can handle the Enter event of ListView:
//Enter event handler for listView1
private void listView1_Enter(object sender, EventArgs e){
foreach(ListViewItem item in listView1.Items){
item.BackColor = SystemColors.Window;
item.ForeColor = SystemColors.WindowText;
}
}
EDIT
you better not use Text_Changed, rather try Key_Down method as follows
private void txt_Search_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter) //apply your search only when pressing ENTER key
{
// you do your search as it was before
// i personally don't have suggestions here
if (!txt_Search.AutoCompleteCustomSource.Contains(txt_Search.Text)) txt_Search.AutoCompleteCustomSource.Add(txt_Search.Text);
//the line above will save all your searched contacts and display it in a beautiful format
}
else if (txt_Search.Text == "")
{
LoadContacts();
RefreshAll();
}
}
Of course don't forget to set the properties of txt_Search
AutoCompleteMode = SuggestAppend and AutoCompleteSource = CustomSource
This kind of feels like a hack, but you could track the length of text that has been typed into the textbox, and only perform your searching and focus logic if the length of text is greater than the previous time the event was called. That way if someone deletes a letter, the searching and focusing won't occur. Something like:
// declare lastSearchLength as a int outside of your TextChanged delegate
if (!String.IsNullOrEmpty(txt_Search.Text) && txt_Search.Text.Length > lastSearchLength)
{
foreach (ListViewItem item in listView1.Items)
{
if (item.Text.ToLower().Contains(txt_Search.Text.ToLower()))
{
item.Selected = true;
}
else
{
listView1.Items.Remove(item);
}
}
if (listView1.SelectedItems.Count == 1)
{
listView1.Focus();
}
lastSearchLength = txt_Search.Text.Length;
}
else
{
LoadContacts();
RefreshAll();
}
}
You are doing a postback with every key press. When the page reloads, it will not retain focus where you would expect. I recommend implementing this in JavaScript on the client, or using a search button instead of TextChanged event.
Here is a short program that reproduces the problem I just encountered. This was compiled under MS Windows 7 with .NET 4.0, just in case that makes a difference.
using System;
using System.Drawing;
using System.Windows.Forms;
// Compile with "csc /target:exe /out:comboboxbug.exe /r:System.dll /r:System.Drawing.dll /r:System.Windows.Forms.dll comboboxbug.cs"
// in a Visual Studio command prompt.
static class Program
{
[STAThread]
static void Main()
{
//Create a label.
Label oLabel = new Label();
oLabel.Location = new Point (10, 10);
oLabel.Size = new Size (100, 15);
oLabel.Text = "Combo box bug:";
// Create a combo-box.
ComboBox oComboBox = new ComboBox();
oComboBox.Location = new Point (10, 50);
oComboBox.Size = new Size (150, 21);
oComboBox.Items.AddRange (new object[]
{ "A", "A B", "A C", "A B C", "A C B", "A B C D", "A C B D" });
oComboBox.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
oComboBox.AutoCompleteSource = AutoCompleteSource.ListItems;
oComboBox.SelectionChangeCommitted
+= new EventHandler (comboBox_SelectionChangeCommitted);
// Create a form.
Form oForm = new Form();
oForm.Size = new Size (200, 150);
oForm.Controls.Add (oLabel);
oForm.Controls.Add (oComboBox);
// Run this form.
Application.Run (oForm);
}
static void comboBox_SelectionChangeCommitted (object sender,
EventArgs e)
{
MessageBox.Show ("SelectionChangeCommitted");
}
}
Click in the text portion of the combo-box and type "A". You will get a list of autocomplete suggestions. Click one of the selections with your mouse. The SelectionChangeCommitted event doesn't happen!
Select a menu-item without using autocomplete. You'll get a message-box showing that the SelectionChangeCommitted event happened!
Given that the selection was changed by the user in both cases, shouldn't SelectionChangeCommitted be called in both cases?
Using the SelectedIndexChanged event is not an option, because for the application behind this canned example, I only want it to happen when the user makes a selection, not when it's set programmatically.
EDIT 2020-Oct-28: I found another case where SelectionChangeCommitted doesn't get called! Auto-complete doesn't even need to be set for the problem to happen! Click to open the combo-box, press a key that matches the beginning of one of the combo-box items, and then press Tab to leave. The combo-box item gets selected, but SelectionChangeCommitted is not called! My revised answer is below.
Using the SelectedIndexChanged event is not an option, because for the application behind this canned example, I only want it to happen when the user makes a selection, not when it's set programmatically.
You could also accomplish this by writing a wrapper method for changing the selection that temporarily disables your event.
Unfortunately I do not know off hand a solution to the issue that SelectionChangeCommitted not being started for the more general case (such as where you don't control the ComboBox or how it is accessed).
EDIT:
I made a streamer of all the events that ComboBox calls, and it doesn't appear that any other event will do what you are looking for. The only solution I can think of would involve hooking into the events that the AutoComplete triggers. The difficulty is knowing what those events are, since they don't appear to trigger off the ComboBox from what my minor testing shows.
FYI, here was the best solution I ever came up with. Obviously, this is a Leave event-handler on a ComboBox subclass. The SelectionChangeCommitted event doesn't happen on the mouse-click, but at least it happens during the normal flow of GUI interaction.
private void this_Leave (object sender, EventArgs e)
{
// If this is an autocomplete combo-box, select the
// item that was found by autocomplete.
// This seems like something that ComboBox should be
// doing automatically...I wonder why it doesn't?
if (this.AutoCompleteMode != AutoCompleteMode.None)
{
// Determine which combo-box item matches the text.
// Since IndexOf() is case-sensitive, do our own
// search.
int iIndex = -1;
string strText = this.Text;
ComboBox.ObjectCollection lstItems = this.Items;
int iCount = lstItems.Count;
for (int i = 0; i < iCount; ++i)
{
string strItem = lstItems[i].ToString();
if (string.Compare (strText, strItem, true) == 0)
{
iIndex = i;
break;
}
}
// If there's a match, and this isn't already the
// selected item, make it the selected item.
//
// Force a selection-change-committed event, since
// the autocomplete was driven by the user.
if (iIndex >= 0
&& this.SelectedIndex != iIndex)
{
this.SelectedIndex = iIndex;
OnSelectionChangeCommitted (EventArgs.Empty);
}
}
}
If someone got this problem, I suggest a solution that works fine to me...
Think with me, to accept the suggest of Combo-box, generally the user needs to key down with an Enter key.
You can write into KeyDown event of Combo-box property, something like this:
private void cboProperty_SelectionChangeCommitted(object sender, EventArgs e)
{
//Call here the event of SelectionChangeCommitted
cboProperty_SelectionChangeCommitted(sender,null);
}
It will raise the SelectionChangeCommitted on the right time.
This workaround worked fine for me and hope for you too. When use Autocomplete by typing data in your combo box to get an item through keyboard or mouse selection you need _KeyDown event. From inside invoke _SelectionChangeCommitted method which contains code for other operations. See code below:
private void YourComboBox_KeyDown(object sender, KeyEventArgs e)
{
//Works also when user select and click on autocomplete list.
if (e.KeyCode == Keys.Enter && YourComboBox.SelectedItem != null)
YourComboBox_SelectionChangeCommitted(sender, e);
}
For the non-auto-complete case mentioned above (i.e. my 2020-Oct-28 edit), this Leave event-handler on a ComboBox subclass incorporates the new case as well as the old one, as long as your SelectionChangeCommitted event-handler is idempotent. Compared to my previous answer, it removes the test for auto-complete, and always calls OnSelectionChangeCommitted().
private void this_Leave (object sender, EventArgs e)
{
// Determine which combo-box item matches the text.
// Since IndexOf() is case-sensitive, do our own
// search.
int iIndex = -1;
string strText = this.Text;
ComboBox.ObjectCollection lstItems = this.Items;
int iCount = lstItems.Count;
for (int i = 0; i < iCount; ++i)
{
string strItem = lstItems[i].ToString();
if (string.Compare (strText, strItem, true) == 0)
{
iIndex = i;
break;
}
}
// If there's a match, and this isn't already the
// selected item, make it the selected item.
if (iIndex >= 0
&& this.SelectedIndex != iIndex)
this.SelectedIndex = iIndex;
// Force a selection-change-committed event, since
// the autocomplete was driven by the user.
OnSelectionChangeCommitted (EventArgs.Empty);
}
Hey. I've got the following code that populates my list box
UsersListBox.DataSource = GrpList;
However, after the box is populated, the first item in the list is selected by default and the "selected index changed" event fires. How do I prevent the item from being selected right after the list box was populated, or how do I prevent the event from firing?
Thanks
To keep the event from firing, here are two options I have used in the past:
Unregister the event handler while setting the DataSource.
UsersListBox.SelectedIndexChanged -= UsersListBox_SelectedIndexChanged;
UsersListBox.DataSource = GrpList;
UsersListBox.SelectedIndex = -1; // This optional line keeps the first item from being selected.
UsersListBox.SelectedIndexChanged += UsersListBox_SelectedIndexChanged;
Create a boolean flag to ignore the event.
private bool ignoreSelectedIndexChanged;
private void UsersListBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (ignoreSelectedIndexChanged) return;
...
}
...
ignoreSelectedIndexChanged = true;
UsersListBox.DataSource = GrpList;
UsersListBox.SelectedIndex = -1; // This optional line keeps the first item from being selected.
ignoreSelectedIndexChanged = false;
Well, it looks like that the first element is automatically selected after ListBox.DataSource is set. Other solutions are good, but they doesn't resolve the problem. This is how I did resolve the problem:
// Get the current selection mode
SelectionMode selectionMode = yourListBox.SelectionMode;
// Set the selection mode to none
yourListBox.SelectionMode = SelectionMode.None;
// Set a new DataSource
yourListBox.DataSource = yourList;
// Set back the original selection mode
yourListBox.SelectionMode = selectionMode;
i using the following, seems to work for me:
List<myClass> selectedItemsList = dataFromSomewhere
//Check if the selectedItemsList and listBox both contain items
if ((selectedItemsList.Count > 0) && (listBox.Items.Count > 0))
{
//If selectedItemsList does not contain the selected item at
//index 0 of the listBox then deselect it
if (!selectedItemsList.Contains(listBox.Items[0] as myClass))
{
//Detach the event so it is not called again when changing the selection
//otherwise you will get a Stack Overflow Exception
listBox.SelectedIndexChanged -= listBox_SelectedIndexChanged;
listBox.SetSelected(0, false);
listBox.SelectedIndexChanged += listBox_SelectedIndexChanged;
}
}
set IsSynchronizedWithCurrentItem="False" and Also SelectedIndex=-1 and every thing should work for you
If you just want to clear the selected value, you can use ClearSelected after you set the DataSource. But if you dont want the event to fire, then you'll have to use one of Joseph's methods.
Perhaps in DataSourceChanged you could check the state of SelectedIndex, if your lucky you could then just force SelectedIndex = -1.
I am coding Windows Forms application in C# and using CheckListBox Control.
How to check CheckListBox item with just single click?
I think you are looking for
CheckOnClick property
set it to true
Gets or sets a value indicating
whether the check box should be
toggled when an item is selected.
Set the property at Design Time in this way
or by code:
CheckedListBox.CheckOnClick = true;
I just finished working through an issue where I had set CheckOnClick to True via the designer, but the UI was still requiring a second click to check items. What I found is that for whatever reason, the designer file was not updating when I changed the value. To resolve, I went into the designer file and added a line
this.Product_Group_CheckedListBox.CheckOnClick = true;
After this, it worked as expected. Not sure why the designer didn't update, but maybe this workaround will help someone.
You can also use a check box exterior to the CheckListBox to check/uncheck all items. On the same form add a checkbox near the CheckedListBox and name it CkCheckAll. Add the Click event for the CheckBox (which I prefer to the CheckChanged event).
There is also a button (BtnAdd) next to the CheckedListBox which will add all checked items to a database table. It is only enabled when at least one item in the CheckedListBox is checked.
private void CkCheckAll_Click(object sender, EventArgs e)
{
CkCheckAll.Text = (CkCheckAll.Checked ? "Uncheck All" : "Check All");
int num = Cklst_List.Items.Count;
if (num > 0)
{
for (int i = 0; i < num; i++)
{
Cklst_List.SetItemChecked(i, CkCheckAll.Checked);
}
}
BtnAdd_Delete.Enabled = (Cklst_List.CheckedItems.Count > 0) ? true : false;
}
you can also check all by button click or click on checklist
private void checkedListBox1_Click(object sender, EventArgs e)
{
for (int i = 0; i < checkedListBox1.Items.Count; i++)
checkedListBox1.SetItemChecked(i, true);
}