Thread.Sleep and Painting UI - c#

I have this stoplight program that models a strange intersection near where I live. I am pretty sure I have the logic down. I have functions that represent every possible state of the lights, including yellows. The problem is that none of my yellow lights are painting. Five seconds after a car pulls up to a light and no other cars have passed through green lights, yellow lights for appropriate lights are supposed to turn on for three seconds, and then turn green. For some reason when I step through my program, no yellow lights are painting. Here is my code:
private void WaitThreeSecs()
{
//somehow I think this is to blame
int threeSeconds = 3000;
Thread.Sleep(threeSeconds);
}
private void FromStateOneToTwo()
{
//Stop Countdown Timers
fiveSec.Stop();
thirtySec.Stop();
//Indicate Current State
CurrentState = 2;
//Paint lights
BottomGreen.Fill = gray;
BottomYellow.Fill = yellow; // this never happens
BottomRed.Fill = gray;
//Wait 3 secs
WaitThreeSecs();
btnbottom.Content = "Car Stops";
//Call function that changes the state for other lights to be green
StateTwo();
}
FromStateOneToTwo() is called after the five seconds, but the light is never yellow. I have no idea why this doesn't happen. The light waits 8 seconds from when a button is pressed to when the appropriate light goes straight from red to green, meaning that my 3 second timer is working, but my painting the light is not. (I paint in other function exactly like i do here, and it works fine)
I can show more/all of my code if needed.

You aren't getting a cross-thread InvalidOperation exception, so I'll assume that the provided function runs on the UI thread. This will happen if you invoke it off of a button-click, or the elapsed event of a DispatcherTimer.
By sleeping the UI thread, you don't give it time to paint the screen. It switches colors, then back, without a paint method ever running.
Your best solution is to use a standard Timer so you aren't on the UI thread, then marshal your changes to the UI thread with Dispatcher.BeginInvoke (WPF) or Control.Invoke(WinForms). You can also eliminate Thread.Sleep and make more timers to trigger the next changes.

Use Application.DoEvents right before the WaitThreeSecs() tot force a screen refresh

Related

Helix Viewport continues zooming while paused at a breakpoint

I am running a Helix viewport and am catching a mousewheeled event with previewmousewheel to run a method. When debugging some changes I made to my methods I found out that when I set a breakpoint (anywhere between the catching of the mousewheeledevent and the subsequent applying of the zoom in the viewport) the amount of zoom from one detent of the mousewheel will increase continuously in proportion to the amount of time the program has been paused (basically the amount of time elapsed from the mousewheeledevent and the applying of the delta to the viewport). This makes debugging my changes the way I normally do seem not possible, though of course I can work around this.
The real problem is that now I realize that the zoom is completely erratic in this way, because some operations take longer than others between the event and the setting of the viewport, depending on what I'm doing.
This seems like a completely haphazard thing to have going on when I'm trying to make changes to a camera and completely control the camera behavior based on details specific to my program.
Is there any way to disable this from happening or mitigate it almost entirely?
The following code is a snippet from my program that seems to be the main driver of the issue, but I'm not sure I can just remove this method of keeping time in the program, because we definitely need an asynchronous timekeeper as far as I know.
private async Task DoWorkAsyncInfiniteLoop()
{
while (true)
{
if (Run)
{
Time.timeElapsed += Time.runSpeed;
Time.simTime += Time.runSpeed;
updateAll();
}
// don't run again for at least 200 milliseconds
await Task.Delay(Time.interval);
}
}
perhaps there is some lines I can add that will basically allow me to apply the zoom to the viewport without waiting for the await Task.Delay(Time.interval) line to run?
I must admit I'm not too clear on how that code works, but I do know that it stops at the await Task.Delay line before finally i hit "step into" (F11) one more time and it somehow just applies the monstrous zoom that was not wanted, without my seeing any code after that being run.
Any workarounds?
Try to set IsInertiaEnabled = false on viewport

How to redraw quickly on a canvas. C# WINFORM

