Why isn't this causing an infinite loop of events? - c#

I have a simple application that reverses any text typed to it in another textbox. The catch is, you can modify either textbox and the changes will be (literally) reflected in the other.
I wrote this code, believing for it to cause problems.
private void realText_TextChanged(object sender, EventArgs e)
{
mirrorText.Text = mirror(realText.Text);
}
private void mirrorText_TextChanged(object sender, EventArgs e)
{
realText.Text = mirror(mirrorText.Text);
}
private string mirror(string text)
{
return new string(text.Reverse().ToArray()).Replace("\n\r", "\r\n");
}
I then tried it out, believing that it would cause an infinite loop (realText changes mirrorText, another event happens, mirrorText changes realText, etc). However, nothing except the intended behavior happened.
I'm of course happy about this, I could just leave it here. Or could I?
I'm quite sure the TextChanged event is supposed to be fired whenever Text is changed. Is this intended behavior of some error protection in the events, or was I just lucky? Can this code misbehave on another computer, with other build settings, etc? It can be easily fixed:
private void realText_TextChanged(object sender, EventArgs e)
{
if (realText.Focused)
{
mirrorText.Text = Mirror(realText.Text);
}
}
I'll probably do it anyway to be safe, but is it required to check this? (I'm not even going to ask if it's recommended.)

Per the comments, and as already answered, the TextChanged event is not getting raised when you set the Text property to the value it already has.
It's not clear whether this is something you can safely rely upon. It is a sensible optimisation, and I would be very surprised if future versions of .NET Framework drop it, but I cannot speak for older versions, nor for third-party implementations (Mono).
To be absolutely safe, I would not use the Focused check you put in your question. I would do exactly what the Text setter does now.
private void realText_TextChanged(object sender, EventArgs e)
{
var newMirrorText = Mirror(realText.Text);
if (mirrorText.Text != newMirrorText)
mirrorText.Text = newMirrorText;
}
This has the same advantage of preventing infinite recursion, but plays more nicely with other code you may put in your form that changes the text as a result of some other event.

The reason it doesn't cause a loop is that it checks whether the Text property actually changed, i.e. if the new value does not equal the old value. In your case the mirror function happens to reverse itself, which leads to the same text after two passes.

It's pretty easy to check.
First, replace both textbox controls with
class T : TextBox
{
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
}
}
}
Second, set the breakpoint on setter. Add these expressions to the Watch window:
Name
Text
value
Third, launch the app, copy '123' from somewhere and paste it to the first textbox. Here it goes:
1st break:
Name: "mirrorText"
Text: ""
value: "321"
2nd break:
Name: "realText"
Text: "123"
value: "123"
3rd... whoops, it does not breaks anymore. To detect why we had to go deeper. Look at referencesource: text box setter does nothing unusual, but TextBoxBase's one looks interesting:
set {
if (value != base.Text) { // Gotcha!
base.Text = value;
if (IsHandleCreated) {
// clear the modified flag
SendMessage(NativeMethods.EM_SETMODIFY, 0, 0);
}
}
}
So, as hvd already answered, the reason is the textbox does not raise TextChanged if old and new values are the same. I don't think the behavior will change, at least for winforms. But if you want more robust solution, here it is:
private void RunOnce(ref bool flag, Action callback)
{
if (!flag)
{
try
{
flag = true;
callback();
}
finally
{
flag = false;
}
}
}
private bool inMirror;
private void realText_TextChanged(object sender, EventArgs e)
{
RunOnce(ref inMirror, () =>
{
mirrorText.Text = mirror(realText.Text);
});
}
private void mirrorText_TextChanged(object sender, EventArgs e)
{
RunOnce(ref inMirror, () =>
{
realText.Text = mirror(mirrorText.Text);
});
}
private string mirror(string text)
{
return new string(text.Reverse().ToArray()).Replace("\n\r", "\r\n");
}
P.S. mirror() will fail on surrogate pairs. Here're some solutions.

If textbox has a Text, and we try to change it with the same Text, the TextChange event is not raising because new text is same as the previous.
In your code, the realText_TextChanged event reverses the text and changes the mirrorText with it.
The mirrorText_TextChanged event reverses the text and try to change the realText.
The realText has already this text and does not raises the realText_TextChanged event.

Related

checking items in a list and take a decision

