2

我正在使用 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 事件。什么是解决这个问题的好方法?

4

2 回答 2

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,你应该使用简写() => { ... }代替)。

于 2013-05-30T20:25:14.397 回答
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 文件。当然,所有这些都是最初提示用户进行打印机设置,然后将n 个水合报告发送到打印机。

谢谢您的帮助!

于 2013-05-31T17:54:22.837 回答