C# 简单倒计时 - 我做错了什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/942207/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C# Simple Countdown - What am I doing wrong?
提问by Kevin Dungs
I wanted to make a simple Countdown-Application with C# to show as an example.
我想用 C# 制作一个简单的倒计时应用程序来作为示例。
For the very first and basic version I use a Label to display the current time left in seconds and a Button to start the countdown. The Button's Click-Event is implemented like this:
对于第一个和基本版本,我使用 Label 来显示当前剩余时间(以秒为单位)和一个 Button 开始倒计时。Button 的 Click-Event 是这样实现的:
private void ButtonStart_Click(object sender, RoutedEventArgs e)
{
_time = 60;
while (_time > 0)
{
_time--;
this.labelTime.Content = _time + "s";
System.Threading.Thread.Sleep(1000);
}
}
Now when the user clicks the Button the time is actually counted down (as the application freezes (due to Sleep())) for the chosen amount of time but the Label's context is not refreshed.
现在,当用户单击 Button 时,时间实际上是倒计时的(因为应用程序冻结(由于 Sleep()))所选的时间量,但 Label 的上下文不会刷新。
Am I doing something generally wrong (when it comes to Threads) or is it just a problem with the UI?
我是在做一些普遍错误的事情(当涉及到线程时)还是只是 UI 的问题?
Thank you for your answers! I now use a System.Windows.Threading.DispatcherTimer to do as you told me. Everything works fine so this question is officially answered ;)
谢谢您的回答!我现在使用 System.Windows.Threading.DispatcherTimer 来做你告诉我的。一切正常,所以这个问题得到正式回答;)
For those who are interested: Here is my code (the essential parts)
对于那些有兴趣的人:这是我的代码(基本部分)
public partial class WindowCountdown : Window
{
private int _time;
private DispatcherTimer _countdownTimer;
public WindowCountdown()
{
InitializeComponent();
_countdownTimer = new DispatcherTimer();
_countdownTimer.Interval = new TimeSpan(0,0,1);
_countdownTimer.Tick += new EventHandler(CountdownTimerStep);
}
private void ButtonStart_Click(object sender, RoutedEventArgs e)
{
_time = 10;
_countdownTimer.Start();
}
private void CountdownTimerStep(object sender, EventArgs e)
{
if (_time > 0)
{
_time--;
this.labelTime.Content = _time + "s";
}
else
_countdownTimer.Stop();
}
}
采纳答案by ripper234
Yes, event handlers should not block - they should return immediately. You should implement this by a Timer, BackgroundWorker or Thread (in this order of preference).
是的,事件处理程序不应该阻塞——它们应该立即返回。您应该通过 Timer、BackgroundWorker 或 Thread(按此优先顺序)来实现它。
回答by Marc Gravell
What you are seeing is the effect of a long-running message blocking the windows message queue/pump - which you more commonly associate with the white application screen and "not responding". Basically, if your thread is sleeping, it isn't responding to messages like "paint yourself". You need to make your change and yield controlto the pump.
您所看到的是长时间运行的消息阻塞 Windows 消息队列/泵的效果 - 您通常将其与白色应用程序屏幕和“无响应”相关联。基本上,如果您的线程处于休眠状态,则它不会响应诸如“绘制自己”之类的消息。您需要对泵进行更改和产量控制。
There are various ways of doing this (ripper234 does a good job of listing them). The badway you'll often see is:
有多种方法可以做到这一点(ripper234 很好地列出了它们)。你经常会看到的坏方法是:
{ // your count/sleep loop
// bad code - don't do this:
Application.DoEvents();
System.Threading.Thread.Sleep(1000);
}
I mention this onlyto highlight what notto do; this causes a lot of problems with "re-entrancy" and general code management. A better way is simply to use a Timer
, or for more complex code, a BackgroundWorker
. Something like:
我提到这一点只是为了强调不该做什么;这会导致很多“重入”和通用代码管理问题。更好的方法是简单地使用 a Timer
,或者对于更复杂的代码, a BackgroundWorker
。就像是:
using System;
using System.Windows.Forms;
class MyForm : Form {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.Run(new MyForm());
}
Timer timer;
MyForm() {
timer = new Timer();
count = 10;
timer.Interval = 1000;
timer.Tick += timer_Tick;
timer.Start();
}
protected override void Dispose(bool disposing) {
if (disposing) {
timer.Dispose();
}
base.Dispose(disposing);
}
int count;
void timer_Tick(object sender, EventArgs e) {
Text = "Wait for " + count + " seconds...";
count--;
if (count == 0)
{
timer.Stop();
}
}
}