多页文档

本文介绍 Windows 打印协议并说明如何打印包含多个页面的文档。 本文包含以下主题:

打印协议

为了打印多页文档,框架和视图将按以下方式交互。 首先,框架显示“打印”对话框,为打印机创建设备上下文,并调用 CDC 对象的 StartDoc 成员函数。 然后,对于文档的每一页,框架将调用 CDC 对象的 StartPage 成员函数,指示视图对象打印该页,并调用 EndPage 成员函数。 如果在开始打印特定页之前必须更改打印机模式,视图将调用 ResetDC,这将更新包含新打印机模式信息的 DEVMODE 结构。 打印完整个文档后,框架将调用 EndDoc 成员函数。

重写视图类函数

CView 类定义框架在打印期间调用的若干成员函数。 通过在视图类中重写这些函数,便能在框架的打印逻辑和您的视图类的打印逻辑之间建立连接。 下表列出了这些成员函数。

CView 的可重写的打印函数

名称 重写的原因
OnPreparePrinting 在“打印”对话框中插入值,尤其是文档的长度
OnBeginPrinting 分配字体或其他 GDI 资源
OnPrepareDC 为给定页调整设备上下文的特性,或执行打印时分页
OnPrint 打印给定页
OnEndPrinting 释放 GDI 资源

您也可以在其他函数中执行与打印有关的处理,但这些函数是驱动打印过程的函数。

下图演示了打印过程涉及的步骤,并显示了 CView 的每个打印成员函数的调用位置。 本文的其余部分将更详细地说明大部分这些步骤。 打印过程的其他部分在分配 GDI 资源一文中有述。

Printing loop process.
打印循环

分页

框架将很多有关打印作业的信息存储在 CPrintInfo 结构中。 CPrintInfo 中的某些值与分页有关;这些值是可访问的,如下表所示。

存储在 CPrintInfo 中的页码信息

成员变量或

函数名
引用的页码
GetMinPage/SetMinPage 文档的第一页
GetMaxPage/SetMaxPage 文档的最后一页
GetFromPage 要打印的第一页
GetToPage 要打印的最后一页
m_nCurPage 当前打印的页

页码从 1 开始,即第一页的页码为 1,而不是 0。 有关 CPrintInfo 的这些成员和其他成员的详细信息,请参阅“MFC 参考”。

在打印过程开始时,框架将调用视图的 OnPreparePrinting 成员函数,并传递指向 CPrintInfo 结构的指针。 应用程序向导提供调用 DoPreparePrintingCView 的另一个成员函数)的 OnPreparePrinting 的实现。 DoPreparePrinting 是显示“打印”对话框并创建打印机设备上下文的函数。

