在 WPF/C# 中生成复杂内容并将其传递到 GUI 线程
我知道并使用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您尝试在后台线程中创建
Run
,但Run
是一个继承自DispatcherObject
的FrameworkContentElement
并且因此绑定到创建它的线程。You are trying to create the
Run
in the background thread, butRun
is aFrameworkContentElement
which inherits fromDispatcherObject
and thus is bound to the thread that created it.正如 Franci 所说,Run 是一个 DispatcherObject,因此它只能在创建它的线程上更新。如果代码像这样调用 Dispatch.Invoke 或 Dispatcher.BeginInvoke ,则应该运行:
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: