Stopping Multiple Events from Looping - c#

I have a function that is intended to allow my user to move a set of objects (an airport) to a new location.
On the dialog that for this I have four controls - two textboxes that represent coordinates and two numeric up downs one for distance and one for bearing. The user can choose to enter either a distance and bearing or a set of coordinates. This is done via a checkbox and the non used set is disabled. So that the user has feedback I would like to update the disabled pair values as the user enters the enabled pair. If they are entering coordinates then the distance and bearing updates and vice versa.
My method for doing this is to respond to the TextChanged event of each object to update the others. Of course this creates one event firing another and back again as the other control updates. It does not create an infinite loop as far as I can tell but it does mess up the caret position in the active boxes.
I have tried some approaches including including trying to disable events based on which boxes are active or using some bool flags. I also tried using a timer rather than events. It is all very messy and no approach seems to work. I would like to know if there is a better way.
Thanks

Assuming you are using Windows Forms:
// this.ckPosition.CheckedChanged += new System.EventHandler(this.ckPosition_CheckedChanged);
private void ckPosition_CheckedChanged(object sender, EventArgs e)
{
txtX.Enabled = txtY.Enabled = ckPosition.Checked;
numBearing.Enabled = numDistance.Enabled = !ckPosition.Checked;
}
// this.txtX.TextChanged += new System.EventHandler(this.Position_TextChanged);
// this.txtY.TextChanged += new System.EventHandler(this.Position_TextChanged);
private void Position_TextChanged(object sender, EventArgs e)
{
if (ckPosition.Checked)
{
// Replace with your calculations
numBearing.Value = Convert.ToInt32(txtX.Text);
numDistance.Value = Convert.ToInt32(txtY.Text);
}
}
// this.numBearing.ValueChanged += new System.EventHandler(this.BearingDistance_ValueChanged);
// this.numDistance.ValueChanged += new System.EventHandler(this.BearingDistance_ValueChanged);
private void BearingDistance_ValueChanged(object sender, EventArgs e)
{
if (ckPosition.Checked == false)
{
// Replace with your calculations
txtX.Text = numBearing.Value.ToString();
txtY.Text = numDistance.Value.ToString();
}
}
As long as you don't change the position in the position event handler or the bearing/distance in it's handler, there should not be any 'looping' of events.

I know this is probably a big performance no-no but have you tried finding the origin of your event using reflection to tell if it's a forwarded event or not? you could look further up the stack to find out if the event is a forwarded event, or you could abort if the call stack depth gets ridiculous.
Or, if you're calling TextChanged events manually, you could derive from the eventArgs class to pass your own custom eventArgs and then abort if the eventArgs is your custom type or use the custom eventArgs to pass state information to help you.
Or maybe you could swap the TextChanged event that sits on the control with a similar event handler of your own.

Related

Repeat same steps on every textChanged event

I have in my project 5 text boxes.
Every TextBox should accept only digits.
For that I created a function which takes not prepared text and returns the proper one.
Now I'm wondering if there is any simpler way to perform this action on every TextBox, on every TextChanged event without repeating almost same code?
private void TextGoldPack_TextChanged(object sender, EventArgs e)
{
(sender as TextBox).Text = Only_digits((sender as TextBox).Text);
}
private void TextGoldTake_TextChanged(object sender, EventArgs e)
{
//repeat here and on every _TextChanged event
}
If I'm understanding you correctly, just because it's named TextGoldTake_TextChanged, doesn't mean that's the only textbox that can use that code. On the events tab, you can set the TextChanged function for all you textboxes to lead to that function. If it helps, rename it something that doesn't sound textbox-specific such as TextChanged.
Change all the TextBoxes to refer this method upon TextChanged.
Use the sender property to get the actual caller TextBox.

Method for Focus Change

Is there a method which initiates on focus change and can be overridden?
My goal is for the program to fetch closest data automatically from database to input fields whenever user changes his focus/presses enter or tab when on corresponding field. I'm still looking for a way to do this when user selects an item by mouse.
I'm aware that this could be implemented on mouse click but I refuse to believe that there is not a general method for focus change.
What about something like this:
foreach(Control ctrl in this.Controls)
{
ctrl.Enter += new EventHandler(Focus_Changed); // Your method to fire
}
Iterate through all controls and add a enter-event. Bind this handler to your method.
Edit:
Just in case you are wondering why "Enter" and not "LostFocus" or something like that: From my knowledge not every control got focus-events. As I've seen so far "Enter" is presented for all. Maybe there are exceptions. Should be checked out...
You could use Control.Enter event and Control.Leave event for that purpose.
See on MSDN Control.Enter and
Control.Leave.
textBox1.Enter += textBox1_Enter;
textBox1.Leave += textBox1_Leave;
private void textBox1_Enter(object sender, System.EventArgs e)
{
// the control got focus
}
private void textBox1_Leave(object sender, System.EventArgs e)
{
// the control lost focus
}

How can I know Text changes for textboxes without Textchanged event

