[2005] ComboBox_SelectedIndexChanged Errors... how to prevent *properly*?

Denaes

Senior Contributor
Joined
Jun 10, 2003
Messages
956
Ok, I'm loading some values into a combobox (and other controls) and I have a SelectedIndexChanged event for this combobox. Now, in the event I need to evaluate either the Value or the Text.

Now what happens is that the SelectedIndexChanged event fires multiple times while the form loads and the data is fed into the combobox from the datasource.

Before there are any items fed into the combobox, the Text and value are Null, which causes errors on trying to evaluate.

I have two solutions to this problem:

1. Try statements around anything reading from the combobox.

2. Set a variable like isLoaded which is set to false and made true after the formLoad event is finished or the database is finished loading.

The first solution looks like a huge waste. I'm not sure how much wasted resources go into a Try statement, but doing it with every control seems kind of silly. Maybe thats the best way to go.

The second solution looks really silly. Performance wise, checking for a true value shouldn't be hard, but it seems rather messy to go throwing around this variable and making all these checks (there are other controls with the same problem)

As well, I get this problem with the RecordChanged event for the BindingSources as well. Again a Try statement might be the correct way to go on this.

Are there any other solutions to this problem?
 
penfold69 said:
Are you using .BeginUpdate and .EndUpdate when populating your controls?

B.

No, 2005 does the loading automatically. You just choose which bindingsource is distributing the information and link each control to it.

I even went into the form designer and there is no generated code with BeginUpdate or EndUpdate.
 
I know of three workarounds. The first seems to be the most common, but I don't like it as much:
1. Add a class level variable (bool). Set to true before binding, false when done. Check this variable in the event and exit if true (skips the SelectedIndexChanged event while loading/binding).

2. Put this at the top of the event:
if (!((Control)sender).ContainsFocus) return;

3. Put this at the top of the event:
if (this.ActiveControl != sender) return;

The second two work on loading. They basically say that if the user wasn't on that control, making the index change, then ignore it.

-ner
 
Nerseus said:
I know of three workarounds. The first seems to be the most common, but I don't like it as much:
1. Add a class level variable (bool). Set to true before binding, false when done. Check this variable in the event and exit if true (skips the SelectedIndexChanged event while loading/binding).

I've used this in the past, only in reverse setting the bool to true when done, but same principle. It's the messy solution I was hoping to avoid.

Nerseus said:
2. Put this at the top of the event:
if (!((Control)sender).ContainsFocus) return;

3. Put this at the top of the event:
if (this.ActiveControl != sender) return;

The second two work on loading. They basically say that if the user wasn't on that control, making the index change, then ignore it.

-ner

These second two were what I was hoping existed. They'll work for the comboboxes just fine and are a bit cleaner than having to create and set a variable.

Doesn't seem like it would work for the BindingSource as you don't give it focus. But I'm sure I could find a similar check for changing rows... probobly the navigation control.

Thanks much :)
 
Doesn't seem like it would work for the BindingSource as you don't give it focus

If I jump ahead, I believe you're saying that you manually move through the Binding collection yourself (next/prev) and you want the SelectedIndexChanged to fire then? If so, I'd recommend something a bit different - but first a question. What are you doing in the SelectedIndexChanged event? Normally you'd apply some filtering logic on a second combo when the first one changes it's value. If that's the case, then I'd recommend NOT handling the filtering on the control's event - it ties the changing of another value (or a filtering of another combo) to the UI related code. Rather, use a ColumnChanged event.

Here's a pseudo-code of what I guess you have right now:
1. User presses Next
2. Bump the Position property of the BindingContext (or similar)
3. The binding automatically changes a value in a combobox, which triggers SelectedIndexChanged
4. In SelectedIndexChanged you filter a second combo

Here's how I'd change this:
1. User presses Next
2. Bump the Position property of the BindingContext (or similar)
3. The binding automatically changes a value in a DataSet, which triggers ColumnChanged
4. In ColumnChanged you filter a second combo

This works off the data, not the controls. As a general rule, I try to put as little as possible in my events. The only code I usually need is "immediate update" code. For example, in a SelectedIndexChanged event I may take the value out of the control and update the dataset manually. I do this for user feedback so that when the user pulls down the combo and selects a value, it updates the dataset immediately and triggers any changes they want to see (calculated fees, other combos that filter, controls that disable, etc.).

-ner
 
