Win Forms Label Updates Super Slow - c#

I have a Windows Forms application written in C# that does some automated testing. My test runs on the thread pool. The UI has a bunch of labels to display the data read from an external device. During the test, a method writes the new values to a singleton class. The singleton class broadcasts the value change and an event handler in the forms code does the updating after invoking on the UI thread. My problem is that it takes about 1.5 seconds just to update the UI (72 Labels). Why does it take so long? FYI, there is only one event broadcast for all 72 values, not one broadcast for each of the 72 values.
Here is the code (ProgramData.MaximumUnits = 18):
this.InvokeIfRequired(() =>
{
var stopwatch = new Stopwatch();
var rawData = TestData.Instance.RawDataDisplay;
stopwatch.Start();
for (int i = 0; i < ProgramData.MaximumUnits; i++)
{
Frequency1Label[i].Text = rawData[i].F1;
Frequency2Label[i].Text = rawData[i].F2;
TempSensorLabel[i].Text = rawData[i].T1;
UnitInfoLabel[i].Text = rawData[i].ErrorCode;
}
stopwatch.Stop();
MessageBox.Show(stopwatch.ElapsedMilliseconds.ToString());
});
The elapsed time is usually around 1500ms. I feel like this operation should be performed in less than 100ms.
Why this is a problem: I am displaying externally acquired data that comes in every 1 second. Since it takes 1.5 seconds to update the UI, I am in a serious bind, especially when the data can come in as fast as every half a second.

It's possible you're paying the cost of redrawing the form as you change the text of each label. You can try to freeze the form first, by calling SuspendLayout, make all of the changes to the different controls, and when you're done call ResumeLayout to redraw the whole screen at once.
e.g.
SuspendLayout();
try
{
// Update the labels
}
finally
{
ResumeLayout(performLayout: true);
}
Also, check if any of the labels have event handler(s) for the TextChanged event, as changing the text of the labels would trigger this event (and it could be doing something that is slowing you down).

Put the code into Form_load
try
{
ResumeLayout(performLayout: true);
}
finally
{
}
This.Refresh();
You have to This.Refresh() after the ResumeLayout Method Called

Related

UI button running on separate thread?

my issue is the following:
1.I have an intensive method which updates GUI element (chart) in a while loop
2.I need to break out of this loop when a button is pressed.
3.Problem is the button event handler doesn't get executed until the while loop is finished.
4.I've tried running the method on separate thread but that is very problematic since it sets and reads many UI elements, pretty much I couldn't get that to work, so I'm hoping of there being a way to run just the stop button on a separate thread and update a global variable which let's me break out of the loop.
Any Idea how that can be accomplished?
private void playBack(int playTime, int runUntil)
{
var frameTime = new DateTime(); var frameTime_ = new DateTime();
bool fwd = true;
if (runUntil < playTime) fwd = false;
playTime = trPlay.Value;
playGoStop = true;
lbPlayTime.Text = (playTime * numDtStepSize.Value).ToString();
while (true) //trPlay.Maximum + 1)
{
frameTime = DateTime.UtcNow;
if ((frameTime - frameTime_).TotalMilliseconds > (double)(1000 / numFps.Value))
{
systemUpdate(playTime);
trPlay.Value = playTime;
trPlay.Update();
lbPlayTime.Update();
frameTime_ = frameTime;
if (fwd)
{
playTime++;
if (playTime > runUntil) break;
}
else
{
playTime--;
if (playTime < runUntil) break;
}
}
if (!playGoStop) break;
}
}
In your while loop, you can call Application.DoEvents(). It will fetch UI events from event queue, and process those events. Then, your button events will be processed.
You can search by the keyword Application.DoEvents() and C#. There are many topics about it.
UPDATE:
In your codes, it is a infinite while loop inside. I don't like to run a infinite in main thread. I will prefer to run it in a worker-thread. And send message to do UI updates in main-thread. Generally, UI events needs to be processed in the main-thread.
If the infinite while loop is already in the worker-thread, it should sleep about 5~10 ms per loop, in order to free CPU resources to process some events in the other threads.
You should look at binding your UI element (chart) data to a DependencyProperty. This allows you to run the intensive method on a non-UI thread, allowing your UI thread to be responsive to button clicks. While on the background thread, simply make Dispatcher.BeginInvoke() calls to update your DependencyProperty (as it can only be updated from the UI thread) and this will update your control bound to it.
As far as your button interrupt goes, a simple solution is to set a flag from your UI which is checked within each loop iteration. A more complex solution would be to run this intensive method in a task Task, giving it CancellationTokenSource, then cancel the source upon button click.

