DocumentViewerのコンテンツを別のUIスレッドに印刷する
-
27-10-2019 - |
質問
私のWPFアプリには、特別なものがあります Window
他のコントロールの中でも、a DocumentViewer
.
このウィンドウが開いてロードされると、動的に構築されます FixedDocument
進行状況インジケーターを使用して、 DocumentViewer
. 。それは機能し、ユーザーエクスペリエンスを改善するために、このウィンドウを独自のスレッドで実行するため、ドキュメントが構築されている間、メインアプリケーションウィンドウがまだ応答します。
のヒントに基づいています このWebページ, 、このような新しいスレッドでウィンドウを開きます:
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();
}
私はこれまでこのセットアップに満足していますが、問題に遭遇しました。
MyDocumentViewerWindow
documentViewerをターゲットにした内蔵プリントコマンドを参照する印刷ボタンが含まれています。
<Button Command="Print" CommandTarget="{Binding ElementName=MyDocumentViewer}">Print</Button>
窓を独自のスレッドにする前に、これは正常に機能しました。しかし、今、クリックすると、アプリケーションがクラッシュします。 Visual Studio 2010は、上記のコードからの次の行をクラッシュの場所として強調しています。別のスレッドが所有しているため、呼び出しスレッドはこのオブジェクトにアクセスできません。':
System.Windows.Threading.Dispatcher.Run();
スタックトレースは次のように始まります:
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; writer, PrintTicket&amp; partialTrustPrintTicket, PrintQueue&amp; partialTrustPrintQueue, Double&amp; width, Double&amp; height, String jobDescription)
at System.Printing.PrintQueue.CreateXpsDocumentWriter(String jobDescription, PrintDocumentImageableArea&amp; documentImageableArea)
at System.Windows.Controls.Primitives.DocumentViewerBase.OnPrintCommand()
at System.Windows.Controls.Primitives.DocumentViewerBase.ExecutedRoutedEventHandler(Object target, ExecutedRoutedEventArgs args)
...
私の予想では、印刷ダイアログがメインUIスレッドで開いており、自分のスレッドで作成および所有されているドキュメントにアクセスしようとしているため、クラッシュが行われます。
これを解決する方法はありますか?ウィンドウを独自のスレッドに保持したいと思います。
解決
もう少しグーグルで、次のスレッドに出くわしました。これは、私が抱えている正確な問題のようです。
そのスレッドでは、男は最終的にカスタムPrintDialogクラスを使用します(そのソースコードが見つかりました ここ)、これは組み込みのprintdialogとほぼ同じですが、これらのクロススレッドバグを修正するためのいくつかの微調整があります(また、XPSドキュメントライターもオーバーライドします。
私はそのカスタムPrintDialogのコードをコピーして貼り付けました(そしてクラスの名前を変更しました ThreadSafePrintDialog
)、印刷ボタンのコマンドタルゲットを削除し、代わりに自分の印刷方法を使用します。
private void Print_Executed(object sender, ExecutedRoutedEventArgs args) {
var printDialog = new ThreadSafePrintDialog();
if (!printDialog.ShowDialog(this)) return;
printDialog.PrintDocument(DocumentViewer.Document.DocumentPaginator, "My Document");
}
完全に機能します。
他のヒント
あなたの予感は正しいです。 UIスレッドが別のスレッドによって作成された場合、このオブジェクトにアクセスすることはできません。
いくつかの選択肢があると思います:
このドキュメントをUIスレッドで作成し、おそらくバックグラウンドスレッドで必要な情報を収集してから、実際にUIスレッドでオブジェクトを構築できます。それはあなたのドキュメントの作成に伴うものに依存します。あなたは次のようなことをすることができます:
public void CreateDocument(T inputDataForDocumentCreation) { var uiDispatcher = Dispatcher.CurrentDispatcher; ThreadPool.QueueUserWorkItem(_ => { // Load and create document components with yourDataForDocumentCreation dispatcher.BeginInvoke(DispatcherPriority.Normal, () => { //Actually create the document (this will happen on the UI thread, so it may be accessed from the UI thread) }); }); }
おそらく、この他のドキュメントを作成するスレッドにこのコマンドを送信できますか?このスレッドを保持して、aを行います
thread.Invoke(printMethod)
調べることができます フリーズ可能なオブジェクト. 。このページの下部を見て、「独自のフリーズ可能なクラスの作成」に向かっています。これにより、ドキュメントが作成されたスレッドとは異なるスレッドからアクセスできるように、ドキュメントセーフになります。