如何在非主线程中创建控件
本文关键字:创建 控件 线程 | 更新日期: 2023-09-27 18:19:36
在我们的应用程序中,我们可以打印报告。对于这些报告,我们使用FlowDocument、Canvas等类和Labels等其他控件。由于控件只能在主线程中创建,因此应用程序会显示一个通知窗口,用户必须等待。一份报告最多可以有100页,因此这可能需要几分钟的时间。并且用户无法对应用程序执行任何其他操作。
有没有办法摆脱这种局面?
正如我发现的那样,问题并不完全是在一个新线程中创建文档。我能做到。但我无法在之后显示该文档,因为它不是由主线程创建的。如果我在主线程中创建它——这就是我所做的——它会很好地工作。但是在生成报告时应用程序会被阻止。
有没有办法将一个对象的所有权转移到另一个线程?尤其是像Canvas这样的UI元素?就像我之前所说的,我可以在一个额外的线程中创建报告,但应用程序拒绝显示它。没有由主线程创建/拥有的控件无法显示。
你不能。任何UI控件都必须由Dispatcher
管理
如果你绝对需要从另一个线程创建控件,你需要通过主Dispatcher调用它们的创建/添加,同步或异步取决于你的选择
异步调用的快速示例(将BeginInvoke
更改为Invoke
进行同步,并相应地播放DispatcherPriority
)
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
// Your control creation here
}), DispatcherPriority.Background);
我曾经处理过类似的问题:全局DockManager
,对接内容的动态处理,我必须从Dispatcher中创建控件,才能将它们实际添加到DockManager
中。确保您避免了这些调用的任何竞争条件,并且您应该很好
我建议创建一个带有调度器的新线程。在这个单独的线程中,您可以创建元素树,并在独立于主窗口UI线程的单独窗口中显示它。然后用户可以选择是否打印。
要创建一个初始化了WPF调度器的新线程,可以使用以下代码:
public class DispatcherBuilder : IBuilder<Dispatcher>
{
public Dispatcher Build()
{
Dispatcher dispatcher = null;
var manualResetEvent = new ManualResetEvent(false);
var thread = new Thread(() =>
{
dispatcher = Dispatcher.CurrentDispatcher;
var synchronizationContext = new DispatcherSynchronizationContext(dispatcher);
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
manualResetEvent.Set();
Dispatcher.Run();
});
thread.Start();
manualResetEvent.WaitOne();
manualResetEvent.Dispose();
return dispatcher;
}
}