孙宏凯 李彩娟
河北建筑工程学院数理系
在数据库应用程序中,报表打印是非常频繁的.PowerBuilder作为目前较为流行的数据库前台开发工具,其显著的一个特点就是其数据窗口技术.利用此技术很容易完成查询结果(甚至是各种复杂的报表)的屏幕浏览,但是实现理想的报表打印却没有直接的方法.为此,需要设计通用的打印预览模块,以提高程序开发效率,并最大限度的满足用户需求.
打印预览模块设计的基本需求如下:
(1)调用简单;(2)预览效果与实际打印一致;(3)能够进行页面调整(如页边距、纸张大小、纸张方向等);(4)动态进行打印设置(如打印份数、页面范围、打印机选择等);(4)打印效果与效率(数据与报表的适合度、处理速度等)(5)适应能力.
对于前3种需求,在文献[1~4]中均有不同程度的讨论,但在通用性方面均存在一些不适应,如列宽的最佳适应、列显示顺序调整导致的列处理错误、不显示列对处理技术的影响等.而后2种需求以及处理技术的完善是本文讨论的重点.着重解决的问题有以下几个方面:
(1)对查询结果进行屏幕浏览,以数据窗口的Grid型(标准网格)为最佳.但打印报表往往需要添加标题、页码等,这是Grid型所不具备的.
(2)处理效率:怎样由Grid数据窗口或datastore(数据存储,不可视)向报表打印进行转化,数据如何传递?中间产生空白页问题如何消除?
(3)不显示列、列显示顺序调整对预览处理技术的影响.
(4)打印效果:列宽的自动适应问题.
(5)适应能力:无法满足用户报表需求的处理对策.
创建两个窗口,不妨命名为w_brow和w_preview,均为response!型.前者用于浏览数据,主要控件包括一个数据窗口dw_1,其数据窗口对象为Grid型,一个命令按钮,用于打开预览窗口(即后者)和传递相关参数;后者用于实现打印预览,也包括一个数据窗口dw_print,其数据窗口对象动态创建,以及其他辅助控件,如纸张选择、页边距、缩放比例、打印、转存等.
需要传递的信息主要包括:数据窗口内容、报表标题、列标题等.在PowerBuilder系统,打开窗口并传递参数的方法是:
OpenWithParm(窗口名,参数变量名)
但只能传递一个参数.为此,需要使用结构变量来传递相关数据.定义全局结构变量print_parm,其成员如下:
几点说明:①列标题数组c_label[].如果在浏览窗口w_brow中各列有汉字标题,则可用程序自动读取,不需传递此参数,但考虑到查询结果及数据库字段设计习惯用英文,以及打印列数的选择等通用性处理,该项参数是必要的.②列合计.虽然能够通过数据窗口直接得到,但从技术处理和效率上讲,设置此项有重要意义.详细用法见下文.
因篇幅问题,这里主要叙述技术原理,仅给出一些关键性语句代码.所涉及的技术、控件及代码均与打印预览窗口w_preview相关,不在一一说明.定义的实例变量如下:
文[2]中的数据窗口对象是事先创建的,难以处理通用的查询结果;文[3]采用动态创建,但其数据是采用完全拷贝,这样则无法改变显示风格,如原来为Grid型,打印时则难以添加标题、页码等信息.
为了实现数据共享,要求两个数据窗口对象的数据列个数及数据类型必须一致,但显示格式可以不同.这里,数据窗口dw_print的数据窗口对象是动态生成的,其格式为FreeForm(自由格式).创建对象及数据共享的语句为:
需要说明的是,Tabular型与Grid最接近,只是没有网格线.不采用它的主要原因是,若原来有较多的数据列,且部分列是不可见,但总宽度超过纸张,则在打印时会产生空白页.即使将这些不可见列移动到第一列或删除,问题也不能解决,因原始位置超出纸张.
按编号获得列名称的语法是:
dw_print.Describe(“#<Column>.Name”)由于列显示顺序调整,或者是原数据窗口对象中间位置的列设置为不显示使其位置自动向后排,于是按列编号k获得的列名可能并不是显示中第k列所对应的.事实上,这个编号是创建列时的顺序号,取决于Select语句中字段的列表顺序.为此,可以先将所有的列名按编号顺序读出,存储在数组is_c_name中,然后再依据显示中每列的横坐标由小到大排序,使其与显示顺序对应.这样数组is_c_name中的第k个元素,就是显示的第k列对应的列名称.
打印列个数il_cp_num由可显示的列数与参数传递来的列标题数组的元素个数的较小者确定,以保证有效处理打印列的属性.
原数据窗口对象中的列宽,是由表结构定义决定的,直接作为打印时的列宽并不合适.另外,列标题文字的宽度、列合计数的宽度对打印效果均有影响.如“性别”,其值仅为1个汉字,其打印列宽则为2字为宜.相反,有时由于对数据估计不足,导致表定义过宽而产生空余,如“通信地址”等.
处理方法并不复杂.对每一列,将欲打印的数据、列标题以及列合计(如果有的话)经比较算出最宽的占用字符数,存储在数组il_cc_num中.这是报表布局和画线的依据.
动态生成的数据窗口对象是FreeForm型,仅一列,每行数据占一页,需要调整布局.此外,网格线要用程序画出,添加报表标题、副标题、页码,甚至列合计的信息.
(1)计算打印时列宽,确定列控件、竖线的横坐标位置
如果以TimesNewRoman的11磅字打印,度量单位为千分之一厘米,则列宽为:字符数×220.考虑到竖线占用及列边距,间隙以78为宜.并由此确定各列控件、右侧竖线的横坐标位置,存储在数组ll_ pos_t_x和ll_pos_l_x中,供布局和画线使用.
(2)调整标题区及细节区高度
dw_print.Modify(“DataWindow.Header.Height=‘1931'”)//表题
dw_print.Modify(“DataWindow.Detail.Height=‘582'”)//细节
(3)列的布局
按照各列控件的位置重新布局.其中在打印列个数il_cp_num之外的列设置为不可见,其位置不动.原列标题标签控件全部删除,再按指定位置创建,并添加中文标题信息.
(4)标题区信息创建
包括报表标题、副标题、页码以及各列标题的标签控件.以页码为例,语句为:
ls_text=“createcompute(band=headeralignment=‘0'expression=~”“+”‘共'+pageCount() +‘页第'+page()+‘页'~“border=‘0'color=‘33554432'x=‘11000'y=‘899'height=‘343'width =‘2989'format=‘[general]'html.valueishtml=‘0'name=page_1visible=‘1'font.face=‘宋体' font.height=‘-9'font.weight=‘400'font.family=‘0'font.pitch=‘2'font.charset=‘134'background.mode=‘1'background.color=‘536870912')”
dw_print.modify(ls_text)
(5)画出网格线.
包括标题区和细节区的横线竖线.如细节竖线:
ls_line_c=“createline(band=detailx1=‘”+string(ll_pos_l_x[ll_j])+“'y1=‘0'x2=‘”+“string (ll_pos_l_x[ll_j])+”'y2=‘555'name=c_l_“+trim(string(ll_j))+”visible=‘1'“pen.style=‘0' pen.width=‘26'pen.color=‘33554432'background.mode=‘1'background.color=‘1073741824'”
dw_print.Modify(ls_line_c)
(6)列合计信息处理
依据传递的参数,在summary区对应列位置上创建标签文本,画网格线.方法同前.
包括预览属性、纸张大小、方向、页边距等.
为方便用户需求,可将报表数据存储于Excel表,供用户二次编辑打印.其中列标题用给定汉字信息添加.主要代码如下:
利用数据窗口技术,完全动态创建数据窗口对象,并使用数据共享,所建立的打印预览模块效率高、列宽自适应、通用应性强,不受不显示列、列显示顺序调整的影响,可完全消除空白页的产生.类似的方法,可以处理更复杂的报表,如两层符合表头报表等,这里不再赘述.该模块在作者的多个项目中得到了很好的应用,如“中小型民营制造业企业管理与成本核算系统”(已鉴定)、“学籍管理系统”、“钟表代销管理系统”等.界面效果图如图1.
图1 打印预览窗口
[1]程沛.基于PowerBuilder实现MIS系统中的通用打印预览和设置.计算机应用,2003(12):335~336
[2]杨安祺.用PowerBuilder实现数据窗口的打印预览、打印技术.计算机工程与应用,2004(08):107~109
[3]万英,周红.PB中一个通用打印预览的实现.计算机与现代化,2006(05):124~126
[4]张长富.PowerBuilder8实例教程.北京:北京希望电子出版社,2002
[5]杨昭.PowerBuilder9.0数据窗口.北京:中国水利水电出版社,2004