c# - 将 WPF 控件绑定(bind)到 Timer 中更新的数据是否安全?

标签 c# wpf data-binding thread-safety

我有一个 WPF 应用程序,它在计时器内更新其数据模型,该计时器以 1 Hz 的频率更新其数据。据我了解,计时器在与 GUI 不同的线程上工作。一切似乎都工作得很好,但我正在阅读并看到关于是否可以在不同于 GUI 线程的线程上更新数据的相互冲突的报告。我们正在使用 .NET Framework 4 Client Profile 运行。下面的文章说事情在 4.5 中得到了修复,但我仍然不清楚它是否是这样。有人可以帮我澄清一下吗?到目前为止,我们还没有在绑定(bind)中使用集合。这就是我们没有遇到麻烦的原因。

WPF Databinding thread safety?

最佳答案

是的。它是线程安全的。 INotifyPropertyChanged 始终从其他线程编码到 UI 线程。

无需将 PropertyChanged 从另一个线程调度到 UI 线程,因为 PropertyChanged 事件会自动编码到 UI 调度程序。

a MSDN article说:

Note that in WPF, things are different and the code shown in Figure 5 works even if the Status property is data-bound to a TextBlock. This is because WPF automatically dispatches the PropertyChanged event to the main thread, unlike all the other XAML frameworks. In all other frameworks, a dispatching solution is needed.

但是,这只适用于标量属性的更改通知(即 PropertyChanged 事件)。集合更改通知(INotifyCollectionChanged.CollectionChanged 事件)不会以这种方式工作,它们必须在 UI 线程上手动引发。也就是说,当使用 INotifyCollectionChanged(例如使用 ObservableCollection)时,这些更改不会编码(marshal)到 UI 线程。这意味着如果您从非 UI 线程修改集合,则会出现异常。例如,有一些 ViewModel 我们在 ViewModel 类中,我们不使用 Dispatcher 来更新 UI。所以我建议你使用 David Rickard 的方法:

public static class DispatchService
{
    public static void Invoke(Action action)
    {
        Dispatcher dispatchObject = Application.Current.Dispatcher;
        if (dispatchObject == null || dispatchObject.CheckAccess())
    {
            action();
        }
        else
        {
            dispatchObject.Invoke(action);
        }
    }
}

和:

DispatchService.Invoke(() =>
{
    this.MyCollection.Add("new value");
});

David Rickard article at msdn blog.

更新:

是的,article使用MVVMLight框架。但是,MVVM Light 使用 Dispatcher 将标量属性编码到 UI 线程的说法是不正确的。从MVVM LightViewModelBase类源码可以看出 there is no marshal between threads to update scalar property 。请参阅 RaisePropertuChanged() 方法。

为了消除对分派(dispatch)标量属性的任何疑问,我做了一个测试:

XAML:

<TextBlock Text="{Binding Number}" FontSize="188" Foreground="Red" />

View 模型:

public int Number { get; set; }

private void UpdateNumber()
{
        Task.Run(() => 
        {
            System.Timers.Timer timer = new System.Timers.Timer(250);
            timer.Elapsed += (sender, eventArgs) =>
            { 
                Number++;
                OnPropertyChanged("Number");//No exceptions, no errors
            };
            timer.Enabled = true;
        });
}

更新1:

毫无疑问,INotifyProperyChanged 事件由 WPF 自动分派(dispatch)到 UI 线程。我相信a MSDN articlelink你已经在你的问题中表明了:)。

请注意这一点:这是因为 WPF 会自动将 PropertyChanged 事件分派(dispatch)到主线程,这与所有其他 XAML 框架不同。

关于c# - 将 WPF 控件绑定(bind)到 Timer 中更新的数据是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36578957/

相关文章:

wpf - 当调用非线程时,在 wpf 中显示 "Please wait"窗口的替代方法是什么

c# - 如何从附加的行为触发组合框 SelectionChanged 事件

wpf - MVVM-如何绑定(bind)到文本框的 SelectionStart 和 SelectionLength 属性?

c++ - C/C++ 将二进制数据映射到结构成员

silverlight - ItemsControl 中的数据绑定(bind)到自定义 UserControl 属性

c# - 高维数据聚类

c# - 如果调用方法不知道返回类型,如何避免 switch 语句?

WPF:使用框架技术从列表框菜单进行页面到页面导航?

c# - .NET 中的可空类型

c# - 检索 SQL Server 中表的 DataTable 信息