阻塞ForEach直到异步事件完成
本文关键字:事件 异步 ForEach 阻塞 | 更新日期: 2023-09-27 18:16:03
我正在使用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;
在这种情况下,printwwindow的类型是PrintDialog, PrintDocument是System.Drawing.Printing.PrintDocument的扩展,用于处理实际的RDLC文件。所有这一切所做的当然是首先提示用户输入他们的打印机设置,然后将n水合报告发送给打印机。
谢谢你的帮助!