后台工作人员无法使用WPF

时间:2015-06-07 21:02:13

标签: c# wpf backgroundworker

在我的WPF程序中,它耗费了大量的处理时间并且长时间冻结。

所以我决定使用后台工作程序并在后台处理它。

但它不起作用。通过调试,程序停在Render3D()。它不会抛出异常。就像你放return时一样。

换句话说,它在到达Render3D()后无效,只会返回。

(我不会说它会返回因为我不确定但行为与返回相同)

    private readonly BackgroundWorker backgroundWorker = new BackgroundWorker();
    private AssetDeclaration _assetDeclaration = new AssetDeclaration();

    public MainWindow()
    {
        backgroundWorker.DoWork += backgroundWorker1_DoWork;
        backgroundWorker.ProgressChanged += backgroundWorker1_ProgressChanged;
        backgroundWorker.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
        InitializeComponent();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i = 1; i <= 1000; i++)
        {
            if (!((BackgroundWorker)sender).CancellationPending)
            {
                Render3D(); // will return at this point. (why?) or waiting for something to start?
                ((BackgroundWorker)sender).ReportProgress(i);
            }
            else
            {
                e.Cancel = true;
                break;
            }
        }
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        MessageBox.Show("Done!");//will show message box instant.
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        ProgressBar1.Value = e.ProgressPercentage;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        //...Some work here before starting Hard job!
        //...From now i want to start heavy process in background.
        //...with report to progress bar at same time.
        backgroundWorker.RunWorkerAsync(100);
    }

Render3D()在没有后台处理的情况下工作正常但会冻结一段时间。

Render3D()位于Partial的{​​{1}}类中。因为有很多方法所以我决定将它们分开。

另外,如何在MainWindow之外使用ReportProgress。例如在Render3D()中。

最后一件事:我想知道如何向用户展示完成了多少过程。

解决:<!/强>

问题是因为我在backgroundWorker1_DoWork

中设置了Viewport3D

我将它与Render3D分开并解决了问题。感谢Henk Holterman的正确答案。

似乎有些任务无法在另一个Thread中完成。使用错误报告,我发现无效任务正在设置Viewport3D属性。

此任务必须在主线程中完成。

下面的

是使后台工作程序停止运行的无效代码。

Render3D()

这部分。

DefineCamera();
Viewport.Children.Add(model); // Must be run in Main thread.

3 个答案:

答案 0 :(得分:4)

首先,您无法找到错误。

  

......程序在Render3D()处停止。它不会抛出异常。就像你放回来一样。

实际发生的是您的方法抛出异常并被Backgroundworker捕获。它被转移到已完成的事件,但您必须在那里采取行动。

private void worker_Completed(object sender, RunWorkerCompletedEventArgs e) 
{
  // check error, check cancel, then use result
  if (e.Error != null)
  {
     // handle the error
  }
  else if (e.Cancelled)
  {
     // handle cancellation
  }
  else      
  {         
      // use the result(s) on the UI thread
  }    
  // general cleanup
}

无法查看e.Errore.Result与程序中的空catch{}块相同。

如果错误处理到位,我们就有

  

哦是的,它显示错误。 System.InvalidOperationException调用线程无法访问此对象,因为另一个线程拥有它

这表明您的Render3D()仍在某处与GUI交互。

基本建议是将所有计算(和I / O,数据库)工作与UI工作分开。您可以在线程中运行CPU绑定和I / O绑定cod,但GUI是单线程的,您只能从主线程与它进行交互。

答案 1 :(得分:0)

在WPF的世界中,与您习惯的Windows窗体不同,您应该考虑 Dispatcher 。为此,您必须导入System.Windows.Threading

    private void ThreadTask()
    {
        Thread.Sleep(TimeSpan.FromSeconds(5));

        this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                    (ThreadStart)delegate()
                    {
                        //Do some heavy task here...
                    });
    }

快速更新

为了从按钮单击或其他任何函数运行线程,添加以下代码行:

    Thread thread = new Thread(new ThreadStart(ThreadTask));
    thread.Start();

这行代码相当于BackgroundWorker.RunWorkerAsync();

答案 2 :(得分:-1)

我强烈建议使用async / await。此功能是在.NET 4.5中引入的,用于将工作从主WPF GUI线程转移到使应用程序快速响应。

基本上,规则是使用 Task.Run async / await 的组合将任何不与GUI交互的计算推送到后台线程。与 Dispatcher.Invoke 一起,您实际上不需要任何其他内容。

例如,可能会从数据库中获取数据的慢速数据调用可以推送到后台线程,因此应用程序会在等待SQL执行时冻结。

我已经用它来制作我写得快速,反应迅速且活泼的应用程序。