C# Timer (TimeSpan) 一直在后台滴答作响(if statements apply when not true)
C# Timer (TimeSpan) keeps ticking in background (if statements apply when not true)
我正在尝试制作非常简单的计时器应用程序。我确实有一个 "text" 对象,它显示使用以下方法转换为字符串后的时间:.ToString("hh\:mm\:ss");
不幸的是,在单击按钮后整个事件再次开始 - if 语句正在执行,就好像计时器只是在重复自己(在后台保留旧的刻度和文本值)所以 if 语句操作开始重叠其他:(.
我在应用程序中放置了名为 timer2 的 windows.forms.timer。我还有一个名为 Button01 的按钮和名为 Button01text1 & Button01textleft 的文本对象。背景颜色和计时器停止事件基于文本值比较。
旧代码(字符串使用无效) :
private void Button01_click(object sender, EventArgs e)
{
var startTime = DateTime.Now;
Button01.BackColor = Color.FromName("Green");
Button01textleft.BackColor = Color.FromName("Green");
timer2.Tick += (obj, args) =>
{
Button01text1.Text =
(TimeSpan.FromMinutes(1) - (DateTime.Now - startTime))
.ToString("hh\:mm\:ss");
Button01textleft.Text =
(TimeSpan.FromMinutes(1) - (DateTime.Now - startTime))
.ToString("hh\:mm\:ss");
if (Button01text1.Text == "00:00:30")
{
Button01.BackColor = Color.FromName("Orange");
Button01textleft.BackColor = Color.FromName("Orange");
}
else if (Button01text1.Text == "00:00:00")
{
Button01.BackColor = Color.FromName("Red");
Button01textleft.BackColor = Color.FromName("Red");
timer2.Stop();
}
};
timer2.Enabled = true;
}
新代码(同样的问题,但感谢@Dleh 进行了更新):
public void Button01_Click(object sender, EventArgs e)
{
Timer timer1 = new System.Windows.Forms.Timer();
var startTime = DateTime.Now;
Button01text1.BackColor = Color.FromName("Green");
Button01textleft.BackColor = Color.FromName("Green");
timer1.Tick += (obj, args) =>
{
var now = DateTime.Now;
var timeDifference = (TimeSpan.FromSeconds(30) - (now - startTime));
var stringValue = timeDifference.ToString("hh\:mm\:ss");
Button01text1 = stringValue;
Button01textleft.Text = stringValue;
if (timeDifference <= TimeSpan.FromSeconds(15))
{
Button01text1.BackColor = Color.FromName("Orange");
Button01textleft.BackColor = Color.FromName("Orange");
}
else if (timeDifference <=TimeSpan.FromSeconds(0))
{
Button01text1.BackColor = Color.FromName("Red");
Button01textleft.BackColor = Color.FromName("Red");
timer1.Stop();
}
};
timer1.Enabled = true;
}
现在举例说明会发生什么:
我按下按钮一次 - 它变成绿色,在还剩 30 秒时变成橙色,在 00 时变成红色并停止计时器。
如果我在计数过程中(在 40 秒时)再次按下按钮,它会变成绿色然后变回 60 秒,但会在还剩 50 秒时变为橙色(就好像之前的刻度仍在倒计时一样)如果我没有再次点击按钮,则达到 30)。
我一无所知,不知道为什么会这样 - 因为它应该检查字符串文本值 - 它不应该作为单独的实例存在...
有什么想法吗?_?
正在发生的事情的示例视频: Screen_recording
玛丽亚
您不应该检查时间的字符串值。有时滴答声会发生,“00:00:30”不会完美命中。此外,每次执行 DateTime.Now
时,您都会得到略有不同的结果。您应该将它存储一次并将其用于任何计算。您应该与时间对象而不是字符串值进行比较。
您可以重新初始化 timer2
以使其停止/重新启动。
private void Button01_click(object sender, EventArgs e)
{
//reinintialize your timer to stop the old one.
timer2 = new System.Windows.Forms.Timer();
var startTime = DateTime.Now;
Button01.BackColor = Color.FromName("Green");
Button01textleft.BackColor = Color.FromName("Green");
timer2.Tick += (obj, args) =>
{
var now = DateTime.Now;
var timeDifference = (TimeSpan.FromMinutes(1) - (now - startTime));
var stringValue = timeDifference.ToString("hh\:mm\:ss");
Button01text1.Text = stringValue;
Button01textleft.Text = stringValue;
if (timeDifference <= TimeSpan.FromSeconds(30))
{
Button01.BackColor = Color.FromName("Orange");
Button01textleft.BackColor = Color.FromName("Orange");
}
else if (timeDifference <= TimeSpan.FromSeconds(0))
{
Button01.BackColor = Color.FromName("Red");
Button01textleft.BackColor = Color.FromName("Red");
timer2.Stop();
}
};
timer2.Enabled = true;
}
这里发生的事情是,每次单击按钮时,您都会不断添加更多事件侦听器,并且它们以奇怪的方式进行交互。因为每个事件可以有 多个 侦听器,所以只要事件发生(在本例中为 Tick
),每个事件都会触发。因此,第一次单击按钮时,您将 lambda A 添加为侦听器。它愉快地继续做你想做的事。下次您单击按钮时,您添加了第二个侦听器 (B),事情变得很奇怪。
这就是你重新开始时会发生的事情。处理程序 A 执行并将时间更改为 40 秒,进行 if
检查但什么都不做(文本在您单击按钮时已经是绿色的,这不会改变它)。然后处理程序 B 执行并立即将时间更改为 60 秒(也保留颜色不变)。这发生在每个滴答声上,所以两个处理程序 运行 并来回更改时间。一段时间后,A 将执行并将时间更改为 30 秒。然后它将颜色更改为橙色。然后 B 将 运行,将时间改回 50, 但不将颜色更改为绿色 (它只是将颜色保留为原来的颜色)。这样继续下去,导致颜色以奇怪的方式变化。
要解决此问题,您需要做的是在单击按钮时删除以前的处理程序。
它表现异常的原因是每次单击按钮时,都会向计时器添加另一个计时器滴答事件。
此代码:
timer2.Tick += (obj, args) =>
不只是将一个事件分配给计时器滴答,它添加一个事件。
如果您将计时器间隔更改为 5000 并在 tick 事件中放置一个断点,然后单击该按钮两次,那么您将看到它每 5 秒到达该断点两次。
这应该有效:
private DateTime startTime;
private void timerTick(Object obj, EventArgs args) {
Button01text1.Text =
(TimeSpan.FromMinutes(1) - (DateTime.Now - startTime))
.ToString("hh\:mm\:ss");
Button01textleft.Text =
(TimeSpan.FromMinutes(1) - (DateTime.Now - startTime))
.ToString("hh\:mm\:ss");
if (Button01text1.Text == "00:00:30")
{
Button01.BackColor = Color.FromName("Orange");
Button01textleft.BackColor = Color.FromName("Orange");
}
else if (Button01text1.Text == "00:00:00")
{
Button01.BackColor = Color.FromName("Red");
Button01textleft.BackColor = Color.FromName("Red");
timer2.Stop();
}
}
public void Button01_Click(object sender, EventArgs e)
{
startTime = DateTime.Now;
Button01.BackColor = Color.FromName("Green");
Button01textleft.BackColor = Color.FromName("Green");
timer2.Tick -= timerTick;
timer2.Tick += timerTick;
timer2.Enabled = true;
}
这里是承诺的多个定时器的更通用的解决方案:
// State of specific counter
private class Counter
{
public Timer timer;
public DateTime startTime;
public Button button;
public Label text;
public Label textLeft;
}
// List of counters
private List<Counter> counters;
private void Form1_Load(object sender, EventArgs e)
{
// Initialize counters
counters = new List<Counter>();
counters.Add(new Counter { timer = timer1, button = Button01, text = Button01text, textLeft = Button01textleft });
counters.Add(new Counter { timer = timer2, button = Button02, text = Button02text, textLeft = Button02textleft });
counters.Add(new Counter { timer = timer3, button = Button03, text = Button03text, textLeft = Button03textleft });
counters.Add(new Counter { timer = timer4, button = Button04, text = Button04text, textLeft = Button04textleft });
counters.Add(new Counter { timer = timer5, button = Button05, text = Button05text, textLeft = Button05textleft });
// Add more if you need
// Attach handlers
foreach (var counter in counters)
{
counter.timer.Tick += timerTick;
counter.button.Click += buttonClick;
}
}
private void timerTick(Object sender, EventArgs args)
{
// Prepare context
var timer = (Timer) sender;
var counter = counters.Find(c => c.timer == timer);
var startTime = counter.startTime;
var button = counter.button;
var text = counter.text;
var textLeft = counter.textLeft;
// Update time
var time = TimeSpan.FromMinutes(1) - (DateTime.Now - startTime);
text.Text = time.ToString("hh\:mm\:ss");
textLeft.Text = time.ToString("hh\:mm\:ss");
if (time.TotalSeconds < 1)
{
button.BackColor = Color.FromName("Red");
textLeft.BackColor = Color.FromName("Red");
timer.Stop();
}
else if (time.TotalSeconds < 31)
{
button.BackColor = Color.FromName("Orange");
textLeft.BackColor = Color.FromName("Orange");
}
}
public void buttonClick(object sender, EventArgs e)
{
// Prepare context
var button = (Button)sender;
var counter = counters.Find(c => c.button == button);
var timer = counter.timer;
var textLeft = counter.textLeft;
// Start counter
counter.startTime = DateTime.Now;
button.BackColor = Color.FromName("Green");
textLeft.BackColor = Color.FromName("Green");
timer.Enabled = true;
}
我正在尝试制作非常简单的计时器应用程序。我确实有一个 "text" 对象,它显示使用以下方法转换为字符串后的时间:.ToString("hh\:mm\:ss");
不幸的是,在单击按钮后整个事件再次开始 - if 语句正在执行,就好像计时器只是在重复自己(在后台保留旧的刻度和文本值)所以 if 语句操作开始重叠其他:(.
我在应用程序中放置了名为 timer2 的 windows.forms.timer。我还有一个名为 Button01 的按钮和名为 Button01text1 & Button01textleft 的文本对象。背景颜色和计时器停止事件基于文本值比较。
旧代码(字符串使用无效) :
private void Button01_click(object sender, EventArgs e)
{
var startTime = DateTime.Now;
Button01.BackColor = Color.FromName("Green");
Button01textleft.BackColor = Color.FromName("Green");
timer2.Tick += (obj, args) =>
{
Button01text1.Text =
(TimeSpan.FromMinutes(1) - (DateTime.Now - startTime))
.ToString("hh\:mm\:ss");
Button01textleft.Text =
(TimeSpan.FromMinutes(1) - (DateTime.Now - startTime))
.ToString("hh\:mm\:ss");
if (Button01text1.Text == "00:00:30")
{
Button01.BackColor = Color.FromName("Orange");
Button01textleft.BackColor = Color.FromName("Orange");
}
else if (Button01text1.Text == "00:00:00")
{
Button01.BackColor = Color.FromName("Red");
Button01textleft.BackColor = Color.FromName("Red");
timer2.Stop();
}
};
timer2.Enabled = true;
}
新代码(同样的问题,但感谢@Dleh 进行了更新):
public void Button01_Click(object sender, EventArgs e)
{
Timer timer1 = new System.Windows.Forms.Timer();
var startTime = DateTime.Now;
Button01text1.BackColor = Color.FromName("Green");
Button01textleft.BackColor = Color.FromName("Green");
timer1.Tick += (obj, args) =>
{
var now = DateTime.Now;
var timeDifference = (TimeSpan.FromSeconds(30) - (now - startTime));
var stringValue = timeDifference.ToString("hh\:mm\:ss");
Button01text1 = stringValue;
Button01textleft.Text = stringValue;
if (timeDifference <= TimeSpan.FromSeconds(15))
{
Button01text1.BackColor = Color.FromName("Orange");
Button01textleft.BackColor = Color.FromName("Orange");
}
else if (timeDifference <=TimeSpan.FromSeconds(0))
{
Button01text1.BackColor = Color.FromName("Red");
Button01textleft.BackColor = Color.FromName("Red");
timer1.Stop();
}
};
timer1.Enabled = true;
}
现在举例说明会发生什么:
我按下按钮一次 - 它变成绿色,在还剩 30 秒时变成橙色,在 00 时变成红色并停止计时器。
如果我在计数过程中(在 40 秒时)再次按下按钮,它会变成绿色然后变回 60 秒,但会在还剩 50 秒时变为橙色(就好像之前的刻度仍在倒计时一样)如果我没有再次点击按钮,则达到 30)。
我一无所知,不知道为什么会这样 - 因为它应该检查字符串文本值 - 它不应该作为单独的实例存在...
有什么想法吗?_?
正在发生的事情的示例视频: Screen_recording
玛丽亚
您不应该检查时间的字符串值。有时滴答声会发生,“00:00:30”不会完美命中。此外,每次执行 DateTime.Now
时,您都会得到略有不同的结果。您应该将它存储一次并将其用于任何计算。您应该与时间对象而不是字符串值进行比较。
您可以重新初始化 timer2
以使其停止/重新启动。
private void Button01_click(object sender, EventArgs e)
{
//reinintialize your timer to stop the old one.
timer2 = new System.Windows.Forms.Timer();
var startTime = DateTime.Now;
Button01.BackColor = Color.FromName("Green");
Button01textleft.BackColor = Color.FromName("Green");
timer2.Tick += (obj, args) =>
{
var now = DateTime.Now;
var timeDifference = (TimeSpan.FromMinutes(1) - (now - startTime));
var stringValue = timeDifference.ToString("hh\:mm\:ss");
Button01text1.Text = stringValue;
Button01textleft.Text = stringValue;
if (timeDifference <= TimeSpan.FromSeconds(30))
{
Button01.BackColor = Color.FromName("Orange");
Button01textleft.BackColor = Color.FromName("Orange");
}
else if (timeDifference <= TimeSpan.FromSeconds(0))
{
Button01.BackColor = Color.FromName("Red");
Button01textleft.BackColor = Color.FromName("Red");
timer2.Stop();
}
};
timer2.Enabled = true;
}
这里发生的事情是,每次单击按钮时,您都会不断添加更多事件侦听器,并且它们以奇怪的方式进行交互。因为每个事件可以有 多个 侦听器,所以只要事件发生(在本例中为 Tick
),每个事件都会触发。因此,第一次单击按钮时,您将 lambda A 添加为侦听器。它愉快地继续做你想做的事。下次您单击按钮时,您添加了第二个侦听器 (B),事情变得很奇怪。
这就是你重新开始时会发生的事情。处理程序 A 执行并将时间更改为 40 秒,进行 if
检查但什么都不做(文本在您单击按钮时已经是绿色的,这不会改变它)。然后处理程序 B 执行并立即将时间更改为 60 秒(也保留颜色不变)。这发生在每个滴答声上,所以两个处理程序 运行 并来回更改时间。一段时间后,A 将执行并将时间更改为 30 秒。然后它将颜色更改为橙色。然后 B 将 运行,将时间改回 50, 但不将颜色更改为绿色 (它只是将颜色保留为原来的颜色)。这样继续下去,导致颜色以奇怪的方式变化。
要解决此问题,您需要做的是在单击按钮时删除以前的处理程序。
它表现异常的原因是每次单击按钮时,都会向计时器添加另一个计时器滴答事件。
此代码:
timer2.Tick += (obj, args) =>
不只是将一个事件分配给计时器滴答,它添加一个事件。
如果您将计时器间隔更改为 5000 并在 tick 事件中放置一个断点,然后单击该按钮两次,那么您将看到它每 5 秒到达该断点两次。
这应该有效:
private DateTime startTime;
private void timerTick(Object obj, EventArgs args) {
Button01text1.Text =
(TimeSpan.FromMinutes(1) - (DateTime.Now - startTime))
.ToString("hh\:mm\:ss");
Button01textleft.Text =
(TimeSpan.FromMinutes(1) - (DateTime.Now - startTime))
.ToString("hh\:mm\:ss");
if (Button01text1.Text == "00:00:30")
{
Button01.BackColor = Color.FromName("Orange");
Button01textleft.BackColor = Color.FromName("Orange");
}
else if (Button01text1.Text == "00:00:00")
{
Button01.BackColor = Color.FromName("Red");
Button01textleft.BackColor = Color.FromName("Red");
timer2.Stop();
}
}
public void Button01_Click(object sender, EventArgs e)
{
startTime = DateTime.Now;
Button01.BackColor = Color.FromName("Green");
Button01textleft.BackColor = Color.FromName("Green");
timer2.Tick -= timerTick;
timer2.Tick += timerTick;
timer2.Enabled = true;
}
这里是承诺的多个定时器的更通用的解决方案:
// State of specific counter
private class Counter
{
public Timer timer;
public DateTime startTime;
public Button button;
public Label text;
public Label textLeft;
}
// List of counters
private List<Counter> counters;
private void Form1_Load(object sender, EventArgs e)
{
// Initialize counters
counters = new List<Counter>();
counters.Add(new Counter { timer = timer1, button = Button01, text = Button01text, textLeft = Button01textleft });
counters.Add(new Counter { timer = timer2, button = Button02, text = Button02text, textLeft = Button02textleft });
counters.Add(new Counter { timer = timer3, button = Button03, text = Button03text, textLeft = Button03textleft });
counters.Add(new Counter { timer = timer4, button = Button04, text = Button04text, textLeft = Button04textleft });
counters.Add(new Counter { timer = timer5, button = Button05, text = Button05text, textLeft = Button05textleft });
// Add more if you need
// Attach handlers
foreach (var counter in counters)
{
counter.timer.Tick += timerTick;
counter.button.Click += buttonClick;
}
}
private void timerTick(Object sender, EventArgs args)
{
// Prepare context
var timer = (Timer) sender;
var counter = counters.Find(c => c.timer == timer);
var startTime = counter.startTime;
var button = counter.button;
var text = counter.text;
var textLeft = counter.textLeft;
// Update time
var time = TimeSpan.FromMinutes(1) - (DateTime.Now - startTime);
text.Text = time.ToString("hh\:mm\:ss");
textLeft.Text = time.ToString("hh\:mm\:ss");
if (time.TotalSeconds < 1)
{
button.BackColor = Color.FromName("Red");
textLeft.BackColor = Color.FromName("Red");
timer.Stop();
}
else if (time.TotalSeconds < 31)
{
button.BackColor = Color.FromName("Orange");
textLeft.BackColor = Color.FromName("Orange");
}
}
public void buttonClick(object sender, EventArgs e)
{
// Prepare context
var button = (Button)sender;
var counter = counters.Find(c => c.button == button);
var timer = counter.timer;
var textLeft = counter.textLeft;
// Start counter
counter.startTime = DateTime.Now;
button.BackColor = Color.FromName("Green");
textLeft.BackColor = Color.FromName("Green");
timer.Enabled = true;
}