利用ML实现通用WEB报表打印_第1页
利用ML实现通用WEB报表打印_第2页
利用ML实现通用WEB报表打印_第3页
利用ML实现通用WEB报表打印_第4页
利用ML实现通用WEB报表打印_第5页
已阅读5页,还剩53页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、利用XML实现通用WEB报表打印(实现篇) 摘要: 本文结合代码详细讲解了利用XML实现通用WEB报表打印(以下简称利)一文中所介绍的报表打印中的.Net Web控件方案的实现及扩充过程。本文为利文的续作,文中所举的代码示例均为C#语言编写。 目录: 引言 软件原理 结构设计 代码实现 方案扩充 总结 引言: 在利刊出后,有大量的读者发E-Mail给我表示对该方案特不感兴趣,同时还询问具体如何实现报表格式的解析和打印细节并索取该程序的源代码。读者的热情让我始料未及,尽管我一一对来信进行解答和发送了源代码,然而依旧深感抱歉和遗憾,因为时刻和精力的关系,我不可能对每封信都作出专门详细的答复,而且我

2、写的源代码也专门乱,事实上,是我花了两个小时赶写出来的(原来的程序因为硬盘故障被销毁了),不但没有什么注解而且还不完善,包括一些标签还没有被实现。 为了弥补往常的缺憾,我花了一些时刻改进了程序的结构,重写了全部的源代码,实现了所有标签的功能,下面就要开始讲解该程序的设计和编码过程,在看此文之前,强烈推举您先阅读利一文来了解一下相关的概念,假如在该文中差不多有清晰讲解的部分,本文将不再详细介绍,那个地点只将要紧讲解利文没有提及或是介绍得不清晰和读者来信提问最多的部分。 软件原理: 该软件的原理事实上专门简单,确实是要方便的解析出定义好的XML格式标记,解读出文件中标记的参数定义,最后将这些信息还

3、原成打印机输出的图形格式。 为了能表达出复杂的报表样式,我们需要定义一些标记,在这些标记中附加上具体的样式信息,作用类似HTML的标签,而我们的解析程序就相当于IE扫瞄器,所不同的是IE将图形输出到屏幕,而我们是将图形输出到打印机,由于打印机相关于显示屏的专门性(例如分页),因此我们不能直接采纳网页扫瞄器的标签解析功能来打印,需要自己来做一个满足需要的打印扫瞄器。 针对大多数报表的功能需要,我只定义了两种格式标签:文本(text)和表格(table),它们的具体属性定义和另外一些设置性的标签定义请参考利文,那个地点再补充一幅结构图关心读者理解。如下所示: 结构设计: 为了描述所有的样式标记,我

4、先定义了一个抽象基类PrintElement,它拥有一个虚拟方法Draw,然后对应表格和文本,从PrintElement派生出两个子类,分不是Table和Text,我还创建了一个Parser类用来解析不同的样式标记和创建对应的对象,它拥有一个静态的方法CreateElement,用来依照不同的格式标签创建出对应的对象。结构图如下所示: 读过设计模式的读者一定差不多看出来了,这种设计应用了设计模式中的一个特不闻名的模式:Abstract Factory。那个地点使用该模式的好处确实是让标签对象和解析器都独立出来,降低了系统的耦合度,有利于今后在需要的时候能够专门容易的增加其它的格式标签(下文将会

5、举一个实例)和方便的更换不同的用户界面(图中Client表示Windows应用程序或者是网页插件)。 代码实现: 首先,创建一个Windows控件库的新项目,在项目名称处写入RemotePrint,如下图所示: 然后把新建项目中的那个默认的UserControl1类,它的构造函数名和文件名都改成PrintControl。再将它的背景颜色设置为白色,添加三个按纽,并将它们的Enable属性都设置为false,Anchor属性设置为Bottom, Right,再添加一个Label控件用来显示程序状态,它的Anchor属性设置为Left。如下图所示: 再从控件栏中拖入三个打印对象:PrintDocu

