C# 如何在标签中显示经过的时间
应该直截了当的东西不在这里,尽管读了很多书,我还是找不到办法 我有一个按钮,执行一个耗时的功能。因此,点击按钮时,标签上的时间间隔应为500毫秒,以毫秒为单位。当达到预期结果时,我希望计时器停止我不需要标签中的最后时间(消耗的总时间),但标签应动态显示经过的时间。我的代码是:C# 如何在标签中显示经过的时间,c#,.net,winforms,timer,C#,.net,Winforms,Timer,应该直截了当的东西不在这里,尽管读了很多书,我还是找不到办法 我有一个按钮,执行一个耗时的功能。因此,点击按钮时,标签上的时间间隔应为500毫秒,以毫秒为单位。当达到预期结果时,我希望计时器停止我不需要标签中的最后时间(消耗的总时间),但标签应动态显示经过的时间。我的代码是: private void btnHistory_Click(object sender, EventArgs e) { Class1 c = new Class1(); c.
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
事件仅在触发按钮下的完整代码事件之后才会触发。也就是说,滴答声事件在通过StopClock函数后被触发!我不知道为什么会这样
2个基本问题:
编辑:我在此处使用了
System.Windows.Forms Timer
,但结果与System.Timers Timer
没有任何不同。问题是,您的长期运行任务也在UI线程上运行。因此计时器无法启动和更新UI,因为线程正忙于处理长时间运行的任务
相反,您应该使用来处理长时间运行的任务
代码:
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()
如果不需要上述任何一项,您可以使用线程池,如StaWho的回答所示。将计时器添加到表单的组件集合中。或者将计时器存储在类中的字段中 计时器是垃圾收集的,因为当方法返回时它不再可访问 我不知道您的长时间运行的代码,但我们应该在单独的线程上运行新代码,或者调用Application.DoEvents
(并删除代码中的ref,因为它未被使用)。耗时的函数正在阻塞主线程。您可以使用
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();
}
@DainelRose的回答对我来说非常有效,但只有在处理无效的跨线程操作时才有效。我可以这样做:
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();
}
在运行耗时函数的实用程序
类中
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)
}
谢谢所有帮忙的人。我在标记丹尼尔的答案。你确定têTick只被解雇一次吗?你真的调试过吗?@Shai,没有,它从秒钟功能中出来后,不停地不停地发射。但是由于我已经给出了一个if子句来停止计时器,它很容易停止计时器,因此只会滴答一次。希望你明白我的意思,既然如此,计时器a的真正意义是什么?毕竟我们通常在一个线程上执行整个程序,对吗?@nawfal:定期运行代码。如果需要多个线程,可以使用多个线程。这与计时器完全正交。我们通常在一个线程上执行整个程序,因为它更容易理解和管理。但是,在这种情况下,您希望同时发生两件事:运行长期运行的函数和更新UI。您可以手动(即在您的函数中偶尔更新UI)或“自动”执行此操作通过2个线程。@Daniel您应该在回答中提到,直接从后台工作程序访问UI是不安全的,但所有UI访问都需要封送回UI线程。@DanielRose为什么您的代码会给出编译错误。似乎花括号没有正确实现请让我注意一下,谢谢..我知道ref,但当我看到标签上没有响应时,我加倍小心了如何向控件集合添加单独创建的计时器?我厌倦了
控件。Add()
,但是它说计时器不是有效的控件。对于BackgroundWorker,您可以显式地更新UI(在ProgressChanged处理程序中)。如果不需要,可以使用线程池。请注意,在.NET4.5中,由于使用了新的异步API,这将更加容易。