C# WInform: Entering a loop stops interaction with buttons and display

C# Winform - prob a simple solution

I Enter a loop that checks the current time and a TimeDate I created and is supposed to display the difference on the fly so I did (and even threw in a this.refresh and 1 second delay)
do {
update time left (pictureboxes are numbers)
} while (nowTime < endTime);

All that happens is the timer box shows up but never changes and I cannot click any buttons?

I have read about this but kind of a noob at winforms.

KarlS2010

do {
 
                totalTimeLeft = endTime.Subtract(nowTime); // Calculate TIME left            
                totalTimeElapsed = startTime.Subtract(nowTime); // Calculate TIME elapsed
 
                // UPDATE COUNTDOWN TIMER IMAGES USING TIME REMAINING (totalTimeLeft)
 
                String Remaining = Convert.ToString(totalTimeLeft);
 
                // Deal with days 
 
                if (Remaining.Substring(1, 1) == ".")
                {
                    D1 = "0";
                    D2 = "0";
                    D3 = Remaining.Substring(0, 1);
                    Remaining = Remaining.Substring(2, Remaining.Length - 2);
                }
                else if (Remaining.Substring(2, 1) == ".")
                {
                    D1 = "0";
                    D2 = Remaining.Substring(0, 1);
                    D3 = Remaining.Substring(1, 1);
                    Remaining = Remaining.Substring(3, Remaining.Length - 3);
                }
                else if (Remaining.Substring(3, 1) == ".")
                {
                    D1 = Remaining.Substring(0, 1);
                    D2 = Remaining.Substring(1, 1);
                    D3 = Remaining.Substring(2, 1);
                    Remaining = Remaining.Substring(4, Remaining.Length - 4);
                }
                else
                {
                    D1 = "0"; D2 = "0"; D3 = "0";
                }
 
                D5 = Remaining.Substring(0, 1);
                D6 = Remaining.Substring(1, 1);
                D8 = Remaining.Substring(3, 1);
                D9 = Remaining.Substring(4, 1);
                D11 = Remaining.Substring(6, 1);
                D12 = Remaining.Substring(7, 1);
                D14 = Remaining.Substring(9, 1);
 
                // UPDATE PICTURE BOXES
                picDay1.Image = Image.FromFile("images/" + D1 + "c.gif");
                picDay2.Image = Image.FromFile("images/" + D2 + "c.gif");
                picDay3.Image = Image.FromFile("images/" + D3 + "c.gif");
                picHour1.Image = Image.FromFile("images/" + D5 + "c.gif");
                picHour2.Image = Image.FromFile("images/" + D6 + "c.gif");
                picMin1.Image = Image.FromFile("images/" + D8 + "c.gif");
                picMin2.Image = Image.FromFile("images/" + D9 + "c.gif");
                picSec1.Image = Image.FromFile("images/" + D11 + "c.gif");
                picSec2.Image = Image.FromFile("images/" + D12 + "c.gif");
                picSec3.Image = Image.FromFile("images/" + D14 + "c.gif");
 
                System.Threading.Thread.Sleep(100); // one second delay 
                this.Refresh();
 
            } while (nowTime < endTime);
        }

Open in new window

KarlS2010Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Mike TomlinsonMiddle School Assistant TeacherCommented:
Yeah...if you don't want to do it "proper" with a separate thread/backgroundworker control then you need to call "Application.DoEvents()" to allow the pending messages in your queue to get processed:

            do {

                // ... your other code ...

                System.Threading.Thread.Sleep(100); // one second delay <--- that's a 1/10th of a second delay, NOT one second!
                Application.DoEvents();
 
            } while (nowTime < endTime);
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
deadlyDevCommented:
The problem is that you are tieing up the UI thread, so no messages can be processed.

There are two solutions to your problem (I believe the 2nd will have the best results)

1. Place a call to Application.DoEvents() in your main loop. This will allow UI calls to be made, and should go some way to leaving your UI functional.

