Preventing event handler loop - c#

I have a form with two text fields, A and B that are supposed to behave in the following way:
Typing something into A should set B.Text = f(A.Text)
Typing something into B should set A.Text = g(B.Text)
...for some arbitrary and potentially unrelated functions f and g.
The problem I'm facing is that the naive implementation of simply throwing the above code into each field's handler will create an infinite loop as A's handler will update B's value and call B's handler, which will update A, etc.
What would be the correct (and preferably thread-safe) way to handle this? Either somehow determining whether a change was done manually or programmatically, somehow suppressing events firing when changing the value, or some other way.

Use a flag to signal that you are doing changes
private bool updating;
private void A_TextChanged(object sender, EventArgs e)
{
if (!updating) {
updating = true;
B.Text = f(A.Text);
updating = false;
}
}
private void B_TextChanged(object sender, EventArgs e)
{
if (!updating) {
updating = true;
A.Text = g(B.Text);
updating = false;
}
}
You don't have to care about thread-safety as this all happens in the unique UI-thread. UI events never create new threads; i.e. an event (click, text changed, etc.) never interrupts another one!
If you want to be sure that the flag is reset, you can use the try-finally statement. The finally block is ensured to run, even if an exception should occur within the try block (unless the application is terminated unexpectedly).
if (!updating) {
updating = true;
try {
A.Text = f(B.Text);
} finally {
updating = false;
}
}

I assume you're using TextChanged event, try this then:
private bool callB=true;
private bool callA=false;
private void A_Click(object sender, System.EventArgs e)
{
callB=true;
callA=false;
}
private void B_Click(object sender, System.EventArgs e)
{
callB=false;
callA=true;
}
private void A_textchanged(object sender, RoutedEventArgs e)
{
if(callB)
B.text=f(A.text);
}
private void B_textchanged(object sender, RoutedEventArgs e)
{
if(callA)
A.text=g(B.text);
}
Anyway, a better way to just edit A when the user is finished with B(finished whatever he wanted to write in it), that's because if expression will be evaluated at every character the user inputs.
By the way, changing a text while the user writes might be surprising to him, so better to avoid textchanged event in this case.

Related

stopping event from code C#, call event by user not by code

Sorry, this may be a duplicate question, but I couldnot understand the solutions already provided in different answers.
I have created a mp3 player in a different manner, it plays one mp3 file at a time but one listbox have the chapters, which is not only handling to move position of that particular mp3 but also changes a picturebox image. Now somewhere I need to change the selection of the listbox from a seekbar but dont want to fire the following event of;
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
Please guide.
One way to inhibit your selection indexed change event doing its normal way is to use a boolean flag. Also, make sure that this inhibition does not stay around when some exception is raised:
private bool inhibit = true;
private void doSomeProcessWithInhibit()
{
try
{
inhibit = true;
// processing comes here
}
// if something goes wrong, make sure other functionality is not blocked
finally
{
inhibit = false;
}
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
// fast return to reduce nesting
if (inhibit)
return;
// do event handling stuff here
}
P.S. Try to use meaningful names for controls (check listBox1). You will thank yourself when revisiting the code and/or others have to.
Add a Boolean with class scope called something like isProcessing. Set it to true. Do your work, then set it to false. Warp your event in the Boolean:
bool isProcessing = true;
private void switchControls(){
isProcessing = true;
//do work;
isProcessing = false;
}
private void MyControl.OnEvent(object sender, EventArgs e){
if(!isProcessing){
//what you would normally do
}
}
OR....
Deregister the event, the re-register it
private void switchControls(){
myButton1.OnClick -= myButtonClick;
//do work
myButton1.OnClick += myButtonClick;
}

Is there a better way to close buttons on click?

