C# blanking a text label, instead of updating it's text property weirdness

This bug tricked me into spending 4 hours debugging perfectly functional code last night.

Basically in certain situations c# isn’t updating a text label’s text, instead it blanks it.

I’ll give you the setup. The code I’m writing currently updates a text label with strings streamed over a network connection. Since it’s coming in from a networking connection, it’s also coming in from a background thread so I have to use Control.Invoke.

If:
I send one or two strings it updates just fine.
I tell it to put the string data on a button control’s text property it does it just fine. No matter how fast it comes in.

However it doesn’t display on labels if it comes in too fast. The label text is blanked instead, but occasionally I’ll see brief flashes of data. I’ve tried reading the label text property from other controls and it’s actually blanked of text, not just displaying nothing. Stopping the data stream leaves it blank.

Here’s the actual code if that helps.



//oncreate event
public debugScreen() {
	InitializeComponent();

	//used incase guiUdater is ran from the wrong thread
	guiupdate = new guiUpdateDelegate(guiUpdaterFromBackgroundThread);
}

//delegate and creation defininiton for above
private delegate Delegate guiUpdateDelegate(Int32 location, string labelText);
guiUpdateDelegate guiUpdate;
 //updates the form control
public void guiUpdater(string labelName, string labelText) {

	//gets theindex psoition of the control in the form's Control array, returns -1 if not found
	Int32 location = findControlIndexNumberFromName(labelName);

	//if the item is found
	if (location >= 0) {

		//if this function was invoked from the wrong thread
		if (this.Controls[location].InvokeRequired) {

			//invoke the delagate with the function to handle gui updating from the corrext thread.
			//location is the index number of the array in the form's Control array
			//labelText is the new value for the label
			this.Controls[location].Invoke(guiupdate, location, labelText);
		}
		else {

			//if in correct thread just update the bloody thing
			this.Controls[location].Text = labelText; 
		}
	}
	else {
		//if control was not found (location = -1) redo with error on label 9
 		guiUpdater("Label9","not found: " + labelName);
	}
}

//updates the text label
private Delegate guiUpdaterFromBackgroundThread(Int32 location, string labelText) {
	Controls[location].Text = labelText;
	return null;
}


It doesn’t do anything essential, just debug output, and since it updates just fine on buttons I know the data is getting through. So if this is the limit of this problem I can live with it. However I’m pretty curious what’s going.

I have had some issues with screen updates at times and usually I end up having to issue a Refresh() to invalidate the control and get it to redraw. I just looked at some code where I did this and it was exactly in the case of updating a Label. Code fragment looks like this:



label1.Text = "Conversion started...";
Refresh();
ReadOldConfig("main.cfg");
label1.Text = "Converting main config file...";
Refresh();
ConvertConfig();


On a Label control, if I don’t do this, it does not update until the entire form is invalidated and redrawn. I think this is a limitation of the Label control.

Is the client Internet Explorer? If yes, did you try another browser?

I tried that, thanks, but no go. Significant lag. I’ll stop the stream and it’ll go crazy updating for a while, and then stop, and still had the same blanking problems, but it did show up more at least.

The client is an app on an android phone I’m writing to control the computer’s mouse with the phone’s touchpad. The strings are x/y coordents for the mouse.

What happens if you put in a DoEvents?

public void guiUpdater(string labelName, string labelText) {

//gets theindex psoition of the control in the form's Control array, returns -1 if not found
Int32 location = findControlIndexNumberFromName(labelName);

//if the item is found
if (location >= 0) {

	//if this function was invoked from the wrong thread
	if (this.Controls[location].InvokeRequired) {

		//invoke the delagate with the function to handle gui updating from the corrext thread.
		//location is the index number of the array in the form's Control array
		//labelText is the new value for the label
		this.Controls[location].Invoke(guiupdate, location, labelText);
	}
	else {

		//if in correct thread just update the bloody thing
		this.Controls[location].Text = labelText; 
                    **Application.DoEvents();**
	}
}
else {
	//if control was not found (location = -1) redo with error on label 9
	guiUpdater("Label9","not found: " + labelName);
}

}

