情况
我在我的应用程序中遇到以下不稳定的行为:大约20次执行中的一次,绑定到DataGrid
的WPFToolkit的DataTable
将不会呈现所有行,在1之间丢失任何内容到预期的整个4行中的3行。
内部工作
DataGrid
绑定到DataTable
, D1 ,这是自定义类 C1 的属性。 BackgroundWorker
,但似乎与使用其中一个没有区别),运行方法 M1 ,打开连接并请求数据。该线程用于避免无响应的应用程序。D1.Clear()
)并提升NotifyPropertyChanged()
(来自主题)。DataTable
传递给 C1 ,后者逐行插入 D1 。完成插入行后, C1 会引发NotifyPropertyChanged()
。线程退出。因此,换句话说,我清除表,通知WPF,插入数据,通知WPF并退出。
在我看来,只要从UI正确使用了最后一个Notify,它就应该始终显示所有行。
除了DataTable
之外,还有大量属性(主要是字符串和int)正在更新并因此得到通知。我们不在任何其他情况下都会观察到此行为,仅使用DataTable
。
我知道这深入到WPF绑定机制,但我希望任何人都可以在这里阐明。 欢迎使用WPF进行WPF绑定或多线程的任何信息。
答案 0 :(得分:3)
DataTable提前约会WPF,因此没有实现INotifyCollectionChanged这是WPF监视集合更改的方式。您有两种选择:
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)
ListChanged
,DataRowView具有PropertyChanged
。PropertyChanged
不是线程安全的。您不能在其他线程上触发PropertyChanged
进行任何更改。必须在调度员上完成,因此更改需要通过调度员完成。
例如,您应该使用Model.Data = newData
或类似的Dispatcher.Invoke(new Action(model => model.Data = newData), Model)
而不是{{1}}。