VS2017 MFC对话框程序打印及打印预览的实现

花了一个星期,研究了网上大量的MFC对话框打印及打印预览功能的demo之后,网上现有的版本都比较旧,所以选中了几个版本,合并修改,得到这个比较完美的最新版本,编译无错,成功运行。
MFC文档/视图的应用程序,向导给了打印及打印预览的标准支持。使这类应用程序打印及打印预览工作得以简化。另一类对话框程序却没有相应支持,从MFC打印及打印预览的标准支持入手,可以在对话框程序中,增加三个类以支持打印及打印预览,本文介绍了这三个类的实现。
打印及打印预览是编写应用程序经常要解决的问题,为了理解VC++对话框程序的打印及打印预览实现,要先掌握基于文档/视图的应用程序打印及打印预览的基本原理。所以分为两部分介绍。
一、基于文档/视图的应用程序的打印及打印预览原理
VC++基于文档/视图的应用程序中用MFC应用程序向导在步骤4对话框中选中Print and Print Preview选项,可以包含基本打印及打印预览的支持,应用程序文件菜单中会生成两个菜单项分别是打印(标识符ID_FILE_PRINT)和打印预览(标识符:ID_FILE_PRINT_PREVIEW),展开程序源代码,可以发现,是Cview类提供标准打印和打印预览菜单命令的消息处理函数:
设应用程序视图类为CMyView,展开MyView.cpp,其消息映象部分有如下两行:

ON_COMMAND(ID_FILE_PRINT,CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW,CView::OnFilePrintPreview)

CView::OnFilePrint 和CView::OnFilePrintPreview函数都进行打印操作,但View::OnFilePrint将实际发送到打印机,而CView::OnFilePrintPreview则将输出发送到程序窗口上方显示的预览窗口显示一个或两个打印页面的复制外观。利用上面加入的缺省打印支持,只能打印或预览图形的一个打印页面,一个页中放不下的部分则放弃,为了加强程序,使它打印整个图形,一页中放不下的部分放在另一页,可以通过覆盖几个打印期间调用的虚拟函数来完成。下图说明了整个打印和打印预览的过程,显示了每个虚拟函数在程序中哪个部分调用。注意每打印一个页面就要经历图中的一个循环。

二、对话框程序打印及打印预览的实现
以上是基于文档/视图的应用程序的打印原理,可以根据需要从CView类派生出视图类覆盖打印及打印预览过程中的CView类的虚拟函数来定制文档/视图应用程序的打印及打印预览。在实际中,有很多基于对话框的应用程序,也需要提供打印及打印预览。但向导没有给基于对话框应用程序的基本支持。有了以上知识,可以构造出无文档的视图类,具体的实现时,增加三个类,用以支持打印及打印预览。以下用一具体实例说明。

函数名 覆盖函数可能完成的任务
CView::OnPreparePrinting() 调用CprintInfo成员函数(如CprintInfo::SetMaxPage设置文档长度)或设置CprintInfo数据成员以影响Print对话框或打印预览操作,然后调用DoPreparePrinting生成用于打印或打印预览的设备描述表(注意必须覆盖OnPreparePrinting并调用DoPreparePrinting)
CView::OnBeginPrinting() 分配专门用于打印的字体,画笔、画刷和其它对象,根据设备描述表计算并设置文档长,在设备描述表上存放所需的消息(这是第一个访问设备描述表的的虚拟函数)
CView::OnPrepareDC() 设置打印当前页面的文本或图形属性,修改视图原点,以打印当前页面,如果没有设置文档长度,在文档末尾终止打印循环(CprintInfo::m_bContinuePrinting赋值FALSE)
CView::OnPrint() 调用OnDraw进行输出;调用OnDraw前选择OnBeginPrinting分配的字体,调用OnDraw后取消对象,打印只出现在文档打印版中的页头和页脚,如果打印输出与屏幕输出的外观不同, 在这里打印,而不是调用OnDraw
CView::OnEndPrinting() 调用Cgdi::DeleteObject删除OnBeginPrinting分配的对象

