作者:Cathy Pountney,Visual FoxPro MVP,www.frontier2000.com
翻译:张洪举,Visual FoxPro MVP,www.vfptop.com
应用于:Visual FoxPro 9.0
概述: 学习Visual FoxPro 9.0报表书写器的新增功能包括新增的可重用数据环境、报表保护和用户界面增强。你可以从中学习到对布局对象的增强、对国际用户的改进、对打印和对数据分组的增强。此外,也可以学习到报表书写器的两个最大改进:多细节带区和报表的可扩展功能。最后,你将学习到Visual FoxPro报表文件(FRX)的详细结构。
目录
前言
可扩展功能
数据环境
保护
用户界面增强
布局对象增强
国际化功能
打印增强
数据分组增强
多细节带区
FRX
结论
微软在当前基于FRX结构的报表上投入了众多的开发者,在很大程度上改进了VFP 9.0的报表书写器。VFP 9.0的报表书写器与先前的VFP版本兼容,它是一个对新版本和老版本的成功融合。
在VFP 9.0之前,报表引擎处理的事情包括:处理数据、对象位置、绘制、打印和预览,并没有挂钩到报表引擎和定制报表的方法,但是这在VFP别的应用领域中却可以。对于VFP 9.0报表书写器的最大改进就是新增的可扩展功能,报表设计器、报表引擎和预览容器都公开给了开发者。
VFP 9.0报表书写器包含了一个新增的设计时功能,叫做Builder Hooks(生成器挂钩)。公开了几个报表设计器事件和一个叫做Report Builder(报表生成器)的独立Xbase组件,这个组件可以被调用后处理这些报表设计器事件。这个组件也可以被用来调用你自己的对话框,扩展本地报表设计器的行为或者覆盖本地行为。
VFP为设计报表包含了一个可扩展的报表生成器应用程序,其包含有各种新增功能,并提供了一个很友好的用户界面。报表生成器由一个新增的系统变量_REPORTBUILDER控制。如果该变量为空,则显示原来版本的对话框。要调用生成器挂钩,可以设置该变量为一个适当的应用程序。例如,要使用VFP 9.0附送的报表生成器,可以执行下列命令:
_REPORTBUILDER = HOME() + "REPORTBUILDER.APP"
该文章中的某些部分假定使用原来版本的对话框,并且_REPORTBUILDER系统变量为空,其他某些地方则假定使用新对话框,并且_REPORTBUILDER系统变量设置为REPORTBUILDER.APP。如果你没有经历过我们描述的这种行为或没有看见过这个对话框,可以改变_REPORTBUILDER的值来体验一下。
在新的输出系统中(Object-Assisted Output),报表引擎用于处理数据中心的事务,如移动整个范围和表达式求值。但是,当建立输出时,它延迟对叫做ReportListener的新增基类的工作,该新增基类使用GDI+以一种更加高级的方法来绘制报表内容,并且它也给Xbase用户一个与输出过程相互作用的机会。图1显示了这些部分如何搭配在一起工作的。
图1 使用新增的ReportListener类来操纵报表
要使用ReportListener类,可以使用REPORT FORM命令的新增子句,如下所示:
oListener = CREATEOBJECT("ReportListener") oListener.ListenerType = 1 && 预览,或设置为0进行打印 REPORT FORM <name> <clauses> OBJECT oListener
VFP 9.0也提供了第二种使用ReportListener类的方式,你可以设置新增系统变量_REPORTOUTPUT的值为一个基于ReportListener类的应用程序名称。
当使用Object-Assisted Output时,报表会根据它的ListenerType属性值设置,使用两种方式中的某个方式进行处理。你可以认为这两种方式是print-appropriate和preview-appropriate,或者是page-at-a-time和all-pages-at-once。在第一种方式中,在准备每页时,Listener触发一个OutputPage事件,例如在发送每页到打印机或打印队列时。
在第二种方式中,Listener为绘制和高速缓存报表准备所有的页面,在完成时,可以调用OutputPage方法来询问是按页号输出所有的页面还是输出其中的任意一个页面。
在VFP 9.0中,另一个可扩展功能难题是预览容器。对于此挂钩,你可以使用由VFP 9.0附送的新预览容器,或者书写自己的预览容器。在不使用新增的object-assiste输出时,旧的预览容器仍旧可用。
新增的系统变量_REPORTPREVIEW可以用来包含预览容器的应用程序名称。缺省情况下,该变量指向ReportPreview.app。该应用程序比旧的预览程序包含了大量的改进,包括更多的缩放级别、工具栏控制、标题控制、多页预览和使用GDI+而带来的更加优良的显示品质。图2和图3显示了旧预览界面和新预览界面的差异。
图2 在旧预览容器中输出很不柔和
图3 新的预览容器含有更多优良性能和更多完善选项
注意旧预览容器和新预览容器之间在品质方面的差异。新容器比旧容器具有更加柔和的字体,这要归功于使用GDI+来输出信息;同时也要注意所停靠工具栏的差别,在新预览容器上的工具栏具有更多选项,包含多页布局按钮等。
新增的SET REPORTBEHAVIOR命令可以用于打开或关闭Object-Assisted Output,新的输出绘制引擎和预览界面与旧类型的输出有相当大的差异,对齐、字母紧排和间距在GDI和GDI+之间是不同的,这会在很大程度上改变现存报表的查看结果。因此,REPORTBEHAVIOR默认设置值为80,即关闭Object-Assisted Output输出,报表处理按照VFP 9.0之前的方式进行。
如果你要全局性的打开Object-Assisted Output,可以在Options对话框中改变这个设置值(如图4所示),或者执行下面的命令:
SET REPORTBEHAVIOR 90
图4 使用Options对话框中的Run-time behavior(运行时行为)选项改变SET REPORTBEHAVIOR设置
在REPORTBEHAVIOR设置90时, REPORT FORM命令会与使用OBJECT子句一样来执行,而不需要改变代码,VFP使用_REPORTOUTPUT系统变量为每个REPORT FORM命令指定基于ReportListener类的应用程序。
通过VFP 9.0报表书写器中的新增扩展功能,你可以使用报表生成器、报表引擎和预览界面,你既可以全局地打开这些新增功能,也可以为各个报表逐个单独打开。也可以关闭这些新增功能,就象在先前VFP版本中运行一样。
VFP 9.0报表书写器现在可以与其他报表共享数据环境,数据环境也可以被作为一个类进行保存,然后加载到所需要的报表中,这为定义公共报表需求提供了一个很好的数据重用解决方案。
要将一个数据环境存储为类,可以在报表中定义数据环境时进行。当数据环境窗口处于活动状态时,从File菜单中选择新增的Save As Class...选项(参考图5)。
图5 当数据环境为活动窗口时,新增的Save As Class...选项会出现在File菜单上
在选择Save As Class...选项后,出现如图6所示的Save As Class 对话框。在保存一个报表的数据环境时,Save选项组DataEnvironment是惟一可用的选项按钮。
图6 为存储报表的数据环境,使用Save As Class对话框来声明新建类和类库的名称
在Name文本框中为类输入一个名称,接下来在File文本框中输入要保存的新建类的类库名称。如果所输入的类库名称不存在,则创建新类库文件,也可以使用省略(...)按钮来定位到一个已存在的类库。最后,你还可以输入对新建类的说明。
除了手动为一个新建报表定义数据环境外,VFP 9.0也提供了从已有报表或已存储的DataEnvironment类中加载数据环境的选项。Report菜单上的Load Data Environment(加载数据环境)...选项(如图7所示)允许选择一个数据环境加载到当前报表中。
图7 使用Load Data Environment...选项从已有报表或DataEnvironment类中加载数据环境
注意:Load Data Environment功能仅在新增的ReportBuilder对话框中可用,你应当通过设置_REPORTBUILDER = HOME() + "ReportBuilder.app"来使用此功能
要从其他报表加载数据环境时,所有原数据环境的代码和成员会被复制到新报表中,这里的含义是:在复制后,再对原报表的数据环境的改变不会再影响到所建立的报表中。
可以从Report菜单中选择Load Data Environment...后打开如图8所示的Report Properties对话框,选择所要复制数据环境的报表到当前报表中。
图8 使用Report Properties对话框的Data Environment选项卡来选择你要复制数据环境的的报表
选择Copy from another report file(从其他报表文件复制)选项按钮后并选择Select...按钮,这会打开Open对话框,可以从中选择要复制的报表,一旦选择一个报表后,将出现一个确认对话框(如图9所示)。
图9 使用此对话框来确认要复制一个数据环境到当前报表中
VFP 9.0要从其他报表复制数据环境到当前报表,会通知你会覆盖当前数据环境,并且要单击Yes才可以继续执行,这说明你在当前报表数据环境中定义的任何内容都将被覆盖掉。如果单击No,报表不会发生变化并终止此过程,单击Yes时,数据环境被复制并出现如图10所示的提示对话框。
图10 该对话框确认数据环境已被成功复制到当前报表中
现在完成了复制数据环境,就可以按照程序需求来操作数据环境了。但是,需要注意的是,在这之后再对原来报表数据环境做的改变,并不会影响当前报表的数据环境。
当从一个类中加载数据环境时,一些代码会被添加到新建报表的数据环境中,用于绑定到指定的DataEnvironment类,并在运行时实例化这个类。这里的含义是:以后再对DataEnvironment类所做的改变会影响到所有使用这个类的报表。
使用如图8所示的Report Properties 对话框,单击Link to a visual DE class(连接到可视数据环境类)按钮,接下来单击Select...按钮来打开Open对话框,可以从中选择类库和要使用的类。在选择了一个类后,将会出现与图9同样的确认对话框,确认后,数据环境被更新,并且显示如图10所示的对话框,通知加载数据环境成功。
在此时,一些代码会被添加到数据环境的某些方法中,在一些方法中只具有十分简单的代码,如只有一个DODEFAULT()命令,其原因是在方法中只少要包含一行代码,否则BindEvents()函数不能运转。
从一个类加载数据环境后,会自动添加一些代码到下面描述的几个方法中。
在Init方法中的下列代码用于保证BindEvents()能按预定目标工作。
*-----------------------------------------------------* * 此方法代码由报表生成器插入 * *-----------------------------------------------------* DODEFAULT()
下列代码在数据环境上建立了几个新属性,来支持被加载的DataEnvironment类的对象引用。接下来将该数据环境中的事件绑定到在DataEnvironment类中的相对应事件,但是,Init和Destroy事件不绑定。最后,DataEnvironment类的BeforeOpenTables事件被触发。
*-----------------------------------------------------* * 此方法代码由报表生成器插入 * *-----------------------------------------------------* LOCAL loMember, laDEEvents[1], liMember, liMembers, loBoundMember THIS.AddProperty( "BoundDE", NEWOBJECT( "de_insurance", "c:\vfp9\rwde.vcx" )) IF VARTYPE( THIS.BoundDE ) = "O" AND UPPER( THIS.BoundDE.BaseClass ) = "DATAENVIRONMENT" * 在这里绑定事件,跳过Init和Destroy事件 * FRX DE和它的成员只能具有基本事件,所以许多PEMSTATUS不是必需的 liMembers = AMEMBERS( laDEEvents, THIS, 3 ) FOR liMember = 1 TO liMembers IF INLIST( UPPER( laDEEvents[ liMember, 1] ), "INIT", "DESTROY" ) LOOP ENDIF IF INLIST( UPPER( laDEEvents[ liMember, 2] ), "EVENT", "METHOD" ) BINDEVENT( THIS, ; laDEEvents[ liMember, 1], ; THIS.BoundDE, ; laDEEvents[ liMember, 1] ) ENDIF ENDFOR * 现在做私有成员的检查,再次跳过Init和Destroy事件 FOR EACH loMember IN THIS.Objects IF PEMSTATUS( THIS.BoundDE, loMember.Name, 5 ) AND ; UPPER( PEMSTATUS( THIS.BoundDE, loMember.Name, 3 ) = "OBJECT" loBoundMember = EVAL( "THIS.BoundDE." + loMember.Name ) IF ( loBoundMember.BaseClass == loMember.BaseClass ) liMembers = AMEMBERS( laDEEvents, loMember, 3 ) FOR liMember = 1 to liMembers IF INLIST( UPPER( laDEEvents[ liMember, 1] ), "INIT", "DESTROY" ) LOOP ENDIF IF INLIST( UPPER( laDEEvents[ liMember, 2] ), "EVENT", "METHOD" ) BINDEVENT( THIS, ; laDEEvents[ liMember, 1], ; loBoundMember, ; laDEEvents[ liMember, 1] ) ENDIF ENDFOR ENDIF ENDIF ENDFOR THIS.BoundDE.BeforeOpenTables() ENDIF
在AfterCloseTables方法中的下列代码用于保证BindEvents()按预定目标工作。
*-----------------------------------------------------* * 此方法代码由报表生成器插入 * *-----------------------------------------------------* DODEFAULT()
添加到Destroy方法中的下列代码,用来解除报表数据环境方法与DataEnvironment类之间的绑定,该代码也移除对DataEnvironment类的对象引用。
*-----------------------------------------------------* * 此方法代码由报表生成器插入 * *-----------------------------------------------------* LOCAL loMember UNBIND( THIS ) FOR EACH loMember in THIS.Objects UNBIND( loMember ) ENDFOR IF PEMSTATUS( THIS, "BoundDE", 5 ) AND UPPER( PEMSTATUS( THIS, "BoundDE", 3 )) = "PROPERTY" THIS.BoundDE = NULL ENDIF
在Error方法中的下列代码用于保证BindEvents()按预期目标工作。
*-----------------------------------------------------* * 此方法代码由报表生成器插入 * *-----------------------------------------------------* LPARAMETERS nError, cMethod, nLine DODEFAULT( nError, cMethod, nLine )
在VFP 9.0中使用报表设计器或标签设计器时,可以为一个或多个布局对象建立保护。这就允许用户来修改一个报表,但是却可以保持某些对象不被改变。
布局对象有5种可以设置的不同保护模式,Field(字段)对象具有一个额外的保护选项,Bands(带区)具有2个可以设置的保护模式,报表自身还具有多种可以设置的保护模式。
注意:新增的保护功能只能通过新增的ReportBuilder对话框进行设置,可以设置_REPORTBUILDER = HOME() + "ReportBuilder.app"来使用这些保护功能。
要在报表设计器中保护一个布局对象,应当打开对象的Properties对话框进行设置。在选定对象后,可以从Report菜单中选择Properties打开Properties对话框。也可以单击右键后从对象的上下文菜单中选择,或者双击对象自动打开Properties对话框。图11展示了一个字段对象的Properties对话框的Protection(保护)选项卡。
图11 可以使用Properties对话框的Protection选项卡来设置布局对象的保护模式
可以为布局对象设置如下5种保护模式:
该对话框的Design-time caption(设计时标题)部分仅用于Field对象,在该文本框中输入的字符串将在报表设计器中代替Field的Expression显示,这样做的好处是可以显示一些友好易用的信息,从而替代某些复杂的表达式显示。
要在报表设计器中保护带区,可以选择带区的Properties对话框。可以从Report菜单中选择Edit Bands...菜单项打开Properties对话框,也可以从带区的上下文菜单中选择,或者双击带区中的灰色带区条来打开。图12显示了一个带区的Properties对话框的Protection选项卡。
图12 可以使用带区的Properties对话框的Protection选项卡来设置带区的保护模式
可以为带区设置如下两种保护模式:
要设置整体的报表保护,可以打开Report Properties对话框进行设置。可以从Report菜单中选择Properties菜单项,或者从报表的上下文菜单中打开Report Properties对话框。图13显示了Report Properties对话框的Protection选项卡。
图13 可以使用Report Properties对话框的Protection选项卡来设置报表的整体保护模式
在对话框的上部,可以允许定义Report Properties对话框的哪个选项卡对于用户不可用。对于在该区域中的每个选择,Report Properties对话框的相应选项卡会禁止使用。Protection选项总是处于选定状态,并且不可用。Ruler/Grid选项不可用是因为该选项卡不能被保护,该选项之所以出现在这里,是为了和Report Properties对话框上的选项卡保持一致。
该对话框的下部允许哪个菜单选项对于用户不可用,对于在该区域中的每个选择,相应的菜单项会禁止使用。
要在与报表设计器或标签设计器会话期间调用保护功能,必须在命令中使用如下所示的PROTECTED关键字:
CREATE REPORT MyReport PROTECTED MODIFY REPORT MyReport PROTECTED CREATE LABEL MyLabel PROTECTED MODIFY LABEL MyLabel PROTECTED
如果没有使用PROTECTED关键字,则报表设计器不启用对于布局对象的保护功能。
对于用户界面做的许多修改,可以使设计报表更加容易和直观。VFP 9.0彻底更新了菜单、上下文菜单,并在报表设计器工具栏上增加了许多新选项。Expression Builder对话框和Expression Builder Options对话框也增加了新功能,以及一些其他用户界面增强也被添加到VFP 9.0报表书写器中。
VFP 9.0中的菜单已被彻底更新,以适应新增的选项。此外,为了更加明确,一些原有菜单选项被重新标注,并且为了更加容易访问,一些选项被重复放置在几个菜单中。图14至图16展示了新菜单的变化。
图14 在File菜单上新增的“Save As Class...(另存为类)”选项
图15 为报表设计器工具栏新增的Report Designer Toolbar选项,并把Grid Lines、Show Position选项与其他选项进行分割开
图16 Report菜单上的更多变化,包括重新标注选项、新增选项和新增的Print Preview重复选项
现在的上下文菜单也增加了一些菜单项,这样可以与所调用的对话框更加一致。在报表设计器中的一些项目,先前并不具有上下文菜单,但是现在可以了。图17至图19展示了这些新变化。
图17 全局上下文菜单所具有的新增选项和一个重新标注选项
图18 通过右键单击任何带区的灰色带区条可以打开新增的带区上下文菜单
图19 通过右键单击任何布局对象可以打开布局对象上下文菜单
报表设计器工具栏也有一些新增的按钮,如图20所示。
图20 为页面设置和字体属性新增的按钮
Report Expression对话框具有了一些小改变,图21展示了这种变化,现在的对话框为输入报表表达式提供了更大的区域。
图21 在当前更高的Expression for Field on Report文本框中可以输入较长的表达式
在_REPORTBUILDER系统变量为空时,Expression Builder对话框原来的行为如下:仅在数据环境中定义的表的字段显示在Fields列表框中,但是在数据环境外打开的表在列表框中并不可用。
当_REPORTBUILDER系统变量设置为ReportBuilder.app时,Expression Builder的行为却大不相同。首先,在_GETEXPR中定义的Expression Builder会替代原来的Expression Builder。图22显示了默认的Expression Builder对话框。
图22 使用"From table"组合框从打开的表中选择字段
Expression Builder对话框现在具有了一个为Fields列表框中的字段选择数据表的组合框,但是,只有当前使用的数据表在组合框中才会列出。记住这一点是至关重要的,因为在数据环境中定义的表不会被报表设计器自动打开,因此,它们不会自动出现在组合框中。
之所以这样做,是因为当在应用程序中允许用户修改报表时,你可以控制哪些表对最终用户可用。如:你可能在数据环境中定义了一些你所需要的数据表,但是却不希望用户可以访问它们,所以,你可以明确地打开想让用户访问的表,而忽略掉那些要保护的表。
在Expression Builder Options对话框中的Field aliases选项组现在可用了,如图23所示。该选项组用于指定在从Expression Builder对话框中选择字段时是否添加表的别名到报表表达式中。
图23 使用Field aliases选项组可以指定在从Expression Builder对话框中选择字段时是否添加表的别名到报表表达式中
Always add alias(总是添加别名)和Never add alias(不添加别名)选项按钮指定VFP 9.0是否为所有字段自动添加表的别名,Add non-selected alias only(仅添加未选定的别名)选项按钮的行为受_REPORTBUILDER系统变量的值影响。如果_REPORTBUILDER系统变量为空,任何不是从数据环境InitialSelectedAlias属性所指定表中选择的字段将使用表别名作为前缀,从InitialSelectedAlias指定表中选择的字段则不使用表别名前缀。
如果_REPORTBUILDER系统变量设置为ReportBuilder.app,Add non-selected alias only选项的使用略有不同,在确定是否以表别名作为字段的前缀时,将以当前所选择的表别名代替InitialSelectedAlias指定的表别名。
除了从Expression Builder对话框选择字段之外,也可以从数据环境中拖放字段到报表设计器中,这会使用Field aliases选项组中的设置。与此相同,在Options对话框的Report选项卡中也新增了一个选项,如图24所示。该选项决定了为所有新建立的报表使用什么样的Field aliases默认设置。
图24 使用Options对话框的Report选项卡上的Expression Builder选项来设置默认Expression Builder行为
几个对于用户界面的其他增强也改进了报表设计方式,详述如下。
现在当对象可以被调整大小时,鼠标形状会进行相应变化,给予提示。如图25所示。
图25 使用新增的鼠标形状变化可以知道对象在什么时候可调整大小
VFP 9.0现在具有了一个Multiple Selection对话框,可以一次为多个布局对象设置Protection和Print when属性,自然也可以改变其中任何单个布局对象的其他属性。要使用这个新增功能,可以在选择多个布局对象后,双击其中的任何一个对象来打开Multiple Selection对话框,如图26所示。
图26 使用Multiple Selection对话框的Selection选项卡选择要操作的布局对象
所选择的布局对象会在对话框打开时显示在对话框的第一个选项卡中,要对报表中的所有对象进行操作,可以在打开该对话框之前按CTRL+A来选择所有布局对象。
Sort by(排序)选项允许对布局对象列表按在报表中的Type(类型)或Location(位置)进行排序,Remove from list(从列表中删除)按纽从列表中删除选定的布局对象,双击列表中的任何项目,则打开与该对象对应的Properties对话框。Multiple Selection对话框的Properties选项卡,用于改变在Selection选项卡中所列出全部项目的属性,如图27所示。
图27 使用Multiple Selection对话框的Properties选项卡一次改变所有选定对象的Protection属性和Print when逻辑条件
选择Apply these protection settings to the selected objects(应用保护设置到选定对象)复选框来打开Protection选项;选择Apply this condition to the selected objects upon saving(应用下面的条件到选定对象)复选框来打开Print when选项,在改变了Protection和Print when的设置后,可以单击OK按钮关闭对话框并保存这些改变到所有Selection选项卡中列出的布局对象中。
Preview窗口具有了更多的缩放级别,如图28所示。
图28 为了更好的可视性,可以使用在VFP 9.0预览新增的缩放级别
VFP 9.0对用户界面进行了许多改进,包括菜单、上下文菜单和工具栏。对Expression Builder对话框和Expression Builder Options对话框也增加了一些有效的选项,其他改进包括鼠标形状、Multiple Selection对话框以及更多报表预览的缩放级别。
在布局对象中增加了一些小改进,包括控制模板字符的选项、对于字符表达式的剪裁模式,以及相对和绝对位置。
注意:要使用增强的剪裁模式,应当使用新增的Object Assisted Output,或者直接通过ReportListener对象,或者通过间接的SET REPORTBEHAVIOR 90命令。
Field Properties对话框现在具有了一个新增部分-Template characters,如图29所示,两个可用选项是Overlay(覆盖)和Interleave(插入)。这决定了如何使用在Format expression文本框中指定的字符串。
图29 Template characters决定了如何将格式化字符用于字段数据
在使用Overlay选项时,指定的格式化字符作为数据的一部分来对待,并且覆盖在格式化字符给定位置上的任何字符。例如,在Format expression为“999-999”时,并且字段数据包含“123456”,则报表显示为“123-56”,请注意其中的“4”是如何被Format expression表达式中的破折号给替换掉的。
在使用Interleave选项时,指定的格式化字符将插入到当前数据中。例如,在Format expression为“999-999”时,并且字段数据中包含“123456”,则报表显示“123-456”,请注意Format expression表达式中的破折号如何插入到了“3”和“4”之间。
在VFP 9.0之前,在文本过长时,字段对象总是被剪裁到最近的单词。在VFP 9.0中,在Field Properties对话框上的一个新增选项可以指定剪裁文本的方式,如图30所示。
图30 使用Trim mode for character expressions来指定剪裁超长文本的方式
对6个剪裁选项说明如下:
布局对象的另一个新增功能是可以更好地控制对象的大小和位置,如图31所示。
图31 使用在Properties对话框上的Size and position in layout(大小和布局位置)选项来控制相对和绝对位置
在对象被添加到报表上时,From page top、From left、Height和Width的值是自动设置的,注意From page top属性值是相对于报表设计器中页顶部的位置,对象上方的所有灰色带区条的高度也会被考虑在内,改变From page top属性可能会不经意地移动对象到其他带区。
From page top属性值和Height属性值将共同来确定是使用绝对位置还是相对位置。当From page top属性是一个报表设计器页面大小范围内的一个值时,并且Height的属性值小于或等于对象其所在带区的高度,则使用相对位置。相对位置需要对象在带区中,但是页标头和页脚注带区除外。
当From page top属性设置成了一个报表设计器页面范围大小之外的一个值,或者Height属性的值超过了对象所在带区的高度,则使用绝对位置,绝对位置的含义是对象将在每页中相同的精确位置进行打印。
绝对位置可以用于在报表上建立水印,例如,在页标头带区中放置一个图像,并设置为Scale contents, retain shape(缩放图片,保留形状)。改变From page top和From left属性来指定水印开始的左上角位置,改变Height和Width属性来指定水印的大小,并确认水印没有超出打印机的可打印区域。
在VFP 9.0中,报表在国际化支持方面有着更好的表现,特别是可以为字体设置字符集或语言脚本。网格单位具有了更多选项,并且STRCONV()函数也具有了更多选项。
字体的字符集或语言脚本可以在Font对话框中设置,如图32所示。这可以为报表上的一个特定对象或整个报表设置默认字体。
图32 可以使用Script组合框选择一个字符集或语言脚本
Set Grid Scale对话框现在增加了3个Ruler Scale(标尺刻度)选项来允许设置刻度为inches(英寸)或metric/centimeters(米/厘米),或者关闭标尺。这为具有不同系统默认设置的客户给予了更好的控制需求,图33显示了在Set Grid Scale对话框中的新增选项。
图33 为更好地控制标尺刻度,可以在Set Grid Scale对话框中使用新增的下拉组合框
与Set Grid Scale对话框相同的功能增强也被添加到Options对话框的Report选项卡中。
VFP 9.0另一个对国际化报表的改进是修改了STRCONV()函数,现在可以使用该函数变化Locale ID的代码页和fontcharset(字符集)的值,下面的帮助文档解释了STRCONV()的新增功能。
STRCONV(cExpression, nConversionSetting [,nRegionalIdentifier[,nRegionalIDType]]) nRegionalIDType: 0 - 默认值, nRegionalIdentifier为Locale ID. 1 - nRegionalIdentifier为代码页 2 - nRegionalIdentifier为字符集 对于nConversion的设置值1、2、3、4、7和8,只有在nRegionalIdentifier=0时支持。
对于设计非英文报表,FontCharSet、网格单位和STRCONV()函数的改进在报表设计时更具灵活性。
为了实现更好的打印功能,一些命令和对话框都被进行了增强和改进。SYS(1037)函数现在新增了一个参数,并且具有了返回值;GetFont()函数的对话框使用了新样式;APrinters()函数生成的数组现在具有3列元素。
SYS(1037)函数用于打开Print Setup对话框,以及用于改变VFP的默认打印机。在VFP 9.0的以前版本中,SYS(1037)函数没有实际意义的返回值,不管用户如何操作,总是返回一个空串。即使用户按下了Cancel按钮,你也无法捕获这个操作的信息。
在VFP 9.0中,一个新参数被添加到SYS(1037)中,并且函数具有了返回值,如表1所示。
表1 SYS(1037)的新增参数和返回值
SYS(1037) | 除了可以返回一个具有实际意义的值,其行为与先前版本一样。 | 0 = 用户取消了操作 1 = 用户选择了OK按钮 |
SYS(1037,1) | 如果当前工作区包含一个与FRX匹配的结构,并且没有打印机信息存储在当前工作区中,则打开页面设置对话框。如果用户在对话框中选择了OK按钮,VFP会在临时表的第一条记录的EXPR、TAG和TAG2字段中写入打印机信息,然后恢复临时表到先前的RECNO()记录位置。 如果当前工作区没有包含相应的结构,则没有对话框被激活,并且返回0。 |
0 = 用户取消了操作或表结构无效。 1 = 用户选择了OK按钮,对FRX进行了相应的更新。 |
SYS(1037,2) | 如果当前工作区包含一个与FRX匹配的结构,VFP的默认打印机信息被写入到第一条记录的EXPR、TAG和TAG2字段中,然后恢复临时表到先前的RECNO()记录位置。 如果当前工作区不包含与FRX匹配的结构,则没有信息被写入到临时表中,并返回0。 |
0 = 表结构无效,无动作发生。 1 = VFP的默认打印机信息成功写入到FRX中。 |
SYS(1037,3) | 如果当前工作区包含一个与FRX匹配的结构,FRX中的打印环境信息被作为VFP的默认打印机,此外,并将打印环境信息重新写入到FRX中,这就可以防止无效信息的发生。 如果当前工作区不包含与FRX匹配的结构,则没有信息被写入到临时表中,并返回0。 |
0 = 无动作发生。 1 = VFP从FRX中读取并设置默认打印机信息。 |
GetFont()函数有如下的一些小改动。
GetFont()的第三个参数现在新增了一个可选值,可以传递P给函数,通知VFP只显示对于默认打印机的可打印字体,如下所示:
=GetFont(' ', 0, 'P')
在以前的VFP版本中,GetFont()的第4个参数用于识别FontCharSet。但是,现在传递0和1给第4个参数的含义有了一些变化。
在以前的版本中,会忽略第4个参数,并禁止字符集组合框,并且返回值中不包含FontCharSet部分。
APrinters()新增了一个参数,用来搜集打印机的信息。使用可选的第2个参数,会使生成的数组比以前多出3个附加列,如下所示:
lnPrinters = APrinters(laPrinters, 1)
前面的两列与以前版本相同,是打印机名称和打印机端口。新增的3列分别是驱动程序、注释和位置。
调用APrinters(),如果不包含可选的第2个参数,则提供与先前版本相同的行为,即:只返回两列。
Print Setup对话框现在已经被更名为Page Setup,并且更具现代化。Print对话框在Windows 2000和以后的操作系统中也更具现代化。
Page Setup对话框如图34所示,可以使用如下方法打开:
图34 新Page Setup对话框代替了旧Print Setup对话框
Print对话框如图35所示,可以使用如下方法打开:
图35 在Windows 2000或更后期的系统中,新的Print对话框更具现代化
VFP 9.0报表书写器在数据分组方面有一些小的增强。
数据分组的最大值已经从20增加到74。
在以前,报表中超过一列的位置被定义为一个数据分组水平面,浪费了许多空间。第一个位置(第1行,第1列)留下了许多空白,并且数据在第1行的第2列开始。同样的,在每个数据分组之间浪费了一个空白带,如图36所示。即使组标头带区的高度为0,VFP仍旧保留这个空间,如图37所示。带区条
图36 以前版本的VFP,在使用数据分组和水平列时浪费了许多的空间
图37 VFP的以前版本为组标头保留了空间,即使没有定义任何对象,也是这样
在VFP 9.0中,数据分组和水平列的行为发生了改变。当遇到一个数据分组时,将在下一个整行的第1列打印分组表达式的内容,该行的剩余部分作为遗留空白,并且不再用于打印细节内容。属于该数据分组的细节内容在分组打印行的后面的第1列开始打印,如图38所示。同样的,如果组标头的高度为0,则不再保留任何额外空间,如图39所示。
图38 在使用水平列和数据分组时,VFP 9.0不再象以前版本那样浪费空间
图39 在组标头带区设置为0高度时,VFP 9.0不再保留额外的空间
这种新行为,虽然避免了先前描述的浪费空间情况,但是可能会损坏一些已存在报表。不过,新行为的一个额外好处是:数据分组带区可以伸展为所有列的宽度,如图40所示。
图40 在VFP 9.0中,可以伸展组标头带区为多个列的宽度
除了在VFP 9.0报表书写器中的这些扩展性增强,新增的多细节带区功能是最大的改变,并且在报表中经常需要使用到这样的功能。该新增功能允许为父表的每条记录处理多个子表,一个这种类型报表的示例如图41所示。
图41 这个多细节带区示例报表的每个客户具有3个独立的细节带区
作为多细节带区,理解父表和子表如何一起工作是理解如何使用这些新增功能的关键。假设要写一个如图41所示的报表,对于这种情况的数据库如图42所示。
图42 一个保险公司的Customer(客户)、Members(成员)、Vehicles(车辆)和Homes(住址)数据库
Customer表是父表,并且为保险公司的每个客户包含一条记录,Members、Vehicles和Homes表是Customer的子表。Members表为每个客户的家庭成员保留一条记录,Vehicles表为每个客户投保的车辆保留一条记录,Homes表为每个客户的住址保留一条记录。
将其中的一个报表用于驱动报表是必需的,在这个示例中,Customer表是驱动表,如果要使用报表的数据环境来定义表,可以设置InitialSelectedAlias属性为该表,如果要使用代码来定义表,要确认在报表运行时Customer表在当前工作区中。
目标别名是用于为一个特定细节带区描述哪个表是驱动表的术语,在这个示例中,Members表是细节带区1的目标别名,Vehicles表是细节带区2的目标别名,Homes表是细节带区3的目标别名。
如果没有为细节带定义目标别名,将使用VFP以前版本的行为,换句话说,细节带区将处理每个父表的记录。但是,如果输入了父表的名称作为目标别名,却会得到非常不同的结果。对于父表中的每条记录,VFP处理父表中的所有记录,即在细节带区打印父表的每个记录。因此,如果有一个具有10条记录的父表,并设置细节带区的目标别名为这个父表,则最终报表会打印10遍这10条记录,总共100条记录。
了解关系在一个多细节带区中如何运作是相当重要的,VFP使用父表和子表之间的关系来定位所有记录,可以使用SET RELATION或SET SKIP来定义这些关系。如果在数据环境中打开了表,并且关系已经被定义在数据库中,则会自动使用这些关系。
如果使用代码打开表,下面的代码演示了如何为Insurance Customer Listing(保险客户列表)来设置表,如图41所示。
*-- 打开子表 USE Members IN 0 ORDER CustomerFK USE Vehicles IN 0 ORDER CustomerFK USE Homes IN 0 ORDER CustomerFK *-- 打开父表 SELECT 0 USE customer ORDER CustomerPK *-- 设置父表和子表之间的关系 SET RELATION TO CustomerPK INTO Members SET RELATION TO CustomerPK INTO Vehicles ADDITIVE SET RELATION TO CustomerPK INTO Homes ADDITIVE *-- 运行报表 REPORT FORM Insurance PREVIEW
缺省情况下,新建立的报表只具有一个细节带区。
附加细节带区可通过如图43所示的Optional Bands对话框添加,要调用这个对话框,可以从Report菜单中选择Optional Bands...。这是与以前Title/Summary相同的对话框,现在被重命名为Optional Bands,该名称更加符合添加附加选项。
图43 使用Optional Bands对话框上的Details微调控制来调节细节带区的总数
可以使用Details微调控制来添加报表所需要的细节带区数量,最多可以为每个报表定义20个细节带区。图44显示了选择3个细节带区后的报表设计器效果。
图44 该示例报表具有3个细节带区
目标别名通过Detail对话框赋值给一个细节带区(如图45所示)。该对话框可以从Report菜单中选择Edit Bands...,然后选择适当的细节带区来调用。也可以通过双击细节带区的灰色带区条来打开这个对话框。
图45 在Detail对话框上为每个细节带区定义Target alias
Target alias是一个表达式,因此,应当将表的名称放置在引号中,一旦定义了目标别名,表示细节带区的灰色带区条将显示目标别名,如图46所示。
图46 报表设计器把每个细节带区和一个数值以及与其关联的目标别名结合在一起,所以可以很容易地识别不同细节带区
在建立多细节带区报表时,在字段名称前面加上相应的别名是十分重要的,这可以防止产生字段是来自于哪个表的混乱情况。
多细节带区的另一个增强功能是可以为每个细节带区增加标头和脚注,这在某些方面类似于组标头和组脚注。对于每个被处理的父记录,发生如下事件:
要打开细节标头和细节脚注带区,可以在每个细节带区的Detail对话框中复选Detail Header/Footer,如图45所示。为了帮助从其他带区中分辨出细节带区,细节带区前面的三角是实心的,其他三角则是空心的,如图47所示。
图47 细节带区标以实心三角,其他带区标以空心三角
除了在报表设计器中标识每个细节带区外,Edit Bands对话框也使用带区的连续数字来标明每个细节标头带区、细节脚注带区和细节带区。Edit Bands对话框可以从Report菜单中选择Edit Bands...进行调用,如图48所示。
图48 Edit Bands对话框使用每个细节带区的连续数字来帮助识别每个细节带区、细节标头带区和细节脚注带区
需要注意的是:对于一个特定的细节带区,即使没有细节带区记录存在,相应的细节标头和细节脚注带区仍旧打印,如果不希望细节标头和细节脚注出现这种情况,可以对带区中的每个布局对象使用下面的Print when逻辑条件来屏蔽这种打印操作,并要确认为这些布局对象复选 Remove line if blank选择框。
NOT EOF(<target alias>)
与组标头和组脚注类似,细节标头和细节脚注具有一些相同的选项,如图45所示。Detail对话框具有一些新增选项: