在后台线程中创建WPF组件

时间:2010-06-10 20:37:51

标签: wpf multithreading documentpage documentpaginator

我正在处理报告系统,将通过DocumentPage创建一系列DocumentPaginator。这些文档包括一些 WPF组件,这些文件将被实例化,以便在以后发送到XpsDocumentWriter(然后将其发送到实际打印机)时,分页器包含正确的内容。 / p>

我现在的问题是DocumentPage个实例需要很长时间才能创建(足以让Windows将应用程序标记为已冻结),所以我尝试在后台线程中创建它们,这是有问题的,因为WPF期望从GUI线程设置它们的属性。我还希望显示一个进度条,指示到目前为止已创建了多少页。因此,看起来我试图在GUI上并行发生两件事情。

问题很难解释,我真的不确定如何解决它。简而言之:

  • 创建一系列DocumentPag e。
    • 这些包括 WPF组件
    • 这些是在后台线程上创建的,或者使用其他技巧,因此应用程序不会被冻结。
  • 创建每个页面后,应更新 WPF ProgressBar

如果没有合适的方法可以做到这一点,我们非常欢迎其他解决方案和方法。

5 个答案:

答案 0 :(得分:1)

只要线程是STA,您就应该能够在后台线程中运行paginator。

设置完线程后,请在运行之前尝试此操作。

thread.SetApartmentState(ApartmentState.STA);

如果你真的必须在GUI线程上,那么请查看Freezable类,因为你可能必须将对象从后台线程移动到GUI线程。

答案 1 :(得分:0)

如果需要UI线程的部分相对较小,则可以使用Dispatcher执行这些操作,而不会阻止UI。这有与此相关的开销,但它可能允许大量计算在后台进行,并将UI线程上的工作与其他UI任务交错。您也可以使用Dispatcher更新进度条。

答案 2 :(得分:0)

我的猜测是创建的所有内容都在您的Visual中。如果是这样,有一个简单的解决方案:在调用DocumentPaginator.GetPage()之前,不要创建实际的DocumentPage对象及其关联的Visual。

只要使用您的文档的代码一次只请求一个或两个页面,就不会出现性能瓶颈。

如果您要打印到打印机或文件,一切都可以在后台线程上完成,但如果您在屏幕上显示,则无论如何都只需要一次显示几个DocumentPages。在任何一种情况下,您都不会获得任何UI锁定。

最糟糕的情况是应用程序以缩略图视图显示页面。在这种情况下,我会:

  1. 缩略图视图会将其ItemsSource绑定到“RealizedPages”集合,该集合最初填充虚拟页面
  2. 每当测量虚拟页面时,它会在DispatcherPriority.Background对调度程序操作进行排队,以调用DocumentPaginator.GetPage(),然后将RealizedPages集合中的虚拟页面替换为真实页面。
  3. 如果由于单独项目的数量而在实现单个页面时存在性能问题,则可以在中使用内的相同通用方法,无论页面上的ItemsControl具有大量项目。< / p>

    还有一点需要注意:XPS打印系统一次不会处理多个DocumentPage,所以如果您知道这是您的客户端,您实际上可以通过适当的修改一遍又一遍地返回相同的DocumentPage。

答案 3 :(得分:0)

进一步阐述Ray Burns的回答:难道你不能在后台线程的类中完成数据处理,然后在处理完成后将DocumentPage的属性数据绑定到这个类吗?

答案 4 :(得分:0)

这个游戏有点晚了,但我刚刚找到了解决方案,所以我想我会分享。为了显示UI元素,必须在将要显示它们的UI线程上创建它们。由于长时间运行的任务在UI线程上,因此将阻止进度条更新。为了解决这个问题,我在新的UI线程上创建了进度条,并在主UI线程上创建了页面。

        Thread t = new Thread(() =>
            {
                ProgressDialog pd = new ProgressDialog(context);
                pd.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
                pd.Show();
                System.Windows.Threading.Dispatcher.Run();
            });
        t.SetApartmentState(ApartmentState.STA);
        t.IsBackground = true;
        t.Start();

        Action();   //we need to execute the action on the main thread so that UI elements created by the action can later be displayed in the main UI

'ProgressDialog'是我自己的WPF窗口,用于显示进度信息。

'context'保存我的进度对话框的进度数据。它包含一个已取消的属性,以便我可以中止在主线程上运行的操作。它还包含一个完整的属性,因此当Action完成时,进度对话框可以关闭。

'Action'是用于创建所有UI元素的方法。它监视取消标志的上下文,并在设置标志时停止生成UI元素。它完成时设置完整的标志。

我不记得我必须将Thread't'设置为STA线程并将IsBackground设置为true的确切原因,但我很确定如果没有它们将无法工作。