【问题标题】:How to update ObservableCollection from inside a BackgroundWorker using MVVM?如何使用 MVVM 从 BackgroundWorker 内部更新 ObservableCollection?
【发布时间】:2011-01-21 13:43:16
【问题描述】:

两天以来,我一直在尝试解决以下问题: 我有一个 WPF 控件,其中 WrapPanel 绑定到 ObservableCollection。一个动作会改变 ObservableCollection 的内容。内容在 BackgroundWorker 中加载。在导致内容更改的操作之后,新内容需要在 foreach 循环中立即执行。问题是内容的加载速度很慢,所以需要一点准备。

我的第一次尝试是等待后台工作程序,直到 IsBusy 属性设置为 false。但是 IsBusy 属性在等待期间从未改变! 第二次尝试是尝试直接从 BackgroundWorker 操作 ObservableCollection。当然没有成功,因为 ObservableCollection 位于 BackgroundWorker 之外的另一个线程中。

我读了很多关于如何在跨线程范围内操作内容的文章。但他们都没有工作。尝试使用 Dispatcher 解决方案,“ThreadSafeObservableCollection”,.....

谁能告诉我如何解决这个问题? 是否有一种简单的方法可以在另一个线程中编辑 UI 线程的内容? 或者我如何正确等待 BackgroundWorker 完成?

编辑: 但是我怎么能等待BackgroundWorker完成???

【问题讨论】:

    标签: wpf multithreading mvvm backgroundworker dispatcher


    【解决方案1】:

    BackgroundWorker 可以通过两种方式帮助您。

    要在 BGWorker 运行时更新集合,请使用 ProgressChanged 事件。此事件的名称具有误导性 - 虽然您可以更新任务的进度,但您可以通过传递一个对象来将其实际用于需要在 UI(调用)线程中完成的任何事情ProgressChangedEventArgs 的 UserState 属性。

    BGWorker 完成时也有一个事件。同样,您可以在 RunWorkerCompleted 事件的 RunWorkerCompletedEventArgs 的 Result 属性中将您想要的任何信息传递回它。

    以下代码来自another thread,我回答了有关BackgroundWorker:

    BackgroundWorker bgWorker = new BackgroundWorker();
    ObservableCollection<int> mNumbers = new ObservableCollection<int>();
    
    public Window1()
    {
        InitializeComponent();
        bgWorker.DoWork += 
            new DoWorkEventHandler(bgWorker_DoWork);
        bgWorker.ProgressChanged += 
            new ProgressChangedEventHandler(bgWorker_ProgressChanged);
        bgWorker.RunWorkerCompleted += 
            new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
        bgWorker.WorkerReportsProgress = true;
    
        btnGenerateNumbers.Click += (s, e) => UpdateNumbers();
    
        this.DataContext = this;
    }
    
    void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        progress.Visibility = Visibility.Collapsed;
        lstItems.Opacity = 1d;
        btnGenerateNumbers.IsEnabled = true;
    }
    
    void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        List<int> numbers = (List<int>)e.UserState;
        foreach (int number in numbers)
        {
             mNumbers.Add(number);
        }
    
        progress.Value = e.ProgressPercentage;
    }
    
    void bgWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        Random rnd = new Random();
        List<int> numbers = new List<int>(10);
    
        for (int i = 1; i <= 100; i++)
        {
            // Add a random number
            numbers.Add(rnd.Next());            
    
            // Sleep from 1/8 of a second to 1 second
            Thread.Sleep(rnd.Next(125, 1000));
    
            // Every 10 iterations, report progress
            if ((i % 10) == 0)
            {
                bgWorker.ReportProgress(i, numbers.ToList<int>());
                numbers.Clear();
            }
        }
    }
    
    public ObservableCollection<int> NumberItems
    {
        get { return mNumbers; }
    }
    
    private void UpdateNumbers()
    {
        btnGenerateNumbers.IsEnabled = false;
        mNumbers.Clear();
        progress.Value = 0;
        progress.Visibility = Visibility.Visible;
        lstItems.Opacity = 0.5;
    
        bgWorker.RunWorkerAsync();
    }
    

    【讨论】:

      【解决方案2】:

      ObservableCollection.Add 推送到 UI 线程的调度程序应该可以工作。

      App.Current.Dispatcher.Invoke(new Action(() => collection.Add(item)));
      

      【讨论】:

      • 这并没有帮助节省时间,它运行但在同一个线程上!
      【解决方案3】:

      您可以在 BackgroundWorker.RunWorkerCompleted 事件处理程序中更新您的集合。它在您启动它的同一同步上下文中运行,通常是一个 UI 线程,因此您可以从那里安全地使用任何与 UI 相关的东西。

      【讨论】:

        【解决方案4】:

        BackGroundWorker 完成后会触发一个事件。 我在类似情况下一直在做的是:

        我有一个不是可观察集合的列表

        • 在我的窗口中设置 enabled= false 并显示一个微调器
        • 启动后台工作人员
        • 在 DoWork 我填写列表
        • 在 RunWorkerCompleted 事件中,我将列表内容复制到我的 observablecollection 并启用内容并隐藏微调器

        所以与集合的所有交互都在同一个线程上 - 复制通常不是昂贵的部分。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-04-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-30
          • 2012-02-17
          • 2021-08-31
          相关资源
          最近更新 更多