In my application I'd like to give the user the option to delete items from a ComboBox control by the press of the Del button. I have managed to effectively do the first part, but what I can't seem to accomplish is actually delete an item (especially when it's the last one remaining) from the control without the whole application crashing. Why is that? Here's my code. It always throws an ArgumentOutOfRange exception when the last item is removed and contains the message InvalidArgument=Value of '0' is not valid for 'index'.
private void cboSource_KeyDown(object sender, KeyEventArgs e)
{
deleteItem(cboSource, e);
}
private void cboTarget_KeyDown(object sender, KeyEventArgs e)
{
deleteItem(cboTarget, e);
}
private void deleteItem(ComboBox comboBox, KeyEventArgs e)
{
if (e.KeyCode == Keys.Delete)
{
comboBox.Items.Remove(comboBox.SelectedItem);
}
}
I have two ComboBoxes and a function to delete the items, but I guess it won't be much of a problem now, right?
EDIT: The crash usually happens when no items are left and the control loses focus.
I solved this problem by closing the drop-down list, before remove the last element.
private void cmbUserLogin_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Delete && cmbUserLogin.DroppedDown)
{
if (cmbUserLogin.Items.Count > 0)
{
int currentItem = cmbUserLogin.SelectedIndex;
if (currentItem >= 0)
{
// todo
if (cmbUserLogin.Items.Count == 1)
cmbUserLogin.DroppedDown = false;
cmbUserLogin.Items.RemoveAt(currentItem);
}
}
e.Handled = true;
}
}
You might add a default item to the combox which will never be deleted and likely to the first item or you can reinitialize an empty item list to avoid crash.
Here's a fix I figured out by messing with the code a bit. I'm not entirely sure as to why it works, but it works. This by no means is a viable solution for me, though, as it still is a "work around", in the sense that I'm working around the problem and am not actually solving it. Until someone finds a better way, I guess I will stick to this. Here is the code:
private void deleteItem(ComboBox comboBox, KeyEventArgs e)
{
if (e.KeyCode == Keys.Delete)
{
if (comboBox.Items.Count == 1)
{
comboBox.Items.Clear();
return;
}
comboBox.Items.Remove(comboBox.SelectedItem);
e.Handled = true; // I do not think this really contributes anything in this case.
}
}
I created a small sample in WPF with your code and it doesn't crash. From what I see you are not using the WPF. Still, to make you feel better - your code should be work out of the box. But there are a couple of things that I see from your post:
1.The error that you are getting ArgumentOutOfRange happens when you try to manipulate an item in the list using the index that cannot get you that item. So if there are 3 items in the list and you try to access list.Items[3], you'll get that error.
2.InvalidArgument=Value of '0' is not valid for 'index' tells me that you are not only accessing the index that doesn't exist, but you are accessing the element with index zero, assuming that the combo box list has at least one element in it. So what's happening is that you are somehow attempting to delete the last item in the combo box, when the combo box is already empty. Hence the error.
To solve that:
//before delete, check if combo box has items
if(comobBox.Items.Count > 0)
{
comboBox.Items.Remove(comboBox.SelectedItem);
//edit: this might help with that last crash (from comments)
if(comboBox.Items.Count == 0)
comboBox.SelectedItem = null;
}
That's not a bad practice to check especially if there are multiple entry points to the same code, not even mentioning multi-threaded possibilities..
Also, it might not be a bad idea to check whether the selected item is not null as well.
Related
So I have a bunch of CheckBoxes on a Form with a Button at the bottom that I want to click and it Executes the Methods for those Checked CheckBoxes.
Each CheckBox will use its own Method.
Seems Like a Common/Simple thing to do. Yet I have found nothing too helpful searching around regarding dealing with multiple CheckBoxes on one Event.
I am relatively new to programming and I'm learning on my own.
Idk if there's a simple for or foreach loop I can use in conjunction with something else to make this simpler, but I don't even know a long way to make it work...
All I can Come up with is a bunch of if statements under button click event to test if checked and if so run method. However, this seems like the wrong way to do this.
private void btnExecutedSelected_Click(object sender, EventArgs e)
{
if (ChkBox_Test.Checked == true)
{
ClassName.MethodName();
}
//Then an if statement for each CheckBox
}
Any Help Much Appreciated
There are several ways to approach this, and usually the best ways are a little more advanced, but this is a good opportunity to learn some basics about delegates, which are like a mix between variables and methods/functions. For now, think of them like a card in Monopoly that tells you to go directly to jail. The card itself isn't really anything except an instruction that you pick up. When you USE / read the card, you have to follow the resulting instruction, which is to go to jail. So delegates are sort of like those cards.
To get an idea of how they work, create a new Winforms app, drop 4 checkboxes on the form and a button. Don't worry about renaming them. Then add in the following code:
// This defines the "monopoly cards"
// Community Chest cards give or take money, so we'll expect an int to be returned
public delegate int CommunityChestCard();
// Chance cards just do things without any return values
public delegate void ChanceCard();
private void Form1_Load(object sender, EventArgs e)
{
checkBox1.Tag = new ChanceCard(GoDirectlyToJail);
checkBox2.Tag = new ChanceCard(AdvanceToGo);
checkBox3.Tag = new CommunityChestCard(WinBeautyContest);
checkBox4.Tag = new CommunityChestCard(PayDoctorsFees);
}
private void GoDirectlyToJail()
{
MessageBox.Show("You went to jail!");
}
private void AdvanceToGo()
{
MessageBox.Show("You advanced to Go!");
}
private int WinBeautyContest()
{
MessageBox.Show("You won $20 in a beauty contest!");
return 20;
}
private int PayDoctorsFees()
{
MessageBox.Show("You had to pay $50 in doctor's fees!");
return -50;
}
// Now when we click the button, we'll loop through our checkboxes,
// see which ones were checked, and then execute the methods defined
// in the associated chance/communitychest cards.
private void button1_Click(object sender, EventArgs e)
{
// this.Controls is a collection of the child controls on the current form
foreach(Control ctl in this.Controls)
{
// See if the control is a CheckBox
if(ctl is CheckBox)
{
// It is - let's cast it for easier coding...
CheckBox chk = (CheckBox)ctl;
// Is it checked?
if (chk.Checked)
{
// Yep! Does it have a value in its Tag?
if (chk.Tag != null)
{
if(chk.Tag is CommunityChestCard)
{
CommunityChestCard ccCard = (CommunityChestCard)chk.Tag;
// Call the function on the card and get the result
int adjustMoneyByAmount = ccCard();
}
else if(chk.Tag is ChanceCard)
{
ChanceCard cCard = (ChanceCard)chk.Tag;
// Call the function on the card
cCard();
}
}
}
}
}
}
Now, just some words of warning - I used the Tag property as a quick fix to cut down on extra coding for illustration purposes. As you get better with code and custom/extended controls, you might want to have your own properly-typed properties for these kinds of things. Using Tag is NOT an elegant solution.
If you run that code as described, you should be able to check some of the checkboxes and click the button and see the resulting functions being executed.
I'd also suggest not just looping through all the controls on a form and checking to see if they're checkboxes. You seemed to have some trouble with looping through controls, so that approach was there as an example. Checkboxes can be grouped together in many different ways. You might consider a List object and adding your checkboxes to that list. That way, you can simply loop through that List later and you'll know exactly which controls you're dealing with (no ugly casting or checking to see if a control is a checkbox).
You can do something like this:
private void CheckBoxOperations(Control parentControl)
{
foreach (Control c in parentControl.Controls)
{
if (c is CheckBox)
{
if (((CheckBox)c).Checked)
{
//DoSomething
}
}
if (c.HasChildren)
{
CheckBoxOperations(c);
}
}
}
private void btnExecutedSelected_Click(object sender, EventArgs e)
{
CheckBoxOperations(this);
}
I'm in a situation with a multiple select ListView where a maximum of three items may be selected. I currently have following code
private void grassListView_SelectedIndexChanged(object sender, EventArgs e)
{
string landType = this.grassLandTypeComboBox.Text;
if (this.grassListView.SelectedIndices.Count < 4)
{
ArrayList selectedGrassTextures = (ArrayList)((Hashtable)this.paintGrass[landType])["textures"];
selectedGrassTextures.Clear();
foreach (ListViewItem listViewItem in this.grassListView.SelectedItems)
{
selectedGrassTextures.Add(listViewItem.Text);
}
}
else
{
MessageBox.Show("You cannot have more than 3 grasses selected for any given attribute type");
}
}
this code works and in the end my selectedGrassTextures HashTable has only three elements. However the GUI still shows the element as (selected/focused?). So to the user it seems that it is still selected. So I like to prevent this, is there anything I can use to either find the last element clicked on and put the selection of it or the focus. Another way would be if there is an event Before SelectedIndexChanged that I can move my < 4 check into. Is there such an event, I thought ListView.SelectedIndexChanging, but in my IDE it shows up as not existing. I'm using visual studio express and framework 3.5, can't use 4.5 for what I'm doing.
Put this in the ItemSelectionChanged event:
private void listView1_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
if (listView1.SelectedItems.Count > 4) e.Item.Selected = false;
}
background
I have a c# application written that interacts with an SQL database. a recent feature request was to allow for different types of authentication (3 types). I originally decided to use 3 radio buttons (one for each auth option) for this and 2 textboxes (username/password). this worked fine and all the background code works fine but they have now requested that when they use SSPI auth (requires no extra input from user) that i gray out the textboxes so information cannot be entered and when one of the other two options is chosen, to allow the boxes to be editable again. To make this cleaner i now have a single combobox with 3 items (auth) and 2 textboxes (un/pw).
question
how do i have the application listen for changes to the combobox before the user clicks run? I have always used a button as a catalyst event and have not had to do this before. I have seen a few examples where i can use the condition (if selected index is equal to x) do blah, but it seems to require my start button to be pressed anyways and not a workable solution. i have also found this example C# -comboBox Selected IndexChange that i do not quite understand and i believe requires a 2 boxes to be made but i dont know why.
sudo code
if ((combobox item is not selected) or (combobox selection == indexitem1))
{
//then keep textboxes read only
}
else
{
//change textbox to editable
}
request
i need this listener to be able to tell when the combobox selection is any of the 3 choices and moves between any of them and will correctly change the textboxes to reflect the current selection regardless of previous state to current state.
Any help is greatly appreciated. Links, code, comments, questions. everything helps me see something i havent yet or helps me search for better answers. Thanks!
solution
i just figured this out
private void AuthSelect_SelectedIndexChanged(object sender, EventArgs e)
{
//listen if combobox selection is changed
if ((AuthSelect.SelectedIndex == 0) || (AuthSelect.SelectedIndex == -1))
{
userName.ReadOnly = true;
password.ReadOnly = true;
}
else
{
userName.ReadOnly = false;
password.ReadOnly = false;
}
}
private void AuthSelect_SelectedIndexChanged(object sender, EventArgs e)
{
//listen if combobox selection is changed
if ((AuthSelect.SelectedIndex == 0) || (AuthSelect.SelectedIndex == -1))
{
userName.ReadOnly = true;
password.ReadOnly = true;
}
else
{
userName.ReadOnly = false;
password.ReadOnly = false;
}
}
This question already has answers here:
Which CheckedListBox event triggers after a item is checked?
(18 answers)
Closed 8 years ago.
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (checkedListBox1.GetItemChecked(i) == false)
{
...
}
else
{
...
}
}
For some reason when the code above executes, it does the opposite of what I'd like it to do. When an item is checked for the first time it doesn't do anything, however, when it is unchecked it does what's in the else statement (again, opposite of what it's supposed to do). Is there some property that I'm forgetting about here?
You should use e.NewValue instead of checkedListBox1.GetItemChecked(i). The reason being that checkedListBox1.GetItemChecked is a cached state, because the ItemCheck event occurs before the internal value is updated.
This'll work as you are expecting:
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (e.NewValue == CheckState.Checked)
{
...
}
else
{
...
}
}
Secondly, as to why the first time you click the checkbox, it doesn't react: that's because the CheckedListBox object requires the item to be highlighted before changing the checkbox value through mouse clicks.
In order to achieve a similar effect, set checkedListBox1.CheckOnClick = true. This will cause the checkbox to become checked whenever clicking on the checkbox or on the list item itself.
MSDN indicates that the check state isn't updated in the ItemCheck event until after it finishes. You're probably looking for e.NewValue.
I have 2 ListView controls here, let's say Listview1 and Listview2, respectively. What I would like to achieve is that I want the first item in Listview2 to be selected & highlighted whenever Listview1's SelectionChanged event is triggered.
I have tried to use the following line of code to make it happened but I guess it's not correct.
private void ListView1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Listview2.SelectedIndex = 0;
}
The first item in Listview2 is still not selected & highlighted. Can anyone help? Thanks very much in advance.
Edit:
That line is correct. It didn't work because I placed it before the line of code that was doing dynamic loading. No wonder.... :)
I tried with this code and its running fine.
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
//listView.Focus();
listView2.Items[0].Selected = true;
}
catch { }
}
But after running its output when i select the first item in "listView1" the item of "listView2" is selected but you can not see it because the focus on Listview1. When you click on the listView2 then you will see a blink of selected item. Ithink there is no way to focus on two listview at the same time. When you will uncomment the "listView.Focus()" then you will see that the selected item is highlighted.
Try
private void ListView1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
((ListViewItem)Listview2.Items[0].Selected) = true;
}
I cannot see any problem with your code. I think you cannot see the highlighting because ListView2 items are not focused. Make the item Focused and see.