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;
}
Related
I am making a Windows Forms application with algorithms for school and I want to add some nice functionality to display that the algorithm is working well. One of those things is that when the user selects an item in one listbox, the items that are part of that one item get automatically selected in another listbox. This is done by the application.
I would like it if the user could not select another item in the listbox that is automatically monitored, but enabled = false sets the color to gray which makes the text invisible when an item is automatically selected.
Is there any other way to achieve this?
What you could do is
When the program selects an entry in the second list, set a flag
When an entry in the list is selected, read that flag
If the flag is not set, unselect the item
Unset the flag
Code wise, this equates to something like the following (please note that I would not write code like this in a real-world szenario, but to get the gist of it, it should suffice)
private bool _valueIsSetProgrammatically = false;
private void listBox1_SelectedValueChanged(object sender, EventArgs e)
{
this._valueIsSetProgrammatically = true;
this.listBox2.SelectedItem = this.listBox1.SelectedItem;
}
private void listBox2_SelectedValueChanged(object sender, EventArgs e)
{
if (!this._valueIsSetProgrammatically)
{
this.listBox2.SelectedItem = null;
}
this._valueIsSetProgrammatically = false;
}
Please note that this snippet unselects the second listbox. If you'd like to retain the selected item, you could change the second method to
private void listBox2_SelectedValueChanged(object sender, EventArgs e)
{
if (!this._valueIsSetProgrammatically)
{
this.listBox2.SelectedItem = this.listBox1.SelectedItem;
}
this._valueIsSetProgrammatically = false;
}
(Technically the flag is not needed in this case, you could simply set the SelectedItem of listBox2 to the SelectedItem of listBox1.)
I'm developing a C# Windows Form program and I've implemented a DataGridView on it. Now, after setting the data source, when I click the top left button on the datagridview, it selects all rows, just like Microsoft Excel. However, I dynamically hide and show rows on it, and after clicking that button I realized that it also selects the invisible ones. I don't want to implement "SelectionChanged" event because I constantly select some rows and normally I can't select the invisible ones. Only this button selects it. I'm looking for an event like this:
datagridView1_SelectAllClicked(object sender, EventArgs e)
{
// do stuff
}
Something like this will also work since I don't have to check all selections:
dataGridView1_SelectionChanged(object sender, EventArgs e)
{
if(dataGridView1.IsSelectAllCells())
{
// do stuff
}
}
If I have to, I will add the event to deselect the invisible rows, but I prefer some solution like the first one. Any advices? Thanks in advance.
Edit: I'm checking "dataGridView1.SelectedRows" property on button clicks only, not after the selection was made. So, some function that I can implement to button click events will also solve my problem.
The DataGridView class provides the AreAllCellsSelected methode:
Returns a value indicating whether all the DataGridView cells are currently selected. (MSDN)
With that we can get a solution like your second one:
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
DataGridView view = sender as DataGridView;
if (view.AreAllCellsSelected(true))
{
foreach (DataGridViewRow row in view.Rows)
{
//deselect all invisible rows
if (!row.Visible)
row.Selected = false;
}
}
}
I've managed to solve it finally, the solution was simpler than I expected.
private void RemoveInvisibleSelection()
{
if (dataGridView1.SelectedRows.Count == dataGridView1.Rows.Count)
{
for (int i = 0; i < dataGridView1.SelectedRows.Count; i++)
if (!dataGridView1.SelectedRows[i].Visible)
dataGridView1.SelectedRows[i--].Selected = false; // decreased the index value since SelectedRows property loses an object
}
}
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);
}
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.
I've got a combobox that opens a new form window with a datagridview, and I want the users to choose the items through that datagridview rather than through the combobox. I've got this code to achieve that:
private void comboBox1_DropDown(object sender, EventArgs e)
{
valSel.incBox = (ComboBox)sender;
valSel.Show();
if (this.comboBox1.DroppedDown)
{
MessageBox.Show("test");
SendMessage(this.comboBox1.Handle, CB_SHOWDROPDOWN, 0, 0);
}
}
As you see I'm also trying to hide the dropdown of the combobox but it isn't working. I assume it's because the combobox hasn't actually "dropped down" yet, so that part of the code is never run.
Is there an event or something I can cell when the combobox has fully "dropped down" so i can send the message to close it again?
You should be able to simply set the height of the ComboBox to something really small. Last time I looked at it, this determined the height of the popup part (the actual height of the control is determined by the UI/font size).
The more elegant way, however, would be using a custom control that just mimics the appearance of dropdown boxes (I'm rather sure that can be done some easy way).
In comboBox1.Enter set the focus to a different control if condition is met.
private void comboBox1_Enter(object sender, EventArgs e)
{
if (comboBox1.Items.Count < 1)
{
comboBox1.DroppedDown = false;
comboBox2.Focus();
MessageBox.Show("Select a list first");
comboBox2.DroppedDown = true;
}
}
1) create a KeyPress event on ComboBox from the properties.
2) write code
private void cmbClientId_KeyPress(object sender, KeyPressEventArgs e)
{
((ComboBox)sender).DroppedDown = false;
}