1.用MFC应用向导创建对话框应用程序,设主对话框类为CPrintPreviewDlg,在主对话框上放一按钮,(标题:打印预览,ID:IDC_PRINTPREVIEW_BUTTON),用类向导增加其BN_CLICKED的消息响应函数OnPrintPreviewButton生成打印预览界面

void CPrintPreviewDlg::OnPrintPreviewButton()
{CPrintFrame* pf = new CPrintFrame(this);
}

2、增加新类:
增加新类步骤:项目->增加新项->MFC->MFC类
用ClassWizard新建CPrintFrame类(基类CFrameWnd),功能上相当于文档/视图的应用程序的框架窗口类。
用ClassWizard新建CPrintView类(基类CScrollView),功能上相当于文档/视图的应用程序的视图类。
增加CPrintPreviewView类(基类CPreviewView,注意在头其定义头文件中加入包含afxpriv.h),用于打印预览界面的视图类。(如果没有基类CPreviewView,只有CPreviewViewEx基类,手动将CPreviewViewEx类改成基类CPreviewView)

3、对新生成的各类修改如下:
CPrintFrame类:

CPrintFrame.h:
#define WM_BEGIN_PRINTING (WM_USER+1004)

1、增加公有数据成员

CPrintPreviewDlg*    m_pOldWnd;        // 用于保存主对话框对象;
CPrintView*              m_pView;              // 用于保存视图类对象;

2、重载构造函数,保存对主对话框对象指针,创建窗口
CPrintFrame.h:

CPrintFrame();           // 动态创建所使用的受保护的构造函数
CPrintFrame(CPrintPreviewDlg* pOld);
virtual ~CPrintFrame();

CPrintFrame.cpp:


CPrintFrame::CPrintFrame(CPrintPreviewDlg* pOld)
{m_pOldWnd = pOld;if ( !Create(NULL, "打印预览", WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, CRect(200, 200, 500, 500)))TRACE0("Failed to create view window! \n");
}

3、修改析构函数,让主对话框显示

CPrintFrame::~CPrintFrame()
{m_pOldWnd->ShowWindow(SW_SHOW);
}

4、添加打印模式函数,设置A4纸纵向打印或横向打印(可以选择使用)

