今天,分享下ReportViewer"后台打印"的实现方法!
至于,怎么自定义批量打印,我后面仅提供下我自己的实现方法.不过,可实现的方法很多!
首先,给出微软官方的解决方案:
http://msdn.microsoft.com/zh-cn/library/ms252091%28v=vs.100%29.aspx
而且,如果有这方面需求的朋友,应该都这网上搜索过,网上的帖子,基本都是复制粘贴的微软源码,有的把注释翻译了一下!
是不是不好用啊,格式错乱有木有?
其实,微软给出的解决方案,确实能解决这个后台打印的问题.
而且,我也是用的这个方案.不过,我稍微做了点修改!
咱们先看这部分代码:
1 private void Export(LocalReport report) 2 { 3 string deviceInfo = 4 @"<DeviceInfo> 5 <OutputFormat>EMF</OutputFormat> 6 <PageWidth>8.5in</PageWidth> 7 <PageHeight>11in</PageHeight> 8 <MarginTop>0.25in</MarginTop> 9 <MarginLeft>0.25in</MarginLeft> 10 <MarginRight>0.25in</MarginRight> 11 <MarginBottom>0.25in</MarginBottom> 12 </DeviceInfo>"; 13 Warning[] warnings; 14 m_streams = new List<Stream>(); 15 report.Render("Image", deviceInfo, CreateStream, 16 out warnings); 17 foreach (Stream stream in m_streams) 18 stream.Position = 0; 19 }
这个强行做了报表的页宽,页高和上下左右的边距设置.
然后,将报表的内容按照deviceInfo指定的格式输出到CreateStream函数提供的Stream中。
这里就有了个小问题,我们自己实现的报表,上面的这些边距设置,都是早就固定好的.
所以,只要和上面代码不同边距设置的,打印后,格式就全乱套了.
当然,也可以手动编码为和报表一样的设置,但是你要是有几十张报表,累死你,而且,也不便于维护.
直接给大家,看我修改后的代码:
1 private void Export(LocalReport report) 2 { 3 string deviceInfo = 4 @"<DeviceInfo> 5 <OutputFormat>EMF</OutputFormat> 6 </DeviceInfo>"; 7 Warning[] warnings; 8 m_streams = new List<Stream>(); 9 //将报表的内容按照deviceInfo指定的格式输出到CreateStream函数提供的Stream中。 10 report.Render("Image", deviceInfo, CreateStream, out warnings); 11 foreach (Stream stream in m_streams) 12 stream.Position = 0; 13 }
这下通用了!其中,"EMF"可以理解为,文件后缀名.
这样就解决了报表的设置问题.
但是,微软给出的解决方案里,还有个问题,可能有朋友实验的时候,碰到了!
就是,微软给出的这个方案,只能打印纵向单据,打不了横向的(或者说,报表的第一页,打不了横向的).
大家看这里:
1 private void Print() 2 { 3 if (m_streams == null || m_streams.Count == 0) 4 throw new Exception("Error: no stream to print."); 5 PrintDocument printDoc = new PrintDocument(); 6 if (!printDoc.PrinterSettings.IsValid) 7 { 8 throw new Exception("Error: cannot find the default printer."); 9 } 10 else 11 { 12 printDoc.PrintPage += new PrintPageEventHandler(PrintPage); 13 m_currentPageIndex = 0; 14 printDoc.Print(); 15 } 16 }
这里的"printDoc"没有相关的打印设置(或者说,没有提高可操作的参数).
下面是我修改后的,能自动识别是否需要横向打印的方案.
1 private void PrintSetting() 2 { 3 if (m_streams == null || m_streams.Count == 0) 4 throw new Exception("错误:没有检测到打印数据流"); 5 //声明PrintDocument对象用于数据的打印 6 PrintDocument printDoc = new PrintDocument(); 7 //判断指定的打印机是否可用 8 if (!printDoc.PrinterSettings.IsValid) 9 { 10 throw new Exception("错误:找不到打印机"); 11 } 12 else 13 { 14 //设置打印机方向遵从报表方向 15 printDoc.DefaultPageSettings.Landscape = isLandSapces; 16 //声明PrintDocument对象的PrintPage事件,具体的打印操作需要在这个事件中处理。 17 printDoc.PrintPage += new PrintPageEventHandler(PrintPage); 18 m_currentPageIndex = 0; 19 //执行打印操作,Print方法将触发PrintPage事件。 20 printDoc.Print(); 21 } 22 }
//设置打印机方向遵从报表方向
printDoc.DefaultPageSettings.Landscape = isLandSapces;
其中"isLandSapces"是我这个类中,定义的一个全局bool值.
问题就来了,这个isLandSapces是根据什么取得值?
1 /// <summary> 2 /// 为Report.rdlc创建本地报告加载数据,输出报告到.emf文件,并打印,同时释放资源 3 /// </summary> 4 /// <param name="rv">参数:ReportViewer.LocalReport</param> 5 public void PrintStream(LocalReport lr) 6 { 7 //获取LocalReport中的报表页面方向 8 isLandSapces = lr.GetDefaultPageSettings().IsLandscape; 9 Export(lr); 10 PrintSetting(); 11 Dispose(); 12 }
这是我在类中添加的一个方法!
对,这个isLandSapces,就是取自ReportViewer.LocalReport,这个bool值,你用来加载报表的ReportViewer控件是肯定知道的,把它传递进来,就OK了!
下面给出完整的后台打印代码:

1 using System; 2 using System.Collections.Generic; 3 using System.Drawing; 4 using System.Drawing.Imaging; 5 using System.Drawing.Printing; 6 using System.IO; 7 using System.Text; 8 using Microsoft.Reporting.WinForms; 9 10 namespace HYC.RDLC 11 { 12 public class ReportViewer_Stream_Print : IDisposable 13 { 14 /// <summary> 15 /// 用来记录当前打印到第几页了 16 /// </summary> 17 private int m_currentPageIndex; 18 19 /// <summary> 20 /// 声明一个Stream对象的列表用来保存报表的输出数据,LocalReport对象的Render方法会将报表按页输出为多个Stream对象。 21 /// </summary> 22 private IList<Stream> m_streams; 23 24 private bool isLandSapces = false; 25 26 /// <summary> 27 /// 用来提供Stream对象的函数,用于LocalReport对象的Render方法的第三个参数。 28 /// </summary> 29 /// <param name="name"></param> 30 /// <param name="fileNameExtension"></param> 31 /// <param name="encoding"></param> 32 /// <param name="mimeType"></param> 33 /// <param name="willSeek"></param> 34 /// <returns></returns> 35 private Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek) 36 { 37 //如果需要将报表输出的数据保存为文件,请使用FileStream对象。 38 Stream stream = new MemoryStream(); 39 m_streams.Add(stream); 40 return stream; 41 } 42 43 /// <summary> 44 /// 为Report.rdlc创建本地报告加载数据,输出报告到.emf文件,并打印,同时释放资源 45 /// </summary> 46 /// <param name="rv">参数:ReportViewer.LocalReport</param> 47 public void PrintStream(LocalReport rvDoc) 48 { 49 //获取LocalReport中的报表页面方向 50 isLandSapces = rvDoc.GetDefaultPageSettings().IsLandscape; 51 Export(rvDoc); 52 PrintSetting(); 53 Dispose(); 54 } 55 56 private void Export(LocalReport report) 57 { 58 string deviceInfo = 59 @"<DeviceInfo> 60 <OutputFormat>EMF</OutputFormat> 61 </DeviceInfo>"; 62 Warning[] warnings; 63 m_streams = new List<Stream>(); 64 //将报表的内容按照deviceInfo指定的格式输出到CreateStream函数提供的Stream中。 65 report.Render("Image", deviceInfo, CreateStream, out warnings); 66 foreach (Stream stream in m_streams) 67 stream.Position = 0; 68 } 69 70 private void PrintSetting() 71 { 72 if (m_streams == null || m_streams.Count == 0) 73 throw new Exception("错误:没有检测到打印数据流"); 74 //声明PrintDocument对象用于数据的打印 75 PrintDocument printDoc = new PrintDocument(); 76 //判断指定的打印机是否可用 77 if (!printDoc.PrinterSettings.IsValid) 78 { 79 throw new Exception("错误:找不到打印机"); 80 } 81 else 82 { 83 //设置打印机方向遵从报表方向 84 printDoc.DefaultPageSettings.Landscape = isLandSapces; 85 //声明PrintDocument对象的PrintPage事件,具体的打印操作需要在这个事件中处理。 86 printDoc.PrintPage += new PrintPageEventHandler(PrintPage); 87 m_currentPageIndex = 0; 88 //执行打印操作,Print方法将触发PrintPage事件。 89 printDoc.Print(); 90 } 91 } 92 93 /// <summary> 94 /// 处理程序PrintPageEvents 95 /// </summary> 96 /// <param name="sender"></param> 97 /// <param name="ev"></param> 98 private void PrintPage(object sender, PrintPageEventArgs ev) 99 { 100 //Metafile对象用来保存EMF或WMF格式的图形, 101 //我们在前面将报表的内容输出为EMF图形格式的数据流。 102 Metafile pageImage = new Metafile(m_streams[m_currentPageIndex]); 103 104 //调整打印机区域的边距 105 Rectangle adjustedRect = new Rectangle( 106 ev.PageBounds.Left - (int)ev.PageSettings.HardMarginX, 107 ev.PageBounds.Top - (int)ev.PageSettings.HardMarginY, 108 ev.PageBounds.Width, 109 ev.PageBounds.Height); 110 111 //绘制一个白色背景的报告 112 //ev.Graphics.FillRectangle(Brushes.White, adjustedRect); 113 114 //获取报告内容 115 //这里的Graphics对象实际指向了打印机 116 ev.Graphics.DrawImage(pageImage, adjustedRect); 117 //ev.Graphics.DrawImage(pageImage, ev.PageBounds); 118 119 // 准备下一个页,已确定操作尚未结束 120 m_currentPageIndex++; 121 122 //设置是否需要继续打印 123 ev.HasMorePages = (m_currentPageIndex < m_streams.Count); 124 } 125 126 public void Dispose() 127 { 128 if (m_streams != null) 129 { 130 foreach (Stream stream in m_streams) 131 stream.Close(); 132 m_streams = null; 133 } 134 } 135 } 136 }
调用方法:
1 ReportViewer_Stream_Print rvsp = new ReportViewer_Stream_Print(); 2 rvDoc.LocalReport.ReportEmbeddedResource = string.Format("项目名称.Report.{0}.rdlc", reportname); 3 rvsp.PrintStream(rvDoc.LocalReport);
后台打印完成!
自定义批量打印:
其实,这个就是用上面的调用方法,
然后,遍历一组报表名称,传递给其中的"reportname"参数!
这个组可以是一维数组,也可以是二维数组,[0]里记录报表名称,[1]里记录打印份数!
当然,也不一定非是数组,方法多多!
现在还没想好下篇写啥,或者大家有想交流的,欢迎拍砖,素质留言,恳请指教!