下载本文示例代码
在数据库应用中,一般把纸式报表的打印输出放在比较重要的位置,特别是在银行、保险等金融部门,打印出来的纸式硬拷贝一般都要永久保存,因此程序的打印输出结果是否美观、输出方式是否灵活是衡量程序优劣的重要标志。比起其他编程语言来说,VC编程中的打印控制比较复杂,但它也给程序员最大的灵活性,而这种灵活性正是我们需要的,因为金融部门的报表一般都不太规整,特别是表头部分,二、三重嵌套的情况很常见。下面我们就开发中碰到的一些问题与同行们探讨。
一、 Document/View框架之外的打印
熟悉VC的程序员都知道Microsoft的AppWizard生成的应用程序框架中,可以选择免费的打印及打印预览功能,但是前提是必须选择Document/View结构,然而我们 廯 应用中一般都不需要一个文档类来保存文档,因为数据库(源)就是我们的文档,数据一般是在一个对话框或视中与用户交互,编辑或修改结果直接通过数据库引擎写回数据库中。我们的程序主框架要么是基于对话框的、要么是基于无文档类的单视(或多视)结构,在这种情况下,AppWizard在打印控制部分并不能给我们任何帮助,只能自己负责完成打印控制。
分析MFC提供的打印功能,在一个视中实现定制的打印,需要分别重载视的以下函数:
OnPreparePrinting():在开始打印文档之前,定制或初始化打印对话框; OnPrepareDC():准备设备场景,分别打印或预览准备PrintDC和ScreenDC; OnBeginPrinting():开始一次新的打印作业,并分配图形设备接口; OnEndPrinting(),OnEndPrintPreview():终止打印作业,释放图形设备接口:
OnPrint():完成打印或预览每页文档的输出。 总结起来:一次打印操作要遵循以下步骤:
1.得到或生成打印设备场景,可通过显示打印对话框让用户选择打印机与纸张等设置,也可在程序中直接取系统缺省打印机设置,然后根据报表格式设置纸张大小和打印方向;
2.开始在该设备场景中的一次打印作业,实际打印报表内容,终止打印作业;
3.清除打印设备场景,完成本次打印操作。
在下面的例子中,我们在一个对话框中让用户选择打印某个报表,没有显示打印设置对话框,而是直接取系统缺省打印机设置,然后根据报表格式设置纸张大小和打印方向,之所以这样做是因为金融系统的报表格式一般都是至上而下的统一格式。函数DoPreparePrintDC(),DoPrint()和DoC1earPrintDC()分别对应上述的三个步骤:
BOOL CMyDialog::DoPreparePrintDC() //准备打印场景 { #define FONTSIZE 14 //获取打印机的设备属性 CPrintDialog dlgPrint(FALSE); //得到当前系统缺省打印机设置 if(!dlgprint.GetDefau1ts() )return FALSE; LPDEVMODE pDM=dlgPrint.GetDevMode(); if (pDM==NULL)return FALSE; //调整打印方向 //pDM->dm0rientation=DMORIENT_LANDSCAPE(横向)‖ DMORIENT_PORTRAIT(纵向); //调整纸张大小 //制式纸张:pDM->dmPaperSize=DMPAPER_A3等; //或自定义大小:单位:1/10毫米 //pDM->dmPaperSize=DMPAPER_USER; //pDM->dmPaperLength=纸张高度; //pDM->dmPaperWidth=纸张宽度; //解锁由CPrintDialog::GetDevMode()封锁的内存 ::GlobalUn1ock(pDM); //联结打印DC,m_hDC是定义为HDC m_hDC的类成员变量 m_hDC=dlgPrint.CreatePrinterDC(); //m_DC是定义为CDC m_DC的类成员变量 if (!m_DC.Attach(m_hDC)return FALSE; //设置打印廯 标志 m_DC.m_bPrinting=TRUE; //根据打印机分辨率建立打印字体 //得到每英寸点数 short cxInch=m_DC.GetDeviceCaps(LOGPIXELSX); short cyInch=m_DC.GetDeviceCaps(LOGPIXELSY); //建立打印字体,m_fontPrint是定义为CFont m_fontPrint的类成员变量 if (!m_fontPrint.CreateFont(MulDiv(FONTSIZE,-cyInch,72), 0,0,0,FW_NORMAL,0,0,0, GB2312_CHARSET,OUT_CHARACTER_PRECIS,CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY,DEFAULT_PITCH|FF—DONTCARE,HFONTNAME) return FALSE; return TRUE; } void CDlgDataPrint::DoClearPrintDC()//清除打印场景 { m_fontPrint,Delete0bject(); m_DC.Detach(); ::DeleteDC( m_hDC); } void CDlgDataPrint::DoPrint() //实际打印输出 { //准备打印场景,横纵向居中 if (DoPreparePrintDC()==FALSE)return; //开始一次打印作业 CString str; str.LoadString(AFX_IDS_APP_TITLE); DOCINFO di; ::ZeroMemory(&di,sizeof (DOCINFO)); di.cbSize=sizeof (DOCINFO); di.lpszDocName=str; m_DC.StartDoc(&di); m_DC.StartPage(); //将打印字体选进设备场景 CFont* p0ldFont=m_DC.Select0bject(&m_fontPrint); //输出报表,建议用CDC::DrawText()函数便于控制打印范围…… //终止打印作业 m_DC,EndPage(); m_DC.EndDoc(); m_DC.Select0bject(p0ldFont); //清除打印场景 DoClearPrintDC(); }
二、 表格问题
人们常用的报表格式几乎都是封闭式的,要实现这种报表不外乎两种方法:一是在进行打印输出时直接绘制横线和竖线,这种方法适用于报表格式不固定或格式较简单的情况,对那种格式固定或表头和表体比较复杂的情况,特别是有嵌套表格的情况下,我们认为先将打印结果以文本形式写入一个临时文件,然后再对文件进行打印输出的方法要使事情简单一些,因为这样的程序就可以只关心数据(表体)的输出,而表头则可以固定格式存入数据库。但是,我们在对文本形式的报表进行打印输出时却发现一个有趣的现象,有时输出结果的相邻两行表格竖线不能对齐,而有时又可以。原来是Windows图形输出的结果,只要我们在建立打印字体时,字体的高度是偶数就可保证竖线对齐,如果是奇数则不能对齐。知道了原因,在建立打印字体时只需:
…… #define FONTSIZE 14 //建立打印字体,m_fontPrint是定义为CFont m_fontPrint的类成员变量 int nFontHeight=MulDiv(FONTSIZE,-cyInch,72); if (nFontHeight % 2)nFontHeight++; if (!m_fontPrint.CreateFont(nFontHeight,0,0,0, FW_NORMAL,0,0,0, GB2312_CHARSET,OUT_CHARACTER_PRECIS,CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY,DEFAULT_PITCH|FF_DONTCARE,HFONTNAME) return FALSE; ……
还有一点小技巧,输出文本形式的报表时,对报表的横线只需要输出其正常高度的二分之一或三分之一,这样报表看起来更紧凑美观。这也是我们在前面建议用CDC::DrawText()进行绘制输出的原因。
在数据库应用中,一般把纸式报表的打印输出放在比较重要的位置,特别是在银行、保险等金融部门,打印出来的纸式硬拷贝一般都要永久保存,因此程序的打印输出结果是否美观、输出方式是否灵活是衡量程序优劣的重要标志。比起其他编程语言来说,VC编程中的打印控制比较复杂,但它也给程序员最大的灵活性,而这种灵活性正是我们需要的,因为金融部门的报表一般都不太规整,特别是表头部分,二、三重嵌套的情况很常见。下面我们就开发中碰到的一些问题与同行们探讨。
一、 Document/View框架之外的打印
熟悉VC的程序员都知道Microsoft的AppWizard生成的应用程序框架中,可以选择免费的打印及打印预览功能,但是前提是必须选择Document/View结构,然而我们 廯 应用中一般都不需要一个文档类来保存文档,因为数据库(源)就是我们的文档,数据一般是在一个对话框或视中与用户交互,编辑或修改结果直接通过数据库引擎写回数据库中。我们的程序主框架要么是基于对话框的、要么是基于无文档类的单视(或多视)结构,在这种情况下,AppWizard在打印控制部分并不能给我们任何帮助,只能自己负责完成打印控制。
分析MFC提供的打印功能,在一个视中实现定制的打印,需要分别重载视的以下函数:
OnPreparePrinting():在开始打印文档之前,定制或初始化打印对话框; OnPrepareDC():准备设备场景,分别打印或预览准备PrintDC和ScreenDC; OnBeginPrinting():开始一次新的打印作业,并分配图形设备接口; OnEndPrinting(),OnEndPrintPreview():终止打印作业,释放图形设备接口:
OnPrint():完成打印或预览每页文档的输出。 总结起来:一次打印操作要遵循以下步骤:
1.得到或生成打印设备场景,可通过显示打印对话框让用户选择打印机与纸张等设置,也可在程序中直接取系统缺省打印机设置,然后根据报表格式设置纸张大小和打印方向;
2.开始在该设备场景中的一次打印作业,实际打印报表内容,终止打印作业;
3.清除打印设备场景,完成本次打印操作。
在下面的例子中,我们在一个对话框中让用户选择打印某个报表,没有显示打印设置对话框,而是直接取系统缺省打印机设置,然后根据报表格式设置纸张大小和打印方向,之所以这样做是因为金融系统的报表格式一般都是至上而下的统一格式。函数DoPreparePrintDC(),DoPrint()和DoC1earPrintDC()分别对应上述的三个步骤:
BOOL CMyDialog::DoPreparePrintDC() //准备打印场景 { #define FONTSIZE 14 //获取打印机的设备属性 CPrintDialog dlgPrint(FALSE); //得到当前系统缺省打印机设置 if(!dlgprint.GetDefau1ts() )return FALSE; LPDEVMODE pDM=dlgPrint.GetDevMode(); if (pDM==NULL)return FALSE; //调整打印方向 //pDM->dm0rientation=DMORIENT_LANDSCAPE(横向)‖ DMORIENT_PORTRAIT(纵向); //调整纸张大小 //制式纸张:pDM->dmPaperSize=DMPAPER_A3等; //或自定义大小:单位:1/10毫米 //pDM->dmPaperSize=DMPAPER_USER; //pDM->dmPaperLength=纸张高度; //pDM->dmPaperWidth=纸张宽度; //解锁由CPrintDialog::GetDevMode()封锁的内存 ::GlobalUn1ock(pDM); //联结打印DC,m_hDC是定义为HDC m_hDC的类成员变量 m_hDC=dlgPrint.CreatePrinterDC(); //m_DC是定义为CDC m_DC的类成员变量 if (!m_DC.Attach(m_hDC)return FALSE; //设置打印廯 标志 m_DC.m_bPrinting=TRUE; //根据打印机分辨率建立打印字体 //得到每英寸点数 short cxInch=m_DC.GetDeviceCaps(LOGPIXELSX); short cyInch=m_DC.GetDeviceCaps(LOGPIXELSY); //建立打印字体,m_fontPrint是定义为CFont m_fontPrint的类成员变量 if (!m_fontPrint.CreateFont(MulDiv(FONTSIZE,-cyInch,72), 0,0,0,FW_NORMAL,0,0,0, GB2312_CHARSET,OUT_CHARACTER_PRECIS,CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY,DEFAULT_PITCH|FF—DONTCARE,HFONTNAME) return FALSE; return TRUE; } void CDlgDataPrint::DoClearPrintDC()//清除打印场景 { m_fontPrint,Delete0bject(); m_DC.Detach(); ::DeleteDC( m_hDC); } void CDlgDataPrint::DoPrint() //实际打印输出 { //准备打印场景,横纵向居中 if (DoPreparePrintDC()==FALSE)return; //开始一次打印作业 CString str; str.LoadString(AFX_IDS_APP_TITLE); DOCINFO di; ::ZeroMemory(&di,sizeof (DOCINFO)); di.cbSize=sizeof (DOCINFO); di.lpszDocName=str; m_DC.StartDoc(&di); m_DC.StartPage(); //将打印字体选进设备场景 CFont* p0ldFont=m_DC.Select0bject(&m_fontPrint); //输出报表,建议用CDC::DrawText()函数便于控制打印范围…… //终止打印作业 m_DC,EndPage(); m_DC.EndDoc(); m_DC.Select0bject(p0ldFont); //清除打印场景 DoClearPrintDC(); }
二、 表格问题
人们常用的报表格式几乎都是封闭式的,要实现这种报表不外乎两种方法:一是在进行打印输出时直接绘制横线和竖线,这种方法适用于报表格式不固定或格式较简单的情况,对那种格式固定或表头和表体比较复杂的情况,特别是有嵌套表格的情况下,我们认为先将打印结果以文本形式写入一个临时文件,然后再对文件进行打印输出的方法要使事情简单一些,因为这样的程序就可以只关心数据(表体)的输出,而表头则可以固定格式存入数据库。但是,我们在对文本形式的报表进行打印输出时却发现一个有趣的现象,有时输出结果的相邻两行表格竖线不能对齐,而有时又可以。原来是Windows图形输出的结果,只要我们在建立打印字体时,字体的高度是偶数就可保证竖线对齐,如果是奇数则不能对齐。知道了原因,在建立打印字体时只需:
…… #define FONTSIZE 14 //建立打印字体,m_fontPrint是定义为CFont m_fontPrint的类成员变量 int nFontHeight=MulDiv(FONTSIZE,-cyInch,72); if (nFontHeight % 2)nFontHeight++; if (!m_fontPrint.CreateFont(nFontHeight,0,0,0, FW_NORMAL,0,0,0, GB2312_CHARSET,OUT_CHARACTER_PRECIS,CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY,DEFAULT_PITCH|FF_DONTCARE,HFONTNAME) return FALSE; ……
还有一点小技巧,输出文本形式的报表时,对报表的横线只需要输出其正常高度的二分之一或三分之一,这样报表看起来更紧凑美观。这也是我们在前面建议用CDC::DrawText()进行绘制输出的原因。
下载本文示例代码
VC数据库编程中的打印控制VC数据库编程中的打印控制VC数据库编程中的打印控制VC数据库编程中的打印控制VC数据库编程中的打印控制VC数据库编程中的打印控制VC数据库编程中的打印控制VC数据库编程中的打印控制VC数据库编程中的打印控制VC数据库编程中的打印控制VC数据库编程中的打印控制VC数据库编程中的打印控制VC数据库编程中的打印控制VC数据库编程中的打印控制VC数据库编程中的打印控制