【问题标题】:Show System Time in WPF: Dispatcher timer在 WPF 中显示系统时间:调度程序计时器
【发布时间】:2022-03-02 19:23:02
【问题描述】:

我使用 DispatcherTimer 在我的应用程序中显示时间(基本上在标签 Datetime.Now 格式为 hh:mm:ss tt)。

_timer = new DispatcherTimer {Interval = new TimeSpan(0, 0, 1)};
                _timer.Tick += _timer_Tick;
                _timer.Start();

private void _timer_Tick(object sender, EventArgs e)
            {
                try
                {
                    var now = DateTime.UtcNow;
                    var nowUserSelected =
                        TimeZoneInfo.ConvertTimeFromUtc(now,
                            (DataContext as MainViewModel)?.UserSelectedTimeZone ?? TimeZoneInfo.Local);
                    ClockCurrent.Text = $"{nowUserSelected:hh:mm:ss tt}";
                }
                catch (Exception exp)
                {
                    Log.Error("Error occurred while reporting time", exp);
                }
            }

问题是,如果用户从任务栏中打开时钟(如果您单击任务栏中的时间,则会弹出显示当前时间(包括秒数)的窗口)有时我的应用程序中的时间似乎是关闭一秒钟(有时!)。我相信这可能会发生,因为必须将计时器设置为从当前时间开始每秒滴答一次,这可能是 10:50:53 PM 和 950 毫秒 54 和 950 毫秒 ..... 毫秒等等,因为我只显示时间直到几秒钟,所以它给人一种我落后一秒的错觉。

什么是显示时间的推荐方式与任务栏显示的时间相匹配,可以将计时器间隔设置为每 500 毫秒滴答一次?

【问题讨论】:

  • 只需使用更短的间隔,甚至可能是 200 毫秒。额外的工作量可以忽略不计。您还可以将当前秒保留在字段中,并在调用 DateTime.Now 后将其与新值进行比较。只有当它发生变化时,才进行转换和标签更新。

标签: c# .net wpf


【解决方案1】:

在开始时检查 ms。在整秒后立即刷新,然后每秒刷新一次

更好的是:在 UI 更新结束时,您可以确定下一次 UI 更新的时间。

【讨论】:

  • 这不起作用。计时器不是每 1000 毫秒精确计时一次,它大约每 1000 毫秒计时一次,它可能会漂移。如果您在 UI 线程上工作繁重,很快您的 ticking 事件就会在一秒钟的中间触发。
  • 如果滴答频率取决于 UI 负载或其他因素,那么唯一可行的解​​决方案是确定每次迭代的下一次更新
  • 记住所有这些都是在 UI 线程上运行的,这最好快,不要阻塞执行 (Thread.Sleep) 或切换上下文 (await Task.Delay());我不认为你的目标 - 让滴答事件在一秒开始时触发 - 是可行的,至少不容易完成。使用更短的间隔(~ 200 毫秒)会更容易。
  • 我对@9​​87654323@ 的期望是它会触发 ecery second (取决于 cpu 架构,但不取决于方法执行的持续时间。我是 谈论任何阻塞操作。
【解决方案2】:

@iPirat 是正确的,但我猜其他人没有理解他的意思,所以让我先解释一下是什么问题,然后是如何解决的。

DispatcherTimer 在 WPF GUI 线程上运行。这样做的好处是DispatcherTimer.Tick的代码可以访问任何WPF控件。缺点是,一旦自上次滴答声过去 1 秒后,WPF GUI 线程可能正忙于其他事情,滴答声会在几毫秒后触发。下次执行 Tick 时,会有另一个小的延迟,并且会累积,直到总的延迟大于 1 秒,并且您“失去”了一个 tick。

您可以通过在每次滴答期间更改 DispatcherTimer.Interval 来防止这种情况发生。如果您希望每 1000 毫秒执行一次滴答,并且该滴答执行得太晚 x 毫秒,则将 DispatcherTimer.Interval 设置为 1000-x 毫秒。

const int constantInterval = 1000;//milliseconds

private void Timer_Tick(object? sender, EventArgs e) {
  var now = DateTime.Now;
  var nowMilliseconds = (int)now.TimeOfDay.TotalMilliseconds;
  var timerInterval = constantInterval - 
   nowMilliseconds%constantInterval + 5;//5: sometimes the tick comes few millisecs early
  timer.Interval = TimeSpan.FromMilliseconds(timerInterval);
}

顺便说一句,您可能希望像 DispatcherTimer timer = new DispatcherTimer (DispatcherPriority.Input); 这样提高优先级。这将使延迟更小,但 GUI 的渲染速度仍然足够快。

更多详情请看我在 CodeProject 上的文章:Improving the WPF DispatcherTimer Precision

【讨论】:

    猜你喜欢
    • 2013-11-13
    • 2017-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-20
    • 2019-08-10
    • 1970-01-01
    相关资源
    最近更新 更多