Possible causes for thread/timer delay?

In relation to a previous question of mine ([question] : Which thread will timer method run in? ), I've added a Timer to my Windows Forms app to run every 100ms to show how long the program session has been running. I've defined it as follows (these are only the snippets relevant to the timer):
private System.Timers.Timer timerPureTime = new System.Timers.Timer(100);
timerPureTime.Elapsed += new System.Timers.ElapsedEventHandler(updateTimeElapsed);
this.timerPureTime.SynchronizingObject = currentForm; //where currentForm is my main Form
public void updateTimeElapsed(object sender, ElapsedEventArgs e)
{
if (currentForm.lblTimeElapsed.InvokeRequired) //lblTimeElapsed is your standard Windows Form label
{
currentForm.lblTimeElapsed.Invoke((MethodInvoker)delegate //also, trying to make make GUI invoking thread-safe here
{
TimeSpan t = TimeSpan.FromSeconds(purelyTime);
string showTime = string.Format("{0:D2} min {1:D2} sec",
t.Minutes,
t.Seconds);
currentForm.lblTimeElapsed.Text = showTime;
});
}
else
{
TimeSpan t = TimeSpan.FromSeconds(purelyTime);
string showTime = string.Format("{0:D2} min {1:D2} sec",
t.Minutes,
t.Seconds);
currentForm.lblTimeElapsed.Text = showTime;
}
purelyTime += 0.1;
}
As I understand it the Timer should be running in a thread of its own (taken from the Threadpool) however it still experiences some delay every now and then, throwing the timer off-course. Other threads within the application run pretty regularly (every 250ms) and computation-intensive, but shouldn't these be independent of Timer threads?
What could be the possible causes for timer lag in such cases?
Windows cannot guarantee a precisely regular callback for a timer, so you will definitely see that kind of variance.
You need to take a different approach:
Initialise a Stopwatch field in your class.
Call Stopwatch.Restart() when you want to reset the timing.
Inside updateTimeElapsed() use Stopwatch.Elapsed instead of purelyTime.
Note that your code is completely ignoring the amount of time spent in the timer handler function itself. Inside the handler, you are using Invoke to send a message to the UI and waiting for it to return. That can take an arbitrary amount of time, particularly if the UI is busy.

Multiple Timers WPF application, The calling thread must be STA Error

