I have a form that only contains an empty ComboBox.
I set the DataSource to an empty BindingList.
When I add something to the BindingList, it is selected and combobox1.SelectedIndex changes, but the event comboBox1_SelectedIndexChanged is not raised even tough it should in my opinion. Why is it not raised? When the single item is removed, the comboBox1_SelectedIndexChanged is fired correctly.
public partial class Form1 : Form
{
public Form1()
{
var test_ = new BindingList<int>();
InitializeComponent();
comboBox1.DataSource = test_;
Console.WriteLine(comboBox1.SelectedIndex); // -1
test_.Add(42); // BUG? no comboBox1_SelectedIndexChanged -> 0
Console.WriteLine(comboBox1.SelectedIndex); // 0
test_.Remove(42); // comboBox1_SelectedIndexChanged -> -1
Console.WriteLine(comboBox1.SelectedIndex); // -1
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Console.WriteLine("index changed " + comboBox1.SelectedIndex);
}
}
You are not correct with your logic.
comboBox1.SelectedIndex is -1 doesn't mean that you have an item selected in -1 position!
It means there is no item selected in comboBox1.
Adding an item, the SelectedIndex becomes 0. No change on the selection has happened because there was no item been selected in the first place (SelectedIndex = -1).
One way to work around the bug is to tap into the ListChanged event of the BindingList collection you are using:
var test_ = new BindingList<int>();
comboBox1.DataSource = test_;
test_.ListChanged += (sender, e) => {
if (e.ListChangedType == ListChangedType.ItemAdded && test_.Count == 1) {
comboBox1_SelectedIndexChanged(comboBox1, EventArgs.Empty);
}
};
test_.Add(42);
Related
I have two ComboBoxs 1st with CmdGuarantor and the 2nd with CmdGuarantorClass as names.
CmdGuarantor has a list of 7 items:
NSSF Private MOH Army IS GS UNHCR while CmdGuranatorClass has 1st Class 2nd Class 3rd Class in its items list.
I want to disable CmdGuarantorClass whenever CmdGuarator.SelectedItem.ToString() == "Private" || CmdGuarator.SelectedItem.ToString() == "UNCHR"
how can I accomplish that?
P.S.: I tried using the EnableChanged event on CmdGuarantorClass ComboBox using this method
private void ComboBox2_EnabledChanged(object sender, EventArgs e)
{
if (CmdGuarantor.SelectedItem.ToString() == "Private" || CmdGuarantor.SelectedItem.ToString() == "UNCHR")
{
CmdGuarantorClass.Enabled = false;
}
else CmdGuarantorClass.Enabled = true;
}
but with no luck.
thank you in advance.
You should be listening for the SelectedIndexChanged event, not the EnabledChanged event. EnabledChanged is only raised when the Enabled property is changed.
Assuming the datagridview tag is a mistake and you are talking about two different winform ComboBoxes… then… I suggest you wire up the first combo boxes SelectedIndexChanged event.
This event will fire when the user changes the selection in the combo box. In that event you can check the combo box values as you have done and then set the other combo boxes Enabled property as needed. Something like…
private void comboGuarantor_SelectedIndexChanged(object sender, EventArgs e) {
if (comboGuarantor.SelectedItem.ToString() == "Private" ||
comboGuarantor.SelectedItem.ToString() == "UNCHR") {
comboGuarantorClass.SelectedIndex = -1;
comboGuarantorClass.Enabled = false;
}
else {
comboGuarantorClass.Enabled = true;
}
}
I'd like to have a basic ListBox with associated Add and Delete buttons. The Delete button be enabled if and only if there is a selection in the ListBox, and the Add button should append an item to the end of the ListBox.
My first attempt was to just make a List<string> items field, like so:
public partial class Form1 : Form {
public Form1 {
InitializeComponent();
this.listBox1.DataSource = this.items;
}
private void addButton_Click(object sender, EventArgs e) {
this.items.Add("item {this.items.Count + 1}");
}
private void deleteButton_Click(object sender, EventArgs e) {
this.items.RemoveAt(this.listBox1.SelectedIndex);
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e) {
this.deleteButton.enabled = (this.listBox1.SelectedIndex != -1);
}
List<string> items = new List<string>();
}
But, it turns out that the ListBox does not seem to update automatically as the items in the list change. Here's a related question about it.
Most advice suggested just running listBox1.DataSource = null; this.listBox1.DataSource = this.items; every time I updated the list, but that to me defeats the purpose of binding.
Another answer suggested using an ObservableCollection for my list's data type, but that did nothing different.
Finally I found the BindingList class and it seemed to do what I want; the ListBox automatically reflects the contents of the items field. (If there is another solution that is actually a best practice and not some piece of cargo cult folklore pasted around the Internet, please let me know.)
However, when the first item I add to items appears in the ListBox, the row becomes selected. And I can see that listBox1.SelectedIndex changes from -1 to 0. But, the listBox1_SelectedIndexChanged event does not fire. Hence, the state of the Delete button doesn't get updated properly.
(As an aside, this UI pattern is extremely commonplace, and it seems disastrous that there's so much conflicting information on implementing such a simple thing.)
I also ran into the same problem. Since this was the first question I came across, maybe my answer will save someone some time.
When DataSource is assigned an empty BindingList and items are added to that BindingList later, like so:
private BindingList<MyType> _myList = new BindingList<MyType>();
public MyForm()
{
listBox1.DataSource = _myList;
}
private void LoadData()
{
foreach (MyType item in GetDataFromSomewhere())
{
_myList.Add(item);
}
}
then after adding the first item, that will be selected in the ListBox, and listBox1.SelectedIndex will be 0 - but the SelectedIndexChanged event will not have been fired.
Looks like this could be a bug in ListBox as suggested here and here.
A workaround could be to derive from ListBox as Juan did, or just check if the BindingList contains any items before adding:
bool isFirstItem = (0 == _myList.Count);
_myList.Add(item);
if (isFirstItem)
{
// Your choice: set listBox1.SelectedIndex to -1 and back to 0,
// or call the event handler yourself.
}
Try manually selecting the last element, instead of relying on the default behavior. This will trigger the event.
public partial class Form1 : Form
{
BindingList<string> items = new BindingList<string>();
public Form1()
{
InitializeComponent();
this.listBox1.DataSource = items;
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
this.deleteButton.Enabled = (this.listBox1.SelectedIndex != -1);
}
private void addButton_Click(object sender, EventArgs e)
{
this.items.Add($"item {this.items.Count + 1}");
this.listBox1.SelectedIndex = -1;
this.listBox1.SelectedIndex = this.listBox1.Items.Count - 1;
}
private void deleteButton_Click(object sender, EventArgs e)
{
this.items.RemoveAt(this.listBox1.SelectedIndex);
}
}
I have a combobox for wich I am using the SelectIndexChanged event to capture both user and programmatically changes.
Clearing and reloading the list bound to the combobox will fire the eventhandler with index -1 naturally.
But then with selectedindex=-1
combobox1.SelectedIndex = 0 ; // will NOT fire the event.
but
combobox1.SelectedIndex = 1 ; // or higher number WILL fire the event.
In both cases I AM programmatically changing the selextedindex and expect the event to be fired.
I verified the behavior in a simple form.
namespace cmbTest
{
public partial class Form1 : Form
{
private BindingList<string> items = new BindingList<string>();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
comboBox1.DataSource = items;
loadItems();
}
private void loadItems()
{
items.Add("chair");
items.Add("table");
items.Add("coffemug");
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
MessageBox.Show("Fired with selected item index:" + comboBox1.SelectedIndex);
}
private void button1_Click(object sender, EventArgs e)
{
int index = comboBox1.SelectedIndex;
// Clear and reload items for whatever reason.
items.Clear();
loadItems();
// try select old item index; if it doesnt exists, leave it.
try { comboBox1.SelectedIndex = index; }
catch { }
}
}
}
The form has a combobox1 and a button1.
EDIT for clarity (I hope):
Run program
Select 'chair'. Message "Fired with selected item index:0"
Hit button. Message "Fired with selected item index:-1"
Select 'table'. Message "Fired with selected item index:1"
Hit button. Messages "Fired with selected item index:-1" AND
"Fired with selected item index:1".
I expect to get two messages when hitting button when "chair" is selected too, since I programmatically changes the index to 0.
So, why is this not working as I expect it to do, and what will be an acceptable workaround?
When your first item is added to the items collection the index is automatically changed to 0. That's why when your previous index is saved as 0, and then you set it again using this line comboBox1.SelectedIndex = index;, the index is not changed. That's why the event is not fired.
Looking at the source code for the ComboBox, the event is not fired in 2 cases : Either an expcetion is thrown, or the index is set to the same value that it is was.
If you really want some hack around this, you can do it this way:
int index = comboBox1.SelectedIndex;
// Clear and reload items for whatever reason.
items.Clear();
loadItems();
if(comboBox1.SelectedIndex == index)
{
comboBox1.SelectedIndexChanged -= comboBox1_SelectedIndexChanged;
comboBox1.SelectedIndex = -1;
comboBox1.SelectedIndexChanged += comboBox1_SelectedIndexChanged;
}
// try select old item index; if it doesnt exists, leave it.
try { comboBox1.SelectedIndex = index; }
catch
{
}
I have a problem with ListBox in c#(winforms). This ListBox contains the list of files(only pictures) in a specified directory like this:
private ListBox FileList = new ListBox();
private List<string> Directories = new List<string>
{
#"some path",
#"some other path",
#"..."
};
private List<string> Pictures;
private int selectedDirectory = 0;
public int SelectedDirectory
{
get { return selectedDirectory; }
set
{
//the list of pictures is returned correctly
Pictures = GetPictures(Directories[value]);
selectedDirectory = value;
EventHandler handler = this.DirectoryChanged;
// noone who subscribed to the event
// changes FileList.SelectedItem or FileList.SelectedIndex
if(handler != null)
handler(this, EventArgs.Empty);
this.SelectedPicture = Pictures.Count == 0 ? -1 : 0;
}
this.DirectoryChanged += (sender, e) =>
{
FileList.Items.Clear();
FileList.Items.AddRange(Pictures);
};
private int selectedPicture
public int SelectedPicture
{
get { return selectedPicture; }
set
{
selectedPicture = value;
if(value != -1)
PictureBox1.Load(Pictures[value]);
FileList.SelectedIndex = value;
}
}
// after this method returns, FileList.SelectedIndex changes to a random value depending on what Directory was selected before the change
private void MainFormKeyDown(object sender, KeyEventArgs e)
{
if(e.KeyCode == Keys.NumPad5)
SelectedDirectory --;
if(e.KeyCode == Keys.NumPad2)
SelectedDirectory ++;
} // the value of SelectedIndex changes after leaving this exact line
I tried to debug it and the result was that from setting the value of FileList.SelectedIndex in this.SelectedPicture it remained 0 until MainFormKeyDown returned. EDIT: I forgot to mention that at that point FileList.SelectedIndex changes to a random value instead of 0 or -1 as it should.
What can cause this kind of behaviour and how can I solve it?
I have checked if I change the value anywhere else in the code or in any event subscription but not.
I also tried using ListView, but the result remained the same.
Now I'm out of ideas.
If the listbox has focus it could be that the KeyDown event fires the user lets go of the key and the KeyUp event fires and the list box changes its selectedIndex. Did you try changing to the KeyUp event and setting
e.Handled = true;
Handled Property
If i hold down the key KeyDown will fire a few times. You can put a Debug.WriteLine to see this if you hold down a key it will fire a few times. Another reason to use KeyUp.
I have a ComboBox with several items. I have added an event handler to the SelectedIndexChanged event. Suppose, the list has two items, A and B. If the currently selected item is A and the user changes it to B then the event is fired and that's OK. However, if the user clicks on the ComboBox and clicks on A again (meaning the item has not actually changed) the event is still fired. I would like for the event to only be fired if the item has definitely changed, or something which will allow me to accomplish this.
If you don't plan change ComboBox selection diagrammatically try use SelectionChangeComitted.
Better to incapsulate this logic in class derived from ComboBox (ComboBoxEx in my example)
private class ComboBoxEx : System.Windows.Forms.ComboBox
{
Int32 _lastIndex = -1;
protected override void OnSelectedIndexChanged(System.EventArgs e)
{
if (_lastIndex == -1)
{
_lastIndex = this.SelectedIndex;
base.OnSelectedIndexChanged(e);
}
else
if (_lastIndex != this.SelectedIndex)
{
base.OnSelectedIndexChanged(e);
_lastIndex = this.SelectedIndex;
}
}
}
And use it like this:
public Form1()
{
var combobox = new ComboBoxEx() { DropDownStyle = ComboBoxStyle.DropDownList };
combobox.Items.Add("Item 1");
combobox.Items.Add("Item 2");
combobox.Items.Add("Item 3");
this.Controls.Add(combobox);
combobox.SelectedIndexChanged += OnIndexChanged;
InitializeComponent();
}
private void OnIndexChanged(object sender, EventArgs e)
{
MessageBox.Show("Index changed");
}
Simply adding the event handler to the TextChanged event instead of SelectedIndexChanged has solved my problem. This works in my case because I can be sure that no two items in the ComboBox (which is a dropdown list) will have the same text.
I think this is what you mean:
int intIndex; //Global Variable
//In your ComboBox_SelectedIndex Changed event
if(myComboBox.SelectedIndex != intIndex)
{
//your code
intIndex = myComboBox.SelectedIndex;
}
One solution would be to databind the combobox to a property in a model class. The model should then implement the interface INotifyPropertyChanged (and firing it correctly, so only when a value is changed). You can then use the PropertyChanged event to handle a change in the selection of the control, and this event would only be fired when an actual change happened.
You can define a int variable with the SelectedIndex of your ComboBox, then you can check if the variable has the same value as the index.
If yes, don't do anything, else do the stuff.
int lastIndex = myComboBox.SelectedIndex;
Then in the SelectedIndexChangedEvent:
if(lastIndex != myComboBox.SelectedIndex){
//do something
}
in your InitializeComponent(), put this in your combobox
private void InitializeComponent()
{
///combobox
///This line will triger if the combobox has changed
this.combobox.SelectedIndexChanged += System.EventHandler(this.comboboxChanged);
}
Then in your main method create the combobox method like
private string previousValue;
Private void comboboxChanged(object sender, EventArgs e)
{
if (combobox.Text == "A" && previousValue == "A")
{
//Do nothing
previousValue = "A";
}
else if (combobox.Text == "B" && previousValue == "B")
{
//Do Nothing
previousValue = "B";
}
else if (combobox.Text == "A")
{
//Do Something
previousValue = "A";
}
else if (combobox.Text == "B")
{
//Do Something
previousValue = "B";
}
}