I want to build a program in Windows forms where the user can create a pizza by pressing buttons.
My problem is that when the user presses an ingredient more than once, the list will just increment. I tried various methods but they don't seem to work.
I have seen a solution using a for loop checking individual items in the list however I will have to implement that 19 times which is not really efficient (once for every button)
string check = "Thin Base";
if (My_Pizza.Contains(check))
{
My_Pizza.Items.Remove("ThinBase");
My_Pizza.Items.Add("Thin Base");
}
You have "ThinBase" and "Thin Base". Not the same thing.
Also, if an item is already in the list, you don't need to do anything. Simply invert your check
string check = "Thin Base";
if (!My_Pizza.Items.Contains(check))
{
My_Pizza.Items.Add(check);
}
General idea is not to hardcode string values, but create reusable method that will do what you need: check if certain value is already in the list and if it not, add this item to list. This will help you to avoid duplicate code.
In the button event handler you simply call this method and provide string value as parameter. I'm not sure how exactly you handle button clicks, but I would suggest creating single reusable method once again and acquire string value from button.Text property.
Here is code sample for you to demonstrate the idea.
private void OnButtonClick(object sender, EventArgs e)
{
Button clickedButton = (Button) sender;
if (clickedButton != null)
{
string buttonContent = clickedButton.Text;
CheckAndAdd(buttonContent);
}
}
private void CheckAndAdd(string valueToCheck)
{
if (!My_Pizza.Items.Contains(valueToCheck))
{
My_Pizza.Items.Add(valueToCheck);
}
}

Statement performed only when method ends

I'm C# with Compact Framework and I realized something weird today. I'm calling a method by an event that applies a set to an object and when I debug, it passes by this, but just performs after the last close bracket of the method. My example:
public string Loading
{
set { lblLoading.Text = value; }
}
private void btnAuth_Click(object sender, EventArgs e)
{
Loading = "Loading...";
_presenter.PerformAuth();
}
When I debug, it passes by my first statement, applies it, but doesn't change anything on the screen... Oh, until it do PerformAuth(). After it, so, then the label value is changed. Oh, the problem isn't just by it be synchronous. The same occurs when I try to do an asynchronous task:
private void btnAuth_Click(object sender, EventArgs e)
{
ASyncResult res = BeginInvoke(new Action(() =>Loading = "Loading..."));
EndInvoke(res);
_presenter.PerformAuth();
}
I think it might be a bug in thread and in C# design implementation. And also with direct set it is stubborn to me. As you can see in the image below:
I just want to set a text in a label, call a method and unset it in an event. Why does C# get it so complicated?

How to display data received from serial port in a textbox without the text disappearing in Visual Studio C#?

So, I'm trying to develop a simple application in visual C# which gets data from serial port and displays it in a textbox (to monitor temperature). I'm acquiring and displaying the data successfully, using the DataReceived event to update a global string variable and a timer to update the text field on my text box, as shown:
private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
try
{
globalVar.updateTemp = port.ReadLine(); //This is my global string
}
catch (IOException)
{
}
catch (InvalidOperationException)
{
}
catch (TimeoutException)
{
}
}
private void timer1_Tick(object sender, EventArgs e)
{
tempDisplayBox.Text = globalVar.updateTemp; //This is my textbox updating
}
The only issue I have is that the value shown in the textbox keeps flashing, making it hard to read. My timer is set to trigger every 10 ms (which should be fast enough, right?). Is there any way to make it more stable? I realize this may be a newb question, but to be fair I am a newb :) Any help is appreciated! Thanks!
Do you really need it updating every 10ms? What about every 500 ms or if not that then 100ms. 100ms will require your update method run 10 times less and therefore update 10 times less. The flickering you are expiriencing is due to the refresh speed. You could create custom method which will only update the temp only when target Label or textBox value is different than source port. But that will only sort the flickering when temp is steady, when temp will start vary it will bring back the flickering. Good luck ;-)
UPDATE
Hi I tried to reproduce the conditions and could not make my textbox nor Label flash. The way I tested it was by assigning int ntick = 0; and then increment the ++ntick; inside of the timer_tick method. The results didn't make any of the controls flash and were updated even every milisecond at some point. I also tried string.Format to put some load on the method. Is your app responsive?
The trick is to use double buffering. This way the operating system will redraw the Control off-screen, and only show the control when it is fully redrawn.
I have had the same problem, and solved it by extending the TextBox control like this:
public FastLogBox()
{
InitializeComponent();
_logBoxText = new StringBuilder(150000);
timer1.Interval = 20;
timer1.Tick += timer1_Tick;
timer1.Start();
SetStyle(ControlStyles.DoubleBuffer, true);
}
void timer1_Tick(object sender, EventArgs e)
{
if (_timeToClear)
{
_logBoxText.Clear();
_timeToClear = false;
}
if (_logQueue.Count <= 0) return;
while (!_logQueue.IsEmpty)
{
string element;
if (!_logQueue.TryDequeue(out element)) continue;
{
_logBoxText.Insert(0, element + "\r\n");
}
}
if (_logBoxText.Length > 150000)
{
_logBoxText.Remove(150000, _logBoxText.Length - 150001);
}
Text = _logBoxText.ToString();
}
public new void Clear()
{
_timeToClear = true;
while (!_logQueue.IsEmpty)
{
string element;
_logQueue.TryDequeue(out element);
}
}
public void AddToQueue(string message)
{
_logQueue.Enqueue(message);
}
}
I also use a timer and a concurrentQueue to avoid using Invoke to update the control from another thread. I also use a StringBuilder to prepare the string before putting it into the TextBox. StringBuilder is faster when building larger strings.
You can use ReadExisting() to read the whole data at a time.
You need to handle DataReceived Event of SerialPort
serialPort1.ReadExisting();
Sample:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
String myData=serialPort1.ReadExisting();
}
Example Code: Here i would like to show you the code to Read Data(RFID Tag Code which is basically of length 12)
String macid = "";
private void DoWork()
{
Invoke(
new SetTextDeleg(machineExe ),
new object[] { macid });
macid = "";
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string str1;
macid += serialPort1.ReadExisting();
if (macid.Length == 12)
{
macid = macid.Substring(0, 10);
Thread t = new Thread(new ThreadStart(DoWork));
t.Start();
}
}
public void machineExe(string text)
{
TextBox1.Text=text;
}
Thank you so much for the answers! I found a way to work around this issue:
Instead of replacing the contents of my textbox by rewriting the TextBox.Text property - which, as HenningNT implied, refreshes the control and causes the flickering - I'm now using the TextBox.AppendText method. Though, as I want to display only one line of data at a time, I use the textbox in multiline mode and the Environment.NewLine to jump to a new line before appending the text. As for the method of updating, I've gone back to using the timer because with the invoke method was crashing my application when I close the form, for some reason. Also, enabling double buffering didn't do me much good, although I guess I was doing it wrong... It still flickers a bit, but it's much better now :) I know this is not really a perfect solution (much more of a workaround), so I'll keep looking for it. If I find it, I'll be sure to update it here ;) My code:
private void timer1_Tick(object sender, EventArgs e) //Timer to update textbox
{
if (tempDisplayBox.Text != globalVar.updateTemp) //Only update if temperature is different
{
try
{
tempDisplayBox.AppendText(Environment.NewLine);
tempDisplayBox.AppendText(globalVar.updateTemp);
}
catch (NullReferenceException)
{
}
}
}

