進階編程技巧
至此,您已經學會了如何完成一些基礎的編程操作,現在讓我們來討論一些更進階的技巧。在第七章的銷售分析和報表解決方案中會涉及到許多這些進階技巧,因此現在我只對這些技巧作一些簡單的介紹,您可以參考第七章,其中有關於本節中所包含的代碼的詳細解釋。
儲存和恢複視圖
任何在報表系統中使用透視表組件的開發人員都有可能需要向他們的使用者提供如下功能:儲存建立的報表視圖,並在以後恢複這個報表――但報表中的資料將是最新的資料。透視表控制項能夠很容易的完成這個任務。在第七章的解決方案中講述了這種技巧。
最常見的儲存和恢複視圖的方法是使用控制項頂級介面的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 End Function 'BuildFullName() |
代碼的關鍵區段是中間的While迴圈。前面提到過,每一個成員都有一個ParentMember屬性,用於返回這個成員的父成員(如果存在的話)。如果不存在父成員,屬性將返回Nothing(在其它語言中它是一個指向Null對象的指標)。迴圈根據在層中向上遊歷時所遇到的每一個成員的Caption屬性來產生一個字串。當然,您可能需要使用Name屬性,或甚至是UniqueName屬性(而不是Caption屬性)來獲得SQL語句中WHERE子句所需要的值。
顯示空成員
預設情況下,透視表組件是不顯示報表中那些每個儲存格都不包含資料的行或列的。但是,有時沒有資料的行或列也是有意義的,您需要顯示它們。可以使用下面的代碼來實現這個功能:
PivotTable1.ActiveView.RowAxis.DisplayEmptyMembers = True PivotTable1.ActiveView.ColumnAxis.DisplayEmptyMembers = True |
DisplayEmptyMembers是行或列軸的一個屬性,預設值為False。對於OLAP資料來源來說,這個設定會影響透視表組件發往資料來源的MDX查詢語句,因此,如果這個屬性被設定為True,會傳回更多的資料。而對於表列資料來源來說,它也會影響用來查詢臨時cube的MDX語句,但是因為此時用戶端包含了所有的資料,所以這個屬性幾乎不會影響效能――除非臨時立方體非常稀疏。
顯示可視合計值
預設情況下,透視表組件只顯示“可視合計值”。這意味著報表中的小計和總計是當前報表中顯示的資料的合計。如果一個成員被過濾了,那麼這個成員之上的小計和總計就不會包括這個成員的值。這樣,即使您過濾了某些成員,報表中顯示的所有資料也會被累加起來。
假設在行軸上有一個地理層,其中包含兩個層級:州和城市。每個州的小計代表了它的所有城市的合計。現在如果過濾掉城市Seattle和Redmond。華盛頓州的合計應該不變,還是變成現在仍然顯示在報表中的所有城市的合計?這確實是一個爭論的很激烈的問題。不過幸運的是,透視表控制項支援這兩種模式。
透視表組件預設顯示可視合計值。如果需要控制項顯示所有成員的合計值,而不考慮成員是否可視,請使用下列代碼:
PivotTable1.ActiveView.TotalAllMembers = True |
在TotalAllMembers屬性為True的情況下,透視表控制項會在小計和總計的旁邊顯示一個小星號,以顯示這個數值並不是僅代表可視成員的。顯示星號的行為是和Excel 2000的OLAP透視表報表功能中的行為相同的。如果您能夠在透視表控制項的下方使用HTML對星號的含義進行解釋,那就更完美了。
將合計標題顯示為列名
預設情況下,透視表組件在報表上將合計標題顯示為最內層的欄位標題。但有時,可能需要將這些合計標題顯示為最內層的列名。您可以使用下面這一行代碼輕鬆的完成這個任務:
PivotTable1.ActiveView.TotalOrientation = plTotalOrientationRow |
TotalOrientation屬性可以被設定為PivotViewTotalOrientationEnum常量中的一個值,該常量包含標明行方向的值,和標明列方向的值。透視表控制項只能將合計的標題放置在最內層的行軸或列軸上。
自動延伸
預設情況下,透視表組件會在將所有的欄位和成員添加到視圖中時,使它們保持收縮狀態。使用者可以沿軸以任何路徑進行展開,以獲得他們需要的更詳細的資訊。您可能需要對您的報表進行配置,使得在所有的欄位和成員被添加到視圖中時,會自動延伸。因為這可能會產生一個巨大、難以處理的顯示介面,所以應該只在自動延伸功能產生的是一個大小適當的報表時采使用這個功能。
可以使用下面這一行代碼來開啟自動延伸功能:
PivotTable1.MemberExpand = plMemberExpandAlways |
這個屬性的值取自PivotTableMemberExpandEnum枚舉值,該枚舉值中其它可用的值有plMemberExpandNever和plMemberExpandAutomatic(預設值)。
當在一個web伺服器上使用透視表控制項產生報表的GIF圖形時(下一節我們將討論這個功能),這個屬性也很有用。設定這個屬性為plMemberExpandAlways,以便成員總是被展開,這樣就能保證在產生圖形時,報表中的所有資訊都被顯示出來――這是很重要的,因為使用者得到的只是報表的一個靜態圖形,是不能展開圖形中的成員的。
在Web伺服器上使用透視表組件
和其它Office Web組件一樣,透視表組件也能以沒有使用者介面的方式被使用,您可以以記憶體對象的形式來建立透視表組件,然後串連到一個資料來源,使用程式動態產生一個報表,最後產生當前報表的GIF圖形,或通過遍曆報表視圖中的元素來產生您自己的html形式的報表。在web伺服器上使用透視表控制項的方式,可能比書寫MDX查詢語句,然後使用ADO MD,最後手工格式化查詢結構要更加吸引人。
當在一個伺服器上使用透視表控制項時,可以使用目前為止我們所討論的所有編程技巧。唯一一個您需要瞭解的新的編程元素是ExportPicture方法。這個方法幾乎和第三章中討論的圖表組件的ExportPicture方法完全一樣。下面的代碼可以實現將當前報表輸出為一個GIF圖形檔案:
Set fsoTemp = CreateObject("Scripting.FileSystemObject") strFilename = fsoTemp.GetTempName PivotTable.ExportPicture Session("strTempFilePath") & strFilename, _ "gif", PivotTable.MaxHeight, PivotTable.MaxWidth Response.Write "<IMG SRC='" & Session("strTempURLPath") & _ strFilename & "'>" Session("Pivot" & Session("cntPivotImages")) = strFilename Session("cntPivotImages") = Session("cntPivotImages") + 1 |
這段代碼的關鍵行是對ExportPicture方法的調用。FileSystemObject(微軟指令碼執行階段程式庫的一個類)的一個執行個體獲得了一個用於新的GIF圖形的臨時檔案名稱,然後程式將這個檔案名稱加上根路徑,形成的結果傳遞給ExportPicture方法,作為第一個參數。和圖表組件一樣,第二個參數必須是”gif”。最後兩個參數指明了高度和寬度,但這裡和圖表組件中的編程不同(圖表組件中可以將輸出的圖形改變成任何尺寸),您必須使用透視表組件的MaxHeight和MaxWidth屬性,否則產生的報表圖形可能不完整。