I'm a beginner and have an assignment in which I must program the game of NIM. I begin with 15 "tokens" and at each turn a maximum of three can be removed, or "hidden". So far I am hiding these tokens on click by doing the following.
private void button1_Click(object sender, EventArgs e)
{
button1.Visible = false;
}
private void button2_Click(object sender, EventArgs e)
{
button2.Visible = false;
}
I simply copied and pasted that multiple times and changed the button numbers so that my buttons will close on click. This might be obvious, but is there a more efficient way to do this, instead of having 15 button close methods?
You can use the same click event for every single button, and make use of the sender object, casting it to Button:
private void buttonsToClose_Click(object sender, EventArgs e)
{
((Button)sender).Visible = false;
}
Then just add that handler to every single button you want to close itself on click.
Note, though, that this will throw an InvalidCastException if you or anyone else uses this handler on an object that is not a Button, so if you're actually going to use this code I would add some sort of conditional to check the real type of the sender.
Additionally, you could reuse this for any Control object by casting sender to Control instead, given that Button inherits from Control, and all Control objects have the Visible property. Here's an example, with a conditional to guard against an invalid cast:
private void controlToMakeInvisible_Click(object sender, EventArgs e)
{
if (sender.GetType() == typeof(Control))
{
((Control)sender).Visible = false;
}
}
A final note - it seems from your post like you may have a slight misunderstanding about the way events are created and wired in with objects in Windows Forms. If you go into the Designer, add a click event, and see it pop into your Form code as follows:
private void button1_Click(object sender, EventArgs e)
the name of this method has no bearing on its function. The button1 part of button1_Click doesn't actually have any logical linkage with the Button button1 - it's just the default name assigned by the Designer. The actual assignment of the method button1_Click to the Button.Click event is auto-generated into your Form's Designer.cs method.
The point of this is that if you copy and paste button1_Click and change every incidence of button1 with button2, like so:
private void button2_Click(object sender, EventArgs e)
{
button2.Visible = false;
}
it's not going to fire when button2 gets clicked. In actual fact, it's never going to fire at all, because the method hasn't actually been connected to any controls/events.
just call your event in a foreach loop.
private void Form1_Load(object sender, EventArgs e)
{
foreach (var button in Controls.OfType<Button>())
{
button.Click += button_Click;
}
}
void button_Click(object sender, EventArgs e)
{
((Control) sender).Visible = false;
}
if you change:
Controls.OfType<Button>()
to
Controls.OfType<Control>()
it will set visible to false for any Control. so you can control what item you want the event to be raised for easily.
OfType summary: Filters the elements of an IEnumerable based on a specified type.

how to generate a SelectionRangeChanged Event Programatically ChartControl WinForms

want to create a selectionRangeChanged event programatically not really getting how to do it
private void btn_10D_Click(object sender, EventArgs e)
{
double varRange = 10;
double var_Sel1 = DatesX[0].ToOADate();
Chart1.ChartAreas["ChartArea1"].CursorX.IsUserEnabled = true;
Chart1.ChartAreas["ChartArea1"].CursorX.IsUserSelectionEnabled = true;
Chart1.ChartAreas["ChartArea1"].CursorX.SelectionColor = Color.LightGray;
Chart1.ChartAreas["ChartArea1"].CursorX.SelectionStart = var_Sel1;
Chart1.ChartAreas["ChartArea1"].CursorX.SelectionEnd = varRange + var_Sel1;
Chart1.ChartAreas["ChartArea1"].CursorX.Position = varRange + var_Sel1;
Chart1.SelectionRangeChanged += new EventHandler<CursorEventArgs>(Chart1_SelectionRangeChanged);
}
void Chart1_SelectionRangeChanged(object sender, CursorEventArgs e)
{
throw new NotImplementedException();
}
thank you
For all events in C# is true that if class creator did not make extra effort to allow event firing form outside of class it is impossible to fire them.
According to MSDN
Chart.SelectionRangeChanged event Occurs when the selection start position or end position is changed.
But from my tests I can see that it is fired only if it is changed by user not program.
If I understand your intention correctly you want to handle those small buttons under your chart and btn_10D_Click method is a click handler for one of them. Try to move this line
Chart1.SelectionRangeChanged += new EventHandler<CursorEventArgs>(Chart1_SelectionRangeChanged);
to your constructor and ensure it is called once (remove it form other handlers). This will ensure your code is executed when user changes selection. If you want to execute same code for your button you should simply extract handler contents to method and call it form button click handler.
void Chart1_SelectionRangeChanged(object sender, CursorEventArgs e)
{
DoSomething(/*some arguments if you need them*/);
}
private void btn_10D_Click(object sender, EventArgs e)
{
\\your code
DoSomething();
}

2 events calling each other