In my C# Windows form MyForm I have some TextBoxes.
In these TextBoxes, we have to detect if the TextChanged event occurs,
if there're changes in these TextBoxes and click close button, it will ask if we want to cancel the changes when we close the form.
However, when I run the MyForm, I can't know text change for each textbox caused by user typing for without textchanged event property.
But I am thinking how do I make the TextBox's TextChanged know the
event cuased by user typing without textchanged event?
Thanks for help.
Sorry for my English.
There is no (decent) way of knowing what's typed without a TextChanged or a Leave event.
You need to use one of these events to get the typed content. Doing this enable you to set a "dirty" flag that you can check at close and clear at save.
Comparing old and new value has no point without this cause you won't know what the value should be set to without knowing something was changed.
With one exception: If your original data came from a database you could use the compare old/new approach as you would compare the textbox of that which came from the database.
Update:
Addressing this comment:
"Because Myform have many textboxes and if no text change ,this will
not display the confirm message.If I catch textchanged event for all
textboxes, this is so many code."
You can use a common handler to collect the changes for all textboxes in one single method. Use the sender object (cast it to Textbox) to identify which textbox is changed, if needed, or simply set a dirty flag for whatever textbox has a change.
bool isDirty = false;
void SomeInitMethod() //ie. Form_Load
{
textbox1.TextChanged += new EventHandler(DirtyTextChange);
textbox2.TextChanged += new EventHandler(DirtyTextChange);
textbox3.TextChanged += new EventHandler(DirtyTextChange);
//...etc
}
void DirtyTextChange(object sender, EventArgs e)
{
isDirty = true;
}
void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (isDirty) {
//ask user
}
}
// to clear
void Save()
{
SaveMyDataMethod();
isDirty = false;
}
If you have a lot of textboxes in the form loop through the forms control collection and use typeof to address the textboxes. If you have textboxes requiring different approaches use the Tag property of the textbox to distinguish.
A possible approach is using the timer. Have a timer that ticks every 1000 ms (say) and checks the textBox.Text.
A second possible approach is overriding WndProc for the textbox (by inheriting a new class) and handling the change text message. This would be the same as overriding TextBox.OnTextChanged.
Why dont you use a timer which will check after a few intervals if the textboxes do contain any text

Stop TextChanged event from firing Leave event

I'm working on a simple WinForms application for a public school where users can identify themselves by entering either their network IDs (which are not protected information) or their system IDs (which are protected information). I want to switch to a password character when the program detects a system ID (which is working just fine); however, when I do this, my application also fires the textbox's Leave event, which tells users to fix a problem with the login data...before there's even a problem.
Here's my code:
void login_TextChanged(object sender, EventArgs e)
{
login.UseSystemPasswordChar = login.Text.StartsWith(<prefix-goes-here>);
}
private void login_Leave(object sender, EventArgs e)
{
if (login.Text.StartsWith(<prefix-goes-here>) && login.Text.Length != 9)
{
signInError.SetError(login, "Your System ID must be nine digits.");
login.BackColor = Color.LightPink;
}
else if (login.Text.IsNullOrWhiteSpace())
{
signInError.SetError(login, "Please enter your username or System ID.");
login.BackColor = Color.LightPink;
}
else
{
signInError.SetError(login, string.Empty);
login.BackColor = Color.White;
}
}
Ultimately, I don't know that this will cause a ton of problems, and I could move this validation step to the Click event of the sign in button on my form, but I'd rather do validation piece-by-piece if possible.
Putting the TextBox inside a GroupBox does reproduce that behavior-- which is odd.
If you want to keep your GroupBox, here is a work around:
private void login_TextChanged(object sender, EventArgs e)
{
login.Leave -= login_Leave;
login.UseSystemPasswordChar = login.Text.StartsWith(<prefix-goes-here>);
login.Leave += login_Leave;
}
For whatever reason, the Leave event fires when the login TextBox is inside a GroupBox control. Replacing the GroupBox with a simple Label control prevented the code within the TextChanged event from firing the Leave event.
Yes, this is a quirk of the UseSystemPasswordChar property. It is a property that must be specified when the native edit control is created (ES_PASSWORD). Changing it requires Winforms to destroy that native control and recreate it. That has side-effects, one of them is that the focus can't stay on the textbox since the window disappears. Windows fires the WM_KILLFOCUS notificaiton.
Being inside a GroupBox is indeed a necessary ingredient, Winforms doesn't suppress the Leave event when it gets the notification. Bug.
Many possible fixes. You could set a flag that the Leave event handler can check to know that it was caused by changing the property.

checking if form data has been changed

I have been trying the following C# code to check if form data has been changed before closing and saving but it seems that it always reports the form as being changed even when no changes were made.
//Declare a private variable
private bool requiresSaving =false;
//Declare an event
private void SomethingChanged(object sender, EventArgs e)
{
requiresSaving = true;
}
//Hook up this event to the various changed events, eg
this.txtNameDepart.TextChanged += new System.EventHandler(this.SomethingChanged);
//Check this variable when you are closing the form
private void DepartamentEdit_FormClosing(object sender, FormClosingEventArgs e)
{
if (requiresSaving)
{
....
You also need to set requiresSaving false in the saveDepart method.
I think you hook up these events even before you load your initial data. Then SomethingChanged fires and enable the save button even the user doesn't change anything.
You could either unhook these events when loading the default/existing data or hook up these events after loading default/existing data.
//Un-Hook when loading your default/existing data.
private void SetDefaultData()
{
this.txtNameDepart.TextChanged -= new System.EventHandler(this.SomethingChanged);
this.txtNameDepart = "My default text";
this.txtNameDepart.TextChanged += new System.EventHandler(this.SomethingChanged);
}
you should show all the places where you set the flag to true, just in case.
also this code:
//Hook up this event to the various changed events, eg
this.txtNameDepart.TextChanged += new System.EventHandler(this.SomethingChanged)
even understanding what you are trying to do, I think this is bad because if you have many controls and soon or later you will handle the logic of each change event with some more specific code for the individual controls, you should not attach the same event handler to all of them at once.
if all your controls are bound to a BindingSource you could use another approach.
if your controls are populated manually by you with some assignment from a business object on form load, you could also imagine to compare such object's properties with the original one (if you also saved a copy of the original of course) in the form_closing.

Categories