TN030:自定义打印和打印预览

注意

以下技术说明在首次包括在联机文档中后未更新。 因此,某些过程和主题可能已过时或不正确。 要获得最新信息,建议你在联机文档索引中搜索热点话题。

本说明介绍了自定义打印和打印预览的过程,并描述了 CView 中使用的回调例程以及 CPreviewView 的回调例程和成员函数的用途。

问题

MFC 为大多数打印和打印预览需求提供了完整的解决方案。 在大多数情况下,几乎不需要额外的代码就可拥有能够打印和预览的视图。 但有一些方法可以优化打印,这需要开发人员付出巨大的努力,并且某些应用程序需要向打印预览模式添加特定的用户界面元素。

高效打印

当 MFC 应用程序使用标准方法打印时,Windows 会将所有图形设备接口 (GDI) 输出调用定向到内存中元文件。 调用 EndPage 时,Windows 会对打印机需要打印一页的每个物理带播放一次图元文件。 在此呈现过程中,GDI 经常查询中止过程以确定它是否应继续。 通常中止过程允许处理消息,以便用户可以使用打印对话框中止打印作业。

遗憾的是,这可能导致打印过程变慢。 如果应用程序中的打印速度必须快于使用标准技术所能实现的速度,则必须实现手动分段。

要手动分段,必须重新实现打印循环,这样每页才能多次调用 OnPrint(每个带一次)。 打印循环在 viewprnt.cpp 中的 OnFilePrint 函数中实现。 在 CView 派生类中,重载此函数,以便用于处理打印命令的消息映射条目调用打印函数。 复制 OnFilePrint 例程并更改打印循环以实现分段。 你可能还需要将带状矩形传递给打印函数,以便根据要打印的页面部分优化绘图。

其次,必须在绘制带时经常调用 QueryAbort。 否则中止过程不会被调用,用户将无法取消打印作业。

从本质上讲,打印预览尝试将显示转换为打印机的仿真。 默认情况下,主窗口的工作区用于在窗口中完整显示一页或两页。 用户可以放大页面的某个区域以更详细地查看该部分。 借助其他支持,用户甚至可以在预览模式下编辑文档。

自定义打印预览

本说明仅涉及修改打印预览的一个方面:向预览模式添加 UI。 可进行其他修改,但此类更改不在本讨论范围内。

向预览模式添加 UI

  1. CPreviewView 中派生一个视图类。

  2. 为所需的 UI 方面添加命令处理程序。

  3. 如果要向显示添加视觉方面,请重写 OnDraw 并在调用 CPreviewView::OnDraw 后执行绘图。

OnFilePrintPreview

这是打印预览的命令处理程序。 其默认实现如下:

void CView::OnFilePrintPreview()
{
    // In derived classes, implement special window handling here
    // Be sure to Unhook Frame Window close if hooked.

    // must not create this on the frame. Must outlive this function
    CPrintPreviewState* pState = new CPrintPreviewState;

    if (!DoPrintPreview(AFX_IDD_PREVIEW_TOOLBAR, this,
        RUNTIME_CLASS(CPreviewView), pState))
    {
        // In derived classes, reverse special window handling
        // here for Preview failure case

        TRACE0("Error: DoPrintPreview failed");
        AfxMessageBox(AFX_IDP_COMMAND_FAILURE);
        delete pState;  // preview failed to initialize, delete State now
    }
}

DoPrintPreview 将隐藏应用程序的主窗格。 控件条(如状态栏)可以通过在 pState->dwStates 成员中进行指定来保留(这是位掩码,各个控件条的位由 AFX_CONTROLBAR_MASK( AFX_IDW_MYBAR) 定义)。 窗口 pState->nIDMainPane 是将自动隐藏和重新显示的窗口。 然后 DoPrintPreview 将为标准预览 UI 创建按钮栏。 如果需要特殊窗口处理,如隐藏或显示其他窗口,则应在调用 DoPrintPreview 之前完成。

默认情况下,当打印预览完成时,它会将控件条恢复到原始状态,并使主窗格可见。 如果需要特殊处理,则应在重写 EndPrintPreview 的过程中完成。 如果 DoPrintPreview 失败,还将提供特殊处理。

使用以下值调用 DoPrintPreview:

  • 预览工具栏的对话框模板的资源 ID。

  • 指向可执行打印预览的打印的视图的指针。

  • 预览视图类的运行时类。 这将在 DoPrintPreview 中动态创建。

  • CPrintPreviewState 指针。 请注意,CPrintPreviewState 结构(如果应用程序需要保留更多状态,则为派生结构)不得在框架上创建。 DoPrintPreview 是无模式的,在调用 EndPrintPreview 之前,此结构必须存在。

    注意

    如果打印支持需要单独的视图或视图类,则应将指向该对象的指针作为第二个参数传递。

EndPrintPreview

调用此项可终止打印预览模式。 通常需要移动到文档中上次在打印预览中显示的页面。 EndPrintPreview 是应用程序执行此操作的机会。 pInfo->m_nCurPage 成员是上次显示的页面(如果上次显示两个页面,则为左侧页面),指针是用户感兴趣的页面位置的提示。 由于框架不知道应用程序视图的结构,因此你必须提供代码才能移动到所选点。

应在调用 CView::EndPrintPreview 之前执行大多数操作。 此调用将产生与 DoPrintPreview 相反的效果并删除 pView、pDC 和 pInfo。

// Any further cleanup should be done here.
CView::EndPrintPreview(pDC, pInfo, point, pView);

CWinApp::OnFilePrintSetup

必须为“打印设置”菜单项映射此项。 在大多数情况下,不需要重写实现。

页面命名法

另一个问题是页码编号和顺序。 对于简单的字处理器类型应用程序,这是一个简单的问题。 大多数打印预览系统假定每个打印页面对应于文档中的一页。

在尝试提供通用解决方案时,需要考虑几个事项。 假定使用 CAD 系统。 用户有一个涵盖多张 E 大小纸的绘图。 在 E 大小(或更小规模)绘图仪上,页码编号非常简单。 但在激光打印机上,每张纸打印 16 个 A 大小页面,打印预览会考虑是哪一“页面”

正如引言段落所述,打印预览就像打印机一样。 因此,用户将看到所选的特定打印机会产生什么样的结果。 每页上打印什么图像可通过视图确定。

CPrintInfo 结构中的页面说明字符串提供了一种向用户显示页码的方法,前提是每页的页码可以采用一个数字表示(如“第 1 页”或“第 1-2 页”)。 CPreviewView::OnDisplayPageNumber 的默认实现使用此字符串。 如果需要其他显示,可重写此虚拟函数来提供,如“Sheet1、Sections A, B”。

另请参阅

按编号列出的技术说明
按类别列出的技术说明