



全文预览已结束
下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
在 C+ + Builder 6 中建立打印预览类 T P reView杨勇摘 要: 通过应用 Windows 打印相关的 API 函数, 在 C+Builder6 中构建一个打印预览类 TPre-将打印编程进行封装,提供类似于打印类 TPrinter 的接口, 从而简化了 C+Builder6 中的View,报表打印预览编程工作。关键词:打印预览;报表;C+Builder61引言对于数据库应用程序, 报表的打印和预览是必不可少的基 本 功 能 。 C+Builder 6 对打印工作提供了一套专门的控件组 Qreport1, 对常规和比较简单的数据打印基本能够胜任 , 但是实现要点33.1TPreView 的窗体预览需要在窗 口 中 进 行 , 因 此 TPreView 类直接继承于窗 口类 TForm, 图 1 为打印预览的设计期窗口。对于比较复杂的动 态 数 据 报 表 ,虽然可以用动态生成 Qreport控件的方式完成, 但是程序的复杂度大大增加, 失去了使用组件简化编程的初衷。 大量使用控件对系统内存和资源的消耗也 不小。 在这种情况下, 直接用 BCB 提供的打印类 TPrinter 实现 数据的动态打印反而来得简便。 遗憾的是, C+Builder 6 没有 提供与之相配套的预览类, 给打印预览的编程工作带来困难 。 设计一个打印预览类 TPreView, 与 Tprinter 类结合使用, 可以 快速实现报表的预览打印功能。2设计思路TpreView 没有设计成 VCL 组 件 。 如果设计为组件 , 使 用 者在使用之前要安装到 C+Builder6 集成开发环境中 , 增加了 使用难度, 也不便于扩充修改。 因此直接设计为类的形式, 应 用时直接加入 project 中 即 可 调 用 。 使用者还可以根据程序需 要具体修改源码。为 了 让 TPreView 类的使用者快速掌握其用法 , TPreView 类的接口基本上模仿 TPrinter 类2, 打印和打印预览两者完成 功能的主要区别是输出设备不同, 而两个类的工作方法基本一 致, 表 1 列出两个类的主要属性和方法。表 1 Printer 与 TPreView 的主要属性和方法对比TPreView 的 窗 口 主 要 由 ToolBar1 ( 工 具 栏 ) 、 ScrollBox(带垂直滚动条的窗口组件 ) 、 StatusBar ( 状态栏) 3 个组 件 构 成 , 工 具 栏 ToolBar1 上主要有预览页 面 导 航 按 钮 、 打 印 按 钮 显示比例按钮等 。 展示预览图像的图元文 件以平铺的方式置 入一个 TImage 图 像 中, 为实现多页预览, Image 图 像 由 程 序 动态 生 成 ,每个预览页对应一个 Imag e, 然 后 显 示 Image 在ScrollBox 中。3.2基本原理实现预览的基本过程是: 将输入到打印机的图形输入到内 存中以图元文件形式存放, 再把图元文件显示到电脑屏幕上 。预 览 图 形 采 用 Windows 操作系统下的增强型元文件 (EMF) ,元文件与位图(bitmap) 不同, 属于矢量图形, 以图形绘制命令来描述图像, 因此可以对图像进行缩放而不失真, 而图像的任意缩放是一个打印预览类必需的基本功能。 增强型图元文件 的生成和显示涉及以下几个 Windows API 函数3:HDC CreateEnhMetaFile(HDC hdcRef,/ handle to a reference device context类属性或方法TPrinterTPreView输出画板CanvasCanvas打印/预览页面序号PageNumberPageNumber打印/预览工作初始化BeginDoc (void)BeginViewDoc (void)打印/预览输出结束EndDoc (void)EndViewDoc (void)打印/预览换页NewPage (void)NewViewPage (void)件句柄 hdcEMF 代入 ( 文中的图元文件均存于系统内 存 中 ) ,函数返回一个作图已经完成的图元文件句柄 , 并 释 放 被 Cre-ateEnhMetaFile 函数占用的设备描述表句柄。图元文件绘图完毕 ,输出:接 下 来 用 PlayEnhMetaFile 函 数 进 行这是一个生成增强型图元文件的 Windows API 函数, 第一个参数是设备描述表句柄, 这里用 BCB 提供的打印类 TPrinter 获取当前打印机设备描述表句柄 : Printer () -Handle 。 第二 个参数是文件名, 如果该参数是 NULL, 则该函数创建内存元 文件, 仅需要创建内存元文件, 所以设为 NULL, 第三个参数 是一个 RECT 结构的地址, 它指出了以 0.01mm 为单位的元文 件的大小。 函数的返回值为输出设备描述表句柄, 这个输出设 备是一个独立于具体设备的、 抽象的画布, 将返回值赋予一个 画布对象 TCanvas 的句柄, 则在这个画布对象的图形输出, 即 是在创建于内存中的图元文件上作图, 方法如下:第一个是参数输出设备的设备描述表句柄, 输出设备可以是屏幕, 也可以是打印机。 本程序中输出到 Image 组件上, 所 以第一个参数代入 pImage-Canvas-Handle, 第二个参数是图 元文件的句柄, 即 CloseEnhMetaFile 返回的值, 第三个参数是 一个 RECT 结构的指针, 该 RECT 结构指明图元文件的输出区 域 , 用 Image 组件的显示区域 RECT 结 构 地 址 & ( pImage - ClientRect) 代入, 具体调用函数形式为:PlayEnhMetaFile (pImage-Canvas-Handle,hMetaFile, &(pImage-ClientRect) );其 中 , pImage 指 针 指 向 的 Image 组件的父对象指明为 ScrollBox 。 至此, 图元文件正确地显示到预览窗口中。3.3使屏幕上的预览图形与打印机输出图形大小相同要把打印机打出的图像以相同的大小显示在屏幕上, 需要 进行打印机显示点阵与屏幕显示点阵的转换。 无论是打印机还 是屏幕, 都是以像素为图形构成基元。 但是, 同样 1 英寸的长 度, 打印机容纳的像素数与显示屏幕上的像素数是不同的, 一 般要比显示屏幕像素数大很多 。 因此, 要把打印机输出 的 图 形以同样大小显示在屏幕上, 需要按如下公式进行转换:画在内存中的图元文件图形也是即将打印输出的图形, 因此必须保证图元文件大小与打印图形大小相同。 即要满足:图元文件长度当前打印纸张长度 图元文件宽度当前打印纸张宽度在函 数 CreateEnhMetaFile 中 , 图元文件的大小以 0.01mm 为单位, 而打印机输出区域 (即打印纸张) 大小可以由 GetDe- viceCaps 函数获得, GetDeviceCaps 函数形式如下:第一个参数是设备 描 述 表 句 柄 , 用打印机的设备句柄 Prntr-Handle, 第二个参数指 明要获取的信息类别 , 可 以 取 HORZSIZE 和 VERTSIZE 两个常量, 分别表示以 mm 为单位反 回打印区域的宽和高 , 因此, 把 GetDeviceCaps 获得的宽和高 乘以 100, 作为 CreateEnhMetaFile 函数中 RECT 结构参数 met- alFileRect 的宽和高:屏幕分辨率指显示屏上每英寸像素数, 打印机分辨率指打印机每英寸像素数。相应的实现代码如下:PrnPageW=Prntr-PageWidth;PrnPageH=Prntr-PageHeight;float scalefactor=float(Screen-PixelsPerInch)/GetDevice-Caps(Prntr-Handle,LOGPIXELSY); ScrnPageW=float(PrnPageW)*scalefactor;/屏幕页宽度(像素)ScrnPageH=float(PrnPageH)*scalefactor;/屏幕页高度(像素)其 中 , Screen-PixelsPerInch 获取屏幕竖直方向上 每 英 寸在图元文件上的图形文字等输出完成后 , 接 着 调 用CloseEnhMetaFile 函数:像 素 。Screen 是 TScreen 组件的一个实例 , 已 经 由 BCB 在Forms 单元中声明, 程序中直接使用即可。 函数 GetDeviceCaps(Prntr-Handle,LOGPIXELSY) 获取打印机竖直方向上每英寸像参数 hdc 即由前述 CreateEnhMetaFile 函数的返回值图元文HENHMETAFILE CloseEnhMetaFile(HDC hdc);metalFileRect =TRect (0,0,GetDeviceCaps (Prntr -Handle,HORZSIZE)*100,GetDeviceCaps(Prntr-Handle, VERTSIZE)*100);hdcEMF=CreateEnhMetaFile (Printer ()-Handle, NULL,&metalFileRect,NULL);int GetDeviceCaps(HDC hdc,/ device-context handleint nIndex/ index of capability to query);hdcEMF =Cre ateEnhMetaFile (Printer () -Handle, NULL,&metalFileRect,NULL);fCanvas-Handle=hdcEMF;fCanvas-TextOut(); / 在画布上作图BOOL PlayEnhMetaFile(HDC hdc,/ handle to a device contextHENHMETAFILE hemf,/ handle to an enhanced metafileCONST RECT *lpRect / pointer to bounding rectangle);LPCTSTR lpFilename, / pointer to a filename stringCONST RECT *lpRect, / pointer to a bounding rectangleLPCTSTR lpDescription/ pointer to an optional de/scription string);素, 由此, 打印页在屏幕上的高度和宽度被换算出来。3.4预览图像的放大与缩小(PageH+iPageSpace) *indexPageH 为页高, iPageSpace 为页间间隔, index 为页号, 但 是当要显示某页面时, 实际情况是, 用户可能已经滚动过卷滚预览图像生成于图元 文 件 中 ,然 后 用 PlayEnhMetaFile 以条 ,这 时 ScrollBox1-VertScrollBar-Position 不 为 零 ,意 味 着平铺的方式显示在 Image 组 件 中 。 所 以 只 要 改 变 Image 的 大小, 即可以改变预览图像的大小, 而图元文件的缩放不会引起 图形的失真。整个页面序列的 y 轴位置上 移 , 那 么 页 号 为 index 的 Image-Top 坐标计算公式应该变为:(PageH+iPageSpace) *index - ScrollBox1-VertScrollBar- Position在初次显示预览之前 , 或者在改变页面 显 示 比 例 之 后 ,所有预览页面的显示大小都要随之改变。 那么, 是否需要把所 有的预览页全部重画? 显然, 这是一个不恰当的设计思路。 页 面较多时, 全部画出将耗费很多的时间, 从而使窗口对用户操 作的响应滞后。 因此, 当用户点击翻页按钮, 或拖动卷滚条滚 动 窗 口 , 或改变显示比例时 , 只 画 出 现 在 窗口中的那些预览 页, 而其他未显示出来的不画, 这样, 程序对用户操作的响应 速度比较快, 用户体验比较好。 那么, 哪些页面会显示在窗口 中呢? 涉及到两个因素, 预览页当前大小和滚动条当前位置 。 TPreView 类 的 成 员 函 数 ShowPagesInWindow () 计 算 显 示 在 窗 口中的页面范围, 然后通过一个循环显示在窗口中:fRatio 为显示比例参数, fRatio 为 1 时, 显示比例为 100%,屏幕显示大小与打印页大小相同 。 其他依次类推,将 PageW,PageH 代入 TViewPage 类成员函数 ResizeViewPage 中,变 Image 的大小。相应改多页预览实际中的打印输出往往是多页的, 为此设计了一个预览页 面类 TViewPage。 在 TPreView 的成员函数 NewViewPage () 中, 每完成一个新的预览页面, 即生成一个 TViewPage 对象, 并存 储 于 一 个 stl vector 类型的动态数组 ViewArray 中 。 TViewPage 对 象 有 3 个 数 据 成 员 , hMetaFile 储存预览图元文件句柄 , pImage 指针指向一个动态生成的 Image 组 件 , 私 有 数 据 成 员 AOwner 指 明 Image 组 件 的 父 对 象 , 所 有 Image 都 显 示 在 ScrollBox1 中, ScrollBox1 是所有 Image 的父对象。3.5void _fastcall TPreView:ShowPagesInWindow()int frompage = (ScrollBox1-VertScrollBar-Position - MARGIN)/(iPageSpace+PageH);int topage = (ScrollBox1 -VertScrollBar -Position - MARGIN+ScrollBox1-ClientHeight)/(iPageSpace+PageH);if (topage fMaxPage-1 )topage = fMaxPage-1;for( int k=frompage; kHorzScrollBar -Posi-tion,ScrollBox1-VertScrollBar-Position);3.6打印预览图像保存在动态数组中的图元文件 , 可以非常方便输出到打 印 机 打 印 出 来 , 还是利用前面提到的 PlayEnhMetaFile 函 数 , 只需将第一个参数设定为打印机设备句柄就可以了:PlayEnhMetaFile (Printer () -Handle, (HENHMETAFILE)(ViewArrayk.hMetaFile), &PrnPageRect);第一个参数 Printer () -Handle, 表示为当前系统的打印 机输出句柄。 第三个参数 & PrnPageRect 要用当前打印纸张大 小的 RECT 结构, 可以用 Printer () 提供的方法获取:容纳预览图像的多个 Image 元件沿 ScrollBox1 的 y 轴方向按页号顺次放置, 当 ScrollBox1-VertScrollBar-Position0 时,页号为 index 的 Image-Top 坐标计算公式是:解决窗口闪烁问题在窗口中浏览预览页面时, 会发现较明显的闪烁现象, 这 是由于窗口的刷新造成的 , C+Builder6 对 所 有 基 于 TwinCon-3.7pagewidth= Printer()-PageWidth;pageheight= Printer()-PageHeight; PrnPageRect =TRect(0,0,pagewidth,pageheight);void _fastcall TPreView: NewViewPage()TViewPage viewpage(ScrollBox1,PageW,PageH ); viewpage.hMetaFile = CloseEnhMetaFile(hdcEMF); ViewArray.push_back(viewpage);/每增加新预览页,即相应增大 ScrollBox1 卷滚条范围ScrollBox1 -VertScrollBar -Range +=(iPageS-pace+PageH);if(! bShow)ShowPrintView();hdcEMF =CreateEnhMetaFile (Printer () -Handle, NULL,&metalFileRect,NULL);fCanvas-Handle=hdcEMF;fMaxPage+;PreView-RefreshStatusBar();void TViewPage: ResizeViewPage(int width,int height)pImage-Visible = false; pImage-Height = height; pImage-Width = width;pImage-Picture-Bitmap-Height = pImage-Height;pImage-Picture-Bitmap-Width = pImage-Width;PageW = ScrnPageW*fRatio;/预览页宽度PageH = ScrnPageH*fRatio;/预览页高度trol 类的控件提供了一个公共属性 DounbleBuffered, 当 该 属 性设为真时, 窗口控件首先画到内存缓冲区, 然后再画到窗口 , 这样窗口控件刷新自己和子控件时就不再有闪烁。 Image 的父 控件是 ScrollBox1, 在预览窗口的 FormCreate 事件处理函数 中 加入一句, ScrollBox1 中预览页面换页时就不再有闪烁感了。pageleft=pagewidth/25; /页左边缩进rowtop=pagetop; /输出文字的当前行位置canvas-Font-Name=宋体; /设定字体 canvas-Font-Size=11; /字体大小 lineheight=canvas-TextHeight( 人)+rowspace;/ 设 定行高for(int i=1; iLines-Count; i+)rowtop+=lineheight; /改变当前行位置canvas -TextOut (pageleft,pagetop +rowtop, Memo1-Lines-Stringsi-1);if(i%26=0)/ 每页行数 26 行rowtop+=lineheight;canvas-TextOut ( pagewidth/2,pagetop+rowtop, IntToStr(PreView-PageNumber) );rowtop=pagetop; /文字当前行重新设为页顶位置PreView-NewViewPage(); /预览换页PreView-EndViewDoc(); /结束预览4使用示例将 TPreView.cpp 单元文件加入 C+Builder6 工程中, 对使 用 TPrinter 类完成的打印代码作如下修改, 即可变成打印预览的 代 码 :在 Printer* pPrinter;语 句 处 ,添 加 TPreView *pPre-View; ( 声 明 一 个 指 向 TPreView 的 指 针 ) 在 pPrinter Printer() 语句处, 添加 pPreView=PreViewer (); (构造一个 TPreView 的 实 例 ) 把 所 有 的 pPrinter-Canvas 换 成 pPreView -Canvas, 把 Printer () -BeginDoc (); 换成 pPreView-BeginViewDoc (); 把 pPrinter -NewPage ( ) ; 换 成 pPreView - NewViewPage ( ) ; 把 Printer () -EndDoc (); 换成 pPreView-EndViewDoc (); 就 可以实现预览。 下面是一段范例代码:参考文献(澳) Janrrod Hollingworth , 等. C+Builder 5 程序设计大 全 . 康 向 东 , 汪 浩 , 等 , 译 . 北 京 : 机械工业出版社 ,2002.Steve Teixeir
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 北汽知识培训集团课件
- 校园食堂食品安全知识培训课件
- 校园消防知识培训课件新闻稿
- 校园消防安全知识培训
- 物业人民调解员考试试题及答案
- 国画荷花面试题及答案
- 电气制图考试题及答案
- java算法排序面试题及答案
- 法院审判面试题及答案
- 石油普工考试试题及答案
- 知识题库-人社练兵比武竞赛测试题及答案(八)
- SYT 0452-2021 石油天然气金属管道焊接工艺评定-PDF解密
- 《育婴师培训》-课件:环境消毒基础知识
- 煤矿掘进支护工培训课件
- 关于规范村级财务管理的审计建议
- 长安欧尚A800说明书
- 火灾应急预案组织架构图
- FANUC伺服电机选型计算手册-v1
- 小学科学项目化学习活动作业方案案例设计《设计制作动力小车项目化学习》
- 山东省济宁市第十五中学2023-2024学年(五四学制)六年级上学期第一次月考语文试题
- 船舶油污应急计划(标准版)
评论
0/150
提交评论