Determine who fired an event

Background:
In my winforms form, I have a Checked ListView and a "master" checkbox called checkBoxAll.
The behaviour of the master is as follows:
If the master is checked or unchecked, all ListViewItems must change accordingly.
If the user unchecks a ListViewItem, the master must change accordingly.
If the user checks a ListViewItem, and all other ListViewItems are checked aswell, the master must change accordingly.
I have written the following code to mimic this behaviour:
private bool byProgram = false; //Flag to determine the caller of the code. True for program, false for user.
private void checkBoxAll_CheckedChanged(object sender, EventArgs e)
{
//Check if the user raised this event.
if (!byProgram)
{
//Event was raised by user!
//If checkBoxAll is checked, all listviewitems must be checked too and vice versa.
//Check if there are any items to (un)check.
if (myListView.Items.Count > 0)
{
byProgram = true; //Raise flag.
//(Un)check every item.
foreach (ListViewItem lvi in myListView.Items)
{
lvi.Checked = checkBoxAll.Checked;
}
byProgram = false; //Lower flag.
}
}
}
private void myListView_ItemChecked(object sender, ItemCheckedEventArgs e)
{
//Get the appropiate ListView that raised this event
var listView = sender as ListView;
//Check if the user raised this event.
if (!byProgram)
{
//Event was raised by user!
//If all items are checked, set checkBoxAll checked, else: uncheck him!
bool allChecked = true; //This boolean will be used to set the value of checkBoxAll
//This event was raised by an ListViewItem so we don't have to check if any exist.
//Check all items untill one is not checked.
foreach (ListViewItem lvi in listView.Items)
{
allChecked = lvi.Checked;
if (!allChecked) break;
}
byProgram = true; //Raise flag.
//Set the checkBoxAll according to the value determined for allChecked.
checkBoxAll.Checked = allChecked;
byProgram = false; //Lower flag.
}
}
In this example, I use a flag (byProgram) to make sure an event was caused by the user or not, thereby preventing an infinite loop (one event can fire another, which can fire the first one again etc. etc.). IMHO, this is a hacky solution.
I searched around but I couldn't find a MSDN documented method to determine if an User Control Event was directly fired thanks to the user. Which strikes me as odd (again, IMHO).
I know that the FormClosingEventArgs has a field which we can use to determine if the user is closing the form or not. But as far as I know, that is the only EventArg that provides this kind of functionality...
So in summary:
Is there a way (other than my example) to determine if an event was fired directly by the user?
Please note: I don't mean the sender of an event! It won't matter if I code someCheckBox.Checked = true; or manually set someCheckBox, the sender of the event will always be someCheckBox. I want to find out if it is possible to determine whether it was through the user (click) or by the program (.Checked = true).
Aaand also: 30% of the time it took to write this question was to formulate the question and the title correctly. Still not sure if it is a 100% clear so please edit if you think you can do better :)
No, there's no practical way to determine whether the change came from GUI or was done by program (in fact, you could analyze the callstack - but that's not recommended because it's very slow and error-prone).
BTW, there's one other thing you could do instead of setting byProgram. You could remove and add the event handler prior or after, respectively, change your controls:
checkBoxAll.CheckedChanged -= checkBoxAll_CheckedChanged;
// do something
checkBoxAll.CheckedChanged += checkBoxAll_CheckedChanged;
Instead of using the changed event, you could use the clicked event to cascade the change through to the relevant controls. This would be in response to a user click, and not the value being changed programatically.
This is something I come across quite a lot and what I tend to try do is not split it between user interaction vs program interaction - I use more generic code i.e. the UI is being updated and doesn't require any events to be handled. I usually package this up through BeginUpdate/EndUpdate methods e.g.
private int updates = 0;
public bool Updating { get { return updates > 0; } }
public void BeginUpdate()
{
updates++;
}
public void EndUpdate()
{
updates--;
}
public void IndividualCheckBoxChanged(...)
{
if (!Updating)
{
// run code
}
}
public void CheckAllChanged(...)
{
BeginUpdate();
try
{
// run code
}
finally
{
EndUpdate();
}
}

