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 语句操作开始重叠其他:(.

我在应用程序中放置了名为 timer2windows.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;
}