在不同的UI线程中打印DocumentViewer的内容

Ros*_*oss 5 printing wpf multithreading printdialog documentviewer

在我的WPF应用程序中,我特别Window包含其中一个控件DocumentViewer.

打开并加载此窗口时,它会动态构建FixedDocument带有进度指示器的窗口,然后将其显示在DocumentViewer.它工作,并为了改善用户体验,我在自己的线程中运行此窗口,以便在构建文档时主应用程序窗口仍然响应.

基于此网页上的提示,我在一个新的线程中打开我的窗口,如下所示:

public void ShowDocumentViewerWindow(params object[] data) {
    var thread = new Thread(() => {
        var window = new MyDocumentViewerWindow(new MyObject(data));
        window.Closed += (s, a) => window.Dispatcher.InvokeShutdown();
        window.Show();
        System.Windows.Threading.Dispatcher.Run();
    });
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
}
Run Code Online (Sandbox Code Playgroud)

到目前为止,我对这个设置很满意,但我刚遇到了一个问题.

MyDocumentViewerWindow 包含一个打印按钮,它引用内置的Print命令,以DocumentViewer为目标:

<Button Command="Print" CommandTarget="{Binding ElementName=MyDocumentViewer}">Print</Button>
Run Code Online (Sandbox Code Playgroud)

在我把窗口放在自己的线程之前,这很好用.但现在,当我点击它时,应用程序崩溃了.Visual Studio 2010将上面代码中的以下行突出显示为崩溃位置,并显示消息' 调用线程无法访问此对象,因为另一个线程拥有它.":

System.Windows.Threading.Dispatcher.Run();
Run Code Online (Sandbox Code Playgroud)

堆栈跟踪如下所示:

at System.Windows.Threading.Dispatcher.VerifyAccess()
at MS.Internal.Printing.Win32PrintDialog.ShowDialog()
at System.Windows.Controls.PrintDialog.ShowDialog()
at System.Printing.PrintQueue.GatherDataFromPrintDialog(PrintDialog printDialog, XpsDocumentWriter&amp;amp; writer, PrintTicket&amp;amp; partialTrustPrintTicket, PrintQueue&amp;amp; partialTrustPrintQueue, Double&amp;amp; width, Double&amp;amp; height, String jobDescription)
at System.Printing.PrintQueue.CreateXpsDocumentWriter(String jobDescription, PrintDocumentImageableArea&amp;amp; documentImageableArea)
at System.Windows.Controls.Primitives.DocumentViewerBase.OnPrintCommand()
at System.Windows.Controls.Primitives.DocumentViewerBase.ExecutedRoutedEventHandler(Object target, ExecutedRoutedEventArgs args)
...
Run Code Online (Sandbox Code Playgroud)

我的预感是打印对话框在主UI线程中打开,并尝试访问由我自己的线程创建和拥有的文档,因此崩溃.

我有什么想法可以解决这个问题?我想把窗口保持在自己的线程中.

Ros*_*oss 7

经过一些谷歌搜索后,我偶然发现了以下主题,这似乎是我遇到的确切问题.

PrintDialog和辅助UI线程严重问题

在那个线程中,这个人最终使用了一个自定义的PrintDialog类(其源代码在这里找到),这与内置的PrintDialog非常相似,但是通过一些调整来修复这些跨线程的bug(它也是覆盖XPS文档编写器,它显然将自己与应用程序的主UI线程进一步联系起来)

我复制并粘贴了自定义PrintDialog的代码(并将类重命名为ThreadSafePrintDialog),删除了我的Print按钮的CommandTarget,而是使用我自己的Print方法:

private void Print_Executed(object sender, ExecutedRoutedEventArgs args) {
    var printDialog = new ThreadSafePrintDialog();
    if (!printDialog.ShowDialog(this)) return;

    printDialog.PrintDocument(DocumentViewer.Document.DocumentPaginator, "My Document");
}
Run Code Online (Sandbox Code Playgroud)

完美的工作.