2

A basic C# Problem.

I have two disabled textboxes on my form, and one button. The button is currently like so:

    private void start_Click(object sender, EventArgs e)
    {
        this.two();
        this.hold();
        this.one()
    }

As you can see it calls three other methods that are like so:

  • "two" - Makes both the text boxes BackColor, black.
  • "one" - Makes just one of the boxes BackColor Black.
  • "hold"
    • Calls "System.Threading.Thread.Sleep(1000);" to pause the program for a second.
    • Changes the backcolour for both the text boxes back to white.

The problem I am encountering is that program seems to skip the first call to two. And just hold the program for a second and change one textbox to black.

Thanks for any help!

3
  • Don't forget the ; at the end of one. Also, if you only have the button call this.two(), does it successfully change the two boxed to black? Commented Feb 3, 2012 at 22:10
  • you are doing everything on serverside and when all the method call is done u see the behavior on the page. so the behavior you describe i think it's expected. did you step through it to see if it skippig the call to "two"? Commented Feb 3, 2012 at 22:11
  • Please listen to KeithS and then read this: en.wikipedia.org/wiki/Message_loop_in_Microsoft_Windows Commented Feb 3, 2012 at 22:32

5 Answers 5

3

What you are probably experiencing is that you are running this code on the application's main thread, which is also responsible for responding to Windows GUI messages (like the command to repaint itself after you have changed the textboxes' colors). The program currently does not process this message until after this event handler has completely executed, at which time only one textbox is black.

Application.DoEvents(), as mentioned, will solve the problem by interrupting this handler and allowing the program to process the queued messages from the GUI before proceeding. Another way to do this would be to run this code in a BackgroundWorker, which would use a separate processing thread to tell the GUI thread to recolor the textboxes, while not blocking the GUI thread with the Sleep() command. Generally, using a multithreaded approach is better, as Application.DoEvents() can cause unexpected consequences by forcing the invocation of other event handlers while the current one hasn't finished (for instance, a call to another event handler that throws an exception will throw out through the Application.DoEvents call of the one you've interrupted, which didn't actually have a problem).

Sign up to request clarification or add additional context in comments.

Comments

3

As other mentioned, it's because your UI thread began to sleep before having started to refresh the display.

Never call Thread.Sleep on the UI thread. It'll "block" your program and angers your users. If you have some job to do which requires Thread.Sleep, create a new thread but don't do it in the UI thread.

Comments

2

It doesn't skip the call to two, but since you're blocking the UI thread with your call to Thread.Sleep, the UI never has a chance to refresh to show the effects of two.

You could do something like this:

private void start_Click(object sender, EventArgs e)
{
    this.two()
    Task.Factory.StartNew(() => this.hold())
                .ContinueWith(_ => this.one(), TaskScheduler.FromCurrentSynchronizationContext());
}

It will execute hold in a new thread (thus not freezing the UI) then, when hold is complete, it will execute two on the UI thread.

3 Comments

Wouldn’t this cause a thread-pool thread to get blocked for that one-second delay? I know this won’t be an issue in the majority of cases, but if you have several such delays running concurrently, then you could exhaust the thread pool.
@Douglas: well, this could happen every time you perform a long operation on a thread from the pool... this doesn't mean you can never do long operations on the ThreadPool.
You can, but in most cases, you shouldn’t. TPL introduced the TaskCreationOptions.LongRunning option specifically to avoid hogging up the thread pool by long operations (creating a dedicated thread instead). And a long sleep should probably never waste a thread-pool thread, especially when you have a Timer class that achieves the same functionality.
2

Try this:

private void start_Click(object sender, EventArgs e)
{
    this.two();
    Application.DoEvents();
    this.hold();
    this.one();
}

Calling DoEvents(); allows the window to repaint in the middle of the click handler, rather than waiting until completion.

Edit: Please read the comments on this answer... You'll see that it's "bad practice" to perform blocking operations like this.

8 Comments

Yuck. Promoting bad programming practices to a beginner with no illustration as to why this is happening (i.e., the message pump model) is worse than no answer at all. -1
@EdS. Heh... I'm not promoting it, but it's not my job to preach... I'm simply here to provide a quick solution to the stated problem.
I don't think it would be preaching. We have a beginner who obviously doesn't know what is going on behind the scenes. Now we have a beginner who is going to be sprinkling calls to DoEvents throughout there code instead of just writing it correctly to begin with. This is a hack at best, and it doesn't teach the OP anything.
Agree with Ed S. DoEvents() actually make the UI to refresh, but it's a bad practice.
I agree with you both, but look at the question again... the button changes button background colors... does this sound like something that requires best practice? He's already learning something by finding this out.
|
1

Since you’re executing your Thread.Sleep on the UI thread, your form does not get a chance to update itself and repaint with the new colors.

You can introduce an Application.DoEvents call before your hold; however, as others have pointed out, this would result in your UI thread remaining frozen (and unresponsive to user activity) for the one-second delay.

The best way to introduce a delay in your logic is to use a Timer. Here is some sample code which executes two after a one-second delay.

Timer timer = new Timer { Interval = 1000 };
timer.Tick += (sender, e) =>
{
    this.Invoke(new Action(two));
    timer.Stop();
    timer.Dispose();
};
timer.Start();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.