ReportViewer下的RDLC(一)
http://blog.csdn.net/dnfon/article/details/7673081
今天,分享下ReportViewer"后台打印"的实现方法!
至于,怎么自定义批量打印,我后面仅提供下我自己的实现方法.不过,可实现的方法很多!
首先,给出微软官方的解决方案:
http://msdn.microsoft.com/zh-cn/library/ms252091%28v=vs.100%29.aspx
而且,如果有这方面需求的朋友,应该都这网上搜索过,网上的帖子,基本都是复制粘贴的微软源码,有的把注释翻译了一下!
是不是不好用啊,格式错乱有木有?
其实,微软给出的解决方案,确实能解决这个后台打印的问题.
而且,我也是用的这个方案.不过,我稍微做了点修改!
咱们先看这部分代码:
private void Export(LocalReport report)
{
string deviceInfo =
@"<DeviceInfo>
<OutputFormat>EMF</OutputFormat>
<PageWidth>8.5in</PageWidth>
<PageHeight>11in</PageHeight>
<MarginTop>0.25in</MarginTop>
<MarginLeft>0.25in</MarginLeft>
<MarginRight>0.25in</MarginRight>
<MarginBottom>0.25in</MarginBottom>
</DeviceInfo>";
Warning[] warnings;
m_streams = new List<Stream>();
report.Render("Image", deviceInfo, CreateStream,
out warnings);
foreach (Stream stream in m_streams)
stream.Position = 0;
}
这个强行做了报表的页宽,页高和上下左右的边距设置.
然后,将报表的内容按照deviceInfo指定的格式输出到CreateStream函数提供的Stream中。
这里就有了个小问题,我们自己实现的报表,上面的这些边距设置,都是早就固定好的.
所以,只要和上面代码不同边距设置的,打印后,格式就全乱套了.
当然,也可以手动编码为和报表一样的设置,但是你要是有几十张报表,累死你,而且,也不便于维护.
直接给大家,看我修改后的代码:
这下通用了!其中,"EMF"可以理解为,文件后缀名.
这样就解决了报表的设置问题.
但是,微软给出的解决方案里,还有个问题,可能有朋友实验的时候,碰到了!
就是,这个方案,只能打印纵向单据,打不了横向的(或者说,报表的第一页,打不了横向的).
大家看这里:
private void Print()
{
if (m_streams == null || m_streams.Count == 0)
throw new Exception("Error: no stream to print.");
PrintDocument printDoc = new PrintDocument();
if (!printDoc.PrinterSettings.IsValid)
{
throw new Exception("Error: cannot find the default printer.");
}
else
{
printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
m_currentPageIndex = 0;
printDoc.Print();
}
}
这里的"printDoc"没有相关的打印设置(或者说,没有提高可操作的参数).
下面是我修改后的.
private void PrintSetting()
{
if (m_streams == null || m_streams.Count == 0)
throw new Exception("错误:没有检测到打印数据流");
//声明PrintDocument对象用于数据的打印
PrintDocument printDoc = new PrintDocument();
//判断指定的打印机是否可用
if (!printDoc.PrinterSettings.IsValid)
{
throw new Exception("错误:找不到打印机");
}
else
{
//设置打印机方向遵从报表方向
printDoc.DefaultPageSettings.Landscape = isLandSapces;
//声明PrintDocument对象的PrintPage事件,具体的打印操作需要在这个事件中处理。
printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
m_currentPageIndex = 0;
//执行打印操作,Print方法将触发PrintPage事件。
printDoc.Print();
}
}
//设置打印机方向遵从报表方向printDoc.DefaultPageSettings.Landscape = isLandSapces;
其中"isLandSapces"是我这个类中,定义的一个全局bool值.
问题就来了,这个isLandSapces是根据什么取得值?
/// <summary>
/// 为Report.rdlc创建本地报告加载数据,输出报告到.emf文件,并打印,同时释放资源www.it165.net
/// </summary>
/// <param name="rv">参数:ReportViewer.LocalReport</param>
public void PrintStream(LocalReport lr)
{
//获取LocalReport中的报表页面方向
isLandSapces = lr.GetDefaultPageSettings().IsLandscape;
Export(lr);
PrintSetting();
Dispose();
}
这是我在类中添加的一个方法!
对,这个isLandSapces,就是取自ReportViewer.LocalReport,这个bool值,你用来加载报表的ReportViewer控件是肯定知道的,把它传递进来,就OK了!
下面给出完整的后台打印代码:
View Code
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.IO;
using System.Text;
using Microsoft.Reporting.WinForms;
namespace HYC.RDLC
{
public class ReportViewer_Stream_Print : IDisposable
{
/// <summary>
/// 用来记录当前打印到第几页了
/// </summary>
private int m_currentPageIndex;
/// <summary>
/// 声明一个Stream对象的列表用来保存报表的输出数据,LocalReport对象的Render方法会将报表按页输出为多个Stream对象。
/// </summary>
private IList<Stream> m_streams;
private bool isLandSapces = false;
/// <summary>
/// 用来提供Stream对象的函数,用于LocalReport对象的Render方法的第三个参数。
/// </summary>
/// <param name="name"></param>
/// <param name="fileNameExtension"></param>
/// <param name="encoding"></param>
/// <param name="mimeType"></param>
/// <param name="willSeek"></param>
/// <returns></returns>
private Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek)
{
//如果需要将报表输出的数据保存为文件,请使用FileStream对象。
Stream stream = new MemoryStream();
m_streams.Add(stream);
return stream;
}
/// <summary>
/// 为Report.rdlc创建本地报告加载数据,输出报告到.emf文件,并打印,同时释放资源
/// </summary>
/// <param name="rv">参数:ReportViewer.LocalReport</param>
public void PrintStream(LocalReport rvDoc)
{
//获取LocalReport中的报表页面方向
isLandSapces = rvDoc.GetDefaultPageSettings().IsLandscape;
Export(rvDoc);
PrintSetting();
Dispose();
}
private void Export(LocalReport report)
{
string deviceInfo =
@"<DeviceInfo>
<OutputFormat>EMF</OutputFormat>
</DeviceInfo>";
Warning[] warnings;
m_streams = new List<Stream>();
//将报表的内容按照deviceInfo指定的格式输出到CreateStream函数提供的Stream中。
report.Render("Image", deviceInfo, CreateStream, out warnings);
foreach (Stream stream in m_streams)
stream.Position = 0;
}
private void PrintSetting()
{
if (m_streams == null || m_streams.Count == 0)
throw new Exception("错误:没有检测到打印数据流");
//声明PrintDocument对象用于数据的打印
PrintDocument printDoc = new PrintDocument();
//判断指定的打印机是否可用
if (!printDoc.PrinterSettings.IsValid)
{
throw new Exception("错误:找不到打印机");
}
else
{
//设置打印机方向遵从报表方向
printDoc.DefaultPageSettings.Landscape = isLandSapces;
//声明PrintDocument对象的PrintPage事件,具体的打印操作需要在这个事件中处理。
printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
m_currentPageIndex = 0;
//执行打印操作,Print方法将触发PrintPage事件。
printDoc.Print();
}
}
/// <summary>
/// 处理程序PrintPageEvents
/// </summary>
/// <param name="sender"></param>
/// <param name="ev"></param>
private void PrintPage(object sender, PrintPageEventArgs ev)
{
//Metafile对象用来保存EMF或WMF格式的图形,
//我们在前面将报表的内容输出为EMF图形格式的数据流。
Metafile pageImage = new Metafile(m_streams[m_currentPageIndex]);
//调整打印机区域的边距www.it165.net
Rectangle adjustedRect = new Rectangle(
ev.PageBounds.Left - (int)ev.PageSettings.HardMarginX,
ev.PageBounds.Top - (int)ev.PageSettings.HardMarginY,
ev.PageBounds.Width,
ev.PageBounds.Height);
//绘制一个白色背景的报告
//ev.Graphics.FillRectangle(Brushes.White, adjustedRect);
//获取报告内容
//这里的Graphics对象实际指向了打印机
ev.Graphics.DrawImage(pageImage, adjustedRect);
//ev.Graphics.DrawImage(pageImage, ev.PageBounds);
// 准备下一个页,已确定操作尚未结束
m_currentPageIndex++;
//设置是否需要继续打印
ev.HasMorePages = (m_currentPageIndex < m_streams.Count);
}
public void Dispose()
{
if (m_streams != null)
{
foreach (Stream stream in m_streams)
stream.Close();
m_streams = null;
}
}
}
}
调用方法:
ReportViewer_Stream_Print rvsp = new ReportViewer_Stream_Print();
rvDoc.LocalReport.ReportEmbeddedResource = string.Format("项目名称.Report.{0}.rdlc", reportname);
rvsp.PrintStream(rvDoc.LocalReport);
后台打印完成!
自定义批量打印:
其实,这个就是用上面的调用方法,
然后,遍历一组报表名称,传递给其中的"reportname"参数!
这个组可以是一维数组,也可以是二维数组,[0]里记录报表名称,[1]里记录打印份数!
当然,也不一定非是数组,方法多多!