I was wondering about this problem for a while, but couldn't really come up with a solution. I have 2 different event handlers calling each other recursively. As soon as event A is fired, it triggers event B which triggers event A again and so on...
Basically I want to be able to select text in a RichTextBox and show the corresponding font size in a combo box. When I choose a different font size from the ComboBox, I want it's value to be applied to the selected text.
The 2 events are:
1) The selection changed event of text inside a RichTextBox:
private void MyRTB_SelectionChanged(object sender, RoutedEventArgs e)
{
//Get the font size of selected text and select the concurrent size from the ComboBox.
}
2) The selected index changed event of a Combobox:
private void CmbFont_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//Apply the chosen font size to the currently selected text of the RichTextBox.
}
What would be the best solution to make sure they each only "do their thing" and do not fire the other event in doing so?
Sometimes changing a property of a control in code fires an event unintentionally. Changing the data source of a ListBox or a ComboBox will fire the SelectedIndexChanged event, for example. Use a flag to handle this case
private bool _loading;
...
_loading = true;
// Fill the ComboBox or ListView here
_loading = false;
In the event handler do this
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (_loading) return;
...
}
Refactor your code so that A calls DoSomethingA() and B calls DoSomethingB(). This way, if you want A to do the functionality of B you can just call DoSomethingB() and not have any recursive calls.
Just use a bool (maybe called dontFireA) and set it in A just before calling B
notifying properties (used in order to enable binding from WPF to non-WPF properies) use this technique:
public object MyProperty
{
get
{
return myField;
}
set
{
if (value != myField)
{
myField = value;
NotifyProperyChanged("MyProperty"); // raise event
}
}
}
The if (value != myField) condition prevents infinite recursion (stackoverflowexception).
In some cases (e.g. floating point numbers and inaccurate value transfers) if (Math.Abs(value - myField) > someConstant) is used instead to break the recursion.
Could you apply a similar technique to your problem?
If both events are on the same object or the owners have references to each other, you could also store a flag on each e.g.
private void OnEvent()
{
DoSomething();
}
private void DoSomething()
{
this.IsBusy = true;
// do work
// raise event
if (!other.IsBusy)
RaiseEvent();
}
I am going to make the educated guess that you are not raising Event A or Event B yourself; let's say Event A is the TextBox1.TextChanged event and Event B is the TextBox2.TextChanged event, and they have handlers like:
public void Textbox1_TextChanged(object sender, EventArgs e)
{
...
TextBox2.Text = someString;
}
public void Textbox2_TextChanged(object sender, EventArgs e)
{
...
TextBox1.Text = someOtherString;
}
In this case, the handlers are each going to raise the other textbox's TextChanged event by virtue of changing the text, leading to infinite recursion.
The first thing you can do, if you want both to run once and once only, is to mark that they're already running (changing the text of the other textbox results in that textbox's event handler running within the same call stack:
public void Textbox1_TextChanged(object sender, EventArgs e)
{
if(handler1Running) return; //the second time through we exit immediately
handler1Running = true;
...
TextBox2.Text = "Something"; //the other event handler is invoked immediately
handler1Running = false;
}
public void Textbox2_TextChanged(object sender, EventArgs e)
{
if(handler2Running) return; //the second time through we exit immediately
handler2Running = true;
...
TextBox1.Text = "Something Else"; //the other event handler is invoked immediately
handler2Running = false;
}
Now, the deepest it will go is three levels; 1's handler invokes 2's handler which invokes 1's handler again, which sees that 1's handler is already running and quits before doing anything that would deepen the recursion. Same thing if you start by changing TextBox2.
The other thing you can do is make sure you aren't trying to set the textbox to the same value that's already there. Changing from one string reference to another, even if both references are the same string value, will fire the TextChanged event. If the recursion must continue naturally but will reach a steady state, this is actually the first thing to try:
public void Textbox1_TextChanged(object sender, EventArgs e)
{
StringBuilder builder = new StringBuilder();
... //build string
//now, even though the builder's ToString will produce a different reference,
//we're making sure we don't unnecessarily change the text.
if(builder.ToString != TextBox2.Text)
TextBox2.Text = builder.ToString();
}
public void Textbox2_TextChanged(object sender, EventArgs e)
{
StringBuilder builder = new StringBuilder();
... //build string
//now, even though the builder's ToString will produce a different reference,
//we're making sure we don't unnecessarily change the text.
if(builder.ToString != TextBox1.Text)
TextBox1.Text = builder.ToString();
}

How can I stop my while loop?

