文本框未更新
TextBox not being updated
我的 windows 表单上有一个 textBox2(已确认名称和拼写)我将工具箱中的计时器添加到我的 windows 表单中,它被命名为 timer1。我试图让计时器在按下按钮时开始计时,并让 textBox2 显示每个递增的秒数,以便显示整个过程所用时间的 运行 时间。我哪里做错了?
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();
}
}
不清楚First()
、Second()
等方法代表什么。但是根据其余代码,这些似乎很可能是您尝试计时的长期 运行 任务。
无论如何,问题是在停止计时器之前,您永远不会从 button1_Click()
方法中 return。计时器滴答在 UI 线程中引发,并且只有在 UI 线程未被其他事件的处理阻塞时才能引发。就像 button1
的 Click
事件的处理。
你应该在一个单独的线程中执行你的 long-运行 操作,例如使用 Task
。然后计时器将正常工作,或者您甚至可以在 async
方法中使用循环来显示经过的时间,无论您喜欢哪个。
例如:
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();
}
备注:
- C# 5.0 新增的
await
功能在上面的示例中使用。请参阅下文,了解与旧版本的 C# 和 .NET 兼容的不同方法。
- 当使用
await
时,包含语句的async
方法将执行到"awaited"表达式。此时,线程的控制权交还给 async
方法的调用者。稍后,当等待的表达式最终完成时,线程的控制权 return 返回到 async
方法之前产生的点(即 await
语句)。
- 在上面,这意味着包装在
Task
中的五个方法在不同的线程中异步执行。当 Task
启动时,button1_Click()
方法实际上 return 发送给它的调用者。如果它们(即 Task
)最终完成,button1_Click()
方法的执行将在下一个语句处恢复:timer1.Stop()
.
async
/await
特性使得使用异步操作的代码更容易编写。但即使在此之前,还有其他机制可以做同样的事情。这是一种这样的机制,使用 BackgroundWorker
对象:
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();
}
备注:
- 创建
BackgroundWorker
对象时,它会捕获当前同步上下文(如果有),稍后将使用它来引发除 DoWork
之外的所有 BackgroundWorker
事件。
DoWork
事件是使用线程池线程引发的,即主 UI 线程以外的某个线程。这允许在 UI 线程上发生事情,即使正在执行 long-运行 工作。
- 上面的代码可能看起来有点颠倒。那是因为稍后执行的部分实际上是较早声明为订阅
DoWork
和 RunWorkerCompleted
事件的匿名方法。
- 声明了那些匿名方法并订阅了它们的事件后,代码实际上启动了计时器,然后是
BackgroundWorker
本身。只有在 RunWorkerAsync()
方法被调用时,worker 才会启动 运行,引发 DoWork
事件。
顺便说一句,虽然我尽可能完整地保留了您的原始代码,但请注意您不需要 both timer1.Enabled = true;
和 timer1.Start()
。要么足以启动计时器;要么你可以使用你喜欢的任何一个而忽略另一个。
我的 windows 表单上有一个 textBox2(已确认名称和拼写)我将工具箱中的计时器添加到我的 windows 表单中,它被命名为 timer1。我试图让计时器在按下按钮时开始计时,并让 textBox2 显示每个递增的秒数,以便显示整个过程所用时间的 运行 时间。我哪里做错了?
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();
}
}
不清楚First()
、Second()
等方法代表什么。但是根据其余代码,这些似乎很可能是您尝试计时的长期 运行 任务。
无论如何,问题是在停止计时器之前,您永远不会从 button1_Click()
方法中 return。计时器滴答在 UI 线程中引发,并且只有在 UI 线程未被其他事件的处理阻塞时才能引发。就像 button1
的 Click
事件的处理。
你应该在一个单独的线程中执行你的 long-运行 操作,例如使用 Task
。然后计时器将正常工作,或者您甚至可以在 async
方法中使用循环来显示经过的时间,无论您喜欢哪个。
例如:
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();
}
备注:
- C# 5.0 新增的
await
功能在上面的示例中使用。请参阅下文,了解与旧版本的 C# 和 .NET 兼容的不同方法。 - 当使用
await
时,包含语句的async
方法将执行到"awaited"表达式。此时,线程的控制权交还给async
方法的调用者。稍后,当等待的表达式最终完成时,线程的控制权 return 返回到async
方法之前产生的点(即await
语句)。 - 在上面,这意味着包装在
Task
中的五个方法在不同的线程中异步执行。当Task
启动时,button1_Click()
方法实际上 return 发送给它的调用者。如果它们(即Task
)最终完成,button1_Click()
方法的执行将在下一个语句处恢复:timer1.Stop()
.
async
/await
特性使得使用异步操作的代码更容易编写。但即使在此之前,还有其他机制可以做同样的事情。这是一种这样的机制,使用 BackgroundWorker
对象:
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();
}
备注:
- 创建
BackgroundWorker
对象时,它会捕获当前同步上下文(如果有),稍后将使用它来引发除DoWork
之外的所有BackgroundWorker
事件。 DoWork
事件是使用线程池线程引发的,即主 UI 线程以外的某个线程。这允许在 UI 线程上发生事情,即使正在执行 long-运行 工作。- 上面的代码可能看起来有点颠倒。那是因为稍后执行的部分实际上是较早声明为订阅
DoWork
和RunWorkerCompleted
事件的匿名方法。 - 声明了那些匿名方法并订阅了它们的事件后,代码实际上启动了计时器,然后是
BackgroundWorker
本身。只有在RunWorkerAsync()
方法被调用时,worker 才会启动 运行,引发DoWork
事件。
顺便说一句,虽然我尽可能完整地保留了您的原始代码,但请注意您不需要 both timer1.Enabled = true;
和 timer1.Start()
。要么足以启动计时器;要么你可以使用你喜欢的任何一个而忽略另一个。