【问题标题】:C# Timer (TimeSpan) keeps ticking in background (if statements apply when not true)C# Timer (TimeSpan) 在后台一直滴答作响(如果语句不适用)
【发布时间】:2015-03-31 14:20:07
【问题描述】:

我正在尝试制作非常简单的计时器应用程序。我确实有一个“文本”对象,它使用以下方法显示其转换为字符串后的时间:.ToString("hh\:mm\:ss");

不幸的是,在单击按钮让整个事件重新开始之后 - if 语句正在执行,就好像计时器只是在重复自身(在后台保留旧的刻度和文本值),所以 if 语句的操作开始相互重叠: (。

我在应用程序中放置了名为 timer2windows.forms.timer。我还有一个名为 Button01 的按钮和一个名为 Button01text1Button01textleft 的文本对象。背景颜色和计时器停止事件基于文本值比较。

OLD CODE(字符串使用无效)

        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

玛丽亚

【问题讨论】:

  • 只是一个善意的想法:请不要以字符串格式比较 DateTime 格式的数据。这可能会在以后困扰您。为程序中需要的数据使用适当的数据类型。
  • 对,这听起来是个好主意——我会尽快看看如何以原始格式比较它们,不过——我没有任何其他地方可以“存储”它们——这就是为什么我使用了字符串文本值 - 因为它们已经显示在屏幕上。
  • @MariaNowinska,当多次单击按钮时,您订阅了许多处理程序的 Tick 事件,这些处理程序具有不同的 startTime。那些处理程序不同步并创建 stange 效果。
  • 如何重新启动它并将计时器恢复为 0,或删除处理程序/销毁滴答事件?

标签: c# timer


【解决方案1】:

它表现异常的原因是每次单击按钮时,都会向计时器添加另一个计时器滴答事件。

这段代码:

timer2.Tick += (obj, args) =>

不只是为计时器滴答分配一个事件,它添加一个事件。

如果将计时器间隔更改为 5000 并在滴答事件中放置一个断点,然后单击该按钮两次,那么您将看到它每 5 秒到达该断点两次。

【讨论】:

  • 我相信你是对的。如何删除旧活动?还是将其重置为 0?
  • 她还应该使用真实的 DateTime 对象而不是字符串,例如存储在 Tag 属性中。
  • 使用常规方法而不是 lambda 轻松删除处理程序 -=
  • 您好,我已经根据一些cmets更改了代码-仍然没有运气!我在每次点击实例中都创建了全新的计时器,它只会让数字变得疯狂......似乎很难将计时器重置回 0:/
  • 您确实需要将 startTime 和 tick 事件处理程序移至外部范围,现在无法删除事件处理程序,也无法按预期工作。
【解决方案2】:

这应该可行:

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;
}

【讨论】:

  • 我会试试的,我在同一个程序上运行了 10 个不同的计时器 - 我需要创建 10 个 timerTick 函数和 10 个按钮单击函数还是有一种方法可以对其进行模板化? :o
  • 这 10 个计时器,他们在不同的按钮上工作相同吗?首先,看看这个解决方案是否有效,我将帮助您使其更通用。
  • 首先 - 它正在工作 _ 天使你。是的,想象一下让 10 个鸡蛋沸腾并以不同的点击运行 10 个单独的计时器,但是将它们放在上面的人 - 不会同时启动它们(所以它们都在不同的计时器上分别计数,但做同样的事情)。
  • 这很漂亮_非常感谢大家。
【解决方案3】:

您不应该检查时间的字符串值。有时会发生滴答声,“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;
}

【讨论】:

  • 您好,您的评论在代码方面做得很漂亮,但它的行为仍然有问题 - 看看这个按钮的屏幕记录:Screen_recording
  • 我认为我没有在代码中包含某些内容...找不到类型或命名空间名称 Stopwatch。
  • 这只是虚拟代码。但是你正在初始化timer2,在这里再做一次
  • 嗨!我用过: Timer timer2 = new System.Windows.Forms.Timer();并从程序的初始化中删除现有的计时器。现在它只是跳来跳去,看起来所有的滴答事件只是相互倒计时(数字疯狂地显示 30、29、30、29、28、30、29、28、30)。
【解决方案4】:

这里发生的情况是,每次单击按钮时,您都会不断添加更多事件侦听器,并且它们会以奇怪的方式进行交互。因为每个事件都可以有多个侦听器,所以只要事件发生,每个侦听器都会触发(在本例中为Tick)。因此,第一次单击按钮时,您会将 lambda A 添加为侦听器。它愉快地做你想做的事。下次单击按钮时,您添加了第二个侦听器 (B),事情变得很奇怪。

这就是你重新开始时会发生的事情。处理程序 A 执行并将时间更改为 40 秒,它的if 检查并且什么都不做(文本已经从您的按钮单击变为绿色,这不会改变它)。然后处理程序 B 执行并立即将时间更改为 60 秒(也单独保留颜色)。这发生在每个滴答声上,因此两个处理程序都运行并来回更改时间。一段时间后,A 将执行并将时间更改为 30 秒。然后它将颜色变为橙色。然后 B 将运行,将时间改回 50,但不将颜色更改为绿色(它只是将颜色保留为以前的颜色)。这种情况继续下去,导致颜色以奇怪的方式变化。

要解决此问题,您需要在单击按钮时删除之前的处理程序。

【讨论】:

  • 这个解释听起来真的很好,我认为它与乘法事件有关,但我找不到“清除”计时器的方法......仍然 - 它不允许我使用 =空值。 错误1事件'System.Windows.Forms.Timer.Tick'只能出现在+=或-=的左侧
  • @MariaNowinska 你是对的。我忘记了只有当活动是你拥有的东西时才有效。请参阅我的更新答案。
  • 在 **foreach 行的第一个“Tick”( timer2.Tick.GetInvocationList()) )仍然给我完全相同的错误消息。我从没想过基于时间的事件会这么难!
  • @MariaNowinska 然后显然我不记得如何清除所有事件处理程序了。你将不得不谷歌如何删除它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-26
  • 1970-01-01
相关资源
最近更新 更多