//updates the text label
private Delegate guiUpdaterFromBackgroundThread(Int32 location, string labelText) {
Controls[location].Text = labelText;
return null;
}

Same thing as before, button updates fine, text label doesn’t. I looked up DoEvents, it appears to be called automatically when a form is updated. I don’t think it’s anything with that because the button and the text labels are handled with the exact same code, the only difference is the array index position. Buttons always seem to update fine. I think it’s probably just a weird bug with text labels.
What I mean by that is form controls are accessed with an array and the control’s index place. So Control[2].Text might be the text value of a button or a label or any other control. It’s agnostic to what type of control it is, at least in theory, not in practice it seems.

You didn’t specify, but I believe you’re talking about WinForms controls, not ASP.Net or Silverlight or WPF. Right?

If WinForms …

Are you calling Control.Refresh() on the label (or button or whatever), or are you calling .Refresh() on the Form object? Big difference in both behavior and performance. I’d suggest you ought to get good results & acceptable performance if you’re refreshing the correct (minimal) scope.

Refreshing too big a scope or calling .DoEvents() will massacre performance in a resource-constrained environment.

In your original code example, you’re calling


//gets theindex psoition of the control in the form's Control array, returns -1 if not found
Int32 location = findControlIndexNumberFromName(labelName);

on each update. That’s waay inefficient for something that (I assume) won’t change for the life of the form. The built-in control tree walker isn’t very speedy. Suggest you find & save your control reference just once (private property with a null check on the getter?)

Yep winforms!

I wasn’t explicitly calling Refresh() at all or using DoEvents(). I tried adding them to where it updates the controls so like:

private Delegate guiUpdaterFromBackgroundThread(Int32 location, string labelText) {
Controls[location].Text = labelText;
Application.DpEvents();
Refresh();
return null;
}
Didn’t change anything except slow it down, as you note it should. Reading the documentation it looks DoEvents should be running in a background loop automatically as part of the form creation though. I suspect Refresh() is likewise implicitly called because most things usually update without explicitly calling it.
Since I’ve posted that I’ve found I was getting a bunch of ghost packets somehow. Packets that looked blank when spit out for debug.

However if I split the packet characters into separate one character “strings” and concatenate them back into a string I get the packet value for the y coord packets, which also doesn’t display as blank.

I’m using bytes with with a value of 0 to separate strings after transmission down the tcp pipe, kind of a c style null terminated string, not that I know much about c. I think what’s going on is something in my code is tripping up on the terminator characters and passing on strings with null characters in them and gumming up the works later on in the program.

On the bright side snow day tomorrow so I’ll have plenty of time to debug!
I’m learning programming is all the fun of figuring out a tricky mystery novel you wrote yourself.

The whole point of Control.Refresh() is that if you are either waiting or doing extensive computations on the main UI thread, then updates to the controls will not appear to the user until your wait or long computation ends and the message pump again has control of the main UI thread.

For a situation where you want the UI to respond promptly to changes *other *than those caused by the user (i.e from mouse or keyboard), then you want to .Refresh() whichever control(s) *you’re updating *from code.

Conversely, …

Application.DoEvents() is a way for your event code to temporarily relinquish control back to the normal message pump. In effect it says “I (the dev) am willing to suspend my busy calculations long enough for you (the framework message pump) to catch up and process all the keystrokes & mouse clicks & form timer ticks which have accrued since the last time I let you have control” And when that’s caught up, .DoEvents() returns to the caller.

So .DoEvents() isn’t about updating the UI programmatically. It’s about having the UI remain responsive to *the user’s *commands while your code is thinking. (Or in your specific case, waiting for, then diddling with, network traffic)

But …

Under the hood, changing a control’s properties (including the Text property) does a bunch of stuff, including creating a message which directs a repaint of the control’s client area. So a side effect of calling .DoEvents() is that it appears to cause an update of UI controls which were changed by code. It does cause that, but also a lot more besides. And calling .DoEvents isn’t necessary to get code-driven UI changes to appear. Calling .Refresh() on the relevant control(s) is sufficient.

