计时器回调关闭 WPF 应用程序(DispatcherTimer 可用..)

4

我有一个WPF应用程序,其中有一个文本块显示当前时间。该文本块绑定到ViewModel上的DependencyProperty。自然地,我需要不断更新时间,因此我使用了一个计时器(System.Threading.Timer),如下所示:

public MainViewModel()
{
    _dateTimer = new Timer(_dateTimer_Tick, null, 0, 60000);
}

void _dateTimer_Tick(object sender)
{
    Time = DateTime.Now.ToString("HH:mm");
    Date = DateTime.Now.ToString("D");
}

问题在于,当回调被调用时,应用程序会退出...糟糕(输出显示:“在WindowsBase.dll中发生了类型为'System.InvalidOperationException'的第一次机会异常”,就在它要写入Time DP时)。
如果我使用DispatcherTimer,一切都正常。我不介意使用DispatcherTimer,只是这个应用程序相当大,我想尽可能地调整其性能。据我所见,我没有访问UI线程(我只是更新属性),因此不需要DispatcherTimer。
我错过了什么吗?
谢谢。
编辑: 属性定义如下:
    public string Time
    {
        get { return (string)GetValue(TimeProperty); }
        set { SetValue(TimeProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Time.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TimeProperty =
        DependencyProperty.Register("Time", typeof(string), typeof(MainViewModel), new UIPropertyMetadata(""));

(日期相同)


日期和时间属性是如何定义的? - H H
1个回答

3
定时器回调正在非UI线程上执行,这会引起问题;尽管您“仅更新属性”,但原因在于这样的调用会创建一系列调用,即通知对属性更改感兴趣的各方,而在此情况下是UI,因此范围会向上冒泡,从而导致不适当的跨线程调用。
为了解决这个问题,您可以在定时器构造函数中将WindowDispatcher指定为state参数的参数,然后使用Dispatcher.Invoke
例如...
public MainViewModel()
{
    _dateTimer = new Timer(_dateTimer_Tick, Dispatcher, 0, 60000);
}

void _dateTimer_Tick(object state)
{
    ((Dispatcher)state).Invoke(UpdateUI);
}

void UpdateUI()
{
    Time = DateTime.Now.ToString("HH:mm");
    Date = DateTime.Now.ToString("D");
}

编辑:

使用由Henk建议的DispatcherTimer,并且您自己也考虑过这种方式,可能是在这里继续前进的方法,然而 - 我只是不知道这种类型,因此无法在我的答案中展示。关于DispatcherTimer和性能,您担心的基础是什么?


那么更新属性仍然算作访问UI线程吗? - Filipe Miguel
正确的解释,但建议是:使用DispatcherTimer。 - H H
只是我的应用程序非常UI密集(许多页面,每个页面有很多控件,动画,演示模式,通过远程设备进行交互等),我试图摆脱不必要的“UI东西”...我的担忧可能是没有根据的,我只是认为既然我只是更新属性,一个“普通”的计时器就足够了。谢谢! - Filipe Miguel
1
Filipe,如果确实存在DispatcherTimer和常规计时器之间的某些性能差异,无论如何WPF都使用反射进行数据绑定,因此与之相比任何性能提升都将是微不足道的。 - Rafa Castaneda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接