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

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

问题描述

我正在构建一个应用程序,它使用了许多 ItemControls(数据网格和列表视图).为了从后台线程轻松更新这些列表,我将此扩展用于 ObservableCollections,它运行良好:

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

今天我安装了 VS12(它又安装了 .NET 4.5),因为我想使用一个为 .NET 4.5 编写的组件.在将我的项目升级到 .NET 4.5(从 4.0)之前,我的数据网格在从工作线程更新时开始抛出 InvalidOperationException.异常信息:

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:

抛出此异常是因为控件System.Windows.Controls.DataGrid Items.Count:5"的生成器(名称为(未命名)")收到了与项目当前状态不一致的 CollectionChanged 事件序列收藏.检测到以下差异:累计计数 4 与实际计数 5 不同.[累计计数为(上次重置时的计数 + #Adds - 自上次重置后的 #Removes).]

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).]

复制代码:

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>

代码:

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 提供了一些新功能来访问非 UI 线程上的集合.

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

It WPF 使您能够访问和修改线程上的数据集合除了创建集合的那个.这使您能够使用后台线程从外部源接收数据,例如作为数据库,并在 UI 线程上显示数据.通过使用另一个线程来修改集合,您的用户界面保持不变响应用户交互.

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.

这可以通过使用 BindingOperations<上的静态方法 EnableCollectionSynchronization 来完成/code> 类.

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

如果您有大量数据要收集或修改,您可能需要使用一个后台线程来收集和修改数据,以便用户界面将保持对输入的反应.启用多个线程访问集合,调用 EnableCollectionSynchronization 方法.当你调用这个重载EnableCollectionSynchronization(IEnumerable, Object) 方法,当您访问它时,系统会锁定该集合.指定回调要自己锁定集合,请调用EnableCollectionSynchronization(IEnumerable, Object,CollectionSynchronizationCallback) 重载.

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.

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

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.

我已更新您的代码并添加了详细信息.此外,我将集合更改为普通的 ObservableCollection 以避免冲突.

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);
                }
            }
        });                
  }
}

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

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

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