线程和WPF的绑定

时间:2011-11-12 12:50:29

标签: wpf multithreading wpftoolkit

情况

我在我的应用程序中遇到以下不稳定的行为:大约20次执行中的一次,绑定到DataGrid的WPFToolkit的DataTable将不会呈现所有行,在1之间丢失任何内容到预期的整个4行中的3行。

内部工作

  • DataGrid绑定到DataTable D1 ,这是自定义类 C1 的属性。
  • 当用户激发视图时,我们必须从后端检索数据,这可能需要一些时间。为此,我们创建了一个线程(实际上,我们使用BackgroundWorker,但似乎与使用其中一个没有区别),运行方法 M1 ,打开连接并请求数据。该线程用于避免无响应的应用程序。
  • M1检索数据并首先将其存储在DTO上。之后,他要求 C1 清除它的表格。 C1 会这样做(通过调用D1.Clear())并提升NotifyPropertyChanged()(来自主题)。
  • M1将新后端的DataTable传递给 C1 ,后者逐行插入 D1 。完成插入行后, C1 会引发NotifyPropertyChanged()。线程退出。

因此,换句话说,我清除表,通知WPF,插入数据,通知WPF并退出。

在我看来,只要从UI正确使用了最后一个Notify,它就应该始终显示所有行。

除了DataTable之外,还有大量属性(主要是字符串和int)正在更新并因此得到通知。我们在任何其他情况下都会观察到此行为,仅使用DataTable

我知道这深入到WPF绑定机制,但我希望任何人都可以在这里阐明。 欢迎使用WPF进行WPF绑定或多线程的任何信息。

4 个答案:

答案 0 :(得分:3)

DataTable提前约会WPF,因此没有实现INotifyCollectionChanged这是WPF监视集合更改的方式。您有两种选择:

  1. 用新的DataTable替换现有的DataTable(在设置行之后)。然后解雇属性更改通知。
  2. 从DataTable更改为ObservableCollection。只要您更改项目列表,该集合就会触发更改通知。 (注意,如果更改列表中已有项目的内容,则不会触发)
  3. INotifyPropertyChanged会在属性发生更改时通知,而不是在内部状态(无论是属性还是集合)发生更改时通知。当您触发Property Changed事件时,如果该属性与上次绑定数据时的属性不同,则WPF仅重新绑定控件。这样,当您只在对象图中向下更改一个属性时,它就不会刷新整个屏幕。

答案 1 :(得分:2)

您是否将新数据加载到已绑定到DataGrid的相同 DataTable实例中?

如果是这样,那么(a)每次你从后台代码对DataTable进行更改时,它都会从错误的线程中触发通知,这是禁止的; (b)当你最后触发PropertyChanged时,DataGrid可能足够聪明,注意到引用实际上没有改变,所以它不需要做任何事情。 (我不知道DataGrid是否试图变得那么聪明,但这不是不合理的 - 特别是考虑到WPF constructs views on top of collections的方式 - 它可能有助于解释你所看到的症状。)

每次需要刷新时尝试创建一个新的DataTable实例,然后当你完成从后台线程填充该实例时,然后将新的(完全填充的)引用分配到你的通知属性并触发PropertyChanged(和当然,请确保从UI线程中执行赋值+ PropertyChanged。

答案 2 :(得分:2)

基于Asti的第三点,我经常遇到一个跨线程的PropertyChanged场景,并有一个基本的视图模型。视图模型基于PRISM NotificationObject,但是如果您不想使用PRISM,您当然可以直接实现INotifyPropertyChanged接口。如果您使用它,也适用于Silverlight。

namespace WPF.ViewModel
{
    using System.Windows;
    using System.Windows.Threading;

    using Microsoft.Practices.Prism.ViewModel;

    /// <summary>The async notification object.</summary>
    public abstract class AsyncNotificationObject : NotificationObject
    {
        #region Constructors and Destructors

        /// <summary>Initializes a new instance of the <see cref="AsyncNotificationObject"/> class.</summary>
        protected AsyncNotificationObject()
        {
            Dispatcher = Application.Current.Dispatcher;
        }

        #endregion

        #region Properties

        /// <summary>Gets or sets Dispatcher.</summary>
        protected Dispatcher Dispatcher { get; set; }

        #endregion

        #region Methods

        /// <summary>The raise property changed.</summary>
        /// <param name="propertyName">The property name.</param>
        protected override void RaisePropertyChanged(string propertyName)
        {
            if (Dispatcher.CheckAccess()) base.RaisePropertyChanged(propertyName);
            else Dispatcher.BeginInvoke(() => base.RaisePropertyChanged(propertyName));
        }

        #endregion
    }
}

答案 3 :(得分:1)

  1. 不是直接绑定DataTable,而是始终绑定表的DataView。表的视图版本,DataView具有ListChanged,DataRowView具有PropertyChanged
  2. WPF确实支持更新到行级别。如果更改行值,它肯定会立即传播。
  3. PropertyChanged不是线程安全的。您不能在其他线程上触发PropertyChanged进行任何更改。必须在调度员上完成,因此更改需要通过调度员完成。 例如,您应该使用Model.Data = newData或类似的Dispatcher.Invoke(new Action(model => model.Data = newData), Model)而不是{{1}}。