For my software, I am using a Timer from Systems.timer library, every time my timer ticks, it calls a method for repainting my screen. I do not want to clear the screen, then to repaint on it. I just want to paint the new areas on it directly.
At the beginning, I did this:
Constructor{
...
this.timer = new Timer
{
Interval = 10,
};
this.timer.Elapsed += OnPaint;
this.timer.start();
}
public void OnPaint(Object sender, EventArgs e)
{
This.Parent.OnPaintLoadingCircle();
This.Parent.OnPaintReadyToBePaintedAreas();
}
Then I noticed it was much faster for painting when the OnPaint method contains this:
public void OnPaint(Object sender, EventArgs e)
{
This.Parent.Invalidate();
}
So I have two questions:
QUESTION 1 :
Why is it faster???
Because when I call invalidate():
The UI thread clears the screen.
Then UI thread redraws the old areas
Then UI thread draws the loading circle
Then UI thread draws the new areas.
And when I call my two methods OnPaintLoadingCircle() and OnPaintReadyToBePaintedArea():
The timer thread draws the loading circle
Then the timer thread draws the new areas
QUESTION 2 :
I would like to know if it exists a way for asking a controller to draw it surface without clearing it. ( I tried this.Parent.Update(), this.Parent.Refresh(), both of them first clear the screen as well).
Thank you very much for helping me.
Why is it faster???
For the simplest of reasons: because when you call Invalidate() in the OnPaint() method, it forces re-painting of the window immediately, which is much more quickly than a timer could.
The timers in .NET are not suited for high-frequency operations. They only guarantee the time between intervals will be at least what you specify. The actual interval can and often is longer than what you specify, especially if you are using a very short interval (e.g. on the order of less than 10-20ms). This necessarily limits how often you can re-paint the window when using a timer, to a much greater degree than just re-painting the window as fast as you can.
I would like to know if it exists a way for asking a controller to draw it surface without clearing it.
Not easily, no. At the most basic level, you can override OnPaintBackground() and not call the base implementation. But this approach only works if you are prepared to redraw everything, because the system counts on you covering up stale pixels with the correct pixels when you draw.
In fact, a much more common approach is to use double-buffering. The most basic form is to just set the DoubleBuffered property in the control constructor. But you can also combine not clearing the window with maintaining your own offscreen Bitmap object into which you draw your content. Then when a Paint event happens, you just copy the Bitmap to the window.
A much more complicated approach involves hosting a Direct2D surface in your window. Not for the faint of heart, but should offer the best possible performance in a Winforms program.

Why does changing Form.Text to non-empty cause a program to display "(Not Responding)"?