My WPF application involves several timers. Everything works with DispatcherTimers but the delays are huge and freezes occur often (for example 3 sec freeze then the remaining 3 sec get added at once).
My problem is i am not sure how exactly i have to redesign my application to get around this problem. Never worked with threads before and its my first real WPF application as well.
I get the following error:
The calling thread must be STA, because many UI components require this.
I got this error by replacing my Dispatcher timer by a Timer from the namespace System.Timers
Old code with dispatcher timer:
timerW = new DispatcherTimer();
timerW.Tick += new EventHandler(timerW_Tick);
timerW.Interval = new TimeSpan(0, 0, 5000);
timerW.Start();
New code with Timer:
timerW = new Timer();
timerW.Elapsed += new ElapsedEventHandler(timerW_Tick);
timerW.Interval = 5000;
timerW.Start();
A query gets executed every 5 seconds to retreieve a date value. When certain conditions are met buttons get dynamically created. For example if the date remains the same for 3 min a button gets created.
Dynamic created button contains:
Date from the database
A timer that starts running when the buttons gets created. This timer only stops when a new button gets created, this records the downtime.
A reason for the downtime
The buttons get saved in a ObservableCollection and use Dependency Properties so they get notified when something changes. It is a custom button with several textblocks to display the information.
The button part is where all the interaction is at in my program, the rest of the interface just displays information fro the database. The buttons get placed in a custom made slider like control with left and right navigation buttons.
So in short my program has 3 different dispatcher timers.
One to display the current time (hh:mm:ss format)
One to execute a sql query and retrieve a date value every 5 sec
One to update the downtime timer every second inside a dynamically custom styled WPF button. I use a stopwatch to record the downtime in between.
So it seems i need to work with treads and/or backgroundworker?
Not sure how i actually begin with this since i am pretty clue less for some time now. Some example code would be most welcome.
Tldr:
I use timers for the following reasons:
Display the current time
Record downtime (real time, so i actually see it counting by the second)
A SQL query that gets executed every 5 seconds.
EDIT:
public void InitializeDispatcherTimerW()
{
TimerCallback callback = MyTimerCallBack;
timerWegingen = new Timer(callback, null, 0, 5000);
timerWegingen.Change(0, 5000);
}
private void MyTimerCallBack(object state)
{
DisplayWegingInfo();
CaculateTimeBetweenWegingen();
}
Best regards,
Jackz.
System.Timers.Timer seems to require an STA thread. An STA thread was a requirement for Windows Forms, but WPF apparently doesn't need it.
Does System.Threading.Timer meet your need?
Here is an example:
// firstTickInterval and interval are TimeSpans
TimerCallback callback = MyTimerCallback;
Timer timer = new Timer(callback, null, firstTickInterval, interval);
// timer is now running
// To stop the timer, do timer.Change(-1, -1);
The callback function would look like this:
void MyTimerCallback(object state)
{
// This is not guaranteed to execute on the UI thread.
// In this example, state will be null, since we passed null in the Timer constructor.
}
When timers callback code interact with UI elements You need to use:
UIElement.Dispatcher.BeginInvoke(...);
More info: http://msdn.microsoft.com/en-us/magazine/cc163328.aspx

Issue with timer updating Form

