Combobox: Handling Extraneous SelectedIndexChange Events

G

Gene Wirchenko

Dear C-Sharpies:

I am now digging into forms. Combobox gave me some trouble, but
I have mostly handled it. How well did I do though?

I have a combobox with Canadian provinces. When the selected
index changes, I want a message box to be displayed. Here is my code:

***** Start of Code Segment *****
private void cboProvince_SelectedIndexChanged(object
sender,EventArgs e)
{
if (!fInit)
MessageBox.Show(
"Index: "+cboProvince.SelectedIndex.ToString()+
"\nName: "+
((Province)cboProvince.SelectedItem).LongName.ToString()+
"\nAbbr: "+cboProvince.SelectedValue.ToString(),
"Province Selected");
}
***** End of Code Segment *****

Note the if. It seems that when a combobox is initialising,
SelectedIndexChanged fires with SelectedIndex being 0 (I think once
for each displayed item) then once with a SelectedIndex of -1. I do
not want a message box then.

To get rid of the extraneous message boxes, I declared a form
field
bool fInit=true;
At the end of the form's constructor, I set fInit to false. This does
work to suppress the extraneous message boxes. It does have the smell
of a kludge though.

Is there a better way to do it?

Sincerely,

Gene Wirchenko
 
R

Registered User

- snip -
I have a combobox with Canadian provinces. When the selected
index changes, I want a message box to be displayed. Here is my code: - snip -
Note the if. It seems that when a combobox is initialising,
SelectedIndexChanged fires with SelectedIndex being 0 (I think once
for each displayed item) then once with a SelectedIndex of -1. I do
not want a message box then.

To get rid of the extraneous message boxes, I declared a form
field
bool fInit=true;
At the end of the form's constructor, I set fInit to false. This does
work to suppress the extraneous message boxes. It does have the smell
of a kludge though.

Is there a better way to do it?

Load the ComboBox before assigning the event handler.

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// load combobox here
loadComboBox();
// then assign event handlers
comboBox1.SelectedIndexChanged += new
EventHandler(comboBox1_SelectedIndexChanged);
}
...
}

regards
A.G.
 
G

Gene Wirchenko

Load the ComboBox before assigning the event handler.

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// load combobox here
loadComboBox();
// then assign event handlers
comboBox1.SelectedIndexChanged += new
EventHandler(comboBox1_SelectedIndexChanged);
}
...
}

I do load the combobox
The event handler assignment is in InitializeComponent(), not in
the form constructor as you have it. Here is the code for that part
of InitializeComponent:

***** Start of Code Segment 1 *****
//
// cboProvince
//
this.cboProvince.FormattingEnabled = true;
this.cboProvince.Location = new System.Drawing.Point(14,209);
this.cboProvince.Name = "cboProvince";
this.cboProvince.Size = new System.Drawing.Size(144,43);
this.cboProvince.TabIndex = 8;
this.cboProvince.SelectedIndexChanged += new
System.EventHandler(this.cboProvince_SelectedIndexChanged);
***** End of Code Segment 1 *****

Here is the form constructor:

***** Start of Code Segment 2 *****
public frmPlayaround()
{
InitializeComponent();

// Province Combobox Setup
ListProvinces listProvinces=new ListProvinces();
cboProvince.DataSource=listProvinces;

// Set the long name as the property to be displayed and the
short
// name as the value to be returned when a row is selected.
Here
// these are properties; if we were binding to a database
table or
// query these could be column names.
cboProvince.DisplayMember="LongName";
cboProvince.ValueMember="ShortName";

// Bind the SelectedValueChanged event to our handler for it.
// cboProvince.SelectedValueChanged+=
// new EventHandler(CboProvince_SelectedValueChanged);

// Ensure the form opens with no rows selected.
cboProvince.ClearSelected();

fInit=false;
}
***** End of Code Segment 2 *****

This code is a lightly-modified version of some Microsoft code
for U.S. states.

My text warns of doing much editing of InitializeComponent(). How
do I break out the event handler setup as you have done?

Sincerely,

Gene Wirchenko
 
R

Registered User

I do load the combobox
The event handler assignment is in InitializeComponent(), not in
the form constructor as you have it. Here is the code for that part
of InitializeComponent: - snip -

My text warns of doing much editing of InitializeComponent(). How
do I break out the event handler setup as you have done?
Open the form in the IDE, select the combobox, right click the control
and select Properties from the context menu. In the property window
click the lightening bolt icon to view the control's events. Clear the
value of the SelectedIndexChanged property.

The event handler can be removed by editing the InitializeComponent
method but as your text indicates that is a bad practice.

regards
A.G.
 
G

Gene Wirchenko

[snip]
My text warns of doing much editing of InitializeComponent(). How
do I break out the event handler setup as you have done?
Open the form in the IDE, select the combobox, right click the control
and select Properties from the context menu. In the property window
click the lightening bolt icon to view the control's events. Clear the
value of the SelectedIndexChanged property.

But I want something done when that event fires!
The event handler can be removed by editing the InitializeComponent
method but as your text indicates that is a bad practice.

Sincerely,

Gene Wirchenko
 
G

Gene Wirchenko

[...]
My text warns of doing much editing of InitializeComponent(). How
do I break out the event handler setup as you have done?

You shouldn't do _any_ editing of the InitializeComponent() method.
Nor, really, any editing of any *.Designer.cs file. Not unless you know
exactly what you're doing, and even then only with great care.

But that's not what "A.G." is suggesting. And you "break out the event
handler setup" exactly as he's shown: instead of assigning the event
handler using the Designer, write the code explicitly in your object's
constructor, after the call to the InitializeComponent() method.

