代码之家  ›  专栏  ›  技术社区  ›  Walt Stoneburner

在WPF/C中生成复杂内容并将其传递给GUI线程#

  •  3
  • Walt Stoneburner  · 技术社区  · 15 年前

    我知道,并使用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能够显示到目前为止生成的内容,而不必等待完成。

    谢谢!

    2 回复  |  直到 15 年前
        1
  •  5
  •   Franci Penov    15 年前

    您正在尝试创建 Run FrameworkContentElement DispatcherObject 因此被绑定到创建它的线程。

        2
  •  1
  •   JoshVarga    15 年前

    正如Franci所说,Run是一个DispatcherObject,所以它只能在创建它的线程上更新。如果代码调用调度.调用或者调度器.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();
        }