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

发布于 2024-09-16 07:30:32 字数 1526 浏览 8 评论 0原文

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

后台工作者如何充当对象工厂并将内容传递给主线程?

谢谢!

I'm aware, and use, the xxx.Dispatcher.Invoke() method to get the background thread to manipulate GUI elements. I think I'm bumping up against something similar, but slightly different, where I want a long running background task to construct a tree of objects and when done hand it to the GUI for display.

Attempting to do that results in an InvalidOperationException, "due to the calling thread cannot access this object because a different thread owns it." Curiously, this doesn't happen with simple type.

Here's some example code that demonstrates a trivial case the throws the exception. Any idea how to work around this? I'm pretty sure that the problem is the background thread owns the factory constructed object and that the foreground GUI thread can't take ownership, although it works for more simple system types.

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();
}

The grand scope of the problem is that I have a large document that I'm constructing on the fly in the background and would love for the GUI to show what's been generated so far without having to wait for completion.

How can a background worker act as an object factory and hand off content to the main thread?

Thanks!

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

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

发布评论

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

评论(2

2024-09-23 07:30:32

您尝试在后台线程中创建 Run,但 Run 是一个继承自 DispatcherObjectFrameworkContentElement 并且因此绑定到创建它的线程。

You are trying to create the Run in the background thread, but Run is a FrameworkContentElement which inherits from DispatcherObject and thus is bound to the thread that created it.

堇年纸鸢 2024-09-23 07:30:32

正如 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();
    }

As Franci said, Run is a DispatcherObject, so it is only updatable on the thread that created it. The code should run if it calls Dispatch.Invoke or Dispatcher.BeginInvoke like this:

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