繁体   English   中英

阻止ForEach,直到异步事件完成

[英]Blocking ForEach until async event completes

我正在使用ReportViewer控件和自定义打印作业工作流程,这导致了我一些问题。 我的代码看起来像这样:

        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()它将触发其RenderingComplete事件,并且在该事件中,我具有将打印作业排队的逻辑:

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

问题在于ForEach循环在RenderingComplete事件触发之前完成运行,因此我需要一种方法来阻止ForEach循环,直到每次循环传递RenderingComplete事件都触发为止。 有什么好的方法呢?

如果必须将其保留在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 ,您应该改用简写形式() => { ... } )。

尽管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文件。 当然,所有这些操作最初是在提示用户输入打印机设置,然后向打印机发送n个水合报告。

谢谢您的帮助!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM