3

我知道并使用 xxx.Dispatcher.Invoke() 方法来获取后台线程来操作 GUI 元素。我想我遇到了一些类似但略有不同的东西,我想要一个长时间运行的后台任务来构建一个对象树,并在完成后将它交给 GUI 进行显示。

尝试这样做会导致 InvalidOperationException,“由于调用线程无法访问此对象,因为不同的线程拥有它。” 奇怪的是,简单类型不会发生这种情况。

下面是一些示例代码,演示了引发异常的简单案例。知道如何解决这个问题吗?我很确定问题是后台线程拥有工厂构造的对象,而前台 GUI 线程不能取得所有权,尽管它适用于更简单的系统类型。

private void button1_Click(object sender, RoutedEventArgs e) 
{  
   // These two objects are created on the GUI thread
   String abc = "ABC";  
   Paragraph p = new Paragraph();

   BackgroundWorker bgw = new BackgroundWorker();

   // These two variables are place holders to give scoping access
   String def = null;
   Run r = null;

   // Initialize the place holders with objects created on the background thread
   bgw.DoWork += (s1,e2) =>
     {
       def = "DEF";
       r = new Run("blah");
     };

   // When the background is done, use the factory objects with the GUI
   bgw.RunWorkerCompleted += (s2,e2) =>
     {
        abc = abc + def;         // WORKS: I suspect there's a new object
        Console.WriteLine(abc);  // Console emits 'ABCDEF'

        List<String> l = new List<String>();  // How about stuffing it in a container?
        l.Add(def);                           // WORKS: l has a reference to def

        // BUT THIS FAILS.
        p.Inlines.Add(r);  // Calling thread cannot access this object
     };

   bgw.RunWorkerAsync();
}

问题的主要范围是我有一个在后台动态构建的大型文档,并且希望 GUI 能够显示到目前为止生成的内容,而无需等待完成。

后台工作人员如何充当对象工厂并将内容移交给主线程?

谢谢!

4

2 回答 2

5

您正在尝试Run在后台线程中创建,但RunFrameworkContentElement继承自DispatcherObject并绑定到创建它的线程。

于 2010-08-27T21:00:23.767 回答
1

正如 Franci 所说,Run 是一个 DispatcherObject,因此它只能在创建它的线程上更新。如果它像这样调用 Dispatch.Invoke 或 Dispatcher.BeginInvoke 代码应该运行:

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        var button = sender as Button;

        string abc = "ABC";
        var p = new Paragraph();

        var bgw = new BackgroundWorker();

        String def = null;
        Run r = null;

        bgw.DoWork += (s1, e2) =>
          {
              def = "DEF";
              button.Dispatcher.BeginInvoke(new Action(delegate{r = new Run("blah");}));
          };

        bgw.RunWorkerCompleted += (s2, e2) =>
          {
              abc = abc + def;
              Console.WriteLine(abc); 
              var l = new List<String> { def };
              p.Inlines.Add(r);  // Calling thread can now access this object because 
                                 // it was created on the same thread that is updating it.
          };

        bgw.RunWorkerAsync();
    }
于 2010-08-28T04:29:03.640 回答