#define   DMORIENT_PORTRAIT                           1       //纵向
#define   DMORIENT_LANDSCAPE                        2       //横向void SetLandscapeMode(int   PrintMode)
{   PRINTDLG   pd;   pd.lStructSize=(DWORD)sizeof(PRINTDLG);   BOOL   bRet=AfxGetApp()->GetPrinterDeviceDefaults(&pd);   if(bRet)   {   //   protect   memory   handle   with   ::GlobalLock   and   ::GlobalUnlock  DEVMODE   FAR   *pDevMode=(DEVMODE   FAR   *)::GlobalLock(pd.hDevMode);pDevMode->dmPaperSize=DMPAPER_A4;   //将打印纸设置为A4   //   set   orientation   to   landscape   if(PrintMode==1)     //纵向打印   pDevMode->dmOrientation = DMORIENT_PORTRAIT;   else   if(PrintMode==2)    //横向打印   pDevMode->dmOrientation = DMORIENT_LANDSCAPE;       ::GlobalUnlock(pd.hDevMode);   }
}

5、用类向导增加WM_Create消息处理函数(关联CPrintView视图对象;调用其OnFilePrintPreview函数进行打印预览(若要直接打印,可直接向其发送消息);隐藏主对话框。)

int CPrintFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{if (CFrameWnd::OnCreate(lpCreateStruct) == -1)return -1;// TODO:  在此添加您专用的创建代码CCreateContext context;context.m_pNewViewClass = RUNTIME_CLASS(CPrintView);context.m_pCurrentFrame = this;context.m_pCurrentDoc = NULL;context.m_pLastView = NULL;m_pView = STATIC_DOWNCAST(CPrintView, CreateView(&context));if (m_pView != NULL){m_pView->ShowWindow(SW_SHOW);SetActiveView(m_pView);// SetLandscapeMode(DMORIENT_LANDSCAPE);}SetIcon(m_pOldWnd->GetIcon(FALSE), FALSE);SetIcon(m_pOldWnd->GetIcon(TRUE), TRUE);ShowWindow(SW_RESTORE);CWinApp *pApp = AfxGetApp();pApp->m_pMainWnd = this;m_pView->OnFilePrintPreview();//打印预览m_pView->SendMessageW(WM_COMMAND, ID_FILE_PRINT);  // 直接打印m_pOldWnd->ShowWindow(SW_SHOW);return 0;
}

6、添加WM_Close消息处理函数`

void CPrintFrame::OnClose()
{// TODO: Add your message handler code here and/or call defaultCPrintFrame* pf=(CPrintFrame*)::AfxGetMainWnd(); CWinApp *pApp=AfxGetApp();pApp->m_pMainWnd = pf->m_pOldWnd; pf->DestroyWindow();// CFrameWnd::OnClose();
}

7、添加WM_DESTROY消息处理函数

{if (m_pView != NULL)m_pView->DestroyWindow();CFrameWnd::OnDestroy();CFrameWnd::OnDestroy();
}

CPrintView类:

①修改构造函数:将坐标射模式置为缺省模式。

CPrintView::CPrintView(){m_nMapMode = MM_TEXT;
}

②增加消息映射实现打印。

BEGIN_MESSAGE_MAP(CPrintView, CScrollView)ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
END_MESSAGE_MAP()

③重载虚函数OnPreparePrinting,调用DoPreparePrinting生成用于打印或打印预览的设备描述表。

BOOL CPrintView::OnPreparePrinting(CPrintInfo* pInfo)
{return DoPreparePrinting(pInfo);
}

④增加公有函数OnFilePrintPreview,调用DoPrintPreview实现打印预览,该函数需要传入四个参数:打印预览工具条资源ID,执行打印及打印预览的视图对象指针,打印预览界面视图类的 CRuntimeClass指针,打印预览状态类CPrintPreviewState对象指针。

void CPrintView::OnFilePrintPreview()
{// TODO: 在此处添加实现代码.CPrintPreviewState* pState = new CPrintPreviewState;pState->lpfnCloseProc = _AfxPrintPreviewCloseProc; //设置打印预览窗口关闭时的调用函数if (!DoPrintPreview(AFX_IDD_PREVIEW_TOOLBAR, this, RUNTIME_CLASS(CPrintPreviewView), pState)){TRACE0("Error, DoPrintPreview failed. \n");AfxMessageBox(AFX_IDP_COMMAND_FAILURE);delete pState;}}

⑤一些其他函数的实现

void CPrintView::OnInitialUpdate()
{CScrollView::OnInitialUpdate();CSize sizeTotal;// TODO: 计算此视图的合计大小sizeTotal.cx = sizeTotal.cy = 100;SetScrollSizes(MM_TEXT, sizeTotal);}void CPrintView::OnDraw(CDC* pDC)
{CDocument* pDoc = GetDocument();
}void CPrintView::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo)
{// TODO: 在此添加专用代码和/或调用基类CView::OnBeginPrinting(pDC, pInfo);CPrintFrame *pFrame = (CPrintFrame *)GetParent();pFrame->m_pOldWnd->SendMessage(WM_BEGIN_PRINTING, (WPARAM)pDC, (LPARAM)pInfo);
}void CPrintView::OnEndPrinting(CDC* pDC, CPrintInfo* pInfo)
{// TODO: 在此添加专用代码和/或调用基类CScrollView::OnEndPrinting(pDC, pInfo);if (m_fontPrinter.m_hObject != NULL)m_fontPrinter.DeleteObject();return;
}

⑥在PrintView.cpp文件中增加全局函数,_AfxMyPreviewCloseProc,当单击打印预览窗口关闭按钮时被调用。

BOOL CPrintView::_AfxPrintPreviewCloseProc(CFrameWnd * pFrameWnd)
{ASSERT_VALID(pFrameWnd);CPrintPreviewView* pView = (CPrintPreviewView*)pFrameWnd->GetDlgItem(AFX_IDW_PANE_FIRST);ASSERT_KINDOF(CPreviewView, pView);pView->OnPreviewClose();return FALSE;
}

7、增加公有函数OnPrint(CDC* pDC, CPrintInfo* pInfo),添加自己需要打印的文字和图片。
CPrintView.h:

CDC* m_dc;int m_page_v_margin;   //纵向边距int m_page_h_margin;  //横向边距int m_page_height;        //页高,包括marginint m_page_width;      //页宽,包括marginint m_current_y_pos;   //纵向当前打印到哪个位置了,如果到了页底部要调用EndPage重新开一页int m_line_height;      //行高,包括字体高度和行间距int m_v_dist;         //行间距BOOL m_is_printing_page;//正在打印页BOOL m_mission_started; //打印任务已经开始BOOL m_need_start_new_page;//需要开始新的一页int m_max_page;          //总页码int m_page_number;     //当前页码int m_total_line_number; //要打印的内容的总行数,不包括页脚页眉,只包括数据行int m_max_line_count_one_page;CString m_doc_name;       //页眉文字

CPrintView.cpp:

void CPrintView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{// TODO: 在此添加专用代码和/或调用基类CScrollView::OnPrint(pDC, pInfo);m_dc = pDC;m_page_v_margin = 0;m_page_h_margin = 0;m_page_height = 0;m_current_y_pos = 0;m_line_height = 0;m_is_printing_page = FALSE;m_mission_started = FALSE;m_page_number = 0;m_max_page = 0;m_total_line_number = 0;m_v_dist = 2;m_max_line_count_one_page = 0;m_doc_name = AfxGetAppName();m_need_start_new_page = TRUE;TEXTMETRIC tm;m_dc->GetTextMetrics(&tm);m_line_height = tm.tmHeight + tm.tmInternalLeading;m_page_v_margin = m_dc->GetDeviceCaps(LOGPIXELSY) / 2;m_page_h_margin = m_dc->GetDeviceCaps(LOGPIXELSX) / 2;m_page_height = m_dc->GetDeviceCaps(VERTRES);m_page_width = m_dc->GetDeviceCaps(HORZRES);m_current_y_pos = m_page_v_margin;//求出页数m_max_line_count_one_page = (int)((m_page_height - m_page_v_margin * 2 - 50) / (m_line_height + 50));m_max_page = (int)(m_total_line_number / m_max_line_count_one_page);if (m_max_page*m_max_line_count_one_page < m_total_line_number) m_max_page++;//UINT gl_uNumOfPoints = 52;//CDC* pDC   = (CDC*)wParam;//CPrintInfo* pInfo = (CPrintInfo *)lParam;int nPageNumber = pInfo->m_nCurPage;int i, j;CFont *pOldFont;CFont DataFont;DataFont.CreatePointFont(120, _T("宋体"), m_dc);pOldFont = m_dc->SelectObject(&DataFont);if (1){TCHAR *pszTitle[200] = { _T("编号"),_T("数值1"),_T("数值2"),_T("数值3"),_T("数值4"),_T("数值5"),_T("数值6") };pOldFont = m_dc->SelectObject(&DataFont);//画矩形m_dc->Rectangle(m_page_h_margin, m_page_v_margin, m_page_width - m_page_h_margin, m_page_height - m_page_v_margin);//bottomfor (int i = 0; i<7; i++){m_dc->TextOut(m_page_h_margin + 50 + i * (m_page_width - m_page_h_margin * 2) / 7,4 * m_line_height,CString(pszTitle[i]));}m_dc->SelectObject(pOldFont);}
}

CPrintPreviewView类:

手动修改添加类CPrintPreviewView继承CPreviewView类

CPrintPreviewView.h:

#pragma once
#include "afxpriv.h"
#if !defined(AFX_MYPREVIEWVIEW_H__0AE8B670_B1AE_11DA_812E_00E04C39032F__INCLUDED_)#define AFX_MYPREVIEWVIEW_H__0AE8B670_B1AE_11DA_812E_00E04C39032F__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <afxpriv.h>
// CPrintPreviewView 视图class CPrintPreviewView : public CPreviewView
{DECLARE_DYNCREATE(CPrintPreviewView)protected:CPrintPreviewView();           // 动态创建所使用的受保护的构造函数virtual ~CPrintPreviewView();
public:
#ifdef _DEBUGvirtual void AssertValid() const;
#ifndef _WIN32_WCEvirtual void Dump(CDumpContext& dc) const;
#endif
#endif
public:afx_msg void OnPreviewClose();
protected:afx_msg void OnPreviewPrint();DECLARE_MESSAGE_MAP()virtual void OnDraw(CDC* pDC);virtual void OnEndPrintPreview(CDC* pDC, CPrintInfo* pInfo, POINT point, CPreviewView* pView);
};

增加工具栏按钮的消息响应函数OnPreviewClose(),OnPreviewPrint() .cpp如下详尽代码

#include "stdafx.h"
#include "PrintPreview.h"
#include "CPrintPreviewView.h"
#include "CPrintFrame.h"// CPrintPreviewViewIMPLEMENT_DYNCREATE(CPrintPreviewView, CPreviewView)CPrintPreviewView::CPrintPreviewView()
{}CPrintPreviewView::~CPrintPreviewView()
{
}BEGIN_MESSAGE_MAP(CPrintPreviewView, CPreviewView)ON_COMMAND(AFX_ID_PREVIEW_CLOSE, OnPreviewClose)ON_COMMAND(AFX_ID_PREVIEW_PRINT, OnPreviewPrint)
END_MESSAGE_MAP()// CPrintPreviewView 诊断#ifdef _DEBUG
void CPrintPreviewView::AssertValid() const
{CPreviewView::AssertValid();
}#ifndef _WIN32_WCE
void CPrintPreviewView::Dump(CDumpContext& dc) const
{CPreviewView::Dump(dc);
}
void CPrintPreviewView::OnPreviewClose()
{CPrintFrame* pf = (CPrintFrame*)::AfxGetMainWnd();CWinApp *pApp = AfxGetApp();pApp->m_pMainWnd = pf->m_pOldWnd;pf->DestroyWindow();
}
void CPrintPreviewView::OnPreviewPrint()
{m_pPrintView->SendMessage(WM_COMMAND, ID_FILE_PRINT);
}
#endif
#endif //_DEBUG// CPrintPreviewView 消息处理程序void CPrintPreviewView::OnDraw(CDC* pDC)
{// TODO: 在此添加专用代码和/或调用基类CPreviewView::OnDraw(pDC);m_pToolBar->PostMessage(WM_IDLEUPDATECMDUI, (WPARAM)TRUE);// 控制条的命令状态更新
}void CPrintPreviewView::OnEndPrintPreview(CDC* pDC, CPrintInfo* pInfo, POINT point, CPreviewView* pView)
{// TODO: 在此添加专用代码和/或调用基类CPreviewView::OnEndPrintPreview(pDC, pInfo, point, pView);
}

最终结果图;



程序链接:https://download.csdn.net/download/weixin_42403113/11069726

VS2017 MFC对话框程序打印及打印预览的实现相关推荐

  1. 使用VC++6.0创建MFC对话框程序

    使用VC++6.0创建MFC对话框程序

  2. Lodop中特殊符号¥打印设计和预览不同

    Lodop中¥符号样式改变问题 Lodop中对超文本样式的解析,虽然说是按照调用的本机ie引擎,但是调用的ie版本可能不同,导致在ie下是一种样式,预览又是另一种样式.可能是有些样式没有具体设置,走的 ...

  3. VS2010/MFC对话框程序调用Windows Media Player播放器控件

    MFC对话框程序调用Windows Media Player播放器控件播放打开的avi格式的文件,具体步骤如下: 1.根据MFC向导提示,创建一个默认的对话框项目TestMediaPlayer. 2. ...

  4. 用友T6软件设置打印模板后预览没有变化-用友T6

    设置打印模板后预览没有变化 原因分析:操作员在进行打印预览时如果在预览界面选择"保存当前设置",此时会在C盘下生成一个XML格式的文件,无论如何修改单据打印模板,预览调取的都是保存 ...

  5. 自定义模板、自定义打印机、自动打印:grid++ report自动打印、clodop预览和自动打印(electron+vue)

    一.grid++ report选择模板预览和自动打印 这里主要是前后端配合,数据前端传入,打印后端配置. IGridppReport 成员: 封装立即打印,数据格式为: {"reportDa ...

  6. 小程序实现图片放大预览功能

    uniapp 微信 小程序实现图片放大预览功能 实现效果图如下 主要实现代码 <view class="text_img"><image :src="i ...

  7. 微信小程序图片全屏预览组件,并解决svg真机显示黑屏问题

    自己码了个微信小程序图片全屏预览组件,并解决了svg真机显示黑屏问题(即png等格式可以正常显示,但就svg无法显示). /*** 微信小程序图片预览组件* 1.支持预览svg.png.jpeg.jp ...

  8. MFC对话框使用CPrintDialog实现打印,指定打印机、后台打印

    对话框打印,网上一搜一大堆,基本分2类: A类: CPrintDialog.DoModal,然后在模态对话框里选打印机.打印配置: B类:GetPrinterDeviceDefaults,调用默认打印 ...

  9. IE调用WebBrowser控件实现WEB打印、分页打印、无预览打印

    WebBrowser控件 WebBrowser ActiveX 控件提供托管的包装. 托管的包装便可以在 Windows 窗体客户端应用程序中显示网页. 你可以使用WebBrowser控件重复中你的应 ...

最新文章

  1. apache mediawiki 安装_MediaWiki系统的安装、配置和修改方法(转载)
  2. java中io流实现哪个接口_第55节:Java当中的IO流-时间api(下)-上
  3. php程序员应该懂的几个基本概念
  4. 代码审计之Catfish CMS v4.5.7后台作者权限越权两枚+存储型XSS一枚
  5. php项目数据库控制器代码_如何为大型代码库组织Express控制器
  6. ATT全球网络运营中心GNOC
  7. 【matlab】三维曲面
  8. 实践实践在实践,才能牛逼得一逼。Python
  9. paip.判断文件是否存在uapi python php java c#
  10. 七上八下猜数字_猜数字游戏,猜别人心里想的数字
  11. Word如何插入分数
  12. ubuntu查看隐藏文件及显示隐藏文件所占用存储空间
  13. CCC与Android交互的注意点
  14. python解决买鸡问题:3文钱可以买1只公鸡,2文钱可以买一只母鸡,1文钱可以买3只小鸡。用100 文 钱买100 只鸡,那么各有公鸡、母鸡、小鸡多少只?
  15. 谈谈像素以及微信小程序的 rpx
  16. 复旦情商课魅力女教师上课实录
  17. RewriteCond %{REQUEST_URI}
  18. 团购网站的现状和未来
  19. Javaweb核心之servlet规范过滤器-----Filter
  20. MSXML一般使用方法

热门文章

  1. 网络安全入门到精通(总结篇) 最终篇(上)
  2. Alice In Wonderland notes
  3. The 10 Best Neighborhoods in Seattle
  4. Estimator::relativePose
  5. MATLAB-三维柱状图
  6. java tcp门禁_门禁控制器的TCP/IP协议功能
  7. 一级计算机无法正常评分,计算机一级评分标准
  8. 7.5ElGamal算法
  9. (2020.12.7)初次web前端性能优化记录
  10. JavaScript(ES5)