后台工作者更新ObservableCollection

6
我有一个DatGrid,它绑定了var Result_Full = new ObservableCollection<IP_DataRow>()。这是一个简单的类,包含几个字符串和双精度浮点数变量。没有什么困难的。
我的做法是,我使用Telerik RadSpreadProcessing读取Excel文件,并将行解析成我的类。我在一个线程上执行此操作,以便不会阻塞UI。但我遇到了一些问题:
1)在长时间读取Excel文件的过程中(因为Result_Full是绑定到DataGrid的公共属性),我无法使用ref关键字,但我必须创建临时的ObservableCollection<IP_DataRow>(),并将值放置其中。一旦进程完成,我运行以下脚本来复制值:
        foreach (var item in tmpFull)
        {
            InvokeOnUIThread(() =>
            {
                Result_Full.Add(item);
            });
        }

我希望能够实时看到我的DataGrid中项的添加情况(如果可能的话)。由于我使用的是.NET 4.5,我尝试实现BindingOperations.EnableCollectionSynchronization,正如其他帖子所建议的那样,但我无法弄清楚如何将我的UI绑定集合Result_Full与在进程中临时使用的集合进行绑定。

2) 即使使用当前设置,在我移动到包含DataGrid的选项卡下(我的DataGrid位于不同的TabPage上)并尝试使用上述代码添加新项到集合时,它会返回错误,显示“调用线程无法访问此对象,因为不同的线程拥有它”,这非常奇怪,因为InvokeOnUIThread除了Dispatcher.Invoke()之外什么也不是,应该是线程安全的?

任何帮助都将不胜感激。

编辑:展示更多代码:

这是我从BackgroundWorker调用的进程:

    public void ProcessFile()
    {
        var tmpError = new ObservableCollection<IP_DataRow>();
        var tmpFull = new ObservableCollection<IP_DataRow>();

        var _reader = new IP_ExcelReader(sExcelPath, ref tmpError, ref tmpFull);
        string sResult = _reader.ReadExcelFile();
        if (sResult != string.Empty)
        {
            System.Windows.MessageBox.Show("Error processing selected Excel File!" + Environment.NewLine + Environment.NewLine + "Error message:" + Environment.NewLine + sResult);
        }

        foreach (var item in tmpError)//populates error list
        {
            IP_InvokeOnUIThread(() =>
            {
                Result_Error.Add(item);
            });
        }

        foreach (var item in tmpFull)//populates full list
        {
            IP_InvokeOnUIThread(() =>
            {
                Result_Full.Add(item);
            });
        }

        OnPropertyChanged("Result_Full");
        //OnPropertyChanged("Result_Error");

        iSelectedTabIndex = 1;

    }

在这里,您可以看到,我必须创建临时集合 tmpError、tmpFull 来收集我的数据。在整个过程结束后,我会手动将值复制到与 DataGrid 绑定的主要集合中。我希望改变这一点,即在过程中将值复制到主集合(而不是临时集合)中,以便用户实时查看如何将值添加到集合中。
附注2: 出于我不知道的原因,我的 InvokeOnUIThread 调用中存在一个问题。一旦我从 App.Current.Dispatcher.Invoke(action); 更改为 App.Current.Dispatcher.BeginInvoke(action);,错误就停止了,...不同的线程拥有它

哪个线程拥有 'item' 和 'tmpFull'? - Pieter21
哪个调度程序?Dispatcher.Current 保存在 TLS 中,因此每个线程都有自己的调度程序。您需要访问 UI 线程的调度程序。 - user1228
我正在使用 **App.Current.Dispatcher.Invoke(action);**。我认为那是UI调度程序?那么我该如何访问它呢? - Robert J.
foreach调用在后台工作器中执行 - Robert J.
1个回答

5
  1. 你可以使用BackgroundWorker代替线程,实时汇报进度。这里有一个简单的教程:传送门
  2. 我认为仅仅调用Dispatcher会在上下文中使用线程,但不是您的UI线程。请尝试使用Application.Current.Dispatcher

简而言之,建议进行以下操作:

  1. 在UI线程中创建公共ObservableCollection并将其绑定到DataGrid
  2. 创建一个后台工作者。将报告设置为true。订阅ReportProgress和DoWork事件。
  3. 异步运行worker
  4. 在DoWork处理程序中创建List,并读取一些值。当达到某个数量(例如一百个)时,调用(sender as BackgroundWorker).ReportProgress方法,通过事件参数传递已填充的集合。
  5. 在report progress handler中,从通过事件参数传递的列表中填充你的ObservableCollection。
  6. 重复执行步骤4-5,直到完成所有操作

我已经在使用后台工作器。我的问题是,我不知道如何将我的临时集合与绑定到DataGrid的集合连接起来?这意味着,只有在Excel读取完成后,我才会填充我的值。我将分享更多代码。 - Robert J.
我最终将BackgroundWorker作为ref参数传递给我的Excel读取函数,在那里我调用了ProcessChanged方法,这就解决了问题! - Robert J.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接