![]() ![]() |
||||||||||||||||||||||||||||||||||||||||||||||||||
VisualFoxPro9.0中扩展报表系统功能 | ||||||||||||||||||||||||||||||||||||||||||||||||||
作者:佚名 文章来源:不详 点击数 更新时间:2008/4/18 14:51:22 文章录入:杜斌 责任编辑:杜斌 | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
在Visual FoxPro 9.0的报表系统出现了难以置信的改进。在本文中,我只讨论几个新增功能中的一个——扩展运行时报表引擎(runtime reporting engine)的能力。 Visual FoxPro小组在处理运行时改进工作时一直紧记着几个目标,包括:
报表系统的架构 Visual FoxPro 9以前版本中的报表系统类似于单片电路:它处理所有细节信息,只有少量的例外(用户定义的函数、报表带条的OnEntry和OnExit表达式等等),在报表运行的时候,你是不能与它交互操作的。 新的报表引擎把报表的功能分成了两部分:现在的报表引擎只处理数据和对象定位;一种新对象(报表监听器)处理显示和输出的事务。由于报表监听器是类(class),因此我们现在可以使用以前梦寐以求的方式与报表进程交互操作。 新的报表语法 Visual FoxPro 9支持使用旧的报表引擎运行报表;你可以像以前一样使用REPORT命令(尽管你可以使用新命令重载REPORT的行为)。为了得到新式的报表行为,必须使用REPORT命令的OBJECT子句。OBJECT子句支持两种使用方法:指定报表监听器和指定报表样式。微软把它归纳为对象辅助(object-assisted)报表。 报表监听器是提供新式报表行为的对象。报表监听器是基于Visual FoxPro 9的新的基础类ReportListener的。为了让Visual FoxPro 9使用报表指定的监听器,需要实例化监听器类,并在REPORT命令的OBJECT子句中指出该对象的名称。下面是一个例子:
如果不希望手动实例化监听器,可以通过指定报表类型让Visual FoxPro自动为你完成实例化过程,例如:
已经定义好的类型有:
使用这种方式运行报表的时候,将调用新的_REPORTOUTPUT系统变量(默认情况下它位于Visual FoxPro主目录的ReportOutput.APP中)中指定的应用程序来决定指定该类型使用哪种监听器类来实例化。它是通过查看APP中内建的监听器注册表(尽管你可以指定它使用一个外部表)中的监听器类型来实现的。如果它找到了需要的类,它就实例化该类并传递该监听器对象的一个引用到报表引擎中。因此,在REPORT命令中使用OBJECT TYPE 某种类型的效果与下面的代码的效果是相同的:
报表监听器 在报表运行的过程中,伴随着报表事件的发生,Visual FoxPro把这些事件暴露给基于ReportListener基类的对象。Visual FoxPro帮助文件记录了ReportListener的属性、事件和方法(PEMs),但是在本文中我只讨论其中最有用的一些。 表1列举了ReportListener类的最常使用的一些属性。 表1:ReportListener类的一些有用属性
表2显示了经常使用的ReportListener的事件和方法。 表2:ReportListener的一些有用的事件和方法
ReportListener子类
报表引擎只与REPORT或LABEL命令中指定的监听器(称为lead listener,头监听器)通讯。当报表引擎引发报表事件的时候,头监听器调用它的后继者的适当方法,而后继者又调用自己的后继者的适当方法,这样一直沿着链进行下去。这种架构就是响应链,链中的任何监听器都可以决定执行某些操作或者把消息传递给链中的后继节点。 _ReportListener的另外一种有趣的能力是链接报表。AddReport方法把一个报表添加到定制的ReportFileNames集合中。你给这个方法传递报表名、可选参数还有将要使用的报表子句(例如RANGE子句)和另一个监听器对象的引用。RemoveReports方法从集合中删除所有的报表。RunReports运行报表;传递进去的第一个参数是.T.的时候将在报表运行后从集合中删除报表,第二个参数为.T.时将忽略AddReport指定的任何监听器。下面是一个示例,它运行了两个报表,但是表面看起来好像是一个报表:
HTML和XML输出 由于开发小组的设计目标之一是提供更多的报表输出类型,所以Visual FoxPro 9包含了_ReportListener的两个子类,叫做HTMLListener和XMLListener,分别用来来提供HTML和XML输出。这些监听器都内建在ReportOutput.APP中,但是在_ReportListener.VCX中也可以使用。 监听器类型5指定为HTML输出、4指定为XML输出,因此你可以使用下面的命令把输出指定为HTML:
但是如果这样操作你将无法控制将要建立的文件名和其它一些设置。作为替代,调用ReportOutput.APP可以让你得到需要的监听器引用、设置需要的属性、接着告诉REPORT命令使用该监听器。 下面的代码从MyReport报表中建立了一个叫做MyReport.HTML的HTML文件。当你指定为类型5时,ReportOutput.APP使用自己内建的HTMLListener类提供输出。
下面的代码从MyReport报表中建立MyReport.XML文件,只包含了数据。在这种情况下,代码片断使用了XMLListener类(类型4)。
HTML输出实际上使用XML监听器生成XML,接着使用XSLT来生成最终的HTML。 这两个监听器类都有一些附加的属性,你可以使用这些属性进一步控制输出。我推荐你查阅一下Visual FoxPro文档。此外,由于它们是_ReportListener的子类,所以监听器类支持_ReportListener类的能力,包括链接监听器和运行多个报表。下面是一个同时输出XML和HTML的示例:
建立自己的监听器 由于报表监听器是类,所以报表运行的时候,你可以建立子类来改变报表系统的行为。 例如,我一直希望在运行时动态地格式化字段。在某些条件下,我希望字段用红颜色打印,其它条件下用黑颜色打印。一个字段有时需要加粗而其它时候则不需要。 改变字段在报表中的显示样式的关键是EvaluateContents方法。这个方法在字段被显示之前调用每个字段对象,赋予监听器改变字段样式的权力。该方法的第一个参数是被处理的字段对象的FRX记录号,第二个参数是包含属性和字段对象信息的对象(请查看Visual FoxPro帮助文件中该对象包含的属性列表)。你可以修改任何属性来改变报表中字段的样式。如果你是这样做的,那么还需要把该对象的Reload属性设置为.T.,以通知报表引擎你已经改变了一个或多个属性。 列表1显示了定义_ReportListener的一个子类(叫做EffectsListener)的代码片断,该子类处理可能应用于报表中的字段的不同效果类型。这些效果通过效果处理对象来应用,而这些对象都存储在EffectsListener的oEffectsHandlers属性的集合中。每种效果处理对象处理一种效果。 在报表被处理的时候,监听器需要确定哪些字段应用了效果。它在EvaluateContents方法中查看每个将要显示的字段,实现这种功能。EvaluateContents调用SetupEffectsForObject,它调用每个效果处理程序的GetEffect方法来决定是否给该字段应用某种效果。GetEffect查看FRX中的字段记录的USER备注来指令应用哪种效果。如果该字段需要某种特定的处理程序,该处理程序就被添加到处理该字段的处理程序集合中(因为每个字段可能应用多个效果)。 这意味着在每条记录的每个字段上都会调用EvaluateContents,可是没有必要在一个特定字段上进行多次效果检查(这样做将导致报表性能下降)。因此,BeforeReport建立了一个数组,它存储了FRX中记录的行。如果该数组的第一列为默认值.F.,说明监听器还没有检测将要显示的字段的效果,因此EvaluateContents做出检测并把该数组的第一列设置为.T.,这样FRX就不会再次检测了。 在检测某个字段是否应用了效果后,EvaluateContents进入到该字段的效果处理程序集合中,调用每个程序的Execute方法执行必要的操作。 DynamicForeColorEffect就是一个效果处理程序。它用下面的格式查看报表中某个字段的USER备注:
列表1中使用的TestDynamicFormatting报表的ORDERDATE字段的USER备注中有下面的代码片断指令;它告诉EffectsListener:DynamicForeColorEffect对象应该调整字段的颜色,当装运时间大于订单时间10天以上就用红颜色显示,否则就用黑颜色显示:
DynamicForeColorEffect的Execute方法通过把传递到EvaluateContents中的字段属性对象的PenRed、PenGreen和PenBlue属性设置为适当的颜色,并把Reload设置为.T.(告诉报表引擎已经做了一些修改)来改变字段的颜色。 DynamicStyleEffect使用类似的指令来改变字体样式。此处使用的样式必须是一个数值:0是正常体、1是粗体、2是斜体、3是粗斜体。TestDynamicFormatting报表中的SHIPVIA字段的USER中有下面的指令,它引起SHIPVIA为3(因为该字段的表达式实际上显示为Mail)的字段显示为粗体,否则为正常体。
DynamicStyleEffect的工作方式与DynamicForeColorEffect类似,只是改变了字段属性对象的Style属性。 运行TestDynamicFormatting.PRG将出现图1所示的输出结果。
自定义显示 你不仅可以改变字段的外形——你还几乎可以在报表监听器中执行自己需要的任何事务。ReportListener的Render方法负责在报表页面上绘制每个对象。你可以重载这个方法来实现各式各样的输出。
列表2中的代码来自TestColumnChart.PRG,它运行了图2中所示的TestColumnChart.FRX报表,建立了图3所示的输出。请注意,输出结果与报表布局之间有很大的差别,字段和形状(shape)没有显示出来,而绘制示例Northwind数据库中的Category_Sales_For_1997视图的内容的条状图却显示出来了。这部分原因是字段上的Print When子句防止它们被打印出来,但最大的原因在于这个报表使用的监听器类(ColumnChartListener)把Summary(汇总)报表条带中的形状对象更替为列条状图。 下面让我们看看这个监听器是如何实现这种功能的。 ColumnChartListener的Init方法把aColumnColors数组初始化为报表中的列将会使用到的颜色。请注意,GDI+的颜色与RGB()函数返回的值有一点点不同,因此它使用CreateColor方法来进行必要的转换。如果你希望使用不同的颜色集,你可以从ColumnChartListener衍生出子类或者实例化ColumnChartListener之后,在数组中存储另一组颜色集合。请注意,我们只定义了八种颜色,如果报表中的列多于八个,每种颜色可能用于多个带条。
BeforeReport方法实例化一个GPGraphics对象到自定义的oGDIGraphics属性中。GPGraphics是_GDIPlus.VCX中的一个类。它和其它_GDIPlus类都被用在DrawColumnChart方法中来绘制条状图的组件。 GPGraphics需要一个将要显示的GDI+表面的句柄。幸运的是监听器已经有这样一个句柄,存储在GDIPlusGraphics属性中。唯一的复杂因素是该句柄在每个页面上都会改变,因此当标题或页面头部带条被处理的时候,BeforeBand方法(在报表条带被处理前调用)调用GPGraphics对象的SetHandle方法来赋予它句柄。 在报表被处理的时候,监听器必须确定图表中的标签和值来自于何处。在字段将要被显示的时候,它在EvaluateContents方法中通过查看每个字段得到这些信息。如果该字段在FRX中的USER备注包含了LABEL(与CategoryName字段中一样),就表明该字段应该用于条状图的标签。USER备注中的DATA(例子中是CategorySales字段)表明这个字段用作图表的值。有了前面讨论过的EffectListener类之后,根本就不需要多次检查USER备注,因此在示例中使用了相同的机制——在一个数组属性中存储标识以表明某个字段是否被处理过。 如果监听器仍然没有检测某个将显示的字段的USER备注,EvaluateContents将执行这种检测,设置数组中的标识以表明该字段是否用作标签或值,并把数组的第一列设置为.T.,这样FRX记录就不会再次检查了。如果某个字段用作标签或值,EvaluateContents相应地更新aValues数组。 AdjustObjectSize与EvaluateContents类似,除了它在形状(shape)上而不是在字段上调用。AdjustObjectSize检查当前形状的FRX记录的USER备注中是否存在COLUMNCHART,如果存在就表明这个形状应该被条状图代替。有了EvaluateContents之后,监听器只需要检查一次,因此它也使用了相似的逻辑。 Render方法负责在报表上绘制对象。如果将被绘制的对象是一个被条状图代替的形状,它就调用自定义的DrawColumnChart方法,带上NODEFAULT防止该形状被绘制出来。否则,该对象会被正常地绘制出来(请注意,如果没有DEDEFAULT(),初始的行为是绘制对象,因此这个参数是必要的)。 DrawColumnChart计算出图表显示的最大值,这样它才知道条带应该多大,接着它从_GDIPlus类中建立一些执行绘制操作的对象。它调用DrawLine方法绘制出图表的垂直和水平边界,接着进入aValues数组,使用DrawRectangle绘制出每个值的条带并使用FillRectangle用适当的颜色填充。DrawColumnChart使用相同的DrawRectangle和FillRectangle方法绘制方框、使用DrawStringA绘制标签,给图表添加了一个方框和标签图例。 其中一些绘图属性来自于自定义属性中的值,这使得绘制图表更加灵活。例如,属性cLegendFontName和nLegendFontSize指定了图例标签使用的字体和大小,nLegendBoxSize指定了将要绘制的方框的大小。你可以在代码列表2开始处看到这些属性的注释。 微软已经揭开了Visual FoxPro报表系统的面纱!通过给ReportListener对象传递报表事件,我们可以与这些事件交互作用来执行一些自己希望实现的事务,其范围从提供各式各样的输出类型到动态地改变被显示的对象。Visual FoxPro团体使用这些新特性会实现什么样的效果?难以想象! |
||||||||||||||||||||||||||||||||||||||||||||||||||
![]() ![]() |