On a fast desktop you’ll never see the difference from calling DoEvents() unneccesarily vs. just updating the controls you need. On a phone however …
I whipped up a quick WinForms demo with 2 buttons and 3 checkboxes for input and a label & a textbox for output, each with the obvious names. Then the code looks like:


    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private void btnClear_Click(object sender, EventArgs e) {
            lblResult.Text = "";
            txtResult.Text = "";
        }

        private void btnCount_Click(object sender, EventArgs e) {
            for (int i = 0; i < 1000; i++) {
                lblResult.Text = i.ToString();
                txtResult.Text = i.ToString();
                if (chkLabelRefresh.Checked) lblResult.Refresh();
                if (chkTextboxRefresh.Checked) txtResult.Refresh();
                if (chkDoEvents.Checked) Application.DoEvents();
            }
        }
    }


If you run that you’ll see that with all 3 checkboxes unchecked, the label & textbox don’t count up; they just change from empty to 999 after a short delay. Checking either Refresh checkbox causes that control to count up. Checking the DoEvents checkbox alone causes both output controls to update since DoEvents is then processing the control repaint messages caused by setting the control’s Text properties.

Checking all 3 boxes appears to give the same result as just checking DoEvents. From the user’s POV that’s true, but you’re doing a bunch of unnecessary extra work under the hood.

Turning to the problem with null-terminated strings …

If you control both ends of this communication and don’t have any legacy compatibility concerns, then I suggest a good technique is to NOT use null-terminated strings in messages.

Keep stuff printable. If bandwidth isn’t an issue, use .Net serialization or xml. If bandwidth is a bit of an issue, use a fixed character column approach to constructing the message body. But do convert the binary values into Unicode strings. Never transmit raw binaries. That will pay big dividends when you find your message stream is being consumed by both big- and little-endian machines. It also makes debugging & test harnessing a whole bunch easier.
40 years ago we sweated every byte on every communication channel and in every storage medium. Bytes are now much cheaper, while dev’s browsweat is much more expensive. Make the smart trade-off.

I do have control at both ends!

One end runs a kind of mutant java, Android Dalvik.

I started using the null terminated strings because they were getting jammed together going down the TCP pipe. several strings would come out as on giant string orgy. End to end strings.

It makes sense that they would because how would the pipe know which was the end of a string and which was the beginning? So I figured it’d be good to delimited them so I could sort the mess at the end, and the null character didn’t look like it was much risk of a delimiter crash. Now that the null character is removed it’s working fine. I’m transmitting all data as UTF8, I did that so the code could handle other languages, but it’s good to know it translate well to other architectures. When binary data becomes an issue I’m gonna use base64 encoding, it’s cool stuff in general. You can make base64 "URL"s where the URL its self is the file data. Some browsers have security issues with it so it isn’t reliable for that. Learned that the hardway. Was gonna make an entire website, included images, and file downloads that would have been a single file for my final website project in web design class.

I would have done it too, if not for that meddling IE and its URI security restrictions.
Serialization looks really cool. It looks it’d simplify transmitting the x,y coordinates greatly. They could be transmitted as a single point object. Data files could be made into an object with a base64 data string, and some other strings describing the contents such as file name, etc. transmitted and decoded on the other end. Just so they’re not huge.

Pretty cool, thanks!

This is not my area of expertise, but i suspected a similar problem encountered in IE where asynchronous processing could allow contents to be set in an object before a seperate thread clears the thread for initialization. This sound similar. That’s why I asked about the browser. FF is similarly subject to these problems, but processes much faster than IE so is less likely to demonstrate the problem. As far as I know, the blank content for a label would have to be sent by the server, since the client object would not be modifiable until the object was fully instantiated.

Anyway, your analysis has been informative. :cool: I’d like to program my Droid someday, if I don’t reduce it to smithereens first.:slight_smile: