2010-06-10 88 views
1

進出口報告系統的工作,一系列DocumentPage是通過DocumentPaginator創建。這些文件包括許多WPF組件將被實例化,所以分頁程序包括正確的事情,當後發送到XpsDocumentWriter(而這又被髮送到實際打印機)的。在後臺線程創建WPF組件

我現在的問題是,DocumentPage情況下,需要一段時間來創造(足夠的Windows標記應用爲冷凍),所以我想在一個後臺線程,這是有問題來創建它們,因爲WPF期望的屬性從GUI線程設置它們。我還想顯示一個進度條,表明迄今爲止創建了多少頁面。因此,它看起來像我試圖讓兩件事情在GUI上平行發生。

這個問題很難解釋,我真的不知道如何解決它。總之:

  • 創建一系列DocumentPag e's。
    • 包括WPF組件
    • 這些都是要在後臺線程創建,或者使用一些其他的技巧,以便應用程序的心不是凍結。
  • 在創建每個頁面後,應更新WPF ProgressBar

如果沒有體面的方式來做到這一點,替代解決方案和方法是值得歡迎的。

回答

1

你應該能夠只要線程是STA運行在後臺線程分頁程序。

設置完線程後,請在運行之前嘗試此操作。

thread.SetApartmentState(ApartmentState.STA); 

如果你真的必須在GUI線程上,然後檢查出Freezable類,因爲你可能需要的對象從後臺線程移動到GUI線程。

+0

設置apartmentstate可以讓我在後臺線程中創建WPF組件。但我需要稍後將它們移動到GUI線程。本文展示了一種方式(http://www.nbdtech.com/Blog/archive/2007/08/01/Passing-Wpf-Objects-Between-Threads-With-Source-Code.aspx),但是這是與FixedDocument,該方法不支持DocumentPage,因爲它缺少默認構造函數。有沒有辦法可以使用Freezable類將DocumentPage移動到GUI線程? – Mizipzor 2010-06-10 21:10:54

+0

劃傷Freezable建議 - DocumentPage不從Freezable繼承,並且它包含一個Visual,因此創建DocumentPage的Freezable子類很困難。您提到的示例使用XamlReader和XamlWriter跨線程移動對象 - 也許您可以使用BinaryFormatter(或某種其他序列化方法)將DocumentPages序列化爲流,然後在另一側讀取它們? – 2010-06-10 21:27:54

+0

看看BinaryFormatter,它看起來像使用XamlWriter的方法類似。也許它運作得更好,我看着它,謝謝。 =) – Mizipzor 2010-06-10 23:24:14

0

如果需要在UI線程的部分都比較小,你可以使用Dispatcher而不阻塞UI執行這些操作。這有相關的開銷,但它可能允許大部分計算在後臺進行,並將UI線程上的工作與其他UI任務交錯。您也可以使用分派器更新進度條。

0

我的猜測是,所有耗時的工作都在Visual中。如果是這樣,有一個簡單的解決方案:在調用DocumentPaginator.GetPage()之前,不要創建實際的DocumentPage對象及其關聯的視覺對象。

只要消耗你的文檔中的代碼只要求在同一時間不會有性能瓶頸的一個或兩頁。

如果你要打印到打印機或文件,一切都可以在後臺線程來完成,但如果你在屏幕上顯示你只需要反正一次顯示幾個DocumentPages。無論哪種情況,您都不會遇到任何UI鎖定。

最糟糕的情況是應用程序在縮略圖視圖中顯示頁面。在這種情況下,我想:

  1. 縮略圖視圖將它的ItemsSource綁定到最初充滿虛擬頁面的「RealizedPages」收集
  2. 每當一個虛擬頁面被測量後,它會將在的DispatcherPriority調度員操作。背景調用DocumentPaginator.GetPage(),然後將RealizedPages集合中的虛擬頁面替換爲實際頁面。

如果有性能問題,即使由於單獨項目的數量實現一個單一的頁面,在此相同的一般方法可以內頁面上的任何ItemsControl中有大量的項目中使用

還有一點需要注意:XPS打印系統一次不會處理一個以上的DocumentPage,因此如果您知道這是您的客戶端,您可以通過適當的修改反覆重複使用同一個DocumentPage。

0

詳細闡述Ray Burns的回答:難道你沒有在後臺線程的類中完成數據處理,然後在處理完成後將DocumentPage的屬性綁定到這個類?

0

這個遊戲有點晚,但我只是想出了一個解決方案,所以我想我會分享。爲了顯示UI元素,必須在它們將顯示的UI線程上創建UI元素。由於長時間運行的任務位於UI線程上,因此將阻止更新進度欄。爲了解決這個問題,我在新的UI線程上創建了進度條,並在主UI線程上創建了頁面。

 Thread t = new Thread(() => 
      { 
       ProgressDialog pd = new ProgressDialog(context); 
       pd.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen; 
       pd.Show(); 
       System.Windows.Threading.Dispatcher.Run(); 
      }); 
     t.SetApartmentState(ApartmentState.STA); 
     t.IsBackground = true; 
     t.Start(); 

     Action(); //we need to execute the action on the main thread so that UI elements created by the action can later be displayed in the main UI 

'ProgressDialog'是我自己的WPF窗口,用於顯示進度信息。

'context'保存我的進度對話框的進度數據。它包含一個取消的屬性,以便我可以放棄在主線程上運行的動作。它還包含一個完整的屬性,以便在操作完成時進度對話框可以關閉。

'Action'是用於創建所有UI元素的方法。它監視取消標誌的上下文,並在標誌被設置時停止生成UI元素。它完成後設置完整標誌。

我不記得我不得不將線程't'設置爲STA線程和IsBackground爲true的確切原因,但我確信沒有它們就無法工作。

相關問題