2. Create a new thread and place your code on that thread. The issue with doing this is that your modifications to the picture box properties could cause cross thread exceptions. I have included a snippet which should overcome this... please excuse any simple errors as I am currently having issues with my VS and cannot open it :(

To get going call the StartWorker() method
private void StartWorker()
{
    var t = new Thread(Worker);
    t.Start();
}
 
private void Worker()
{
    //Line 1 to line 44
 
    SetPic(picDay1, D1);
    SetPic(picDay2, D2);
    SetPic(picDay3, D3);
 
    //...
 
    SetPic(picSec1, D11);
    SetPic(picSec2, D12);
    SetPic(picSec3, D14);
 
    System.Threading.Thread.Sleep(1000); // one second delay 
 
    //The first method uses this...
    //Application.DoEvents();
}
 
private delegate void SetPicDele(PictureBox pb, string name);
 
private void SetPic(PictureBox pb, string name)
{
    //This will cause the desired method to be executed on the same 
    //thread as the form.
    this.Invoke(new SetPicDele(SetPicImage), new object[] { pb, name });
}
 
private void SetPicImage(PictureBox pb, string name)
{
    pb.Image = Image.FromFile("images/" + name + "c.gif");
}

Open in new window

0
KarlS2010Author Commented:
Idle -

Yeah...if you don't want to do it "proper" with a separate thread/backgroundworker control

what do you mean?
0
Introduction to Web Design

Develop a strong foundation and understanding of web design by learning HTML, CSS, and additional tools to help you develop your own website.

Mike TomlinsonMiddle School Assistant TeacherCommented:
A lengthy operation that monopolizes the main UI thread should really placed into its own thread as in deadlyDev's example.

While Application.DoEvents() technically works, it is generally considered a bad practice (sorta like painting a dead lawn green...it looks pretty but it's still dry and scratchy).

A solution with DoEvents() is easier to understand...just make sure that you prevent the user from causing re-entrancy such as clicking a button again might force a routine to run again while the first routine is still executing.  This is usually done by disabling controls at the beginning of the routine and re-enabling it at the end.  I don't know if this even applies to your situation...just something to think about.

A threaded solution is more flexible but adds complexity to the picture.

Only you can decide which approach best fits YOUR situation...  =)
0
KarlS2010Author Commented:
When I add Application.DoEvents() it still does not update the counter or allow me to click on any uttons that would result in opening another form. Click a button I added to update the counter on the button click event works fine. Does this mean I should go learn about threaded solutions or am I doing something wrong... thanks again bye the way!

               // UPDATE PICTURE BOXES
                picDay1.Image = Image.FromFile("images/" + D1 + "c.gif");
                picDay2.Image = Image.FromFile("images/" + D2 + "c.gif");
                picDay3.Image = Image.FromFile("images/" + D3 + "c.gif");
                picHour1.Image = Image.FromFile("images/" + D5 + "c.gif");
                picHour2.Image = Image.FromFile("images/" + D6 + "c.gif");
                picMin1.Image = Image.FromFile("images/" + D8 + "c.gif");
                picMin2.Image = Image.FromFile("images/" + D9 + "c.gif");
                picSec1.Image = Image.FromFile("images/" + D11 + "c.gif");
                picSec2.Image = Image.FromFile("images/" + D12 + "c.gif");
                picSec3.Image = Image.FromFile("images/" + D14 + "c.gif");

                System.Threading.Thread.Sleep(500); // one second delay
                Application.DoEvents(); // To avoid losing control of UI


            } while (nowTime < endTime);

0
Mike TomlinsonMiddle School Assistant TeacherCommented:
Well...you need to strike a balance here.

The Sleep() call may allow you to control the speed of the execution (to a certain degree) but DURING that sleep interval your app will be unresponsive!  It will not process paint events or user interaction resulting in a "hung" or "sluggish" app.  Typically speaking, when using Sleep() in the MAIN UI thread, you probably don't want to go above 250.  Using 50 or 100 is usually my limit.

You should call DoEvents() BEFORE Sleep() too...

For longer durations you can use a polling loop that makes SMALL calls to Sleep() like this:

    Private Sub Delay(ByVal DelayInSeconds As Integer)
        Dim ts As TimeSpan
        Dim targetTime As DateTime = DateTime.Now.AddSeconds(DelayInSeconds)
        Do
            ts = targetTime.Subtract(DateTime.Now)
            Application.DoEvents() ' keep app responsive
            System.Threading.Thread.Sleep(50) ' reduce CPU usage
        Loop While ts.TotalSeconds > 0
    End Sub

Example:

    Delay(10) ' hold for ten seconds
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.