I have a recursive function and would like to update the form with the time, number of attempts and what the current attempt for the recursive function. Every second, the form should update. The current methodinvoker doesn't work. The compiler will highlight the line but not continue and it will pop back out.
I already tried placing the function on a separate thread but, it preforms poorly, so I would prefer to keep the function on the main thread.
Any ideas?
void bruteForce_DoWork()
{
doBruteForceEID("", 0, wordlen, temp);
}
void _myTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if (InvokeRequired)
{
runTimeSec++;
this.Invoke(new MethodInvoker(delegate() { this.toolStrip.Text = "Running... " + runTimeSec + "s"; }));
Invoke(new MethodInvoker(delegate() { lblAttackA.Text = Convert.ToString(attackCount); }));
Invoke(new MethodInvoker(delegate { lblCurAttempt.Text = brute; }));
}
else
{
runTimeSec++;
toolStrip.Text = "Running... " + runTimeSec + "s";
lblAttackA.Text = Convert.ToString(attackCount);
lblCurAttempt.Text = brute;
}
}
EDIT: Let me explain the program alitte more... Once the user has inputted their password that they would like to check, the following happens after they hit Calculate.
- Runs a dummy recursive algorithm to see how many cycles their machine can do within 5 seconds, then I average them to give a better idea of how many cycles they can do per sec.
back = new Thread(new ThreadStart(testLoop_DoWork)); // Calls the dummy algorithm
back.Start();
if (rbtnTest.Checked)
{
txtEID.Text = txtUID.Text;
lblRunCycle.Text = "Calculating...";
testTimer.Enabled = true; // Starts the Timer
}
Once the 5 second timer is up, I check the length, cycles per second, what characters (upper,lower,numbers,symbols), give how long it will take, add the respective char arrays for to create the brute force char array, then I Join() the thread and disable the timer.
I would get roughy 7million cycles per second avg...(remember this number). Also, I am NOT updating to the GUI during this time.. Just running the created thread.
Then if the user wants to run the attack, it does:
attackBack = new Thread(new ThreadStart(bruteForce_DoWork));
attackBack.Start();
_myTimer.Enabled = true;
Once I start running it on the new thread, it is displaying only about 30,000 cycles per second?? and my timer is only updating every second. What is going on?
Edit: I also just commented out the timer so nothing is updating to the GUI... and the same results occur when the brute force is found (for example the word 'test' should be instant... but it takes 10 seconds to find it...
Yes, this cannot work. The Timer.Elapsed event runs on a threadpool thread. Which means for one that InvokeRequired is always true and doesn't need to be checked. What can't work is the Invoke() call. It requires the UI thread to be idle so it can executed the invoked code.
Your UI thread isn't idle, it is executing the expensive code. This is called deadlock.
You have to do this the other way around, have a worker thread execute the expensive code instead of the UI thread. Which keeps the UI thread responsive, both to update the labels, keep the UI painted and respond to user input. You said you didn't like doing it this way, it is unclear why you are having a problem with it. A classic mistake is updating the UI too often, flooding it with invoke requests so it doesn't get around to its regular duties anymore. A timed update is indeed the solution. Never update more than 25 times per second, the human eye can't tell the difference if you do it more often than that.

"Simple" C# Time Thread Appears to be Freezing

I've been staring at this thread for some time and I believe my mind has shut down on it. Thinking that the best thing to do, in order to update the time in a UI TextBox, would be to create what I thought would be a simple thread to get the time and post it back to the UI control. After having fought with it for a while, I'm getting frustrated and thinking that I might just add the time in some other way. In the intrepid spirit of the adventurer, I'm giving it another go.
I've got a similar thread operating elsewhere in the app that takes a list and populates a DataGridView in a TabControl. I'd have thought that the process would be roughly the same, but I'm missing a key part. The entirety of the thread is below:
private void displayTime()
{
while (true)
{
String time;
String date;
time = DateTime.Now.TimeOfDay.ToString();
int len = time.IndexOf('.');
time = time.Substring(0, len);
date = DateTime.Now.Date.ToString();
len = date.IndexOf(' ');
date = date.Substring(0, len);
updateClock(time, date);
}
}
private void updateClock(String time, String date)
{
if (InvokeRequired)
{
BeginInvoke(new timeDel(updateClock), new object[] {time, date});
return;
}
ctrlTimeTxt.Text = time + "\n" + date;
}
The above thread has been started in various places(in an attempt to debug), but is currently in the Form's Shown event handler. The Form begins to appear, but then everything seems to hang. When I place a breakpoint in the thread, I can step ad infinitum, but the UI never seems to get control back. What am I missing? I'll be happy to expand upon any neglected details.
Edit: A clarification: This thread is being started in a function that is handling the Shown event. The description of the Shown event is given as: Occurs whenever the Form is first shown. I think that might eliminate the theory that the UI thread is Invoking too quickly.
The problem is that your while loop here is generating so many events that the event queue gets flooded:
while (true)
{
// ...
updateClock(time, date);
}
Instead of doing this in a thread you can use a Timer to generate events at a regular interval and do one step of your method for each event. This will also mean that you don't have to use Invoke as the timer generates events on the main thread.
There are also two other errors in your code:
Occasionally TimeOfDay.ToString() could output a time that hits exactly at a second change, in which case the result of ToString() will not be "12:23:34.00000" but just "12:23:34" which will cause your method to throw an exception.
You are calling DateTime.Now twice, but the value could have changed in between the two calls. Normally this won't matter but as it passes midnight it could show '23:59:99.999' but the day shows as the next day. It's probably not a significant error in this application but you ought to get into the habit of only calling DateTime.Now once when you want consistent values for the date and time.
To fix these errors and to simplify your code:
Add a timer to your form.
Set the timer's Enabled property to true in the designer.
Add this event handler to the Tick event:
private void timer_Tick(object sender, EventArgs e)
{
DateTime now = DateTime.Now;
textBox1.Text = now.ToString("HH:mm:ss\nyyyy-MM-dd");
}
You may wish to adjust the format string depending on your needs.
It doesn't surprise me that this isn't going well -- you're stuffing invokes in the UI thread message queue (with your BeginInvoke()) as fast as you can generate them. You may want to put a break (i.e. with System.Threading.Thread.Sleep()) in your displayTime() thread to give the UI thread time to do some work.
How do you call displayTime?
If your Form.Show() method is like this
Show()
{
displayTime();
}
Then as Mark answered you are blocking your main UI thread infinitely.
I would start a new thread
Show()
{
Action displayTimeAction = displayTime;
displayTimeAction.BegingInvoke(null, null);
}

Categories

Resources