见资源 运用.NET Framework中的类来创建看上去很专业的报表。 by Michael Eaton 技术工" name="description" />
下载本文代码 下载本期杂志代码 javascript:openWindowRes('VS/2002_11/xml2html.asp?xmlfile=NetPrinting/Resources.xml&xslfile=../../include/xsl/Resources.xsl')">见资源 |
你可以用几种方法在.NET中编程来生成打印输出结果(如报表)。对Windows程序员来说,Visual Studio提供的Crystal Reports实际上是人们常用的打印报表的工具,但对不太复杂的报表来说,这个工具就有些大材小用了。当然,你可以用很好的Win32 API调用,但是尽管API可以让你有完全的控制权,它同时也把你锁定到一个单一的平台上了。如果Microsoft或另外的公司(如拥有Mono项目的Ximian公司)把Framework转移到另外的平台上,那么运用API的程序仍会锁定在Windows上,除非你为新的平台重新编写它们。
.NET Framework可以让你以一种新的方式来使用这些打印方法,System.Drawing.Printing名字空间的类将Win32 API的细粒度控制(fine-grain control)与相对简单的Visual Basic传统的Printer对象结合了起来(见表下)。
类 | 说明 |
PrintDocument | 可以让你定义一个对象,该对象把输出发送到一台打印机。 |
PrintEventArgs | 为BeginPrint和EndPrint事件提供所有的必需的信息。 |
PrintPageEventArgs | 为PrintPage事件提供所有必需的信息。 |
QueryPageSettingsEventArgs | 为QueryPageSettings事件提供信息。 |
PrintController | 控制一个PrintDocument是如何打印的。 |
StandardPrintController | 派生于PrintController。 |
PreviewPrintController | 派生于PrintController。 |
PreviewPageInfo | PreviewPrintController 经常运用这个类。它为一个单独的页面指定了预览信息。 |
PageSettings | 指定一个单独的打印页面的设置。 |
Margins | 可以让你指定一个打印页面的页边距。页面的顶部、左边、右边和底部边距属性是以英寸(×100)的形式返回的。 |
MarginsConverter | 可以让你把一个Margins对象转换成另外的类型,把另外的类型转换成一个Margins对象。 |
PaperSize | 指定一页纸的大小。当同一个PageSettings对象一起使用时,它指的是一个页面的纸张大小。当用于PrinterSettings时,它可以让你得到打印机上可以用的纸张大小。 |
PaperSizeCollection | PaperSize对象的一个集合。 |
PaperSource | 像PaperSize类一样, PaperSource 是由一个PageSettings对象和一个PrinterSettings对象使用的。当用于PageSettings时,它可以让你为一个特定页面得到纸张来源。当用于PrinterSettings时,它可以让你得到打印机运用的所有纸张来源。 |
PaperSourceCollection | PaperSource 对象的一个集合。 |
PrinterSettings | 指定一个文件是如何打印的,包括文件在哪个打印机上打印。它是与特定的PrintDocument相应的。 |
PrinterResolution | 可以让你得到横向的和纵向的DPI,以及实际的打印机分辨率(draft, high, low, 等等)。 |
PrinterResolutionCollection | PrinterResolution 对象的一个集合。 |
InvalidPrinterException | 表示将抛出的异常(如果你用无效的设置来访问一台打印机)。 |
PrintingPermission | 控制对打印机的使用权限。 |
PrintingPermissionAttribute | 允许检测打印权限。 |
表 1 研究 .NET的众多的打印类.
运用.NET Framework的System.Drawing.Printing名字空间,你就可以程序化地处理许多打印任务,该名字空间包含20多个类、7个枚举类型和3个委托类型(delegate)。让我们来看看对每个类的简要说明吧!
在此,我将重点讲述该名字空间的主要的类,它们可以让你创建复杂程度适度的报表。PrintDocument类是这些类中最重要的。它可以让你定义一个对象,该对象发送输出结果到一个目的地,可以是一台打印机,或者是显示一个打印预览(运用PrintPreviewDialog类)。我还会讲述PrinterSettings类,它可以让你控制文件是如何打印的。
你可以很容易地在C#中创建一个PrintDocument对象,并把它附到一个事件处理程序上(实际处理打印的代码):
PrintDocument doc = new PrintDocument(); doc.PrintPage += new PrintPageEventHandler( doc_PrintPage); |
PrintPage事件处理程序有一个PrintPageEventArgs类型的参数。这个参数包含与该事件相关的数据,包括当前页面的页面设置、页边距信息以及是否有更多的页需要打印。每个页面触发该事件一次,直到ev.HasMorePages等于false:
private void doc_PrintPage(object sender, PrintPageEventArgs ev) |
将Graphics用于Fonts和Fills
ev参数引用了你用来输出数据的Graphics制图区。Graphics有用来打印图象、文本、形状和线条的方法。你可以用DrawString方法来定义你要用的font(字体)和brush(画刷)。一个brush定义了一个图形的内部是如何填充的。你也需要告诉DrawString方法你想在哪里打印文本。PrintPageEventArgs类有一个读写属性——HasMorePages——它指明是否需要打印更多的页面:
Font f = new Font("Arial", 12); ev.Graphics.Drawstring("Hello, World.", f, Brushes.Black, 100, 100); ev.HasMorePages = false; |
现在,你已经创建了一个PrintDocument对象并把它和一个事件处理程序联系起来了。你已经给事件处理程序中添加了代码,来打印一个单独的字符串,它的位置是距页面左边一英寸、距页面顶部一英寸,用的是12磅、 Arial的字体。现在我们来打印文件:
doc.Print(); |
在缺省设置下,PrintDocument类的Print方法在缺省打印机上打印结果,除非你另外指定。你可以用PrintDialog类来另外指定,它是System.Windows.Forms.CommonDialog名字空间的一部分。PrintDialog可以让你选择打印机,选择打印文件的哪部分,选择打印份数,来随意地打印一个文件。
PrintDialog类的PrinterSettings属性可以让你将诸如copies、from page和to page的属性放置到一个特定文件的PrinterSettings对象中。换句话说,单独的PrintDocument对象有一个PrinterSettings对象,它指定打印的份数、打印的范围、要运用的打印机的名字以及关于打印机本身的信息。
IPrinterSettings类中的一个bug会导致Copies属性总是返回一个为1的值,而不管你在PrintDialog中输入了多少份数。然而,PrintDocument.Print()方法仍然打印正确的份数,所以只有当你想将这个值用于其它地方时,才显示这个bug。当你执行Print方法时,系统运用这些设置。你可以提供一个实际的PrinterSettings对象或提供一个Document对象:
PrintDialog pd = new PrintDialog(); pd.Document = doc; pd.ShowDialog(); |
然后,运用CommonDialog名字空间的PageSetupDialog,你可以指定纸张大小、纸张来源、打印方向和页边距。PageSetupDialog和PrintDialog都需要你提供一个Document对象或一个PageSettings对象。PageSettings对象适用于一个单独的打印页面,可以处理页边距、纸张大小以及是否用颜色打印页面:
PageSetupDialog ps = new PageSetupDialog(); ps.Document = doc; ps.ShowDialog(); |
打印一个文件
当然,许多应用程序打印存储在一个文件或某种数据库中的信息,所以现在我将讲述一个更复杂的例子:打印一个以逗号分隔的联系清单文本文件。你可以将一个数据集、甚至一个XML文件中数据所运用的方法用在这里。本例的数据来自Northwind数据库的Customers表,格式如下:
Name, Title, Phone, Fax |
通过在类的级别声明一个StreamReader对象,然后将一个文件分配给它,你就可以从一个文本文件打印该例子了。打开文件,修改PrintPage事件处理程序来打印文件的每一行,按需要格式化数据。
PrintDocument类有好几个事件。你已经用了PrintPage事件了;现在我们来添加更多的事件。当开始一个打印任务时(在实际打印任何东西前),触发BeginPrint事件。相应的事件是EndPrint,该事件是在所有页面结束打印时触发的。创建PrintPage事件需要的任何对象,或在BeginPrint中打开任何数据源,然后在EndPrint中关闭或释放(deallocate)它们。不要忘记将这些事件绑定到PrintDocument对象(见下)。
C#:创建和部署
列表 1.
PrintDocument对象提供了几个事件。BeginPrint是在实际打印页面前触发的,可以给你提供机会来配置对象或打开文件。EndPrint在打印完最后一页时触发。不要忘记将事件绑定到你的PrintDocument对象。
|
|
C#:继承 PrintDocument
列表 2.
从PrintDocument派生一个新类,这样你就可以把所有打印功能封装在一个单独的地方,从而提高代码的重用性。
|
PrintDocument类没有制图工具,所以创建报表需要花些时间——但这是值得的。创建一个新的报表的最好的方法是用一张空白的纸。在了解了报表的目的后,你就可以在纸上通过画方框来代表不同的区域进行设计了,如页眉、正文和页脚(见下)。
图 1. 显示你的报表
在定义了你的Rectangle对象后,你只需要用DrawRectangle方法写一行代码,用你选择的画笔在屏幕上画一个长方形边框就行了。
然后花些时间详细编写每个区域的数据将来自哪里,以及各个数据将放置在什么位置。可以说这个时候是你确保最终用户所看到的页面与你设计的页面是否是一致的最佳时刻。用户对报表比对应用程序的任何部分的挑剔都要多,所以你应该尽可能多花些时间来设计它。一旦你花了很多时间来设计报表并对它的打印结果了如指掌后,那么写代码就不会花很多时间了。