升级到.NET 4.5:一个ItemsControl与它的项目源不一致 [英] Upgrading to .NET 4.5: An ItemsControl is inconsistent with its items source

查看:476
本文介绍了升级到.NET 4.5:一个ItemsControl与它的项目源不一致的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我建立一个应用程序,它使用许多ItemControls(数据网格和列表视图)。为了方便地更新从后台线程这些名单我用这个扩展ObservableCollections,这工作得很好:



http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16 /have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx



今天我安装VS12(这反过来安装.NET 4.5),因为我想用这对.NET 4.5编写的组件。甚至我的项目升级到.NET 4.5(4.0)之前,我的DataGrid开始从的WorkerThread更新时,抛出InvalidOperationException异常。异常消息:




这异常被抛出,因为发电机控制'System.Windows.Controls.DataGrid Items.Count:5'的名字(未命名)已收到不Items集合的当前状态同意CollectionChanged事件顺序。检测到如下区别:
累计计数4是从实际计数5.不同[累计计数(在上次复位+ #Adds计数 - 自去年复位#Removes)]


< /块引用>

摄制代码:



XAML:

 <窗​​口x:类=Test1.MainWindow
的xmlns =htt​​p://schemas.microsoft.com/winfx/2006/xaml/presentation
的xmlns :X =htt​​p://schemas.microsoft.com/winfx/2006/xaml
标题=主窗口HEIGHT =350WIDTH =525>
<网格和GT;
<数据网格的ItemsSource ={绑定表项,模式=一次性}PresentationTraceSources.TraceLevel =高/>
< /网格和GT;
< /窗GT;



代码:

 公共部分类主窗口:窗口
{
公共ExtendedObservableCollection< INT>项目{搞定;私人集; }

公共主窗口()
{
的InitializeComponent();
项=新ExtendedObservableCollection<&诠释GT;();
的DataContext =这一点;
加载+ = MainWindow_Loaded;
}

无效MainWindow_Loaded(对象发件人,RoutedEventArgs E)
{
Task.Factory.StartNew(()=>
{
的foreach(在Enumerable.Range(1 VAR项目,500))
{
Items.Add(项目);
}
});
}
}


解决方案

WPF 4.5提供了一些新的功能在非UI线程访问集合。




据WPF允许您在线程$ b访问和修改数据收集$ b比创建集合的另外一个。这使您可以
使用一个后台线程来从外部源,例如
作为数据库接收数据,并显示在UI线程上的数据。通过使用另一个
线程修改集合,您的用户界面保持
响应用户交互。




这可以通过在 BindingOperations EnableCollectionSynchronization 完成code>类。




如果你有大量的数据收集或修改,您可能需要使用
A后台线程收集和修改数据,使得用户
接口将保持反应性输入。为了使多线程
访问的集合,调用EnableCollectionSynchronization方法。
当调用此超载
EnableCollectionSynchronization的(IEnumerable的,Object)方法中,
系统,当你访问它锁定集合。要指定一个回调
给自己锁定集合,调用
EnableCollectionSynchronization(IEnumerable的,对象,
CollectionSynchronizationCallback)过载。




的用法如下。创建用作集合的同步锁定的对象。然后调用BindingsOperations的EnableCollectionSynchronization方法,并传递给它要同步的收集和用于锁定的对象。



我已经更新了你的代码并添加细节。 。此外,我改变了收集到正常的ObservableCollection,以避免冲突

 公共部分类主窗口:窗口{
公众的ObservableCollection< ; INT>项目{搞定;私人集; }

//锁同步对象;
私有静态对象_syncLock =新的对象();

公共主窗口()
{
的InitializeComponent();
项=新的ObservableCollection< INT>();

//启用交叉存取权限此集合其他地方
BindingOperations.EnableCollectionSynchronization(项目,_syncLock);

的DataContext =这一点;
加载+ = MainWindow_Loaded;
}

无效MainWindow_Loaded(对象发件人,RoutedEventArgs E)
{
Task.Factory.StartNew(()=>
{
的foreach(在Enumerable.Range(1 VAR项目,500))
{
锁(_syncLock){
Items.Add(项目);
}
}
});
}
}



另请参阅:的http://10rem.net/blog/2012/01/20/wpf-45-cross-线程集合同步终极版


I'm building an application, which uses many ItemControls(datagrids and listviews). In order to easily update these lists from background threads I used this extension to ObservableCollections, which has worked fine:

http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx

Today I installed VS12(which in turn installed .NET 4.5), as I want to use a component which is written for .NET 4.5. Before even upgrading my project to .NET 4.5 (from 4.0), my datagrid started throwing InvalidOperationException when updated from a workerthread. Exception message:

This exception was thrown because the generator for control 'System.Windows.Controls.DataGrid Items.Count:5' with name '(unnamed)' has received sequence of CollectionChanged events that do not agree with the current state of the Items collection. The following differences were detected: Accumulated count 4 is different from actual count 5. [Accumulated count is (Count at last Reset + #Adds - #Removes since last Reset).]

Repro code:

XAML:

<Window x:Class="Test1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
   <Grid>
      <DataGrid ItemsSource="{Binding Items, Mode=OneTime}" PresentationTraceSources.TraceLevel="High"/>       
   </Grid>
</Window>

Code:

public partial class MainWindow : Window
{
    public ExtendedObservableCollection<int> Items { get; private set; }

    public MainWindow()
    {
        InitializeComponent();
        Items = new ExtendedObservableCollection<int>();
        DataContext = this;
        Loaded += MainWindow_Loaded;
    }

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
            Task.Factory.StartNew(() =>
            {
                foreach (var item in Enumerable.Range(1, 500))
                {
                    Items.Add(item);
                }
            });                
    }
}

解决方案

WPF 4.5 provides some new functionality to access collections on non-UI Threads.

It WPF enables you to access and modify data collections on threads other than the one that created the collection. This enables you to use a background thread to receive data from an external source, such as a database, and display the data on the UI thread. By using another thread to modify the collection, your user interface remains responsive to user interaction.

This can be done by using the static method EnableCollectionSynchronization on the BindingOperations class.

If you have a lot of data to collect or modify, you might want to use a background thread to collect and modify the data so that the user interface will remain reactive to input. To enable multiple threads to access a collection, call the EnableCollectionSynchronization method. When you call this overload of the EnableCollectionSynchronization(IEnumerable, Object) method, the system locks the collection when you access it. To specify a callback to lock the collection yourself, call the EnableCollectionSynchronization(IEnumerable, Object, CollectionSynchronizationCallback) overload.

The usage is as follows. Create an object that is used as a lock for the synchronization of the collection. Then call the EnableCollectionSynchronization method of the BindingsOperations and pass to it the collection you want to synchronize and the object that is used for locking.

I have updated your code and added the details. Also i changed the collection to the normal ObservableCollection to avoid conflicts.

public partial class MainWindow : Window{
  public ObservableCollection<int> Items { get; private set; }

  //lock object for synchronization;
  private static object _syncLock = new object();

  public MainWindow()
  {
    InitializeComponent();
    Items = new ObservableCollection<int>();

    //Enable the cross acces to this collection elsewhere
    BindingOperations.EnableCollectionSynchronization(Items, _syncLock);

    DataContext = this;
    Loaded += MainWindow_Loaded;
  }

  void MainWindow_Loaded(object sender, RoutedEventArgs e)
  {
        Task.Factory.StartNew(() =>
        {
            foreach (var item in Enumerable.Range(1, 500))
            {
                lock(_syncLock) {
                  Items.Add(item);
                }
            }
        });                
  }
}

See also: http://10rem.net/blog/2012/01/20/wpf-45-cross-thread-collection-synchronization-redux

这篇关于升级到.NET 4.5:一个ItemsControl与它的项目源不一致的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