I would like to use the following code in C# but I just can't seem to get out of it. I would like to terminate the application if the user presses a key or moves the rodent (aka mouse). Here is my code (no laughing!).
private void frmDots_KeyDown(object sender, KeyEventArgs e)
{
bgNotClicked = false;
Close();
}
private void frmDots_Click(object sender, EventArgs e)
{
bgNotClicked = false;
Close();
}
while (bgNotClicked)
{
// Clear the first element in our XY position. This is the reverse of the way I normally create the dots application
System.Drawing.Rectangle clearDots = new System.Drawing.Rectangle(Dots.PositionX[iCounter], Dots.PositionY[iCounter], 8, 8);
// Create the black color and brush to clear dots
Color clearDotsColor = Color.Black;
SolidBrush clearDotsBrush = new SolidBrush(clearDotsColor);
// Finally clear the dot
e.Graphics.FillEllipse(clearDotsBrush, clearDots);
GetRandomPosition(iCounter);
// Fill the elements to display colors on the displays canvas
System.Drawing.Rectangle colorDots = new System.Drawing.Rectangle(Dots.PositionX[iCounter], Dots.PositionY[iCounter], 8, 8);
// Create the color and brush to show dots
Color colorRandom = GetRandomColor();
SolidBrush colorBrush = new SolidBrush(colorRandom);
// Finally show the dot
e.Graphics.FillEllipse(colorBrush, colorDots);
Thread.Sleep(5);
iCounter++;
if (iCounter == 399)
{
iCounter = 0;
}
}
}
Your "busy waiting" strategy is poor design. Instead, you should use event handlers that are fired:
On keypress.
When the mouse is moved.
In either case, you can respond by terminating the application.
Edit:
After seeing your edit, this is definitely your problem. The issue is that your while loop blocks the main UI thread, so it never handles the Windows Messages which trigger your key press/mouse/etc handlers.
You have a couple of options - you can either move some of this onto a separate thread, do what I suggested below, or add a call to Application.DoEvents in your while loop. This would allow your event handlers to run, which would in turn set bgNotClicked = false;. Right now, that's never occurring because your UI is blocked entirely.
Original Post:
If you're doing this loop in your UI thread, you're going to need to rethink the design a bit.
Setting bgNotClicked = false; somewhere in an event handler will work, but only if your event handler is able to run. If you're doing the above code in the UI thread, it will block your UI thread indefinitely, preventing the event handler from firing.
I would recommend reworking this to be based off a timer (so it runs repeatedly on regular intervals), instead of locked into a while loop. This would allow your other UI events to fire between runs, and instead of setting bgNotClicked = false;, your event handler could just set the timer to be not enabled.
Your bgNotClicked variable needs to be set to false by your event handler for key-press.
If the rodent is moved by your mouse, you would need a similar mouse event handler.
The break keyword will terminate a loop. In this case, when you hit the case where you want to stop the loop, you would just use break;.
If you're looping like that you need to give the application a moment to process the events that you're hoping will cause the interruption. This is the job of the DoEvents method.
private bool WeAreDone = false;
private void DoingIt()
{
while (true)
{
Application.DoEvents();
if (WeAreDone)
{
break;
}
}
}
private void InterruptButton_Click(object sender, EventArgs e)
{
WeAreDone = true;
}
I think using a Timer fits the Windows event-driven model better than the busy wait while loop. You might try something like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private int iCounter = 0;
private void Draw()
{
// ....
}
private void timer1_Tick(object sender, EventArgs e)
{
Draw();
iCounter++;
if(iCounter == 399)
{
iCounter = 0;
}
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Interval = 5;
timer1.Enabled = true;
}
private void Form1_Click(object sender, EventArgs e)
{
timer1.Enabled = false;
Close();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
timer1.Enabled = false;
Close();
}
}
This does not seems to be the correct way. .Net Framework has provided you with the events to handle the KeyPress and MouseMove/Click actions. Why are you not using them?
Try moving the loop into a BackgroundWorker's DoWork event handler. Then your GUI will still be responsive and instead of that nasty variable, you can just call the CancelAsync method to stop the loop.
You can exit the loop using the break statement.
EDIT: OK, I take back the flag thing!
Use Environment.Exit(2); (or Environment.Exit(1) it really doesn't make a difference) to exit out of the application.
Exit While
...................

Categories

Resources