5

私のWPFアプリでWindowは、他のコントロールの中でも、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();
}

これまでのところ、この設定に満足していますが、問題が発生しました。

MyDocumentViewerWindowDocumentViewer を対象とした、組み込みの印刷コマンドを参照する印刷ボタンが含まれています。

<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;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)
...

私の推測では、メインの UI スレッドで印刷ダイアログが開いていて、自分のスレッドによって作成および所有されているドキュメントにアクセスしようとしているため、クラッシュしています。

これを解決する方法はありますか?ウィンドウを独自のスレッドに保持したいと思います。

4

2 に答える 2

7

さらにグーグルで調べた後、次のスレッドに出くわしました。これは、私が抱えている問題とまったく同じようです。

PrintDialog とセカンダリ UI スレッドの深刻な問題

そのスレッドで、男は最終的にカスタムの PrintDialog クラス (ソース コードはここにあります) を使用します。 XPS Document Writer をオーバーライドします。XPS Document Writer は、アプリケーションのメイン UI スレッドにさらに結び付いているようです)。

そのカスタム PrintDialog のコードをコピーして貼り付け (クラスの名前を に変更ThreadSafePrintDialog)、印刷ボタンの 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");
}

完璧に動作します。

于 2011-09-07T15:07:34.853 に答える
1

あなたの勘は正しいです。このオブジェクトが別のスレッドによって作成されている場合、UI スレッドでこのオブジェクトにアクセスすることはできません。

いくつかのオプションがあると思います:

  1. 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)
      });
      });
    }
    
  2. このコマンドを、この別のドキュメントを作成するスレッドに送信できますか? このスレッドを保持して、thread.Invoke(printMethod)

  3. Freezable Objectsを調べることができます。このページの下部にある「Creating Your Own Freezable Class」という見出しを見てください。これにより、ドキュメントを作成したスレッドとは別のスレッドからアクセスできるようになり、ドキュメントはスレッドセーフになります。

于 2011-09-07T14:34:06.970 に答える