6、ment, PageSetupDialog, PrintPreviewDialog,如下图所示: 将其中的pageSetupDialog1和printPreviewDialog1的Document属性均设置为printDocument1。 然后为项目添加一个PrintElement的新类,代码如下: using System; using System.Xml; using System.Drawing; namespace RemotePrint public class PrintElement public PrintElement() public virtual bool Draw(

7、Graphics g) return false; 该类中只有一个虚拟方法Draw,注意它规定需要返回一个bool值,那个值的作用是用来指示标签是否在页内打印完毕。 然后再添一个Table的新类,代码如下: using System; using System.Xml; using System.Drawing; namespace RemotePrint public class Table : PrintElement private XmlNode table; public static int count = 0, pc = 1; public Table(XmlNode Table

8、) table = Table; public override bool Draw(Graphics g) /表格坐标 int tableX = int.Parse(table.Attributesx.InnerText); int tableY = int.Parse(table.Attributesy.InnerText); int x = tableX, y = tableY; DrawTopLine(g, table);/画表格顶线 Pen pen = new Pen(Color.FromName(table.Attributesbordercolor.InnerText), flo

9、at.Parse(table.Attributesborder.InnerText); int trheight = 0; /表头 foreach(XmlNode tr in tabletablehead.ChildNodes) trheight = int.Parse(tr.Attributesheight.InnerText); DrawTR(x, y, tr, pen, g); y += trheight; /表项 for(int i = 0; i int.Parse(table.Attributesmaxlines.InnerText); i+) XmlNode tr = tablet

10、ablebody.ChildNodescount; trheight = int.Parse(tr.Attributesheight.InnerText); DrawTR(x, y, tr, pen, g); y += trheight; count+; if(count = tabletablebody.ChildNodes.Count) break; x = tableX; /表底 foreach(XmlNode tr in tabletablefoot.ChildNodes) trheight = int.Parse(tr.Attributesheight.InnerText); Dra

11、wTR(x, y, tr, pen, g); y += trheight; int currentpage = pc; pc+; bool hasPage = false; if(count tabletablebody.ChildNodes.Count - 1) hasPage = true;/需要接着打印 else count = 0; pc = 1; hasPage = false;/表格打印完毕 return hasPage; private void DrawTopLine(Graphics g, XmlNode table) Pen pen = new Pen(Color.From

12、Name(table.Attributesbordercolor.InnerText), float.Parse(table.Attributesborder.InnerText); int width = 0; foreach(XmlNode td in table.FirstChild.FirstChild) width += int.Parse(td.Attributeswidth.InnerText); int x = int.Parse(table.Attributesx.InnerText); int y = int.Parse(table.Attributesy.InnerTex

13、t); g.DrawLine(pen, x, y, x + width, y); /画表格行 private void DrawTR(int x, int y, XmlNode tr, Pen pen, Graphics g) int height = int.Parse(tr.Attributesheight.InnerText); int width; g.DrawLine(pen, x, y, x, y + height);/画左端线条 foreach(XmlNode td in tr) width = int.Parse(td.Attributeswidth.InnerText); D

14、rawTD(x, y, width, height, td, g); g.DrawLine(pen, x + width, y, x + width, y + height);/右线 g.DrawLine(pen, x, y + height, x + width, y + height);/底线 x += width; /画单元格 private void DrawTD(int x, int y, int width, int height, XmlNode td, Graphics g) Brush brush = new SolidBrush(Color.FromName(td.Attr

15、ibutesbgcolor.InnerText); g.FillRectangle(brush, x, y, width, height); FontStyle style = FontStyle.Regular; /设置字体样式 if(td.Attributesb.InnerText = true) style |= FontStyle.Bold; if(td.Attributesi.InnerText = true) style |= FontStyle.Italic; if(td.Attributesu.InnerText = true) style |= FontStyle.Under

16、line; Font font = new Font(td.Attributesfontname.InnerText, float.Parse(td.Attributesfontsize.InnerText), style); brush = new SolidBrush(Color.FromName(td.Attributesfontcolor.InnerText); StringFormat sf = new StringFormat(); /设置对齐方式 switch(td.Attributesalign.InnerText) case center: sf.Alignment = St

17、ringAlignment.Center; break; case right: sf.Alignment = StringAlignment.Near; break; default: sf.Alignment = StringAlignment.Far; break; sf.LineAlignment = StringAlignment.Center; RectangleF rect = new RectangleF( (float)x, (float)y, (float)width, (float)height); g.DrawString(td.InnerText, font, bru

18、sh, rect, sf); Table类将table标签内部的解析和打印独立出来,全部在类的内部完成,如此,我们在对顶层标签解析的时候只要是碰到table标签就直接交给Table类去完成,不需要再关怀事实上现细节。 再添加一个Text类,代码如下: using System; using System.Xml; using System.Drawing; namespace RemotePrint public class Text : PrintElement private XmlNode text = null; public Text(XmlNode Text) text = Tex

19、t; public override bool Draw(Graphics g) Font font = new Font(text.Attributesfontname.InnerText, int.Parse(text.Attributesfontsize.InnerText); Brush brush = new SolidBrush(Color.FromName(text.Attributes fontcolor.InnerText); g.DrawString(text.InnerText, font, brush, float.Parse (text.Attributesx.Inn

20、erText), float.Parse(text.Attributesy.InnerText); return false; 同Table类一样,Text类完成对text标签的解析和打印,只是因为text的简单性,它的代码也少了专门多。它们两者同样继承自PrintElement,都重载了Draw方法的实现。 最后,我们还需要一个解析器用来解析顶层的标签和生成相应的对象,它在此模式中的作用确实是一个工厂类,负责生产出用户需要的产品。代码如下: using System; using System.Xml; namespace RemotePrint public class Parser pu

21、blic Parser() public static PrintElement CreateElement(XmlNode element) PrintElement printElement = null; switch(element.Name) case text: printElement = new Text(element); break; case table: printElement = new Table(element); break; default: printElement = new PrintElement(); break; return printElem

22、ent; 好了,核心的解析和标签的具体打印方法差不多完成了,现在我们回到PrintControl中编写一些代码来测试我们的成果。 首先,需要引用两个要用到的名称空间: using System.Xml; using System.Drawing.Printing; 然后,在打印之前,需要依照XML文件中的pagesetting标签来设置一下打印机的页面,因此我们先写一个方法来设置打印机。在PrintControl类中增加一个私有的方法: private void SettingPrinter(XmlNode ps) /打印方向(纵/横) this.printDocument1.DefaultP

23、ageSettings.Landscape = bool.Parse(pslandscape.InnerText); /设置纸张类型 string papername = pspaperkind.InnerText; bool fitpaper = false; /猎取打印机支持的所有纸张类型 foreach(PaperSize size in this.printDocument1.PrinterSettings.PaperSizes) if(papername = size.PaperName)/看该打印机是否有我们需要的纸张类型 this.printDocument1.DefaultPa

24、geSettings.PaperSize = size; fitpaper = true; if(!fitpaper) /假如没有我们需要的标准类型,则使用自定义的尺寸 this.printDocument1.DefaultPageSettings.PaperSize = new PaperSize(Custom, int.Parse(pspaperwidth.InnerText), int.Parse(pspaperheight.InnerText); 接下来,我们类中添加一个XmlDocument的对象和一个静态变量计算页码: private XmlDocument doc = new X

25、mlDocument(); public static int Pages = 1; 然后再控件的Load事件中为该对象加载XML报表数据,代码如下: private void PrintControl_Load(object sender, System.EventArgs e) try /装载报表XML数据 this.label1.Text = 正在加载报表数据,请稍侯.; doc.Load(http:/localhost/report.xml); this.label1.Text = 报表数据加载完毕!; this.button1.Enabled = this.button2.Enabl

26、ed = this.button3.Enabled = true; catch(Exception ex) this.label1.Text = 出现错误: + ex.Message; 请注意,我们那个地点只是装入了一个本地的测试数据文件(该文件的编写请参考利文),事实上,完全能够改成装载网络上任何地点的静态或者动态的XML文件,例如以上的doc.Load(http:/localhost/report.xml)能够改写成: doc.Load(/report.xml); doc.Load(/report.asp); doc.Load(/report.jsp?date=xxx); 等等,只要装载的

27、数据是符合我们规定的XML数据文档就能够。 然后在控件的构造函数中加入打印事件的托付: public PrintControl() InitializeComponent(); this.printDocument1.PrintPage += new PrintPageEventHandler(this.pd_PrintPage); 该托付方法的代码如下: private void pd_PrintPage(object sender, PrintPageEventArgs ev) Graphics g = ev.Graphics; bool HasMorePages = false; Pri

28、ntElement printElement = null; foreach(XmlNode node in docrootreporttable.ChildNodes) printElement = Parser.CreateElement(node);/调用解析器生成相应的对象 try HasMorePages = printElement.Draw(g);/是否需要分页 catch(Exception ex) this.label1.Text = ex.Message; /在页底中间输出页码 Font font = new Font(黑体, 12.0f); Brush brush = n

29、ew SolidBrush(Color.Black); g.DrawString(第 + Pages.ToString() + 页, font,brush,ev.MarginBounds.Width / 2 + ev.MarginBounds.Left - 30, ev.PageBounds.Height - 60); if(HasMorePages) Pages+; ev.HasMorePages = HasMorePages; 三个按纽的Click事件代码分不如下: /页面设置 private void button1_Click(object sender, System.EventAr

30、gs e) this.pageSetupDialog1.ShowDialog(); this.printDocument1.DefaultPageSettings = this.pageSetupDialog1.PageSettings; /打印预览 private void button2_Click(object sender, System.EventArgs e) try this.printPreviewDialog1.ShowDialog(); catch(Exception ex) this.label1.Text = ex.Message; /打印 private void b

31、utton3_Click(object sender, System.EventArgs e) try this.printDocument1.Print(); catch(Exception ex) this.label1.Text = ex.Message; 好了,我们的打印控件到那个地点就全部做完了,选择生成一个Release的版本,然后到工程目录下将生成的PrintControl.dll文件拷贝到IIS的虚拟根目录下,然后新建一个remoteprint.htm的HTML格式文件,在合适的地点加上: ,为了更加形象和美观,还能够将需要打印的数据做成网页形式放在上面,假如需要猎取的XML是

32、动态数据源,则能够采纳asp等动态脚本来生成该网页表格,假如需要猎取的XML是一个静态的文本,则能够采纳XSLT直接将XML文件转换成网页表格。 打开扫瞄器,输入:http:/localhost/remoteprint.htm,假如您差不多跟我一样,事先做好了一个XML报表数据文件的话,您就能够看到下图所示的效果 请注意:该图示例中的所有数据均为笔者随意虚拟,网页中的表格数据和打印数据并非来自同一数据源,也没有刻意去对等,仅仅只是为了演示一下效果,因此网页显示报表跟打印预览中的报表有一些出入是正常的。在实际应用中能够让网页显示数据跟打印输出数据完全一致。 方案扩充: 有一部分读者在来信中问到如

33、何打印一些专门形态的图表,利文中差不多提到,采纳本方案能够特不方便的定义出自己所需要的标签,在理论上能够打印出任何样式的专门图表。因此本文打算详细介绍一下增加自己定义的标签扩充打印格式的具体过程。 先假设我们的客户看了打印效果后差不多上中意,然而还有觉得一点不足,假如需要打印一些图表如何办?例如折线图、K线图、饼状图、柱状图等等。使用我们现有的标签就不行了,因此我们首先要扩充我们的标签库,让它的表达能力更加强。在那个地点,我将只打算让我们的打印控件学会画简单的折线图,希望读者能举一反三,制造出其它各种各样的打印效果。 最差不多的折线图是由X坐标轴、Y坐标轴和一系列点连接成的线构成的,因此,我定

34、义了以下几种标签: 1. linechart:跟table,text标签一样,为样式根标签。 属性:无 2 coordinate:坐标。 属性:无 3 xcoordinate:X轴坐标线 属性: # x:起点X坐标值 # y:起点Y坐标值 # length:长度值 # stroke:粗细 # color:颜色 # arrow:是否有箭头 4 ycoordinate:Y轴坐标线 属性:同xcoordinate。 5scale:刻度线 标签内容:显示在刻度边的文字 属性: # length:距离起点长度值 # height:刻度线高度 # width:刻度线宽度 # color:颜色 # font

35、size:字体大小 6chart:图表根 属性:无 7lines:线段 属性值: # stroke:粗细 # color:颜色 8 point:点 属性值: # x:X坐标值 # y:Y坐标值 # radius:半径 # color:颜色 其结构图如下所示: 下面是一段用刚才定义的标签制作的XML折线图示例: 100 200 300 400 500 600 700 100 200 300 完成了标签的定义,下一步就要来修改我们的程序,让他能读明白这些标签。 首先,我们先给工程增加一个LineChart的新类,跟Table,Text类一样,它也是继承自PrintElement类,同样重载了Dra

36、w虚方法。代码如下: using System; using System.Xml; using System.Drawing; using System.Drawing.Drawing2D; namespace RemotePrint public class LineChart : PrintElement private XmlNode chart; public LineChart(XmlNode Chart) chart = Chart; public override bool Draw(Graphics g) DrawCoordinate(g, chartcoordinate);

37、/画坐标轴 DrawChart(g, chartchart); return false; private void DrawCoordinate(Graphics g, XmlNode coo) DrawXCoor(g, cooxcoordinate);/画X坐标 DrawYCoor(g, cooycoordinate);/画Y坐标 private void DrawXCoor(Graphics g, XmlNode xcoo) int x = int.Parse(xcoo.Attributesx.InnerText); int y = int.Parse(xcoo.Attributesy.

38、InnerText); int length = int.Parse(xcoo.Attributeslength.InnerText); bool arrow = bool.Parse(xcoo.Attributesarrow.InnerText); int stroke = int.Parse(xcoo.Attributesstroke.InnerText); Color color = Color.FromName(xcoo.Attributescolor.InnerText); Pen pen = new Pen(color, (float)stroke); if(arrow)/是否有箭

39、头 AdjustableArrowCap Arrow = new AdjustableArrowCap( (float)(stroke * 1.5 + 1.5), (float)(stroke * 1.5 + 2), true); pen.CustomEndCap = Arrow; g.DrawLine(pen, x, y, x + length, y);/画坐标 /画刻度 foreach(XmlNode scale in xcoo.ChildNodes) int len = int.Parse(scale.Attributeslength.InnerText); int height = i

40、nt.Parse(scale.Attributesheight.InnerText); int width = int.Parse(scale.Attributeswidth.InnerText); int fontsize = int.Parse(scale.Attributesfontsize.InnerText); Color clr = Color.FromName(scale.Attributescolor.InnerText); string name = scale.InnerText; Pen p = new Pen(clr, (float)width); g.DrawLine

41、(p, x + len, y, x + len, y - height); Font font = new Font(Arial, (float)fontsize); g.DrawString( name, font, new SolidBrush(clr), (float)(x + len - 10), (float)(y + 10); private void DrawYCoor(Graphics g, XmlNode ycoo) int x = int.Parse(ycoo.Attributesx.InnerText); int y = int.Parse(ycoo.Attributes

42、y.InnerText); int length = int.Parse(ycoo.Attributeslength.InnerText); bool arrow = bool.Parse(ycoo.Attributesarrow.InnerText); int stroke = int.Parse(ycoo.Attributesstroke.InnerText); Color color = Color.FromName(ycoo.Attributescolor.InnerText); Pen pen = new Pen(color, (float)stroke); if(arrow)/是否

43、有箭头 AdjustableArrowCap Arrow = new AdjustableArrowCap( (float)(stroke * 1.5 + 2), (float)(stroke * 1.5 + 3), true); pen.CustomEndCap = Arrow; g.DrawLine(pen, x, y, x, y + length);/画坐标 /画刻度 foreach(XmlNode scale in ycoo.ChildNodes) int len = int.Parse(scale.Attributeslength.InnerText); int height = i

44、nt.Parse(scale.Attributesheight.InnerText); int width = int.Parse(scale.Attributeswidth.InnerText); int fontsize = int.Parse(scale.Attributesfontsize.InnerText); Color clr = Color.FromName(scale.Attributescolor.InnerText); string name = scale.InnerText; Pen p = new Pen(clr, (float)width); g.DrawLine

45、(p, x, y + len, x + height, y + len); Font font = new Font(Arial, (float)fontsize); StringFormat sf = new StringFormat(); sf.Alignment = StringAlignment.Far; RectangleF rect = new RectangleF( (float)(x - 100), (float)(y + len - 25), 90f, 50f); sf.LineAlignment = StringAlignment.Center; g.DrawString(

46、name, font, new SolidBrush(clr), rect, sf); private void DrawChart(Graphics g, XmlNode chart) foreach(XmlNode lines in chart.ChildNodes) DrawLines(g, lines); private void DrawLines(Graphics g, XmlNode lines) int Stroke = int.Parse(lines.Attributesstroke.InnerText); Point points = new Pointlines.Chil

47、dNodes.Count; Color linecolor = Color.FromName(lines.Attributescolor.InnerText); for(int i = 0; i lines.ChildNodes.Count; i+) XmlNode node = lines.ChildNodesi; pointsi = new Point( int.Parse(node.Attributesx.InnerText), int.Parse(node.Attributesy.InnerText); int Radius = int.Parse(node.Attributesradius.InnerText); Co

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论