最近一个项目使用ReportViewer来呈现本地RDLC模块的报表,需要用户点击至少三次才能直正打印,用户感觉易用性很不好,需要我们修改。
经过网上查找相关资料,发现直接使用ACTIVEX控件RSClientPrint直接打印使用SQLSERVER报表服务的资料很多,也说的比较详细,可唯独没打印本地报表的相关内容,看来只能自已摸索了。
经过研究有关打印SQLSERVER报表服务的资料,特别是这篇文章:http://www.codeproject.com/KB/reporting-services/RsClientPrint.aspx,决下先写一个简单的HTML文 件测试一下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
< html xmlns ="http://www.w3.org/1999/xhtml" >
< head >
< title ></ title >
</ head >
< BODY onload ="Print()" >
< OBJECT ID ="RSClientPrint"
classid ="CLSID:5554DCB0-700B-498D-9B58-4E40E5814405" CODEBASE ="/RSClientPrint-x86.cab" VIEWASTEXT ></ OBJECT >
< script type ="text/javascript" language ="javascript" >
function Print(oid,name) {
if ( typeof RSClientPrint.Print == " undefined " ) {
alert( " Unable to load client print control. " );
return ;
}
RSClientPrint.MarginLeft = 0 ;
RSClientPrint.MarginTop = 0 ;
RSClientPrint.MarginRight = 0 ;
RSClientPrint.MarginBottom = 0 ;
RSClientPrint.PageHeight = 296.926 ;
RSClientPrint.PageWidth = 210.058 ;
RSClientPrint.Culture = 2052 ;
RSClientPrint.UICulture = 2052 ;
RSClientPrint.UseSingleRequest = true ;
RSClientPrint.UseEmfPlus = true ;
RSClientPrint.Print( " http://localhost:2940/JDCJY/ReportHandler.ashx " , " oid= " + escape(oid) + " &name= " + escape(name), " test " );
}
</ script >
</ BODY >
</ html >
然后实现ReportHandler.ashx
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
public void ProcessRequest(HttpContext context)
{
Warning[] warningArray;
HttpResponse response = context.Response;
response.StatusCode = 200 ;
MemoryStream lastMemoryStream = null ;
context.Response.BufferOutput = false ;
context.Response.ContentType = null ;
context.Response.Expires = - 1 ;
var ds = new DataSet();
LocalReport localReport = new LocalReport();
localReport.ReportEmbeddedResource = string .Format( " {0}.rdlc " , context.Request.QueryString[ " name " ]);
localReport.DataSources.Add( new Microsoft.Reporting.WebForms.ReportDataSource( " ds " , ds));
StringBuilder builder = new StringBuilder( " <DeviceInfo> " );
NameValueCollection requestParameters = context.Request.QueryString ;
for ( int i = 0 ; i < requestParameters.Count; i ++ )
{
if (requestParameters.Keys[i] != null )
{
if (requestParameters.Keys[i].StartsWith( " rc: " , StringComparison.OrdinalIgnoreCase))
{
builder.AppendFormat( " <{0}>{1}</{0}> " , XmlConvert.EncodeName(requestParameters.Keys[i].Substring( 3 )), HttpUtility.HtmlEncode(requestParameters[i]));
}
}
}
builder.Append( " </DeviceInfo> " );
localReport.Render( " IMAGE " , builder.ToString(), delegate ( string name, string extension, Encoding encoding, string mimeType, bool willSeek)
{
if ( ! HttpContext.Current.Response.IsClientConnected)
{
throw new HttpException( " Client disconnected " );
}
if (lastMemoryStream != null )
{
this .SendPrintStream(lastMemoryStream, response);
lastMemoryStream.Dispose();
lastMemoryStream = null ;
}
lastMemoryStream = new MemoryStream();
return lastMemoryStream;
}, out warningArray);
this .SendPrintStream(lastMemoryStream, response);
lastMemoryStream.Dispose();
this .SendPrintStream( null , response);
if ( ! response.BufferOutput)
{
string a = context.Request.ServerVariables[ " SERVER_PROTOCOL " ];
if ( string .Equals(a, " HTTP/1.0 " , StringComparison.OrdinalIgnoreCase))
{
context.Response.Close();
}
}
}
private void SendPrintStream(Stream stream, HttpResponse response)
{
int length = 0 ;
if (stream != null )
{
length = ( int )stream.Length;
}
foreach ( byte num2 in BitConverter.GetBytes(length))
{
response.OutputStream.WriteByte(num2);
}
if (stream != null )
{
stream.Position = 0L ;
StreamToResponse(stream, response);
response.Flush();
}
}
internal static void StreamToResponse(Stream data, HttpResponse response)
{
int count = 0 ;
byte [] buffer = new byte [ 0x14000 ];
while ((count = data.Read(buffer, 0 , 0x14000 )) > 0 )
{
response.OutputStream.Write(buffer, 0 , count);
}
}
public bool IsReusable
{
get
{
return true ;
}
}
}
再后经测试,直接加载上面的HTML页面就会提示直接打印。整个过程花了不少时间,有几个地方需要注意:
1.在服务器端生成DEVICEINFO时一定要使用上面所示的方法,我在测试过程中为了图简单,直接使用我原来在WINFORM写的一个代码,固定的DEVICEINFO信息,结果打印出来时始终会重复,不知道是怎么回事,经过使用Reflector反编译Microsoft.ReportViewer.WebForms,仔细这里面实现的报表相关服务,发现这点实现的区别,才最终搞定问题
2.服务器代码生成EMF流如果有问题,客户端会报一个错误代码,这时你很可能会以为是客户端的错误,我开始就是,因为报错误的速度太快了。我上网对对应问题代码的资料也没有明确结果,最后看还是直接跟踪代码试试,发现是服务器端代码有些问题。