Nerseus said:
If I jump ahead, I believe you're saying that you manually move through the Binding collection yourself (next/prev) and you want the SelectedIndexChanged to fire then?

If so, I'd recommend something a bit different - but first a question. What are you doing in the SelectedIndexChanged event?

The BindingSource position changes will be triggered by at least two different controls, a listbox of sorts to choose by name/id or something and a toolbar with standard navigation tools (next, prev, first, last, type in index).

What some comboboxes do (not all mind you) is modify other comboboxes, textboxes, etc.

For example, the easy one I was working with is a ComboBox with two choices: Social Security Number and Employer ID.

If you choose SSN, the mask for the textbox next to it changes to "000-00-0000" and if you choose Employer ID, the mask for the textbox becomes: "00-0000000".

So just for this one case, you'll need to catch things when the combobox is changed by the user and when it's changed by the BindingSource on a record change.

Also since the combobox the displaying SSN & EmpID choices isn't how it's displayed in the datatable (displayed S & E respectivly) I'll need a bit of code or link the choices somehow automatically. The later seems possible with how automated these binding sources are, but I havn't been able to link them properly yet. So I might need to select the index on each record change manually (grumble grumble).

I want everything related from a database done automatically, but I can't figure it all out yet. But thats another topic.

Nerseus said:
Normally you'd apply some filtering logic on a second combo when the first one changes it's value. If that's the case, then I'd recommend NOT handling the filtering on the control's event - it ties the changing of another value (or a filtering of another combo) to the UI related code. Rather, use a ColumnChanged event.

Here's a pseudo-code of what I guess you have right now:
1. User presses Next
2. Bump the Position property of the BindingContext (or similar)
3. The binding automatically changes a value in a combobox, which triggers SelectedIndexChanged
4. In SelectedIndexChanged you filter a second combo

Here's how I'd change this:
1. User presses Next
2. Bump the Position property of the BindingContext (or similar)
3. The binding automatically changes a value in a DataSet, which triggers ColumnChanged
4. In ColumnChanged you filter a second combo

Yes, I will have a few of these gems, where what you choose filters (or requerries) another control.

That does look very promising hitting up the dataset's columnchanged event...

I'll look into that. Does it fire whenever you change a row and the value is different or just whenever the column is specifically changed in the dataset?

Nerseus said:
This works off the data, not the controls. As a general rule, I try to put as little as possible in my events. The only code I usually need is "immediate update" code. For example, in a SelectedIndexChanged event I may take the value out of the control and update the dataset manually. I do this for user feedback so that when the user pulls down the combo and selects a value, it updates the dataset immediately and triggers any changes they want to see (calculated fees, other combos that filter, controls that disable, etc.).

-ner

Exactly what I'm going for.

I mean I want everything as automated and with as little code as possible. Just getting the hang of the new stuff they have in this 2005. I mean really, a lot of changes to ADO.Net. I think it's for the better. It seems a lot easier than I recall 2003 being.

Thank you for the advice. I'm going to check up on the RowChanged event and also a bit more into the BindingSource.
 
ColumnChanged will fire even if a value stays the same, but only when it's "committed" to the dataset. So if you're in a textbox, it won't fire on every keypress - not until you tab out (by default) and the change is sent from the textbox to the bound dataset. So if you have the name "bob" in a textbox and backspace over the last "b" then retype it, it should fire ColumnChanged again (I think - you'll have to test). If you have a value in a DataSet and set it to itself, that will also call a columnchanged event.

As for formatting data before it goes into the DataSet and back into the control, you'll want to use the Parse and Format events. In this post I have a one line snippet. You can find a lot more info on these events in the help, which also includes sample code. Since it fires an event, you could even change how you want to format values to/from your dataset.

-ner
 
Here is an update on my situation:

I have everything databound 100%. Everything is relational and I don't need to do a lick of code for that.

The BindingNavigator has buttons (and specific methods) on it for adding new records and deleting records and nothing for editing a record. From what I gather, you need to manually use the endEdit on the bindingsource to save an Edit/update. Just sort of annoying.

Now, I have a typed relational dataset based off of my MSQL database. The ColumnChanged is an Event for a Datatable and isn't available for a Dataset. I'm not sure how I could get at the datatable's events as the VS2005 event manager and directly by code doesn't allow that.

So currently I have the changes being made to other controls when the contents of a particular combobox is changed. This is because the combobox is changed on both the user changing it and when the datarow is changed via the BindingNavigator
 
Back
Top