为DataGrid更新收集项时异步为什么数据会消失?

时间:2014-09-16 00:37:04

标签: c# wpf multithreading asynchronous datagrid

我有一个DataGrid绑定到使用Observable Collection的DataGridCollectionView。该系列包含约650多个项目,大约20列。每60秒收到一个新的数据集。将该数据与现有集合进行比较,然后根据需要添加,删除和更新项目。对于更新,我正在执行以下操作:

private async void LoadData()
    {
        await RefreshData(TimeSpan.FromSeconds(100), cts.Token);
    }

private async Task RefreshData(TimeSpan interval, CancellationToken token)
    {
        // Get the writable properties for the ContingencyViewModel 
        var writableProperties = typeof(ContingencyViewModel).GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(p => p.CanWrite);

        while (!token.IsCancellationRequested)
        {
            var list = viewModel.Items;
            var cvs = GetItems();  // Service call that gets the updated collection
            var existingIds = list.Select(s => s.UniqueId).Distinct().ToList();
            var sourceIds = cvs.Select(s => s.UniqueId).Distinct().ToList();

            var toAdd = sourceIds.Except(existingIds).ToList();
            var toRemove = existingIds.Except(sourceIds).ToList();
            var toUpdate = existingIds.Intersect(sourceIds).ToList();

            var itemsToAdd = cvs.Where(x => toAdd.Contains(x.UniqueId)).ToList();
            var itemsToRemove = list.Where(x => toRemove.Contains(x.UniqueId)).ToList();
            var itemsToUpdate = list.Where(x => toUpdate.Contains(x.UniqueId)).ToList();

                // Add new items
                foreach (ItemViewModel item in itemsToAdd)
                {
                    list.Add(item);
                }

                // Remove dropped items
                foreach (ItemViewModel item in itemsToRemove)
                {
                    list.Remove(item);
                }

                // Update existing items
                foreach (ItemViewModel item in itemsToUpdate)
                {
                    // Get a reference to the Updated Item
                    var source = cvs.First(x => x.UniqueId == item.UniqueId);

                    // This works but locks the UI for a little bit
                    this.UpdateItem<ItemViewModel>(source, item, writableProperties);

                   // This also works but all the results in my grid disappear when I scroll or resize screen.  To get them back I have to actually Expand and Collapse groups.
                    /*
                    Action d = delegate()
                    {                        
                        this.UpdateItem<ItemViewModel>(source, item, writableProperties);
                    };

                    Dispatcher.CurrentDispatcher.InvokeAsync(d, DispatcherPriority.Normal, token);
                    */

                } 

                if (token.IsCancellationRequested)
                    token.ThrowIfCancellationRequested();

            if (interval > TimeSpan.Zero)
                await Task.Delay(interval, token);
        }
    }

private void UpdateItem<T>(T source, T target, IEnumerable<PropertyInfo> properties)
    {
        foreach (var p in properties)
        {
            var value = p.GetValue(source);
            p.SetValue(target, value);
        }  
    }

执行直接更新会像我预期的那样滞后于UI,但是当您滚动或调整窗口大小时,尝试从另一个线程执行此操作似乎会导致数据消失。通过消失我的意思是行在那里,但它们是空的。将其取回的唯一方法是折叠和扩展组。我甚至尝试为数据源添加刷新(这对我来说似乎是一个坏主意,因为它会在每次单个字段更新后调用)。

为什么数据会在Async更新中消失?是否有更好或更合适的方法对绑定到数据网格的项目进行这些类型的更新?

1 个答案:

答案 0 :(得分:0)

如您所知,WPF遵循STA架构。因此,作为拇指规则,所有更新都应该在UI线程上完成。您可以将Dispatcher用于上述场景: 您可以阅读有关调度程序herehere

的更多信息

理想情况下,您可以尝试以下方式:

ThreadStart start = delegate()
{
  // make your calls to the db

  Dispatcher.Invoke(DispatcherPriority.Normal, 
                    new Action<object>(UpdateCollection), 
                    new object[] { myData });
};
new Thread(start).Start();

private void UpdateCollection(object data)
{
   //iterate your collection and add the data as needed
}