2011-09-07 36 views
5

Trong ứng dụng WPF của tôi, tôi có Window cụ thể chứa các điều khiển khác, DocumentViewer.In nội dung của một DocumentViewer trong một chuỗi giao diện người dùng khác nhau

Khi cửa sổ này được mở và tải, nó sẽ tự động xây dựng một FixedDocument với chỉ báo tiến trình và sau đó hiển thị nó trong DocumentViewer. Nó hoạt động, và để cải thiện trải nghiệm người dùng, tôi chạy cửa sổ này theo chủ đề riêng của nó, để cửa sổ ứng dụng chính vẫn đáp ứng trong khi tài liệu đang được xây dựng.

Dựa trên những lời khuyên ở this web page, tôi mở cửa sổ của tôi trong một thread mới như thế này:

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

tôi đã hài lòng với thiết lập này cho đến nay, nhưng tôi chỉ chạy vào một vấn đề.

MyDocumentViewerWindow chứa một nút in, mà tham chiếu được xây dựng trong lệnh Print, nhắm vào các DocumentViewer:

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

Trước khi tôi đã có cửa sổ trong chủ đề riêng của mình, điều này làm việc tốt. Nhưng bây giờ, khi tôi nhấp vào nó, ứng dụng bị treo. Visual Studio 2010 làm nổi bật dòng sau từ mã ở trên làm vị trí tai nạn, với thông báo 'Chuỗi cuộc gọi không thể truy cập đối tượng này bởi vì một chuỗi khác sở hữu nó. ':

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

Các vết đống bắt đầu như thế này:

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

linh cảm của tôi là hộp thoại in được mở trong thread UI chính, và cố gắng để truy cập vào các tài liệu được tạo ra và sở hữu theo chủ đề của riêng tôi, do đó sự cố.

Bất kỳ ý tưởng nào về cách tôi có thể giải quyết vấn đề này? Tôi muốn giữ cửa sổ theo chủ đề riêng của nó.

Trả lời

6

Sau khi một số Googling khác, tôi tình cờ gặp phải chuỗi sau, đây dường như là vấn đề chính xác mà tôi đang gặp phải.

PrintDialog and a secondary UI thread severe problem

Trong chủ đề đó, anh chàng cuối cùng sử dụng một PrintDialog lớp tùy chỉnh (mã nguồn trong đó được tìm thấy here), trong đó có nhiều giống như được xây dựng trong PrintDialog, nhưng với một vài tinh chỉnh để sửa chữa các lỗi cross-thread này (và nó cũng ghi đè lên XPS Document Writer, mà dường như ràng buộc chính nó hơn nữa vào luồng giao diện người dùng chính của ứng dụng)

Tôi đã sao chép và dán mã cho PrintDialog tùy chỉnh đó (và đổi tên lớp thành ThreadSafePrintDialog) , đã xóa CommandTarget của nút In của tôi và thay vào đó sử dụng phương thức In của riêng tôi:

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

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

Hoạt động hoàn hảo.

1

Linh cảm của bạn là chính xác. Bạn không thể truy cập đối tượng này trên chuỗi giao diện người dùng khi nó đã được tạo bởi một chuỗi khác.

Tôi tin rằng bạn có một vài lựa chọn:

1) Bạn có thể tạo ra tài liệu này trên thread UI, có lẽ thu thập những thông tin mà bạn cần trong một sợi nền và sau đó thực sự xây dựng các đối tượng trên thread UI. Nó phụ thuộc vào việc tạo tài liệu của bạn. Bạn có thể làm điều gì đó như:

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) Bạn có thể gửi lệnh này đến chuỗi tạo tài liệu khác này? Giữ nguyên chuỗi này và làm một số thread.Invoke(printMethod)

3) Bạn có thể xem Freezable Objects. Nhìn vào dưới cùng của trang này, hướng dẫn "Tạo lớp Freezable riêng của bạn". Điều này sẽ làm cho tài liệu của bạn an toàn chỉ để truy cập từ một luồng khác với chuỗi đã tạo ra nó.

+0

Cảm ơn vì điều đó. Tạo tài liệu của tôi liên quan đến việc tạo một FixedDocument, thêm các đối tượng FixedPage, điền chúng với các điều khiển, vv Bởi vì FixedDocument là một DispatcherObject, tôi không thể tạo nó trong một luồng nền và sau đó đặt nó làm nguồn cho DocumentViewer. vi phạm chủ đề chéo. Tôi thấy rằng tôi phải tạo tài liệu của mình trong cùng một luồng với DocumentViewer - tức là trên một chuỗi giao diện người dùng :-(Nhưng tôi đã tìm thấy giải pháp cho vấn đề của mình - tôi sẽ đăng nó ngay bây giờ. – Ross

Các vấn đề liên quan