If you like, you can just cut/paste the code straight from the
*.Designer.cs file, moving the statement from the InitializeComponent()
method to the constructor. Doing so violates the recommendation to not
edit that file, but it's an edit that shouldn't cause the Designer any
trouble.

If you want to avoid actually editing the *.Designer.cs file, then just
manually add the code in the constructor to subscribe the event handler,
and then delete the event handler from the event using the Designer.

Let me see if I have this straight. Are you stating to:
1) create the method as normally,
2) move the generated event handler setup code from
InitializeComponent() to the constructor, and
3) delete the event being used from the control's properties?

If the order of event firing is of consequence, doing it the way
you suggest seems to me to mean that I might have to move all event
handler setup to outside of InitializeComponent(). Am I correct on
this?

ISTM that my bool fInit kludge is simpler and safer. Are there
any gotchas with doing it this way?

Sincerely,

Gene Wirchenko
 
J

Jeff Johnson

But I want something done when that event fires!

Here's what I understand from this thread:

1) You are loading a combo box during form construction or at some similarly
early point in the form's lifetime.

2) You have an event handler hooked up to this combo box (to handle
SelectedIndexChanged) which is firing during this initial loading sequence
and you don't want that.

3) You DO want the event handler to fire after the initial loading is
complete and the user is manipulating the combo box.

If I understand this correctly, then what you've been told is fine; you're
just not wrapping your head around it. People are telling you NOT to wire up
the event handler in the IDE because then it will be "live" before your
loading sequence is called and therefore it will fire. Instead, they are
telling you to delete it via the IDE and then to MANUALLY wire it up in code
AFTER you have loaded your combo box. Pete, "Reg," do I have this right?
Gene, do you get what I'm saying?

Now, as for your "kludge," in my experience, there are times that, at
run-time, you wish to ignore events if they are not generated by the proper
entity. What that boils down to for me is that I often want to react to the
event if it was caused by the user but ignore it if it was caused by code.

To that end I have a special nested class that looks like this:

#region ChangedByCodeMonitor
private enum ChangedByCodeAction
{
Acquire,
Clear,
Release
}

private static class ChangedByCodeMonitor
{
private static int _count = 0;

public static bool ChangedByCode
{
get { return _count > 0; }
}

public static void ChangeStatus(ChangedByCodeAction action)
{
switch (action)
{
case ChangedByCodeAction.Acquire:
_count++;
break;
case ChangedByCodeAction.Clear:
_count = 0;
break;
case ChangedByCodeAction.Release:
_count--;
if (_count < 0)
_count = 0;
break;
}
}
}
#endregion

Whenever I enter a block of code and I want subsequent code to behave
differently (which usually means I want it to not run), I call
ChangeStatus(ChangedByCodeAction.Acquire). This bumps the reference count.
As long as the count is > 0, the ChangedByCode property will return true.
And as you may guess, I check the ChangedByCode property to see if I should
do my special processing (i.e., ignoring) or not. When I'm out of the block
of code, I call ChangeStatus(ChangedByCodeAction.Release). Generally you
need as many Release calls as you made Acquire calls, but if you know you're
about to step completely out of your special mode you can just make a Clear
call and reset the class immediately.

I don't consider this to be a kludge at all, by the way. It's a glorified
flag, and flags are standard programming constructs.
 
J

Jeff Johnson

To that end I have a special nested class that looks like this:

I forgot to mention that I copy and paste this class into any class that
needs it. I could probably make a non-static version of it, but I just
haven't found a need to.
 
R

Registered User

[snip]
My text warns of doing much editing of InitializeComponent(). How
do I break out the event handler setup as you have done?
Open the form in the IDE, select the combobox, right click the control
and select Properties from the context menu. In the property window
click the lightening bolt icon to view the control's events. Clear the
value of the SelectedIndexChanged property.

But I want something done when that event fires!

Remember what you're trying to accomplish. The idea is to avoid firing
the SelectedIndexChanged event when the combobox' collection is
filled. With the event handler no longer being assigned within the
designer, the combobox can be loaded before the event handler is
assigned.

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// load combobox here
loadComboBox();
// then assign event handlers
comboBox1.SelectedIndexChanged += new
EventHandler(comboBox1_SelectedIndexChanged);
}
...
}

regards
A.G.
 
G

Gene Wirchenko

On Sat, 10 Sep 2011 05:24:11 -0400, Registered User

[snip]
Remember what you're trying to accomplish. The idea is to avoid firing
the SelectedIndexChanged event when the combobox' collection is
filled. With the event handler no longer being assigned within the
designer, the combobox can be loaded before the event handler is
assigned.

I just find it odd that that is the standard behaviour. It is
not as if loading a combobox is never done. My kludge is simpler, and
so I will stick with it. In case there was something I missed, I
posted.

[snip]

Sincerely,

Gene Wirchenko
 
R

Registered User

On Sat, 10 Sep 2011 05:24:11 -0400, Registered User

[snip]
Remember what you're trying to accomplish. The idea is to avoid firing
the SelectedIndexChanged event when the combobox' collection is
filled. With the event handler no longer being assigned within the
designer, the combobox can be loaded before the event handler is
assigned.

I just find it odd that that is the standard behaviour. It is
not as if loading a combobox is never done.

I made a mis-statement above. Actually when a combobox is loaded the
selected index remains as -1. The behavior you describe occurs
because somewhere in the initialization a selection is
programmatically assigned to the combobox. It is this selection which
causes the event to fire.
My kludge is simpler, and
so I will stick with it. In case there was something I missed, I
posted.
It's a work-around which only alleviates the symptoms. You might be
better served by re-evaluating the order in which controls and their
events are being initialized. Splitting initialization tasks between
the designer and a c'tor can cause problems like the one you're
experiencing.

regards
A.G.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top