2013-05-30 82 views
2

我正在使用ReportViewer控件和導致我出現問題的自定義打印作業工作流。我的代碼看起來有點像這樣:阻止ForEach直到異步事件完成

 ids.ForEach(delegate(Guid? guid) 
      { 
       var details = items.Where(e => e.guid == guid); 

       var ds = new ReportDataSource("Form", details); 
       ReportViewer.LocalReport.DataSources.Clear(); 
       ReportViewer.LocalReport.DataSources.Add(ds); 
       ReportViewer.RefreshReport(); 

      }); 

RefreshReport()被稱爲最終它將觸發其​​事件,正是在這種情況下,我有邏輯排隊打印作業:

 if (DisplayPrintDialog) ReportViewer.PrintDialog(); 
     else 
     { 
      var document = new PrintDocument(ReportViewer.LocalReport); 
      document.PrinterSettings = ReportViewer.PrinterSettings; 
      document.Print(); 
     } 
     DisplayPrintDialog = false; 

問題是ForEach循環在​​事件觸發之前完成運行,因此我需要一種方法來阻止我的ForEach循環,直到RenderingComplete事件觸發循環的每次傳遞爲止。什麼是這樣做的好方法?

回答

0

儘管Spike的片段是一個很好的例子,表明它不足以解決我的問題(並非他們自己的錯誤),甚至修改他們的想法會使代碼更加複雜。

經過研究這個問題,我能夠分離ReportViewer控件和這個特定的過程。這就是說,我已經在這個特定的工作流程中刪除了對ReportViewer控件的需求(並因此需要發送信號),並提出了一些更直接的方法。

foreach (var item in guids) 
{ 
    var details = items.Where(e => e.guid == item); 
    var report = new LocalReport 
     { 
      ReportPath = [Path] 
     }; 

    var ds = new ReportDataSource("Form", details); 
    report.DataSources.Clear(); 
    report.DataSources.Add(ds); 

    printingInvoked = ProcessPrintJob(report); 

    if (!printingInvoked) break; 
} 

這與我的主要區別在於我創建一個LocalReport的實例,並與直接代替的ReportViewer的LocalReport物業工作原代碼。然後,我們實際處理打印作業:

if (DisplayPrintDialog) 
{ 
    if (PrintWindow.ShowDialog() != DialogResult.OK) 
    { 
      return false; 
    } 
} 

var document = new PrintDocument(report); 
document.PrinterSettings = PrintWindow.PrinterSettings; 
document.Print(); 

DisplayPrintDialog = false; 
return true; 

在這種情況下PrintWindow的類型PrintDialog類和PrintDocument的是System.Drawing.Printing.PrintDocument的擴展來處理實際RDLC文件。所有這些當然都是在首先提示用戶進行打印機設置,然後將水合報告給打印機。

感謝您的幫助!

5

如果您必須將其保存在foreach中,請使用AutoResetEvent

// Define this elsewhere in your class 
static AutoResetEvent reset = new AutoResetEvent(false); 

// This assumes RenderingComplete takes 1 argument, 
// and you aren't going to use it. If you are, change 
// the _ to something more meaningful. 
ReportViewer.RenderingComplete += _ => 
{ 
    // This happens after the code below, and this tells the "WaitOne" 
    // lock that it can continue on with the rest of the code. 
    reset.Set(); 
} 

ids.ForEach(guid => 
{ 
    var details = items.Where(e => e.guid == guid); 

    var ds = new ReportDataSource("Form", details); 
    ReportViewer.LocalReport.DataSources.Clear(); 
    ReportViewer.LocalReport.DataSources.Add(ds); 

    // Begin the async refresh 
    ReportViewer.RefreshReport(); 

    reset.WaitOne(); // This call will block until "reset.Set()" is called. 
    reset.Reset(); // This resets the state for the next loop iteration. 
}); 

我把讓您的匿名委託不太難看,太自由(在這一點上有沒有真正的理由使用關鍵字delegate了,你應該使用簡寫() => { ... }代替)。

+0

真的嗎?會不會'reset.WaitOne();'阻止UI線程,並將所有東西拖入死鎖? – YK1

+0

此外,您正在重新訂閱新委託給'ReportViewer.RenderingComplete'的foreach非常迭代,這將導致'reset.Set();'被調用超過需要的次數。 – YK1

+1

OP沒有指定這是否是一個UI線程,我不是特別關心它是否阻止UI,他們是那些想要將異步操作轉換爲同步操作的人,而不是我。 :)另外,很好的事件發生,我修正了。 – qJake

相關問題