RDLC 報表不預覽直接列印(Print RDLC Report without Preview)
RDLC 報表在使用時,一般情況下都會開啟預覽畫面,再按列印才能列印出來。
但是如果設計的程式沒有畫面呢?(Console Mode / Service Mode)
所以,這次我就使用 Console 程式設計方式把報表列印出來,並且使用 Class Data Mode 方式處理資料,不使用 RDLC 報表直接連接資料庫。
由於我使用的是 Visual Studio 2017,本身以不含RDLC報表設計功能,所以必須先到 NuGet 安裝 『Microsoft.ReportingServices.ReportViewerControl.Winforms』
這裡測試的方式是把一篇文章(SourceDoc.txt)讀取出來,然後放到 Report1.rdlc 做成的版面上,然後再將它印出來。
1、建立 DataModel.cs 用來儲存資料
namespace PrintRDLCWithoutPreview
{
class DataModel
{
public int no { get; set; }
public string line { get; set; }
}
}
2、先進行方案的建置,因為RDLC要使用Class Model 資料來源時,需要建置過才能看到。
3、建立 Report1.rdlc
展開專案後可以看到自己定義的 DataModel.cs,選擇它作為資料來源
注意資料集的名稱,待會設定 DataSource 需要用到
拉取要顯示的欄位到需要顯示的類型,由於我們只有文字資料,所以只有拉『line』到顯示『值』上面
如果不需要統計值,直接『下一步』即可
調整位置與版面
4、需要用到的參考,由於會使用到Draw和winform集合的功能,所以必須引入
5、記得 資料檔案和 RDLC 檔案必須將屬性設成『一律複製』,才不會發生找不到檔案問題
6、程式碼(說明在注解上)
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace PrintRDLCWithoutPreview
{
class Program
{
//列印記數用的頁數紀錄
static private int m_currentPageIndex;
//列印用串流資料儲存區
static private IList<Stream> m_streams;
static void Main(string[] args)
{
List<DataModel> data = new List<DataModel>();
int lineCount = 0;
Console.WriteLine("讀取測試文字檔 SourceDoc.txt ....");
using (StreamReader sr = new StreamReader("SourceDoc.txt"))
{
while(sr.Peek() >= 0)
{
DataModel lineText = new DataModel()
{
no = ++lineCount,
line = sr.ReadLine()
};
data.Add(lineText);
}
}
if(lineCount > 0)
{
Console.WriteLine("開始列印!");
try
{
// 列印!
RdlcPrint(data);
}
catch(Exception ex)
{
// 發生處理錯誤時
Console.WriteLine("Print Error :" + ex.Message + "[" + ex.InnerException.ToString() + "]");
}
}
else
{
Console.WriteLine("無資料可以列印!");
}
// 讓Console畫面停住,以便檢視相關錯誤訊息 ***按任意鍵關閉***
Console.ReadKey();
}
// 使用RDLC 直接列印
static void RdlcPrint(List<DataModel> data)
{
// 建立 LocalReport,並指定使用的 RDLC
Microsoft.Reporting.WinForms.LocalReport localReport = new Microsoft.Reporting.WinForms.LocalReport();
localReport.ReportPath = "Report1.rdlc";
// 結繫資料時必須指定RDLC裡面資料集名稱
// 且傳遞的資料必須是以 List<T> 的集合存在
localReport.DataSources.Add(new Microsoft.Reporting.WinForms.ReportDataSource("DataSet1", data));
Export(localReport);
// 指定使用 印表機 『Microsoft XPS Document Writer』
Print("Microsoft XPS Document Writer");
localReport.Dispose();
}
// 提供給 the report renderer 使用, 用來建立列印用的 image stream
static Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek)
{
Stream stream = new MemoryStream();
m_streams.Add(stream);
return stream;
}
// 把 RDLC 輸出成列印報表使用的 EMF(Enhanced Metafile file) 格式 的
static void Export(Microsoft.Reporting.WinForms.LocalReport report)
{
//這裡是決定列印的紙張大小,RDLC只是用來排版的
string deviceInfo =
@"<DeviceInfo>
<OutputFormat>EMF</OutputFormat>
<PageWidth>8.27in</PageWidth>
<PageHeight>11.69in</PageHeight>
<MarginTop>0.25in</MarginTop>
<MarginLeft>0.25in</MarginLeft>
<MarginRight>0.25in</MarginRight>
<MarginBottom>0.25in</MarginBottom>
</DeviceInfo>";
Microsoft.Reporting.WinForms.Warning[] warnings;
m_streams = new List<Stream>();
// 此處就是把 LocalReport 顯示的內容 轉成 Stream 資料
report.Render("Image", deviceInfo, CreateStream, out warnings);
foreach (Stream stream in m_streams)
stream.Position = 0;
}
// 提供給 PrintDocument 作業用的 PrintPageEvents
static void PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs ev)
{
//建立影像 Meta 同時把 Image Stream 倒入
System.Drawing.Imaging.Metafile pageImage = new System.Drawing.Imaging.Metafile(m_streams[m_currentPageIndex]);
// 調整繪製方塊大小同等印表機可列印空間
System.Drawing.Rectangle adjustedRect = new System.Drawing.Rectangle(
ev.PageBounds.Left - (int)ev.PageSettings.HardMarginX,
ev.PageBounds.Top - (int)ev.PageSettings.HardMarginY,
ev.PageBounds.Width,
ev.PageBounds.Height);
// 報表背景以白色塗刷
ev.Graphics.FillRectangle(System.Drawing.Brushes.White, adjustedRect);
// 繪製報表內容
ev.Graphics.DrawImage(pageImage, adjustedRect);
// 準備到下一頁繪製. 確保將資料繪製完畢
m_currentPageIndex++;
ev.HasMorePages = (m_currentPageIndex < m_streams.Count);
}
// 列印 m_streams 內的文件
static void Print(string printerName)
{
if (m_streams == null || m_streams.Count == 0)
throw new Exception("Error: no stream to print.");
System.Drawing.Printing.PrintDocument printDoc = new System.Drawing.Printing.PrintDocument();
// 可以設定要使用的印表機
printDoc.PrinterSettings.PrinterName = printerName;
if (!printDoc.PrinterSettings.IsValid)
{
throw new Exception("Error: cannot find the printer [" + printerName + "] .");
}
else
{
printDoc.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(PrintPage);
m_currentPageIndex = 0;
printDoc.Print();
}
}
}
}
說明:
在 main 一開始建立紀錄用的 DataModel ,由於我打算把文字資料一行一行儲存,所以DataModel 必須以 List<T> 方式記錄,然後把測試用文字檔以 StreamReader 一行一行讀進來,並加入 data 中,然後把 data 送到 RdlcPrint 處理。
在 RdlcPrint 中建立 localreport 物件,並把 data 送入 localreport 的 dataset1 內,產生列印報表,此時這個報表只是在 localreport 內的記憶體呈現,然後把 localreport 送到 Export 中,把 localreport 的畫面 Render 出來成為串流資料 stream 紀錄在 m_streams 變數中。
串流資料產生後,再交由 Print 方法處理,在 Print 中先產生PrintDocument 物件,然後設定好 PrintPageEvent 繪製 m_streams 方式,當把 m_streams 繪製好以後 就由 PrintDocument 直接列印及可。
7、列印結果
留言
先前參考您的電子發票熱感機列印程式,獲得許多幫助 謝謝您
現在又遇到了RDLC的A4格式列印問題想請教您
因User希望A4尺寸發票證明聯可連續列印多筆發票起訖
我使用RDLC列印時在SubReport內無法正常顯示頁首的頁碼資料
有何方法可以在SubReport 依發票字軌顯示出例如: 第1頁/共2頁...第2頁/共2頁的頁碼.
我如何提供您我的rdlc程式碼
Hi Amy:
您可以先參考下面網頁提供的解決方法試試看:
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/c4f45c9a-82e6-4e07-8475-8a270a70f611/subreport-header-is-not-showing-inside-a-report-with-a-header-on-its-own?forum=sqlreportingservices
A:此程式寫法適用於Console模式,直接操作印表機的Meta檔,但是web受限瀏覽器權限無法直接操作印表機,所以無法使用此方式喔