利用.NET进行预览和打印

本文详细介绍了在.NET Framework中进行打印和预览的技术,包括使用PrintDocument类、派生PrintDocument类以封装打印逻辑、利用GDI+进行页面绘制、使用PrintController进行打印控制以及PrintDialog和PrintPreviewDialog的运用。文章通过一个示例应用程序展示了如何在后台进行打印,并探讨了线程安全问题。最后,作者指出虽然.NET提供了报表工具,但在需要定制打印功能的场景下,使用本机打印方法更具优势。
摘要由CSDN通过智能技术生成

从开发角度来看,Microsoft.NET 已经改变了几乎所有的方方面面。其中的一些变化(例如,Web 窗体和 ADO.NET)已经要求在完成任务的方式上进行重大改变,而其他一些变化在本质上则更为缓和一些,仅仅是在现有技术(如 System.Xml)的基础上进行了一些改进。对于传统的使用 Visual Basic 和 Visual C++ 的开发人员而言,从 Windows窗体进行打印预示着一项重大改变。但是,对于大量使用 .NET Framework 的情况,该更改无疑会更好。

使用 Visual Basic Print 对象及其 Printers 集合的日子一去不返了。在 .NET Framework 中,没有任何整体式的 Print 对象,并且您再也不会设置 CurrentX 和 CurrentY 属性或者发出象 EndDoc 和 NewPage 这样的命令了。如果您从 Visual C++ 转而使用 .NET,那么您或许意识到打印可能是一项乏味的任务。例如,它要求您使用 Win32 API 仔细跟踪打印过程,以确保页被正确地打印。这并不是说您再也不必做诸如此类的事情了。只是说通过 .NET,您最终会得到更为整洁且更容易维护的打印逻辑。通过 .NET Framework 类可以完全封装打印代码。因为可以从一组基类派生代码,所以您将免费获得所有种类的附加功能。挂钩到 Print Preview 对话框,对于 .NET 来说是小菜一碟,这只不过是个示例而已。

本文中的大多数代码示例,均摘自可从本文页顶部链接处下载的示例打印应用程序。图 1 中显示的示例 Windows 窗体应用程序演示了在 .NET 中打印时可以使用的很多新特性和功能。它使您可以选择任何文本文档并将其发送到 Print Preview 对话框或特定的打印机。出于演示目的,我将为您提供一个选项,便于您选择是否应当在每个页上显示水印。


图 1 Windows 窗体应用程序


在使用 Print Preview 对话框进行打印时,可以启用或禁用消除锯齿功能 — 这是一项内置功能,它使在屏幕上呈现的文本和图形具有更为平滑的外观。但是,请记住,这是以输出速度的降低为代价的。此外,Print Preview 对话框会自动利用 Windows 提供的任何字体平滑显示 (ClearType),因而减少了使用消除锯齿功能的需要。在将输出发送到打印机时,示例打印应用程序也使您可以选择其他多种选项。然后,您可以决定是 显示状态对话框、在状态栏中显示动画打印机,或者是在后台线程中进行打印。

通过首先分析将输出发送到打印机的快速而杂乱的方式,让我们来体验一番 Windows 窗体打印。然后,我将更严密地考察通过 Windows 窗体进行打印的正确方式 — 使用派生的 PrintDocument 类。

使用 PrintDocument 类


从 Windows 窗体进行打印是一种以文档为中心的事件驱动过程。您的大部分精力将花在使用通用的 PrintDocument 对象或实现派生的 PrintDocument 类上。从 PrintDocument 基类继承是一种更好的方式 — 其原因我很快会予以说明。不过,有时候使用 PrintDocument 基类的实例可能会更快、更简单。

用 PrintDocument 基类进行打印需要将该类的 PrintPage 事件关联到其签名与 PrintPageEventHandler 委托匹配的某个处理程序方法(静态或实例)。当代码调用 PrintDocument 对象实例上的 Print 方法时,将激发该事件。要实际绘制页,可以使用 PrintPageEventArgs 对象的 Graphics 属性。PrintPageEventArgs 类的实例作为参数传递给 PrintPage 事件处理程序。PrintPageEventArgs 对象的 Graphics 属性公开了一个 GDI+ 对象,该对象封装您用来绘制页的绘图表面。(本文稍后将对某些基本 GDI+ 命令进行讨论。)要打印一张以上的页,需要通知基础打印控制器您还有更多要打印的页。可以使用 PrintPageEventArgs 对象的 HasMorePages 属性完成该任务。将 HasMorePages 属性设置为真可以确保再次调用 PrintPage 事件处理程序。

