如何在非主线程中创建控件?

时间:2014-08-26 10:56:33

标签: c# wpf multithreading user-interface

在我们的应用程序中,我们可以打印报告。对于这些报告,我们使用类,如FlowDocument,Canvas和其他一些控件,如Labels。由于控件只应在主线程中创建,因此应用程序会显示一个通知窗口,用户必须等待。报告最多可包含100页,因此可能需要几分钟时间。并且用户无法对该应用程序执行任何其他操作。

有没有办法摆脱这种情况?

我发现问题并不是在新线程中创建文档。我可以做这个。但之后我无法显示该文档,因为它不是由主线程创建的。如果我在主线程中创建它 - 这就是我做的 - 它工作正常。但是在生成报告时会阻止应用程序。

有没有办法将对象的所有权转移到另一个线程?尤其是像Canvas这样的UI-Elements?就像我之前说的,我能够在一个额外的线程中创建报告,但应用程序拒绝显示它。不能显示主线程未创建/拥有的控件。

2 个答案:

答案 0 :(得分:2)

你不能。任何UI控件都必须由Dispatcher

管理

如果您绝对需要从另一个线程创建控件,则需要根据您的选择通过主Dispatcher,同步或异步调用它们的创建/添加

异步通话的快速示例(将BeginInvoke更改为Invoke进行同步,并相应地使用DispatcherPriority

 Application.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                // Your control creation here
            }), DispatcherPriority.Background);

我曾经处理类似的问题:全局DockManager,停靠内容的动态处理,我必须从Dispatcher创建我的控件才能将它们实际添加到我的DockManager。确保你通过这些电话避免任何竞争条件,你应该没事。

答案 1 :(得分:0)

我建议创建一个带有调度程序的新线程。在这个单独的线程中,您可以创建元素树并将其显示在独立于主窗口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;
    }
}