Печать содержимого документов в другом потоке пользовательского интерфейса

StackOverflow https://stackoverflow.com/questions/7335507

Вопрос

В моем приложении 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();
}

Я был доволен этой настройкой, но я только что столкнулся с проблемой.

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

Я догадываюсь, что диалог печати открывается в основном потоке пользовательского интерфейса и пытается получить доступ к документу, который создан и принадлежит моей собственной ветке, отсюда и сбой.

Есть идеи, как я могу решить это? Я хотел бы сохранить окно в его собственной ветке.

Это было полезно?

Решение

После еще некоторых гуглей я наткнулся на следующую ветку, которая, кажется, является точной проблемой, которая у меня есть.

PrintDialog и вторичная нить пользовательского интерфейса серьезная проблема

В этой теме парень в конечном итоге использует пользовательский класс printdialog (исходный код которого найден здесь), который почти такой же, как встроенный printdialog, но с несколькими изменениями, чтобы исправить эти кросс-тренажеры (и также переопределяет автора документов XPS, что, по-видимому, еще больше связывается с основным потоком пользовательского интерфейса приложения)

Я скопировал и вставил код для этого пользовательского printdialog (и переименовал класс в ThreadSafePrintDialog), удалил CommandTarget моей кнопки печати и вместо этого используйте свой собственный метод печати:

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

    printDialog.PrintDocument(DocumentViewer.Document.DocumentPaginator, "My Document");
}

Работает отлично.

Другие советы

Ваш догад верен. Вы не можете получить доступ к этому объекту в потоке пользовательского интерфейса, когда он был создан другим потоком.

Я считаю, что у вас есть несколько вариантов:

  1. Вы можете создать этот документ в потоке пользовательского интерфейса, возможно, собрать нужную информацию в фоновом потоке, а затем фактически построить объект в потоке пользовательского интерфейса. Это зависит от того, что влечет за собой создание вашего документа. Вы могли бы сделать что -то вроде:

    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. Вы могли бы посмотреть Замораживаемые объекты. Анкет Посмотрите на нижнюю часть этой страницы, направляя «Создание собственного замораживаемого класса». Это сделало бы ваш документ-поток для доступа из другого потока, чем тот, который создал его.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top