此外,可以为其他常见打印事件(例如,BeginPrint、EndPrint 和 QueryPageSettings)设置事件处理程序。BeginPrint 是初始化 PrintPage 例程可能依赖的任何对象(例如,Fonts)的不错的选择。QueryPageSettings 事件恰好在每个 PrintPage 事件之前激发。它使您可以使用不同的页设置来打印每一页(您可以通过修改 QueryPageSettingsEventArgs.PageSettings 属性得到不同的页设置)。为了修改整个文档的页设置,可以使用 PrintDocument 类的 DefaultPageSettings 属性。

下面的示例说明了如何使用 PrintDocument 基类启动打印作业:

PrintDocument printDoc = new PrintDocument();
printDoc.PrintPage += new PrintPageEventHandler(this.printDoc_PrintPage);
printDoc.Print();

// The PrintPage event is raised for each page to be printed.
private void printDoc_PrintPage(object sender, PrintPageEventArgs e)
{
// TODO: Print your page using the e.Graphics GDI+ object
// Notify the PrintController whether there are any more pages
e.HasMorePages = false;
}

正如您可以看到的那样,该方法存在许多缺点。最大的缺点在于:您必须在对 PrintPage 事件处理程序进行的连续调用之间维护具有状态意识的对象。例如,如果您要打印文本文档,则将需要维持一个打开的 StreamReader 对象。您可以在 BeginPrint 事件期间初始化 StreamReader,然后在 EndPrint 事件期间关闭它。但是,无论您如何分割 StreamReader 变量,都需要将它的作用域与其他变量一起限制在 PrintPage 事件处理程序的外部。当发生这种情况时,您的打印代码将被公开并且易受攻击,而且可能使其余代码变得混乱。

实现派生的 PrintDocument 类


当从 Windows 窗体进行打印时,更好的方法是实现一个从通用的 PrintDocument 类继承的类。这样,您就可以迅速地获得封装的回报。无需为 BeginPrint、EndPrint 和 PrintPage 事件实现事件处理程序,而是重写基础的 PrintDocument 基类的 OnBeginPrint、OnEndPrint 和 OnPrintPage 方法。现在,OnPrintPage 方法所使用的任何具有状态意识的对象都可以保持在私有类字段中。这完全消除了我刚刚提到的潜在的代码问题。此外,您现在可以随意地向派生的 PrintDocument 类中添加自定义属性、方法、事件和构造函数。

示例打印应用程序使用 TextPrintDocument 类型的派生 PrintDocument 类,如图2所 示。TextPrintDocument 类公开了一个采用一个文件名作为参数的重载构造函数。另外,还可以使用自定义 FileToPrint 属性设置和读取该文件名。当被设置为不存在的文件时,该属性会引发异常。该类还公开了一个名为 Watermark 的公用布尔型字段,该字段用于启用或禁用页背景图形。(页背景图形作为名为 Watermark.gif 的嵌入式资源存储在程序集中。)最后,派生的 TextPrintDocument 类公开了 Font 属性,以指定哪个字体是要在呈现页时使用的正确字体。

可以在 OnPrintPage 方法中找到 TextPrintDocument 类的内部机制。在这里,可以使用 PrintPageEventArgs Graphics 属性提供的 GDI+ 绘图表面绘制页。此外,PrintPageEventArgs 对象包含下列属性:Cancel、HasMorePages、MarginBounds、PageBounds 和 PageSettings。通过 Cancel 可以取消打印作业。MarginBounds 属性返回一个 Rectangle 对象,该对象代表位于边距内页的部分。可以使用该矩形来确定每页上开始和停止打印的位置。另一方面,PageBounds 代表页的整个区域(包括边距)。

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值