简体   繁体   中英

TextBox not being updated

I have a textBox2 on my windows form (confirmed name & spelling) I drug a timer from the toolbox over to my windows form and it is named timer1. I am trying to have the timer begin when the button is pressed and have textBox2 show each incrementing second so it shows a running time of how long the entire process has taken. Where did I err?

public partial class Form1 : Form
{
    private int duration = 0;
    private void button1_Click(object sender, EventArgs e)
    {
        timer1.Enabled = true;
        timer1.Start();
        First();
        Second();
        Thirds();
        Fourth();
        Fifth();
        timer1.Stop();
    }
    private void timer1_Tick(object sender, EventArgs e)
    {
        duration++;
        textBox2.Text = duration.ToString();
    }
}

It's not clear what the methods First() , Second() , and so on represent. But based on the rest of the code, it seems likely those are long-running tasks that you are trying to time.

In any case, the problem is that you never return from the button1_Click() method until after you've stopped the timer. The timer ticks are raised in the UI thread, and can only be raised if the UI thread is not blocked by the handling of some other event. Like the handling of button1 's Click event.

You should be executing your long-running operations in a separate thread, eg using Task . Then the timer will work correctly, or you can even just use a loop in an async method to display the elapsed time, whichever you prefer.

For example:

private int duration = 0;
private async void button1_Click(object sender, EventArgs e)
{
    timer1.Enabled = true;
    timer1.Start();

    await Task.Run(() =>
    {
        First();
        Second();
        Thirds();
        Fourth();
        Fifth();
    });

    timer1.Stop();
}

private void timer1_Tick(object sender, EventArgs e)
{
    duration++;
    textBox2.Text = duration.ToString();
}

Notes:

  • The await feature, new to C# 5.0, is used in the example above. See below for a different approach that is compatible with older versions of C# and .NET.
  • When using await , the async method in which the statement is contained will execute up to the "awaited" expression. At that point, control of the thread is yielded back to the caller of the async method. Later, when the awaited expression finally completes, control of the thread is returned back to the async method at the point where it previously yielded (ie the await statement).
  • In the above, what that means is that your five methods, wrapped in the Task , are executed asynchronously in a different thread. When that Task is started, the button1_Click() method actually returns to its caller. If and when they (ie that Task ) finally complete, execution of the button1_Click() method will resume, at the next statement: timer1.Stop() .


The async / await feature makes code that uses asynchronous operations a lot easier to write. But even before that, there are other mechanisms to do the same thing. Here is one such mechanism, using the BackgroundWorker object:

private async void button1_Click(object sender, EventArgs e)
{
    BackgroundWorker worker = new BackgroundWorker();

    worker.DoWork += (sender1, e1) =>
    {
        First();
        Second();
        Thirds();
        Fourth();
        Fifth();
    };

    worker.RunWorkerCompleted += (sender1, e1) =>
    {
        timer1.Stop();
    };

    timer1.Enabled = true;
    timer1.Start();

    worker.RunWorkerAsync();
}

Notes:

  • When the BackgroundWorker object is created, it captures the current synchronization context (if any), which will be used later to raise all of the BackgroundWorker events except DoWork .
  • The DoWork event is raised using a thread pool thread, ie some thread other than the main UI thread. This allows things to happen on the UI thread even while the long-running work is being executed.
  • The code above might seem a little upside-down. That's because the parts that are executed later, are actually declared earlier as anonymous methods subscribed to the DoWork and RunWorkerCompleted events.
  • Having declared those anonymous methods and subscribed them to their events, the code then actually starts the timer, and then the BackgroundWorker itself. It is only when the RunWorkerAsync() method is called that the worker starts running, by raising the DoWork event.


By the way, while I left your original code as intact as possible, please do note that you don't need both the timer1.Enabled = true; and the timer1.Start() . Either suffices to start the timer; you can use whichever one you like and leave out the other.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM