【问题标题】:Update ObservableCollection from background Worker从后台 Worker 更新 ObservableCollection
【发布时间】:2015-09-17 14:21:49
【问题描述】:

我有一个绑定到var Result_Full = new ObservableCollection<IP_DataRow>() 的DatGrid。这是一个包含多个 string & double 变量的简单类。没什么难的。

我所做的是读取一个 Excel 文件(使用 Telerik RadSpreadProcessing),它将行解析到我的类中。我在一个线程上执行此操作,以便 UI 不会被阻塞。不过我遇到了一些问题:

1) 我不能在读取 excel 文件的长过程中使用 ref 关键字(因为 Result_Full 是绑定到 DataGrid 的公共属性),但我必须创建临时的 ObservableCollection<IP_DataRow>() 来放置值。该过程完成后,我运行以下脚本来复制值:

        foreach (var item in tmpFull)
        {
            InvokeOnUIThread(() =>
            {
                Result_Full.Add(item);
            });
        }

我想做的是能够实时(如果可能的话)查看项目是如何添加到我的 DataGrid 中的集合中的。

当我使用 .Net 4.5 时,我尝试按照其他帖子的建议实现 BindingOperations.EnableCollectionSynchronization,但我无法弄清楚如何将我的 UI bould 集合 Result_Full 绑定到临时使用的一个过程。

2) 即使使用当前设置,当(在我的 UI 下)我移动到包含 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 的主集合中。我想更改这一点,这意味着在此过程中将值复制到主集合(不是临时集合),以便用户可以实时看到值是如何添加到集合中的。

P.S.2: 由于我不知道的原因,问题之一在于我的InvokeOnUIThread 电话。一旦我从App.Current.Dispatcher.Invoke(action); 更改为App.Current.Dispatcher.BeginInvoke(action); 错误 .. 不同线程拥有它 停止。

【问题讨论】:

  • 哪个线程拥有'item'和'tmpFull'?
  • 哪个调度器? Dispatcher.Current 保存在 TLS 中,因此每个线程都有自己的调度器。您需要访问 UI 线程的调度程序。
  • 我正在使用 App.Current.Dispatcher.Invoke(action);。我虽然那是 UI 调度程序?那我该如何访问呢?
  • foreach 调用在后台 Worker 中执行

标签: c# wpf multithreading mvvm datagrid


【解决方案1】:
  1. 您可以使用 BackgroundWorker 代替线程来报告进度。 Here是一个简单的教程
  2. 我相信简单地调用 Dispatcher 将使用上下文的线程,在您的情况下这不是 UI 线程。改用Application.Current.Dispatcher

总之,我认为你应该做到以下几点:

  1. 在 UI 线程中创建公共 ObservableCollection 并将其绑定到 DataGrid
  2. 创建后台工作人员。将报告设置为 true。订阅 ReportProgress 和 DoWork 事件。
  3. 异步运行工作线程
  4. 在 DoWork 处理程序中创建一个列表并读取一些值给它。当你达到一定数量时,比如说一百,调用(sender as BackgroundWorker).ReportProgress 方法,传入你填充的这个集合的事件参数。
  5. 在报告进度处理程序中,从您通过事件参数传递的列表中填充您的 ObservableCollection。
  6. 重复步骤 4 - 5,直到一切都完成

【讨论】:

  • 我已经在使用后台工作者了。我遇到的问题是,我不知道如何将我的临时集合与绑定到 DataGrid 的集合连接起来?这意味着,一旦我的 Excel 读取 void 完成,我只会填充我的值。我将分享更多代码
  • 我最终将 BackgroundWorker 作为 ref 参数传递给我的 Excel 阅读函数,然后我调用了 ProcessChanged 方法,它成功了!
猜你喜欢
  • 2018-09-18
  • 1970-01-01
  • 1970-01-01
  • 2019-10-25
  • 1970-01-01
  • 1970-01-01
  • 2014-01-15
  • 1970-01-01
相关资源
最近更新 更多