今天,分享下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]里記錄打印份數!
當然,也不一定非是數組,方法多多!
現在還沒想好下篇寫啥,或者大家有想交流的,歡迎拍磚,素質留言,懇請指教!