What is the best way to mask an UI Event?

I am working with a DataGridView, and I use the CellValueChanged event.
I dont want this event to be triggered when I change a cell value by the code. However, I want it to be triggered when the user edits it.
That's why I enclose my cell value change operations with the following code :
void changeCellOperation()
{
dgv.CellValueChanged -= new DataGridViewCellEventHandler(dgv_CellValueChanged);
...
cell.Value = myNewCellValue
...
dgv.CellValueChanged += new DataGridViewCellEventHandler(dgv_CellValueChanged);
}
I ended to have several differents functions where my DataGridView cells are updated this way.
Because these functions are called from different places and can be nested, I cannot afford to keep this code as is to avoid event unwanted event reactivation.
So I ended up this way :
int valueChangedEventMask = 0;
void changeCellOperation()
{
valueChangedEventMask++;
...
cell.Value = myNewCellValue
...
valueChangedEventMask--;
}
void dgv_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (valueChangedEventMask > 0)
return
...
}
This works fine. Also when the calls are nested, including inside the event itself.
But the CellValueChanged event is now fired too many times for no reasons.
Because I often have to cope with this pattern, I am looking for a solution that can be applicable generally for Events in UIs, not only the DataGridView.
So my question is:
What is the best tip to mask UI Events correctly and avoid unnecessary Events fires ?
CellValueChanged is not an UI event, but a property changed event. That means you can not use it to distinguish user input from programmatic change. You can always use subscriber/unsucscribe or flag+/- or BeginEdit/EndEdit-similar technique, but maybe you have to find another (better) approach. To example, in case of checkbox you can use Click event instead of Changed, because (surprise!) it will tell you when the user click it and otherwise safely change value of Checked programmatically.
In case of DataGridView easiest would be to use Changed with some flag (which will be set when edit begins and reset when ends - see, CellBeginEdit/CellEndEdit ).
You could use CellEndEdit instead of CellValueChange. I don't know what your method dgv_CellValueChanged does, just be careful that CellEndEdit is fired every time you exit the edit mode for the cell, even if its value has not been changed. This means that you have to keep trace of the current values of your cells if you don't want the method to be executed when the value doesn't change.
I would avoid events related with the mouse such as CellClick because your users could use just the keyboard.
Anyway I usually avoid this kind of problems by separating the logic from the user interface, i.e. I write a separate class which is bound to the form. Take a look at MVVM (you can implement your own version in WinForms if you want) or the good old MVC.
I ended up mixing both solutions in a very simple one. I use a counter and I only hook/unhook the events I want to mask.
EventMask valueChangedEventMask;
// In the class constructor
valueChangedEventMask = new EventMask(
() => { dgv.CellValueChanged += new DataGridViewCellEventHandler(dgv_CellValueChanged); },
() => { dgv.CellValueChanged -= new DataGridViewCellEventHandler(dgv_CellValueChanged); }
);
// The value change operation I want to hide from the event
void changeCellOperation()
{
valueChangedEventMask.Push();
...
cell.Value = myNewCellValue
...
valueChangedEventMask.Pop();
}
// The class
public class EventMask
{
Action hook;
Action unHook;
int count = 0;
public EventMask(Action hook, Action unHook)
{
this.hook = hook;
this.unHook = unHook;
}
public void Push()
{
count++;
if (count == 1)
unHook();
}
public void Pop()
{
count--;
if (count == 0)
hook();
}
}

Categories

Resources