如何显示标签中经过的时间

时间:2011-12-22 07:29:14

标签: c# .net c#-4.0 timer

什么应该是直截了当不在这里,尽管阅读了很多,但我还是找不到办法。

我有一个执行耗时功能的按钮。因此,单击该按钮应显示间隔为500毫秒的标签中经过的时间(以毫秒为单位)。当达到预期的结果时,我希望计时器停止。 我不需要标签中的最终时间(消耗的总时间),但标签应该动态显示已过去的时间。我的代码将是:

    private void btnHistory_Click(object sender, EventArgs e)
    {
        Class1 c = new Class1();
        c.StartClock(ref label12);

        Utility.PopulateHistory(dgvRecords_history, _util); //time consuming function

        c.StopClock();
    }

Class1我写这个:

    internal void StartClock(ref Label l)
    {
        Timer t = new Timer();
        t.Interval = 500;
        t.Enabled = true;
        t.Tag = l;
        t.Tick += new EventHandler(t_Tick);
        t.Start();
    }

    int i;
    bool stop;
    void t_Tick(object sender, EventArgs e)
    {
        if (stop)
        {
            ((Timer)sender).Stop();
            return;
        }

        ((Label)((Timer)sender).Tag).Text = (++i).ToString();
    }

    internal void StopClock()
    {
        i = 0;
        stop = true;
    }

发生的情况是,t_Tick事件仅在触发按钮事件下的完整代码后才会触发。这是tick事件在通过StopClock函数后触发!我不知道为什么它应该是那样的!

基本上有两个问题:

  1. 如何以正确的方式处理这些要求?我知道我应该使用其他内置类来评估性能,但这仅用于显示目的。为此,理想的方法是什么?

  2. 为什么我的代码无效?

  3. 编辑:我在这里使用了System.Windows.Forms Timer,但结果与System.Timers Timer没有任何区别

4 个答案:

答案 0 :(得分:6)

问题是您的长时间运行任务也在UI线程上运行。因此,计时器无法触发并更新UI,因为线程正忙于处理长时间运行的任务。

相反,您应该使用BackgroundWorker来处理长时间运行的任务。

在代码中:

private void btnHistory_Click(object sender, EventArgs e) 
{ 
    Class1 c = new Class1(ref label12); 
    c.StartClock(); 

    var backgroundWorker = new BackgroundWorker();
    backgroundWorker.DoWork += (s, e) =>
        {
            // time consuming function
            Utility.PopulateHistory(dgvRecords_history, _util);
        };

    backgroundWorker.RunWorkerCompleted += (s, e) =>
        {
            c.StopClock();
        };

    backgroundWorker.RunWorkerAsync();
} 

正如ChrisWue所说,既然你现在在一个单独的线程中有长时间运行的任务,它需要调用对UI线程上的UI控件的任何访问。

如果长时间运行的任务只需要UI中的某些数据启动,则可以将该数据作为RunWorkerAsync()的参数传递。如果需要将一些结果数据输出到UI,可以在RunWorkerCompleted事件的处理程序中执行此操作。如果您偶尔需要在进行更新时更新UI,可以在ProgressChanged事件的处理程序中执行此操作,在DoWork处理程序中调用ReportProgress()

如果不需要上述任何一项,您可以使用ThreadPool,就像StaWho的回答一样。

答案 1 :(得分:2)

耗时的功能阻塞了主线程。您可以使用BackgroundWorker或以下技巧:

    public Form1()
    {
        InitializeComponent();
        t.Tick +=new EventHandler(t_Tick);
        t.Interval = 500;
    }

    int timeElapsed = 0;
    System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
    private void button1_Click(object sender, EventArgs e)
    {
        t.Start();
        ThreadPool.QueueUserWorkItem((x) =>
        {
            TimeConsumingFunction();
        });

    }

    void TimeConsumingFunction()
    {
        Thread.Sleep(10000);
        t.Stop();
    }

    void t_Tick(object sender, EventArgs e)
    {
        timeElapsed += t.Interval;
        label1.Text = timeElapsed.ToString();
    }

答案 2 :(得分:0)

将计时器添加到表单的Components集合中。或者将计时器存储在班级的字段中。

计时器是垃圾收集的,因为当方法返回时,它不再可用。

我不知道你的长时间运行的代码,但是如果新的运行在一个单独的线程上,或者调用Application.DoEvents

(并删除代码中的引用,不使用它。)

答案 3 :(得分:0)

@Dainel Rose的答案对我很有帮助,但前提是处理了无效的跨线程操作。我可以这样做:

private void btnHistory_Click(object sender, EventArgs e) 
{ 
    Class1 c = new Class1(ref label12); 
    c.StartClock(); 

    var backgroundWorker = new BackgroundWorker();
    backgroundWorker.DoWork += ((s, e) =>
        {
            // time consuming function
            Utility.PopulateHistory(dgvRecords_history, _util);
        });

    backgroundWorker.RunWorkerCompleted += ((s, e) =>
        {
            c.StopClock();
        });

    backgroundWorker.RunWorkerAsync();
} 

在耗时函数运行的Utility类中,

    internal static void PopulateHistory(DataGridView dgv, Utility util)
    {
        SetDataGridView_History(dgv, util);
    }

    delegate void UpdateDataGridView_History(DataGridView dgv, Utility util);
    static void SetDataGridView_History(DataGridView dgv, Utility util)
    {
        if (dgv.InvokeRequired)
        {
            UpdateDataGridView_History updaterDelegate = new UpdateDataGridView_History(SetDataGridView_History);
            ((Form)util._w).Invoke(updaterDelegate, new object[] { dgv, util });
        }
        else
            //code that utilizes UI thread (long running process in my case)
    }

感谢所有帮助过的人。我正在标记丹尼尔的回答..