打印程序的编写在
windows
程序设计中非常有用,针对不同的用户需要,通常用
sdk
方式实现打印代码量比较大,而且要对打印流程的底层有非常清楚的了解,需要一个程序员有非常深入的打印方面的知识,利用
MFC
提供的文档视图结构,不但可以实现一些常用的标准界面元素,把数据的处理的界面的处理分离出来,而且其提供的打印功能更是方便快捷,功能强大。打印程序的编写本质是是一种
GDI
绘图,只是绘图的对象是在打印机的设备描述表。
1 、文档视图结构的程序的打印程序的编写
通常情况下,一个结构组织的比较好的 MFC 程序是基于文档视图结构的,这一框架结构给我们提供了很多功能,比如菜单,注册表的关联,文件类型的注册,打印功能等。
下面是 MFC 的打印的函数的实现,该函数名为 OnFilePrint 它不是一个虚函数,而是响应缺省的 COMMAND 消息的处理函数,因为 MFC 提供了向导生成的菜单和工具栏,关于打印的命令 ID 为 ID_FILE_PRINT ,而在视图类的 MessageMap 里有这样一项, ON_COMMAND (ID_FILE_PRINT, CView::OnFilePrint) ,因此实际使用的过程中可以不用原来的 ID, 而使用自己的 ID 如 ID_MYPRINT, 再在 MessageMap 里加入 ON_COMMAND (ID_MYPRINT, CView::OnFilePrint) 即可完成原来一样的功能。 ViewPrnt.cpp 中有 CView 的 OnFilePrint 的函数的具体实现,下面是 MFC 的源代码中的主要代码:
1 、文档视图结构的程序的打印程序的编写
通常情况下,一个结构组织的比较好的 MFC 程序是基于文档视图结构的,这一框架结构给我们提供了很多功能,比如菜单,注册表的关联,文件类型的注册,打印功能等。
下面是 MFC 的打印的函数的实现,该函数名为 OnFilePrint 它不是一个虚函数,而是响应缺省的 COMMAND 消息的处理函数,因为 MFC 提供了向导生成的菜单和工具栏,关于打印的命令 ID 为 ID_FILE_PRINT ,而在视图类的 MessageMap 里有这样一项, ON_COMMAND (ID_FILE_PRINT, CView::OnFilePrint) ,因此实际使用的过程中可以不用原来的 ID, 而使用自己的 ID 如 ID_MYPRINT, 再在 MessageMap 里加入 ON_COMMAND (ID_MYPRINT, CView::OnFilePrint) 即可完成原来一样的功能。 ViewPrnt.cpp 中有 CView 的 OnFilePrint 的函数的具体实现,下面是 MFC 的源代码中的主要代码:
void CView::OnFilePrint() { // get default print info if (OnPreparePrinting(&printInfo)) { if (dlg.DoModal() != IDOK) return; } OnBeginPrinting(&dcPrint, &printInfo); OnPrepareDC(&dcPrint, &printInfo); OnPrint(&dcPrint, &printInfo); OnEndPrinting(&dcPrint, &printInfo); // clean up after printing } |
对于 OnPreparePrinting() 函数的具体内容必须有 return DoPreparePrinting(pInfo); 这是在一个打印过程中最先调用的。当然也可以包含一些其它的打印初始化操作。最主要的是要重载三个函数:
OnBeginPrinting(); OnPrint(); OnEndPrinting(); |
而以 OnPrint 最为复杂,它要写大量代码实现打印功能。对于默认的 OnPrint 实现是调用 CView 的 OnDraw ,也就是和绘制视图类的客户区的内容完全相同的方法来在打印机上绘图。实际中我们在两种地方绘图的内容是完全不同的,可能用户在客户区绘的是一个曲线,而在打印机上要绘制表格和数据。 OnPrint(CDC* pDC, CPrintInfo* pInfo) 的第二个参数是一个 CPrintInfo 类型的指针,我们可以从这个指针指向的对象中获得很多信息,如总共的页数,当前的页数,这在打印页眉页脚时可能是很有用的信息。 CPrintInfo 的定义如下:
struct structCPrintInfo // Printing information structure { CPrintInfo(); ~CPrintInfo(); CPrintDialog* m_pPD; // pointer to print dialog BOOL m_bDocObject; // TRUE if printing by IPrint interface BOOL m_bPreview; // TRUE if in preview mode BOOL m_bDirect; // TRUE if bypassing Print Dialog BOOL m_bContinuePrinting;// set to FALSE to prematurely end printing UINT m_nCurPage; // Current page UINT m_nNumPreviewPages; // Desired number of preview pages CString m_strPageDesc; // Format string for page number display LPVOID m_lpUserData; // pointer to user created struct CRect m_rectDraw; // rectangle defining current usable page area // these only valid if m_bDocObject UINT m_nOffsetPage; // offset of first page in combined IPrint job DWORD m_dwFlags; // flags passed to IPrint::Print void SetMinPage(UINT nMinPage); void SetMaxPage(UINT nMaxPage); UINT GetMinPage() const; UINT GetMaxPage() const; UINT GetFromPage() const; UINT GetToPage() const; UINT GetOffsetPage() const; }; |
OnBeginPrinting() 通常用来设定要打印的总页数,以及一些和页面尺寸有关的初始化工作,在 OnBeginPrinting() 中设定打印的页数是必要的,默认的页数是只有一页,如果开发人员打印的页数大于 1 ,则必须在此函数中设定打印的页数。然后在 OnPrint(CDC* pDC, CPrintInfo* pInfo) 中用 pInfo-> m_nCurPage 获取当前的页码,根据当前的页码打印该页相应的内容。 OnEndPrinting 用来释放在 OnBeginPrinting 中申请的资源,如果没有申请,则不需重载该函数。
关于打印预览只需要将自己的执行打印预览功能的命令 ID 和 CView::OnFilePrintPreview 关联起来就行了,具体方法是在用户的视图类的 MessageMap 中加入: ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview);
其中 ID_FILE_PRINT_PREVIEW 是默认的 ID ,开发人员也可以使用自己的 ID 。其实只要重载了 OnPrint 函数,在打印和打印预览中就可以重用该函数了。
2 、没有文档视图结构的程序中利用 MFC 进行打印程序的编写
如果程序不是文档视图结构的,我们要使用 MFC 来进行打印,则可以通过建立一个虚拟的文档视图结构来进行打印,其实 MFC 的打印的强大功能是在 CView 里提供的,而 CView 类的对象是一个子窗口,它必须是某一个框架窗口的子窗口,而在对话框程序中,我们只需要打印,而不需要显示这个框架窗口和视图。我们以按下按钮 " 打印 " 来执行打印程序,例如按钮为 ID 为 IDC_PNT ,消息相应函数为 OnPnt() ,即:
ON_BN_CLICKED(IDC_PNT, OnPnt)
;
|
需要在 OnPnt 中建立一个框架窗口,同时使某个 CView 类的对象为该窗口的子窗口。因此建立两个类,一个为框架窗口类 CPrintFrame ,另一个为 CPrintView 。在新建一个用于打印的虚拟框架窗口时,需要将执行打印的对话框的指针传给框架窗口,这以便在对话框来响应 WM_BEGIN_PRINTING 和 WM_END_PRINTING 消息,使对话框可以完成打印的初始化和释放操作。在执行一个打印任务时,将打印的流程交给 CView 来进行,而这个 CView 是虚拟的,只是用来完成打印一些操作,其它内容则完全不负责处理,而当要执行 CView::OnPrint 时,则又将处理的具体内容传回到对话框,而对话框则只需要响应 WM_MY_PRINT 消息即可:
pFrame->m_pCallerDlg->SendMessage(WM_MY_PRINT,(WPARAM) pDC, (LPARAM) pInfo);
|
使打印的具体处理又传回到对话框中,使开发人员根据具体的需要写 WM_MY_PRINT 的处理函数就可以实现打印,而 CView::OnPrint(CDC* pDC, CPrintInfo* pInfo) 的参数也从 WM_MY_PRINT 的消息参数传出来,在用户的对话框程序中 , 需要写的代码就很少,主要有以下几个步骤 :
建立一个 CPrintFrame 的对象 , 设该对象的指针为 pFrame ,并将对话框的指针传给该对象的 m_pCallerDlg ,即 pFrame->m_pCallerDlg = this ;
调用对象的 Create 函数创建框架窗口;例如 pFrame->Create(NULL," 打印 " , WS_OVERLAPPEDWINDOW,CRect(0,0,0,0));
如果要执行打印,则调用 pFrame->m_pView->OnMyPrint();
如果要执行打印预览,则调用:
pFrame->m_pView->OnMyPrintPreview();
|
例如:
void CDlgPrintDlg::OnPrint() //
执行打印功能
{ CPrintFrame *pFrame = new CPrintFrame; pFrame->m_pCallerDlg = this; pFrame->Create(NULL,"Curve Print",WS_OVERLAPPEDWINDOW,CRect(0,0,0,0)); pFrame->m_pView->OnMyPrint(); } void CDlgPrintDlg::OnPrintPreview() // 执行打印预览功能 { CPrintFrame *pFrame = new CPrintFrame; pFrame->m_pCallerDlg = this; pFrame->Create(NULL,"Curve Print Preview",WS_OVERLAPPEDWINDOW,CRect(0,0,0,0)); pFrame->m_pView->OnMyPrintPreview(); } |
在对话框中响应 WM_BEGIN_PRINTING , WM_END_PRINTING , WM_MY_PRINT 消息,分别完成打印的初始化,释放和具体的打印操作;如在示例程序中添加了三个消息响应函数来执行该功能。
ON_MESSAGE(WM_BEGIN_PRINTING,OnBeginPrinting) ON_MESSAGE(WM_END_PRINTING,OnEndPrinting) ON_MESSAGE(WM_MY_PRINT,OnMyPrint) |
其中 OnMyPrint 是跟具体要打印什么内容有关的开发人员要重点完成的代码,可以打印表格,图片,数据,只要 GDI 绘图可以进行的操作在这里都可以完成。由于打印预览的一部分工作在 CView 类里完成,因此在用户程序中只需要相应 WM_MY_PRINT 消息就可以执行打印预览的功能,而不需要另外编写打印预览代码。
本文提供的 CPrintFrame 和 CPrintView 类是可重用的类,开发者只需要把这两个类对应的四个文件拷贝到工程文件所在目录中 (PrintFrame.h, PringtView.h,PrintFrame.cpp, PrintView.cpp) ,并将这四个文件加入工程,并在需要执行打印功能的代码处加入 #include "PrintFrame.h"
#include "PrintView.h"
然后按照上述 5 个步骤进行即可以实现一个功能完整的打印程序,利用上述类实现对话框打印不但节省开发者许多时间,而且功能很强大,能达到很专业的水平,但是该方法有一个缺点,发现如果开发者使用静态连接的 MFC 库时则会出错,只适用于 Use MFC in a Shelled DLL 情况,而且必须使程序为 Debug 版本的。