I have come across some behavior that I have no idea how to explain. When I repeatedly draw to a System.Windows.Forms.Form without being careful not to hang the thread, after several seconds the drawing output freezes and I see the dreaded (Not Responding) text in the title:
This screenshot isn't mid-draw; the image it was drawing is completely red at the end of the program, but the drawing has stopped about halfway through. The weird thing is if I skip setting the Form.Text property before drawing, it doesn't freeze. Can anybody explain why?
I was using a loop to draw the progress of some multithreaded graphical fill algorithms, but was seeing the screen output freeze. Since I was drawing and then calling Thread.Sleep() in a loop, I should have expected this behavior (other posts point out I probably should have used BackgroundWorker, but just using Application.DoEvents() stopped the freezing for me).
But what really surprised me when chopping down the code to find my bug was that when I removed the call to change form.Text, it doesn't freeze anymore! It isn't calling the setter that does anything either; assigning to an empty string (form.Text = "";) also causes it not to freeze. So, for some reason when Text is empty Windows doesn't care the program isn't responding, and will happily keep showing the drawing progress! What is going on? Does Windows treat programs with empty titles differently?
I reduced the code down to the bare minimum of what was required to see the freeze. While the program is running the form image will get filled with red by the loop. About halfway through filling it just stops, even though the loop continues running! You see that after it finishes it handles events and draws one more time to show the whole image is red, then stays around for two more seconds.
(If you run with debugging (F5) it doesn't freeze; you must run without debugging (Ctrl-F5). That made figuring all this out all that much harder!)
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
// Include resources System.Drawing, System.Windows.Forms in VS before compiling
class Program
{
static void Main(string[] args) {
Bitmap image = new Bitmap(400, 20);
Form form = new Form();
form.Text = "some title"; // Comment this out to avoid the freeze
form.SetBounds(30, 30, image.Width + 16, image.Height + 38);
form.Show();
Graphics graphics = Graphics.FromHwnd(form.Handle);
for (int x = 0; x < 400; ++x)
for (int y = 0; y < 20; ++y) {
image.SetPixel(x, y, Color.Red);
graphics.DrawImage(image, 0, 0);
Thread.Sleep(1);
// Calling this would prevent the freeze:
//Application.DoEvents();
}
Application.DoEvents();
graphics.DrawImage(image, 0, 0);
Thread.Sleep(2000);
}
}
The Thread.Sleep(); will block your gui thread. Your form isn't updated and you message loop (handling of mouse/paint/keyboard events) is blocked.
Calling the Application.DoEvents will handle messages from the message loop, but it is a bad practice. You allow to close the form even when you for loops aren't ready.
I think the problem is, form.Text = "some title"; sends a message (WM_PAINT) to the window to repaint the window, but it is never handled. whichs turns out non responsive. Probably putting the Application.DoEvents behind the form.Text = "some title";, would fix that.
If you want to update your graphics on a interval, you should take a look at the Timer class. http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx

Is there a way to force C#.NET to finish executing a line of code before sleeping?

What I'd like my program to do is something along these lines:
for (int i = 0; i < btns.Length; i++)
{
//Flash red
btns[i].BackColor = Color.Red;
System.Threading.Thread.Sleep(500);
//Change to green
btns[i].BackColor = Color.Green;
}
Where btns is a collection of buttons that change colour.
But when my code executes, it sleeps for 2.5 seconds (because of the 5 buttons) and then they all change colour to green at once, without flashing red. My guess is that this is because it takes longer to finish changing the colour than it does to reach the Sleep function, which pulls everything to a halt.
I've tried using timers, which had their own problems. For this, I'd rather just change > wait > change. Is there a way I can accomplish that?
My guess is that this is because it takes longer to finish changing the colour than it does to reach the Sleep function, which pulls everything to a halt.
No, the problem is that the thread which is responsible for doing the actual drawing is the one which you've put to sleep. Blocking the UI thread is never a good idea.
I've tried using timers, which had their own problems.
Nevertheless, they're quite possibly the simplest approach before C# 5. Basically you want to change the colour, then let the UI thread get on with whatever it wants to, then change the colour again half a second later, etc. A timer will let you do that.
Using C# 5 does make it easier though. You could write an async method like this:
public async Task FlashButtons()
{
foreach (Button button in buttons) // No need for a for loop here
{
button.BackColor = Color.Red;
await Task.Delay(500);
button.BackColor = Color.Green;
}
}
Using await in conjunction Task.Delay "pauses" your async method, but without actually blocking the UI thread. I don't have time to give details of how async/await works right now, but there are plenty of resources online, such as this MSDN page.

How to Pause C# code execution without blocking main thread?

OK so we have a program where we want to animate the controls on a WinForm and then then resume subsequent operations of the remaining block of code. Here is the sample code.
The function is on the WinForm, which is running on main thread presumably
Private void DoThisWork();
{
do some work here
animateControls()
//<NEED TO PAUSE HERE WHILE THE GUI ANIMATES AND UPDATES DISPLAYING THE ANIMATION OF THE CONTROL>
//Tried Option 1: thread.sleep. When we do this the main thread blocks and the animation is //not seen. The control is directly painted at x1,y1 and thats it, the intermediate rendering is not seen
// Tried Option 2: Application.DoEvents. This works very well except that the CPU maxes out and the animation then appears very jittery
continue doing remaining work // must execute only after animateControls() completes the animation part.
}
Now, animateControls() is simply a function that is on a timer and moves a control from point (x,y) to (x1,y1) and this takes about 3 seconds.
SuspendLayout and ResumeLayout dont force GUI update because thread.sleep caused the main thread to block so everything is virtually at a standstill.
Using a different thread to animate the GUI does not seem to help because I still need for the entire animation to complete.
Also, I cannot add anything in the animation code because it is called from multiple functions and therefore is used as a common function.
Your are going down the wrong path. Put your work to do on a seperate thread and let your UI Thread do your animation till the work thread is finished.
The BackgroundWorker class might come in handy. http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

Categories