在一般的软件开发中打印和打印预览是经常要用到的功能,对于VC6.0中的文档/视图(Doc/View)框架,可以很方便的使用缺省的打印和打印预览。但是,如果应用程序是基于对话框的就没有办法利用这种便利。而很多情况下,基于对话框的程序也需要打印和打印预览功能。那该怎么办呢?这正是本文将要解决的问题。
1 实现打印
在对话框应用程序中不具备MFC的视和框架交互,要想实现打印和打印预览必须直接获取打印机的设备环境句柄,这时可以设置CPringDialog类中的构造函数的参数,获取打印机的设备环境句柄。利用这个句柄,转换为指针,则按打印流程实现打印任务。
(1)从CFrameWnd类派生出主窗口类CMyFrameWnd加入项目,这个类将作为控制类来实现程序的打印和打印预。
(2)在CMyFrameWnd中加入打印和打印预览相关的函数,为了方便起见,名字和CView视图类中缺省的打印和打印预览虚函数名相同。但这些函数是以成员函数的形式加入的,而不是重载。这里主要介绍Print和OnPrint函数。
Print函数是打印控制函数,由它来弹出打印对话框,取得用户设置信息,如打印机、纸张大小等。还有建立一个打印机DC和设置DOCINFO结构,该结构包含输入输出文件名及其它一些信息,StartDoc函数要该结构作参数。还要设置打印区域,调用打印函数等
void CMyFrameWnd::Print()
{
CDC dc;
CPrintDialog printDlg(FALSE);
if (printDlg.DoModal() != IDOK)
//弹出打印对话框,取得用户设置参数
return;
dc.Attach(printDlg.GetPrinterDC());
//绑定一个打印机DC到CDC
dc.m_bPrinting=TRUE;
DOCINFO di; //初始化打印机的DOCINFO
memset(&di,0,sizeof (DOCINFO));
di.cbSize=sizeof (DOCINFO);
BOOL bPrintingOK=dc.StartDoc(&di); //开始一个打印任务
CPrintInfo Info;
Info.m_rectDraw.SetRect(0,0,dc.GetDeviceCaps(HORZRES),
c.GetDeviceCaps(VERTRES));
//设置打印区域大小
OnBeginPrinting(&dc,&Info); //打印初始化
for (UINT page = Info. GetMinPage ( ); page < = Info.
GetMaxPage() && bPrintingOK; page++)
{
dc.StartPage(); //开始一个新的打印页
Info.m_nCurPage=page;
OnPrint(&dc,&Info); //打印
bPrintingOK=(dc.EndPage() > 0); //打印页结束
}
OnEndPrinting(&dc,&Info); //打印完成后释放资源
if (bPrintingOK)
dc.EndDoc(); //一个打印任务结束
else
dc.AbortDoc(); //终止打印任务
dc.Detach(); //释放打印机DC
}
OnPrint函数主要设置映射参数,调用对话框的绘图函数等功能,具体如下:
void CMyFrameWnd::OnPrint(CDC *pDC,CPrintInfo *pInfo)//
{
if (!pDC || !pInfo) return;
CFont *pOldFont=pDC->SelectObject(&m_PrinterFont);
//设置映射参数及边界
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetWindowExt(m_LogicalPageSize);
pDC->SetViewportExt(m_PaperSize);
pDC->SetWindowOrg(-LEFT_MARGIN*m_CharSize.cx,0);
pDC->SetWindowOrg(0,0);
pDC->SelectObject(pOldFont);
}
2 实现打印预览
通过以上几个函数就已经可以打印了,要实现打印预览,还要作一些工作。因为打印预览函数的执行都依赖于CView和CFrameWnd框架,所以,我们建立临时的框架类和视图类,然后调用这个临时视图类的缺省函数OnFilePrint和OnFilePrintPreview。
(1)从CView类派生出CMyView类加入项目。并在CMyView类中重载相关的打印和打印预览函数。这个类主要用来传递OnBeginPrinting,OnPrint,OnEndPrinting等相关函数。
(2 )在控制类的PrintPreview函数中用一个CsingleDocTemplate对象创建框架和视图窗口,其中视图类CMyView并不显示,因为它马上就被预览视图(preview view)所掩盖。m_pMainWnd改为指向新的CFrameWnd,这样预览类就可以用它作为主框架。m_pMainWnd的初始值被保存下来,当预览结束时,要将m_pMainWnd恢复到其初始值。当然,要在控制类包含头文件MyView.h。具体程序如下:
void CMyFrameWnd::PrintPreview()
{
if (m_bPrintPreview) //预览标志符
{
AfxGetApp()->m_pMainWnd->SetFocus();
return;
}
CFrameWnd* pOldFrame=(CFrameWnd*)AfxGetThread()->m_pMainWnd;
pOldFrame->ShowWindow(SW_HIDE);
// m_pMainWnd初始值被保存,原来框架被隐藏
if (!m_pTemplate)
{
m_pTemplate = new CSingleDocTemplate ( IDR_MENU,
NULL, RUNTIME_CLASS(CFrameWnd), RUNTIME_CLASS(CMyView)); //建立CSingleDocTemplate对象
AfxGetApp()->AddDocTemplate(m_pTemplate);
}
CFrameWnd * pFrameWnd = m_pTemplate - >
CreateNewFrame(NULL,NULL );
//建立临时框架
m_bPrintPreview=TRUE; //打印预览
m_pTemplate -> InitialUpdateFrame ( pFrameWnd, NULL,
FALSE);
CMyView * pView = ( CMyView * ) pFrameWnd ->
GetActiveView();
pView->m_pCtrlFrame=this;
pView->m_pOldFrame=pOldFrame;
AfxGetApp()->m_pMainWnd=pFrameWnd;
pFrameWnd->ShowWindow(SW_SHOWMAXIMIZED);
//显示临时框架
pView->OnFilePrintPreview();
//调用CView缺省的打印预览
}
现在,我们已经可以看到预览窗口了。但是有个小问题,工具栏上的按钮不能自动更新。这是因为对话框应用程序没有OnIdle()函数,而在SDI或MDI应用程序中都是通过这个函数来发送WM_IDLEUPDATECMDUI消息以更新工具栏。所以我们要自己发送这个消息。首先,我们要包含AfxPriv.h以让程序识别WM_IDLEUPDATECMDUI消息。就像这样:
BOOL CSzyDlg::ContinueModal()
{
if (m_Grid.m_bPrintPreview)
AfxGetApp()->m_pMainWnd->
SendMessageToDescendants(WM_IDLEUPDATECMDUI,
(WPARAM)TRUE,0,TRUE,TRUE);
return CDialog::ContinueModal();
}
基于对话框的应用程序没有包含打印预览的源文件,所以要将afxprint.rc添加到工程中。先用记事本打开资源文件,在3 TEXTINCLUDE部分找到#include afxres.rc,在它下面加入:
"#include ""afxprint.rc"" // printing/print preview resources\ r\ n"
再到资源文件末尾,找到#include afxres.rc,在其下加入:
#include afxprint.rc // printing/print preview resources
最后,再为打印按钮(也就是ID_FILE_PRINT消息)在对话框类中增加一个响应函数:
BEGIN_MESSAGE_MAP(CMyDlg,CDialog)
//{{AFX_MSG_MAP(CMyDlg)
…
//}}AFX_MSG_MAP
ON_COMMAND(ID_FILE_PRINT,OnFilePrint) //消息响应函数
END_MESSAGE_MAP()
3 结语
对话框应用程序简洁、能较好的和用户交换信息,所以应用很广泛。在这个基础上实现打印和打印预览就能更好的满足用户的需要,有一定的实用价值。要想实现打印和打印预览,最关键的部分就是根据程序的具体要求,想法设法得到其设备环境句柄,然后,再按打印和打印预览的流程,实现具体的打印和打印预览任务。我们在开发的“离心泵性能测试系统”中,按上面的方法成功的实现了打印和打印预览。有兴趣的读者还可以根据自己的需要在此基础实现自己想要的打印和打印预览功能。
参考文献
[1] (美)Kruglinski DJ. VC++6.0技术内幕[M] .北京:清华大学出版社,1999.
[2] 王晖,等.精通Visual C++6.0[M].北京:电子工业出版社,1999.
实现步骤:
1. 先将四个文件加入到工程当中来;在CprintDlg.cpp中#include "PrintFrame.h".
2. 在CprintDlg.h中,添加三个消息
protected:
// Generated message map functions
//{{AFX_MSG(CPrint)
virtual void OnOK();
//}}AFX_MSG
LRESULT OnBeginPrinting(WPARAM wParam,LPARAM lParam);
LRESULT OnEndPrinting(WPARAM wParam,LPARAM lParam);
LRESULT OnMyPrint(WPARAM wParam,LPARAM lParam);
DECLARE_MESSAGE_MAP()
3. 在CprintDlg.cpp中,
BEGIN_MESSAGE_MAP(CPrint, CDialog)
//{{AFX_MSG_MAP(CPrint)
//}}AFX_MSG_MAP
ON_MESSAGE(WM_BEGIN_PRINTING,OnBeginPrinting)
ON_MESSAGE(WM_END_PRINTING,OnEndPrinting)
ON_MESSAGE(WM_MY_PRINT,OnMyPrint)
END_MESSAGE_MAP()
4. 在CprintDlg.cpp中添加三个函数
LRESULT CPrint::OnMyPrint(WPARAM wParam, LPARAM lParam)//将要打印的写到这
{
CDC* pDC = (CDC*)wParam;
CPrintInfo* pInfo = (CPrintInfo *)lParam;
pDC->MoveTo(100,200);
pDC->LineTo(500,600);
return TRUE;
}
LRESULT CPrint::OnBeginPrinting(WPARAM wParam,LPARAM lParam)
{
CDC* pDC = (CDC*)wParam;
CPrintInfo* pInfo = (CPrintInfo *)lParam;
return TRUE;
}
LRESULT CPrint::OnEndPrinting(WPARAM wParam,LPARAM lParam)
{
return TRUE;
}
4. void CPrint::OnOK() //打印预览按钮
{ CPrintFrame *pFrame = new CPrintFrame;//001
pFrame->m_pCallerDlg = this;
pFrame->Create(NULL,"Curve Print Preview",WS_OVERLAPPEDWINDOW,CRect(0,0,0,0));//002
pFrame->m_pView->OnMyPrintPreview();
CDialog::OnOK();
}