高级编程技巧
至此,您已经学会了如何完成一些基础的编程操作,现在让我们来讨论一些更高级的技巧。在第七章的销售分析和报表解决方案中会涉及到许多这些高级技巧,因此现在我只对这些技巧作一些简单的介绍,您可以参考第七章,其中有关于本节中所包含的代码的详细解释。
保存和恢复视图
任何在报表系统中使用透视表组件的开发者都有可能需要向他们的用户提供如下功能:保存创建的报表视图,并在以后恢复这个报表――但报表中的数据将是最新的数据。透视表控件能够很容易的完成这个任务。在第七章的解决方案中讲述了这种技巧。
最常见的保存和恢复视图的方法是使用控件顶级接口的XMLData属性。这是一个读/写的属性,它返回一个XML格式的庞大的字符串。别将它和用作数据源的XML流混淆了――XMLData属性返回的是当前视图的布局,格式化,过滤,排序,和等等其它的定义信息。这个定义信息的字符串对视图进行了完整的描述,但是不包含任何数值。
如果需要保存当前视图的定义,应该读取这个属性的值,并将它保存在一个以后您可以重新提取它的地方。一个常用的方法是将这个字符串提交给一个ASP页面或CGI程序,该页面或程序会接着将这个字符串写入一个文件中,或写入数据库中并与当前用户进行关联。当用户需要再次查看报表时,程序应该从永久存储设备中提取这个字符串,并用它来设置XMLData属性。属性被设置后,透视表控件会清除所有当前显示的数据,连接到原始数据源(如果还没有连接到数据源),并执行相应的查询以重新创建报表。因此用户会在报表中看到所有最新的数据,但报表的布局和用户存储报表时完全一样。
对于视图中那些不再有效的部分,透视表控件会简单的丢弃它们。例如,如果在保存视图后,视图中的一个字段集在数据源上被删除了,透视表控件会忽略所保存的与这个字段集有关的任何信息,而不会在视图中尝试恢复它。对于不再包含在查询返回的数据集中的汇总值或成员,也是一样的。
可以运行随书光盘中的XMLDataProperty.htm文件来观看如何使用XMLData属性。页面顶部的按钮允许您获得XMLData属性的值,以及设置这个属性的值。此外,还可以清空透视表报表。请尝试着使用获得属性的值,清空透视表报表,以及重置属性这三个功能。
注意,连接信息(连接字符串,以及cube名称或命令文本)也包含在XMLData属性返回的字符串中。如果在用户保存报表和重新打开它之间数据源的位置发生了变化,透视表将无法连接到数据源,并产生一个错误。如果可能会发生这种情况,您应该添加错误处理代码来进行捕捉,并在将字符串传递给透视表控件之前调整其中的连接信息。连接字符串是存储在<x:ConnectionString>标签中的,数据成员(OLAP数据源的cube的名称)是存储在<x:DataMember>标签中的,而命令文本(用于表列数据源)是存储在<x:CommandText>标签中的。可以使用VBScript或微软VBA中的InStr和Replace函数轻松的查找和替换这些标签的内容。第七章中有关于XMLData属性和如何改变数据源的更多信息。
锁定视图
报表系统经常需要发布两种类型的报表:一种是每个人都可能需要查看的标准报表,另一种是用户可以修改报表结构,并保存的特殊报表。您可能需要对标准报表进行设置,以便用户不能在报表中删除或添加字段,但仍能够进行钻取和过滤。您也可能需要禁止过滤功能,只允许在视图中进行展开和收缩的操作。透视表组件提供了一些设置选项,可以帮助您锁定视图。它还提供了一些事件,可以用来监控当前用户正在进行什么操作;但是,并不存在禁止这些操作的通用机制。
XML历险
所有这些令人惊奇的XML的功能都应该归功于另一位OWC小组的著名开发者,Kevin Grealish,他花费了无数个夜晚来研究xml解析器,名称空间,以及那些不断变化的格式和标准。当我们开发透视表组件时,关于名称空间的xml标准仍然不断的被大幅度的修改。因为我们必须要读取Excel2000发布的XML数据,所以要使我们的代码处理xml的功能与Excel保持一致,这是一件艰苦长期的工作。而如果透视表报表的数据源是表格数据,我们的代码还必须与用来装载Excel保存的XML数据流的MDAC永久提供者保持一致。最后Kevin解决了这个矛盾――所以下次在您使用XMLData属性或使用XML作为数据源时,记得要感谢Kevin呦。
如果不允许用户在行轴或列轴上添加或删除字段,应该将透视表控件的AllowGrouping属性设置为False。当该属性为False时,透视表控件就会禁止用户在行轴或列轴上添加或删除字段。不过用户仍然可以将字段集添加到过滤轴上,以及将新的汇总值添加到视图中。
如果不允许用户改变任何过滤设置,应该将AllowFiltering属性设置为False。当这个属性为false时,透视表组件允许用户打开过滤下拉列表,但是不允许用户改变当前的过滤设置――换句话说,用户可以看到当前的过滤条件,但不能改变它们。控件还会禁止用户将字段集添加到过滤轴上。
为了保证用户不能改变应用到报表上的格式化信息,可以将AllowPropertyToolbox属性设置为False。这会使工具条上的属性工具箱按钮,以及相应上下文菜单中的菜单项不可用。这样用户甚至都不能打开属性工具箱了。但是,用户还是可以使用键盘格式化命令,例如Ctrl-B,Ctrl-I,和Ctrl-U。请查看随书光盘中的LockDownView.htm例子文件,以练习这些属性的用法。
判断所选择的部分,以进行穿透钻取
和图表组件一样,透视表组件在顶级接口中也有一个Selection属性,用来返回当前被选择的对象。而且这个属性返回的对象的类型也是变化的,因此需要使用VBScript和VBA中的TypeName函数来判断返回对象的类型。请运行随书光盘中的DeterminingSelection.htm文件以查看透视表控件可以从这个属性返回哪些不同类型的对象。
如果知道当前被选择的是什么,就可以模拟一些有趣的功能。在这里讨论一个这样的功能:通过穿透钻取以获得详细信息。
OLAP系统善于显示高层的数据摘要,并允许用户通过“向下钻取”来获得各层的详细信息。但是,用户最终会到达超立方体中的最底层,并常常需要“穿透钻取”的功能以获得组成最低层汇总值的下层详细信息。如果当前使用的是一个表列数据源,则透视表控件可以自动完成这个工作,因为在表列数据源的情况下,详细数据总是可用的。但是,对于OLE DB for OLAP来说,还没有一种通用方法,可用来提取合计后面的详细信息的集合。尽管如此,垂直解决方案通常都能获得足够的信息,以找出那些包含了详细数据的表列数据源,并从而能够确定应该组成一个怎样的sql语句,以获得组成这个合计值所需的所有成员的详细信息数据行。??????。
下面取自随书光盘中DeterminingSelection.htm例子文件的代码,显示了如何从被选择的汇总值中获得所需的全部信息,以生成提取汇总值的详细信息的sql语句。
Sub PivotTable1_SelectionChange() ' Local variables Dim sel ' Temporary selection object Dim sFilters ' Current filter strings Dim fSet ' Temporary fieldset reference
' Grab the current selection Set sel = PivotTable1.Selection
' There are many types of objects that the selection ' could be, depending on what was selected ' Examples include PivotAggregates, PivotTotals, ' PivotMembers, PivotFields, and PivotView ' You can use the TypeName function to determine the type ' of the object ' ' If the user selected an aggregate number, ' the TypeName function will return "PivotAggregates"
' Set the type name label lblType.innerText = TypeName(sel)
' If the type is "PivotAggregates", show how to get ' the row and column members that define that aggregate ' You could of course make this a Select Case statement ' and handle other selection types If TypeName(sel) = "PivotAggregates" Then ' PivotAggregates could contain many items, but since ' this is a sample, I will just work with the first item Set pivotagg = sel.Item(0)
' Set the value label lblVal.innerText = pivotagg.Value
' Get the total caption, the row and column members, ' and the current filters lblTotal.innerText = pivotagg.Total.Caption lblColMems.innerText = BuildFullName(pivotagg.Cell.ColumnMember) lblRowMems.innerText = BuildFullName(pivotagg.Cell.RowMember)
For Each fset In PivotTable1.ActiveView.FilterAxis.FieldSets sFilters = sFilters & fset.Caption & "=" & _ fset.FilterMember.Caption & ", " Next lblFilters.innerText = sFilters
Else ' Selection was something other than a PivotAggregates ' object. Clear the labels. lblVal.innerText = "" lblTotal.innerText = "" lblRowMems.innerText = "" lblColMems.innerText = "" lblFilters.innerText = ""
End If 'typename(sel) = "PivotAggregates"
End Sub 'PivotTable1_SelectionChange() |
这段代码首先使用TypeName函数来判断被选择部分的类型;如果类型是PivotAggregates,则表明用户选择了一个汇总值。代码就会提取汇总值的数值,以及汇总值所属的合计的值。(规定了一个汇总值只能属于一个合计。)接着,代码使用辅助函数BuildFullName,来创建一个包含轴上这一层的所有成员的字符串。BuildFullName函数的代码如下:
Function BuildFullName(PivotMem) ' Local variables Dim pmTemp ' Temporary PivotMember reference
' Start by getting the current member's name sFullName = PivotMem.Caption
' Set the temporary reference to the current member Set pmTemp = PivotMem
' Navigate up the parent hierarchy until you hit nothing While Not(pmTemp.ParentMember Is Nothing) Set pmTemp = pmTemp.ParentMember sFullName = pmTemp.Caption & "-" & sFullName Wend
' Return sFullName BuildFullName = sFullName
|