如何使用 MVVM 从 BackgroundWorker 内部更新 ObservableCollection?

发布于 2024-10-13 23:57:20 字数 603 浏览 7 评论 0原文

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

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

我读了很多关于如何跨线程操作内容的内容。但它们都不起作用。尝试使用 Dispatcher“ThreadSafeObservableCollection”解决方案,.....

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

编辑: 但我怎样才能等待BackgroundWorker完成呢???

since two days I am trying to solve the following problem:
I have a WPF control where a WrapPanel is bound to an ObservableCollection. An action changes the content of the ObservableCollection. The content is loaded in a BackgroundWorker. Immediately after the action that caused the content change, the new content is needed in a foreach-loop. The problem is that the loading of the content is slow, so it needs a bit to get ready.

My first attempt was to wait for the backgroundworker until the IsBusy property is set to false. But the IsBusy property never changed while waiting!
Second attempt was to try to manipulate the ObservableCollection directly from the BackgroundWorker. Of course no success because the ObservableCollection is in another thread than the BackgroundWorker.

I read really really much about how to manipulate content in cross-thread-wide. But none of them worked. Tried solutions with Dispatcher, "ThreadSafeObservableCollection", .....

Might anyone tell me how I can solve that problem?
Is there a simple way to edit content of the UI thread within another thread?
Or how do I wait correctly for the BackgroundWorker to get finished?

EDIT:
But how can I wait for the BackgroundWorker to get finished???

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

2024-10-20 23:57:20

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

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

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

以下代码来自另一个线程 我回答了有关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();
}

The BackgroundWorker can help you in two ways.

To update the collection while the BGWorker is running, use the ProgressChanged event. The name of this event is misleading - while you can update the progress of a task, you can use actually use it for anything that needs to be done in the UI (calling) thread by passing an object using the UserState property of the ProgressChangedEventArgs.

The BGWorker also has an event when it finishes. Again, you can pass any information back to it that you'd like in the Result property of the RunWorkerCompletedEventArgs in the RunWorkerCompleted event.

The following code is from another thread that I answered about 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();
}
七秒鱼° 2024-10-20 23:57:20

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

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

Pushing the ObservableCollection.Add to the dispatcher of the UI thread should work.

App.Current.Dispatcher.Invoke(new Action(() => collection.Add(item)));
凤舞天涯 2024-10-20 23:57:20

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

You can update your collection in the BackgroundWorker.RunWorkerCompleted event handler. It runs in the same synchronization context you started it which is a UI thread usually so you can safely use any UI related stuff from there.

他夏了夏天 2024-10-20 23:57:20

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

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

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

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

The BackGroundWorker fires an event when it is finished.
What I have been doing in a similar situation is :

I have a list that is not an observablecollecion

  • Set enabled= false in my window and display a spinner
  • Start background worker
  • in DoWork i fill the list
  • in RunWorkerCompleted event i copy the list content to my observablecollection and enable stuff and hide spinner

so all interaction with the collection is on same thread - the copying is usually not the expensive part.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文