从参加工作开始我就一直喜欢用基于对话框的程序做开发,原因很简单,就是因为对话框的程序写起来比较方便,布置控件也相对容易。我也知道一般做开发都使用单文档,不过我这个人比较懒,而且现在做上位机开发程序界面中要应用大量控件,所以索性将基于对话框的程序进行到底。
当然懂行的朋友都知道,对话框程序其实不是一个一般的应用程序类型,其主要应用是用于系统设置一类的功能性程序。因此MFC封装的CDialog会带有这类程序的一般特性而不具备文档/视图框架程序的一些功能。比如说对话框会默认处理回车键和Esc键,但是却不能轻松实现打印预览功能。嗯,终于说到正题了——对话框的打印预览。
其实上天总是公平的,微软也遵循了这一规律。当我因为对话框的操作相对简单而选择采用基于对话框的程序做开发时,对话框程序对一些成熟功能的不支持也让我大费周章,打印预览就是其中之一。还是去年的时候,项目中要求提供将动态显示的图谱打印的功能,由于只是一个相对简单的打印功能,觉得没有必要自己去实现相关细节,就想能不能将MFC封装好的打印功能集成到对话框中,于是开始上网找资料。做过这个功能的朋友应该知道,关于这个问题网上介绍的相关文章确实不少,不过我下了几个例子没有一个可以完美运行的。不过综合各方面的资料加之自己的一点研究我还是解决了诸多问题实现了项目需求,当时我就想有时间的话把自己的成果整理出来和大家分享,希望以后做要这个功能的朋友们可以少走一些弯路,毕竟是一个简单并且在MFC中封装好的功能,实在没必要花太多时间。
关于将MFC的打印和打印预览功能集成到对话框的基本方法我就不在这里赘述了,这样的文章网上随便搜一搜多的很。其基本原理无非是通过CFrameWnd、CScrollView和CPreviewView派生三个类,也就是通过CFrameWnd做中介在基于对话框的程序中实现打印预览的功能。这里我主要说一些实现过程需要注意的问题,至于整个程序应该怎么去写我就不一步一步详细介绍了,我做了一个例子,有兴趣的朋友可以下来研究一下。
整个功能需要注意的地方有这样几处,首先CFrameWnd的派生类CPrintFrame是一个中介,通过以下代码我们可以看出,在应用打印预览功能的时候通过pApp->m_pMainWnd = this;语句,用CFrameWnd的对象将进程主窗口做了替换,所以如何管理真正的进程主窗口就需要多加注意。
[c-sharp] view plaincopy
- void CPrintFrame::DoPrintView()
- {
- if(m_pPrintView!=NULL)
- {
- m_pPrintView->ShowWindow(SW_SHOW);
- SetActiveView(m_pPrintView);
- }
- SetIcon(m_pMainDlg->GetIcon(FALSE), FALSE);
- SetIcon(m_pMainDlg->GetIcon(TRUE), TRUE);
- ShowWindow(SW_MAXIMIZE);
- m_pMainDlg->ShowWindow(SW_HIDE);
- CWinApp *pApp = AfxGetApp();
- pApp->m_pMainWnd = this;
- m_pPrintView->OnFilePrintPreview();
- }
继承自CScrollView的CPrintView类是实现打印预览功能的主视图类,对于OnPreparePrinting、OnBeginPrinting、OnPrint这三个函数很容易理解了,这里主要说说OnFilePrintPreview函数,其代码如下
[cpp] view plaincopy
- void CPrintView::OnFilePrintPreview()
- {
- CPrintPreviewState* pState = new CPrintPreviewState;
- pState->lpfnCloseProc = ProcClosePrintPreview;
- if(!DoPrintPreview(AFX_IDD_PREVIEW_TOOLBAR, this, RUNTIME_CLASS(CPreviewPrint), pState))
- {
- TRACE0("Error: DoPrintPreview failed./n");
- AfxMessageBox(AFX_IDP_COMMAND_FAILURE);
- delete pState;
- }
- }
可以看到这里是通过由CPreviewView派生的CPreviewPrint类创建了打印预览视图,而请大家特别注意的是DoPrintPreview函数的第一个参数AFX_IDD_PREVIEW_TOOLBAR。用过MFC打印预览功能的朋友都知道打印预览窗口有一个工具条,这个参数就是这个工具条的ID。这是不是说我们需要自己创建工具条呢?不是,不过我们需要手动修改资源脚本以便连入工具条资源,这里给出部分脚本代码,大家可以自己对比例程看看应该填在哪,共有两处
[cpp] view plaincopy
- 3 TEXTINCLUDE
- BEGIN
- "#define _AFX_NO_SPLITTER_RESOURCES/r/n"
- "#define _AFX_NO_OLE_RESOURCES/r/n"
- "#define _AFX_NO_TRACKER_RESOURCES/r/n"
- "#define _AFX_NO_PROPERTY_RESOURCES/r/n"
- "/r/n"
- "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)/r/n"
- "LANGUAGE 4, 2/r/n"
- "#pragma code_page(936)/r/n"
- "#include ""res//PrintDemo.rc2"" // 非 Microsoft Visual C++ 编辑的资源/r/n"
- "#include ""afxres.rc"" // 标准组件/r/n"
- "#include ""l.chs//afxprint.rc"" // printing/print preview resources/r/n"
- "#endif/r/n"
- "/0"
- END
[cpp] view plaincopy
- #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
- LANGUAGE 4, 2
- #pragma code_page(936)
- #include "res/PrintDemo.rc2" // 非 Microsoft Visual C++ 编辑的资源
- #include "afxres.rc" // 标准组件
- #include "l.chs/afxprint.rc" // printing/print preview resources
- #endif
这个类里还有一个函数ProcClosePrintPreview,故名思义它是用来处理打印预览窗口关闭消息的。这里要注意,打印预览窗口可以通过两种途径关闭,一是通过窗口标题栏的关闭按钮,再有就是通过工具栏的关闭按钮,而只有通过窗口标题栏的关闭按钮关闭预览窗口时才会通过ProcClosePrintPreview,那么工具栏的关闭按钮走的是什么途径呢?答案在CPreviewPrint类中,CPreviewPrint类里有这样两个函数OnPreviewPrint和OnPreviewClose,其中OnPreviewPrint是实现打印的主要函数,而OnPreviewClose就是工具栏关闭按钮的消息函数。这样我们就可以通过在ProcClosePrintPreview函数中调用OnPreviewClose来统一打印预览窗口的出口,处理善后工作,将程序的主窗口回复为原来的对话框。
至此在基于对话框的程序中集成打印预览功能的细节问题就介绍完了。以上问题是我在研究此功能时遇到的,如果大家在开发过程中遇到什么其它问题欢迎和我交流,希望我们能共同提高。顺便说一下例子中打印窗口内容功能的实现,其实很简单,主要就是通过PrintWindow函数将窗口内容保存到一个内存位图中,在打印消息中使用,其核心代码如下
[cpp] view plaincopy
- void CPrintDemoDlg::GetWindowGraph()
- {
- CRect rectWnd;
- CBitmap *pOldBitmap;
- CDC* pDC = GetDC();
- CDC* pMemDC = new CDC;
- //获取窗口位图
- GetWindowRect(rectWnd);
- m_bitmapPrint.DeleteObject();
- m_bitmapPrint.CreateCompatibleBitmap(pDC, rectWnd.Width(), rectWnd.Height());
- pMemDC->CreateCompatibleDC(pDC);
- pOldBitmap = pMemDC->SelectObject(&m_bitmapPrint);
- PrintWindow(pMemDC, 0);
- pMemDC->SelectObject(pOldBitmap);
- //释放内存
- ReleaseDC(pMemDC);
- delete pMemDC;
- }
另外,此例中是通过打印预览窗口打印的,如果要直接打印只需通过CPrintFrame声明一个对象,如CPrintFrame pPrintFrame = new CPrintFrame(this);然后直接调用DoPrint即可,即pPrintFrame->DoPrint();
我对打印的具体技术不甚了解,本文也只是一个取巧的方法,也是综合了网上很多资料总结出来的,在此对那些资料的提供者表示感谢,也希望本文能够对想实现这一功能的朋友们有所帮助。