2011-09-07 2 views
5

내 WPF 응용 프로그램에는 다른 컨트롤 중에서 DocumentViewer이 포함 된 특정 Window이 있습니다.다른 UI 스레드에서 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(); 
} 

지금까지이 설정과 행복,하지만 난 그냥 문제로 실행 : this web page에서 팁을 바탕으로

는이 같은 새 스레드 내 창을 엽니 다. 나는 그것의 자신의 스레드에서 창을 가지고 전에

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

, 이것은 잘 작동 :

MyDocumentViewerWindow

는 내장 된 인쇄 명령의 DocumentViewer 대상으로 참조하는 인쇄 버튼이 포함되어 있습니다. 하지만 지금은 그것을 클릭하면 응용 프로그램이 충돌합니다. 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 스레드에서 개방하고, 생성 된 문서에 액세스를 시도하고 소유한다는 것입니다 내 자신의 스레드에 의해, 따라서 충돌.

어떻게 해결할 수 있을까요? 창을 자체 스레드에 보관하고 싶습니다.

답변

6

Google 검색을 한 번 더 해보니 다음 스레드가 발견되어 정확한 문제가있는 것으로 보입니다.스레드에서

PrintDialog and a secondary UI thread severe problem

는, 사람은 결국 내장으로 PrintDialog 거의 동일 사용자 정의 PrintDialog 클래스를 (소스 코드있는이 here를 발견), 사용하지만 약간의 비틀기로 해결 이 크로스 스레드 버그 (또한 응용 프로그램의 기본 UI 스레드에 추가로 묶여있는 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

직감이 정확합니다. UI 스레드가 다른 스레드에 의해 생성되었을 때 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에서 볼 수 있습니다. 이 페이지 하단의 "자신 만의 Freezable 클래스 만들기"를 참조하십시오. 이렇게하면 문서를 만든 스레드와 다른 스레드에서 스레드에 액세스 할 수 있습니다.

+0

감사합니다. 내 문서를 만드는 것은 FixedDocument 인스턴스화, FixedPage 개체 추가, 컨트롤로 채우기 등을 포함합니다. FixedDocument가 DispatcherObject이기 때문에 백그라운드 스레드에서이 인스턴스를 만든 다음 DocumentViewer의 소스로 설정할 수 없었습니다. 크로스 스레드 위반 내 문서를 DocumentViewer와 동일한 스레드 (즉, UI 스레드)에 작성해야한다는 것을 알게되었습니다 .-(하지만 지금은 문제에 대한 해결책을 찾았습니다. 지금 게시 할 것입니다. – Ross