[英]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.