此时,应用程序无法知道文档中有多少页。 它对文档的第一页和最后一页的页码使用默认值 1 和 0xFFFF。 如果你知道文档有多少页,请在将它发送给 DoPreparePrinting 之前重写 OnPreparePrinting,并为 CPrintInfo 结构调用 [SetMaxPage]--brokenlink--(reference/cprintinfo-class.md#setmaxpage)。 这样,您便可以指定文档的长度。

DoPreparePrinting 随后显示“打印”对话框。 当它返回时,CPrintInfo 结构包含用户指定的值。 如果用户希望仅打印选定范围的页,他/她可以在“打印”对话框中指定开始和结束页码。 框架将使用 CPrintInfoGetFromPageGetToPage 函数检索这些值。 如果用户未指定页范围,框架将调用 GetMinPageGetMaxPage 并使用返回的值来打印整个文档。

对于文档要打印的每一页,框架将在视图类中调用两个成员函数(OnPrepareDCOnPrint),并向每个函数传递两个参数:指向 CDC 对象的指针和指向 CPrintInfo 结构的指针。 每当框架调用 OnPrepareDCOnPrint 时,都会在 CPrintInfo 结构的 m_nCurPage 成员中传递不同的值。 这样,框架就能告知视图应该打印的页。

OnPrepareDC 成员函数还用于屏幕显示。 它在绘制发生前对设备上下文进行调整。 OnPrepareDC 在打印中充当类似的角色,但有两个区别:首先,CDC 对象表示的是打印机设备上下文而不是屏幕设备上下文;其次,CPrintInfo 对象作为第二个参数传递。 (当为屏幕显示调用 OnPrepareDC 时,此参数为 NULL。)重写 OnPrepareDC 会根据要打印的页面对设备上下文进行调整。 例如,您可以移动视区原点和剪辑区域以确保打印文档的相应部分。

OnPrint 成员函数执行页的实际打印。 如何执行默认打印一文演示了框架如何使用打印机设备上下文调用 OnDraw 来执行打印。 更准确地说,框架使用 OnPrint 结构和设备上下文调用 CPrintInfo,而 OnPrint 将设备上下文传递到 OnDraw。 重写 OnPrint 以执行只应在打印期间进行并且不是用于屏幕显示的任何渲染。 例如,打印页眉或页脚(有关详细信息,请参阅页眉和页脚一文)。 然后,从 OnDraw 的重写调用 OnPrint 以执行屏幕显示和打印共用的渲染。

OnDraw 同时为屏幕显示和打印执行渲染意味着你的应用程序是 WYSIWYG:“所见即所得”。但是,此处假定你没有编写 WYSIWYG/所见即所得应用程序。 例如,想象一下这样一个文本编辑器:使用粗体字体进行打印但显示控件代码以在屏幕上指示粗体文本。 在这种情况下,您应严格使用 OnDraw 进行屏幕显示。 当重写 OnPrint 时,请用对 OnDraw 的调用替换对单独的绘图函数的调用。 该函数将使用您不在屏幕上显示的特性,按照文档在纸上显示的方式绘制文档。

打印机页与文档页

提到页码,有时候必须区分打印机的页概念与文档的页概念。 从打印机的角度来看,一页就是一张纸。 但是,一张纸不一定等同于一页文档。 例如,如果打印的是要对折纸张的新闻稿,那么一张纸可能会并排放置文档的第一页和最后一页。 同样,如果打印的是电子表格,那么文档完全不包含页。 相反,一张纸可能包含 1 至 20 行,6 至 10 列。

CPrintInfo 结构中的所有页码都是指打印机页。 框架将为要通过打印机的每张纸调用一次 OnPrepareDCOnPrint。 当重写 OnPreparePrinting 函数以指定文档的长度时,必须使用打印机页。 如果存在一对一的对应关系(即一个打印机页等同于一个文档页),则此操作很容易。 另一方面,如果文档页和打印机页不直接对应,则必须对其进行相应的转换。 例如,考虑一下打印电子表格。 当重写 OnPreparePrinting 时,必须计算打印整个电子表需要多少张纸,然后在调用 SetMaxPageCPrintInfo 成员函数时使用该值。 同样,当重写 OnPrepareDC 时,必须将 m_nCurPage 转换为显示在该特定纸张中的行和列的范围,然后相应地调整视区原点。

打印时分页

在某些情况下,视图类不能预先知道文档在实际打印前要等待多长时间。 例如,假定应用程序不是所见即所得的,那么文档在屏幕上的长度与其打印时的长度就不对应。

这将导致为视图类重写 OnPreparePrinting 时出现问题:你无法将值传递到 CPrintInfo 结构的 SetMaxPage 函数,因为你不知道文档的长度。 如果用户未“打印”对话框指定停止处的页码,框架就不知道何时停止打印循环。 确定何时停止打印循环的唯一方法是输出文档并查看它何时结束。 视图类必须在文档打印期间检查其末尾,然后告知框架何时到达末尾。

框架依赖视图类的 OnPrepareDC 函数以告知视图类何时停止。 每次调用 OnPrepareDC 后,框架都将检查称为 m_bContinuePrintingCPrintInfo 结构的成员。 默认值为 TRUE。只要保持此值,框架就会继续打印循环。 如果此值设置为 FALSE,框架就会停止打印循环。 执行打印时分页,重写 OnPrepareDC 以检查是否已到达文档的末尾,然后在到达末尾时将 m_bContinuePrinting 设置为 FALSE

如果当前页大于 1,OnPrepareDC 的默认实现会将 m_bContinuePrinting 设置为 FALSE。 这意味着,如果未指定文档的长度,框架将假定文档的长度是一页。 这样的一个后果是,您在调用基类版本的 OnPrepareDC 时必须小心。 不要假设在调用基类版本后 m_bContinuePrinting 将为 TRUE

你想进一步了解什么

另请参阅

打印
CView 类
CDC 类