Pergunta

Em meu aplicativo WPF, tenho um Window particular que contém, entre outros controles, um DocumentViewer.

Quando esta janela é aberta e carregada, ela cria dinamicamente um FixedDocument com um indicador de progresso e, em seguida, exibe-o no DocumentViewer. Funciona e, para melhorar a experiência do usuário, executo esta janela em seu próprio thread, de modo que a janela principal do aplicativo ainda responda enquanto o documento está sendo compilado.

Com base nas dicas em esta página da web , eu abro minha janela em um novo tópico como este:

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();
}

Estou feliz com essa configuração até agora, mas acabei de ter um problema.

MyDocumentViewerWindow contém um botão de impressão, que faz referência ao comando Imprimir integrado, direcionado ao DocumentViewer:

<Button Command="Print" CommandTarget="{Binding ElementName=MyDocumentViewer}">Print</Button>

Antes de ter a janela em seu próprio thread, isso funcionava bem. Mas agora, quando clico nele, o aplicativo trava. O Visual Studio 2010 destaca a seguinte linha do código acima como o local da falha, com a mensagem ' O thread de chamada não pode acessar este objeto porque um thread diferente o possui. ':

System.Windows.Threading.Dispatcher.Run();

O rastreamento de pilha começa assim:

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

Meu palpite é que a caixa de diálogo de impressão está abrindo no thread de interface do usuário principal e tentando acessar o documento criado e pertencente ao meu próprio thread, daí a falha.

Alguma ideia de como posso resolver isso? Eu gostaria de manter a janela em seu próprio segmento.

Foi útil?

Solução

Depois de mais algumas pesquisas no Google, descobri o seguinte tópico, que parece ser exatamente o problema que estou tendo.

PrintDialog e uma IU secundáriathread grave problema

Nesse tópico, o cara eventualmente usa uma classe PrintDialog personalizada (o código-fonte da qual se encontra aqui ), que é muito parecido com o PrintDialog integrado, mas com alguns ajustes para corrigir esses bugs de thread cruzado (e também substitui o XPS Document Writer, que aparentemente se liga ainda mais ao thread de interface do usuário principal do aplicativo)

Copiei e colei o código desse PrintDialog personalizado (e renomeei a classe para ThreadSafePrintDialog), removi o CommandTarget do botão Imprimir e, em vez disso, usei meu próprio método 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");
}

Funciona perfeitamente.

Outras dicas

Seu palpite está correto.Você não pode acessar este objeto no thread de IU quando ele foi criado por outro thread.

Acredito que você tenha algumas opções:

  1. Você pode criar este documento no thread de IU, talvez reunir as informações de que precisa em um thread de segundo plano e, então, construir o objeto no thread de IU.Depende do que envolve a criação do seu documento.Você poderia fazer algo como:

    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. Você poderia enviar este comando para o thread que cria este outro documento?Segure este tópico e faça um thread.Invoke(printMethod)

  3. Você pode pesquisar Objetos Freezable .Olhe na parte inferior desta página, título "Criando sua própria classe congelável".Isso tornaria seu documento thread-safe para acessar de um thread diferente daquele que o criou.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top