映射模式是MFC甚至SDK界面编程第1个难点。打印则是第2个难点。这2个都是历史遗留的设计缺陷。这些缺陷还不至于到bug程度,但却很难用,不易理解。
MFC提供2个类来实现打印(预览),具体有CPrintDialog和CPageSetupDialog类。这2个类实际上提供3通用对话框。具体看下面3组代码。
“打印”对话框:
//main.h里面的代码
class CMyApp:public CWinApp
{
public:
virtual BOOL InitInstance();
};
class CMainWindow:public CFrameWnd
{
public:
CMainWindow();
protected:
afx_msg void OnLButtonUp(UINT nFlage,CPoint point);
DECLARE_MESSAGE_MAP();
};
//main.cpp里面的代码
#include <afxwin.h>
#include <afxdlgs.h>
#include "main.h"
CMyApp myApp;
BOOL CMyApp::InitInstance()
{
Enable3dControls();
m_pMainWnd=new CMainWindow;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
BEGIN_MESSAGE_MAP(CMainWindow,CFrameWnd)
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
CMainWindow::CMainWindow()
{
Create(NULL,_T("MFC打印"),WS_OVERLAPPEDWINDOW,CRect(200,200,400,400));
}
void CMainWindow::OnLButtonUp(UINT nFlage,CPoint point)
{
CPrintDialog dlg(FALSE);
dlg.DoModal();
}
运行结果如下图:
在上图空白的客户区点击一次,出现如下图:
然后是第2个“打印设置”对话框,代码如下:
//main.h里面的代码
class CMyApp:public CWinApp
{
public:
virtual BOOL InitInstance();
};
class CMainWindow:public CFrameWnd
{
public:
CMainWindow();
protected:
afx_msg void OnLButtonUp(UINT nFlage,CPoint point);
DECLARE_MESSAGE_MAP();
};
//main.cpp里面的代码
#include <afxwin.h>
#include <afxdlgs.h>
#include "main.h"
CMyApp myApp;
BOOL CMyApp::InitInstance()
{
Enable3dControls();
m_pMainWnd=new CMainWindow;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
BEGIN_MESSAGE_MAP(CMainWindow,CFrameWnd)
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
));
}
void CMainWindow::OnLButtonUp(UINT nFlage,CPoint point)
{
CPrintDialog dlg(TRUE);
dlg.DoModal();
}
运行结果如下图:
在上图空白的客户区点击一次,出现如下图:
然后是第3个“页面设置”对话框,代码如下:
//main.h里面的代码
class CMyApp:public CWinApp
{
public:
virtual BOOL InitInstance();
};
class CMainWindow:public CFrameWnd
{
public:
CMainWindow();
protected:
afx_msg void OnLButtonUp(UINT nFlage,CPoint point);
DECLARE_MESSAGE_MAP();
};
//main.cpp里面的代码
#include <afxwin.h>
#include <afxdlgs.h>
#include "main.h"
CMyApp myApp;
BOOL CMyApp::InitInstance()
{
Enable3dControls();
m_pMainWnd=new CMainWindow;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
BEGIN_MESSAGE_MAP(CMainWindow,CFrameWnd)
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
));
}
void CMainWindow::OnLButtonUp(UINT nFlage,CPoint point)
{
CPageSetupDialog dlg;
dlg.DoModal();
}
运行结果如下图:
在上图空白的客户区点击一次,出现如下图:
以上3组代码都有2个共同特点:
①在main.cpp文件里面的第2行代码是#include <afxdlgs.h>。如果没有包含afxdlgs.h头文件,那么任何通用对话框类将都不能使用。
②3个对话框窗口都是锁定式对话框。
主窗口高度有意做高度改变,用于区别。为什么要有3个对话框,这就是最前面所说的“历史问题”。可以先看看Microsoft Office 2003的“打印”和“页面设置”对话框的界面。截图如下:
从以上2图可以看出Office自己的打印功能明显比系统MFC自带的3个对话框加起来的还要复杂,也更为合理。事实上,几乎各个大型软件会自己提供适合自己的打印界面,而不是使用系统自带的打印界面。如果非要使用系统自带的打印界面,那么建议使用第个对话框来设置页面,并从对话框的右下角的“打印机”按钮来设置打印机,这样差不多可以应付大部分打印需要。如果系统把3个界面合并成一个更通用的界面,那么打印功能会大大加强和易用。
以上代码均没有输出代码,以下代码给出MFC下的最原始、最核心、最根本的输出代码范例。
//main.h里面的代码
class CMyApp:public CWinApp
{
public:
virtual BOOL InitInstance();
};
class CMainWindow:public CFrameWnd
{
public:
CMainWindow();
protected:
afx_msg void OnLButtonUp(UINT nFlage,CPoint point);
DECLARE_MESSAGE_MAP();
};
//main.cpp里面的代码
#include <afxwin.h>
#include <afxdlgs.h>
#include "main.h"
CMyApp myApp;
BOOL CMyApp::InitInstance()
{
Enable3dControls();
m_pMainWnd=new CMainWindow;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
BEGIN_MESSAGE_MAP(CMainWindow,CFrameWnd)
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
,));
}
void CMainWindow::OnLButtonUp(UINT nFlage,CPoint point)
{
CDC dc;
CPrintDialog dlg(FALSE);
if(dlg.DoModal() == IDOK)
dc.Attach(dlg.GetPrinterDC());
DOCINFO di;
::ZeroMemory(&di,sizeof(DOCINFO));
di.cbSize = sizeof(DOCINFO);
dc.StartDoc(&di);
dc.StartPage();
//各种CDC输出函数
dc.EndPage();
dc.EndDoc();
}
以上代码中的ZeroMemory函数是SDK函数,但是却不需要使用include命令来包含SDK下的任何h头文件。这是Visual C++已经默认自带并识别大量常用SDK函数了。这是一个不错的功能。
我自己并没有实体打印机做测试,所以只能用1行//各种CDC输出函数省略具体的输出函数。实际上,在StartPage()函数和EndPage()函数期间,并不会真正在打印机有任何输出,必须等到调用EndPage()函数才会把一页内容输出到打印机上,即使使用虚拟打印机或者“打印到文件”功能也是如此。
除以上功能外,MFC还在CView类里面提供9个函数来简化并规范化打印(预览)功能。其中5个函数可以参考《MFC Windows程序设计》的第2版(修订版)里面的第685页的表13-2的解释。剩余4个函数含义大致如下:
函数 | 说明 |
CView::OnFilePrint | 打印 |
CView::OnFilePrintPreview | 打印预览 |
CView::OnDraw | 纯虚拟函数。用于具体绘图 |
CView::OnEndPrintPreview | 结束打印预览 |
OnFilePrint函数已经被MFC映射到ID_FILE_PRINT的命令ID了。该函数不是虚拟函数,可以直接运行。
CView::OnFilePrintPreview函数已经被MFC映射到ID_FILE_PRINT_PREVIEW的命令ID了。该函数不是虚拟函数,可以直接运行。
ON_COMMAND(ID_FILE_PRINT,CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW,CView::OnFilePrintPreview)
《MFC Windows程序设计》的第2版(修订版)里面的第692页已经提供一个含有CView打印的例子,这里便不再给出。
《MFC Windows程序设计》的第2版(修订版)里面的第684页有“打印假脱机”。这里的“脱机”其实是指“后台打印技术”。