第二章第二節 工作表元件的進階功能 我們已經討論了工作表元件的大部分準系統,現在讓我們轉向一些進階功能。大部分的這些進階功能Excel2000都不包含,因為這些是組件專門需要的特殊功能。而那些Excel2000中存在的功能,在工作表元件中也被增強,使得可以提供一些新的功能。
屬性綁定和即時資料 “屬性綁定”是工作表元件中最新奇的新功能之一,它是指控制項能夠將同一個web頁面上其它對象的屬性和方法用作單元值或公式參數的能力。試算表控制項使用標準的COM機制來實現綁定到屬性,並在資料來源對象告知屬性值已經改變時,自動接收新值,並重新計算相關聯的單元。 例如,如果您開發了一個組件,它公開了一些屬性和方法以返回一個給定股票看板代號的最後銷售價格,您就可以使用試算表控制項來查看這個資訊,並在價格更新時觀察到它的變化。如果試算表的其它部分――例如目前使用者的組合資訊(
譯者註:金融專業的術語。)――引用了這個最後銷售價格值,那麼工作表元件也會在值改變時重新計算這些單元。 可以象下面這樣在單元中輸入一個函數來建立屬性綁定:
| =document.StockTicker.Quote("msft").LastSale |
只要公式以“=document.”開始,工作表元件就認為緊接著的是一個屬性綁定。它會認為運算式的下一部分將是頁面上另一個元素的ID,而運算式的其餘部分將會該元素的一個屬性,或是由一個方法返回的另一元素的一個屬性。您可以在方法調用中使用單元引用作為參數,而試算表控制項必然會將真實的值傳遞給方法。
單元綁定到的對象既可以是另一個COM對象,也可以是頁面上的任何HTML元素,例如一個文字框或一個下拉式清單。這樣您就可以直接在重算模型中包含頁面上其它的資料,而不必寫指令碼來將HTML元素的值輸入到試算表單元中。 屬性綁定機制經常在提供即時資料的環境中被提到,因為它包含了實現提供動態資料的兩個必須的特性。l 當得知屬性被修改時,工作表元件立刻更新單元,而不會有一個固定的輪詢間隔。l “即使使用者在編輯其它單元,或執行命令時”,工作表元件依然會繼續監聽新值並更新單元。人們對Excel的DDE連結機制的一個普遍抱怨就是它不能完成這個功能,因此我們確信應該在試算表控制項的屬性綁定特性中避免同樣的錯誤。
屬性綁定可以指向工作表元件自身嗎? 大膽的讀者可能已經在思考將工作表元件中的單元綁定到組件本身,或綁定到另一個工作表元件上的可能性。(thinking wildly)從表面上看,似乎可以通過這個機制,輕鬆的支援關聯不同的試算表上的單元。 但是,唉,這是不可能的。試算表控制項自身禁止了這種支援,因為可能造成的重入引用和循環參考是相當恐怖的。試算表控制項只知道它被綁定到另一個對象上(不是自身或另一個試算表控制項)――因此它無法確保引用沒有產生會掛起重算鏈的一個依賴迴圈。 為了實現將當前工作表元件中的單元關聯到另一個工作表元件的單元上,您必須在另一個組件中通過代碼來響應Change事件,並將新值寫入到相關聯的單元中。
當然,請記住工作表元件處理屬性變化通知的能力完全依賴於它重算當前模型的時間。對於中小型的模型來說,通常不存在問題,因為重算只會花費一秒或者更少的時間――比起大多數人所能容忍的資料更新時間要快的多。然而,如果模型相當巨大,試算表控制項也只能儘快重算來提供新數值,這可能就比新值到達的速度慢多了。 在第十章將講述關於屬性綁定的更多細節,在該章中,您將看到如何在Visual Basic中建立一個股票行情控制系統,為股票組合表格提供即時的報價。
函數外掛程式 和在Excel中一樣,開發人員可以使用函數外掛程式,將新功能添加到工作表元件中。與符合Excel私人的XLL模型的外掛程式不同,工作表元件的函數外掛程式是以COM對象的方式被建立的。這種對象所公布的任何方法都以內建函式的形式被添加(
譯者註:什麼是potential function?),這樣您就可以在公式中象使用那些Excel內建函式一樣使用它。 例如,如果您開發了一個COM對象,包含了一個叫做SumTopN的方法,該方法根據傳入的一列數值返回前N位元值的和,那麼您可以在工作表元件中通過下列代碼來使用這個函數,就像在Window_onLoad事件中一樣:
| Spreadsheet1.AddIn MyObject |
MyObject變數應該指向自訂函數對象的一個執行個體。為了保證您的對象可用,在頁面上使用<object>標籤,並將對象的id傳給試算表控制項的AddIn方法,如下所示:
| <object classid="clsid:0002E510-0000-0000-C000-000000000046" id=Spreadsheet1></object><object classid="clsid:ClsidOfYourObject" codebase=PathToCABfileOfYourObject id=MyObject><script language=VBScript>Spreadsheet1.AddIn MyObject.Object</script> |
<object>標籤中的codebase屬性告訴IE如果用戶端機器上沒有class ID所引用的對象,應該從哪裡去安裝這個對象。如果需要瞭解更多關於codebase屬性的資訊,可以查看MSDN庫中的IE和DHTML主題。 只有在Internet Explorer或其它使用不同的介面來封裝對象的容器中需要使用Object屬性。在我們剛才討論的HTML檔案中,MyObject實際上指向了一個稱作object的COM物件類型(由<object>標籤聲明),而不是標籤建立的實際COM對象。Object屬性返回一個指向實際的COM對象的指標。 在Visual Basic中,您還是需要調用AddIn方法,但是需要傳遞一個指向您建立的類的執行個體的變數。例如:
| Dim MyAddIn As New FunctionLibSpreadsheet1.AddIn MyAddIn |
在C++中,技巧是一樣的,不過您應該使用coCreateInstance函數並向AddIn方法傳遞指向您的對象的IDispatch介面的引用。 工作表元件實際上使用這種外掛程式機制來裝載不常使用的函數。Owc主要的dll檔案Msowc.dll並沒有實現試算表控制項的所有函數,那些不常使用的函數實際上是由Msowcf.dll來實現的(“f”代表擴充函數庫);當第一次使用它們時工作表元件自動將他們添加到外掛程式列表中。擴充函數通過包含一系列方法的COM對象實現,其中,每一個方法對應一個公布的函數。 您可能會懷疑是否函數外掛程式與之前所描述的屬性綁定機制有所不同。答案是肯定的。屬性綁定監聽值改變時資料來源的通知資訊,而函數外掛程式僅在函數的輸入變化(或影響輸入的單元變化)時被調用。這基本上是推模式和拉模式的區別:屬性綁定類似推模式;當它認為必要時會將新值推入試算表控制項中。而另一方面,函數外掛程式沒有到工作表元件的通訊通道;工作表元件決定什麼時候需要調用外掛程式函數來計算新值。 有時推和拉的界限是模糊的,尤其是當您意識到IE將頁面上的指令碼函數以對象的方式暴露出來,這種能力所造成的奇怪而相當有趣的副效果時。您頁面上的所有各種<script>塊都以被名為Script的DOM對象公布出來,在<script>塊中定義的每一個函數或子方法都被當作Script對象的一個方法。這就意味這您可以將您頁面上的指令碼函數用作您試算表中的函數,不過您來完成這個功能的機制更像是屬性綁定,而不是外掛程式函數。 例如,假設您在頁面上有這樣一個<script>塊:
| <script language="VBScript">Function VBDateAdd(interval, number, date) On Error Resume Next VBDateAdd = DateAdd(interval, number, date)End Function</script> |
工作表元件不包括複雜的日期操作函數。不過,VBScript提供了靈活的DateAdd函數,允許您在一個給定的日期上加(或減,或使用一個負數作為一個內部的參數)任何數目的時間間隔。為了使工作表元件能夠使用這個函數。前面的<script>塊定義了一個名為VBDateAdd的函數,返回VBScript函數DataAdd的結果。為了在您的試算表中使用VBDateAdd函數,在您需要在顯示結果的單元中輸入下列的公式:
| =document.script.VBDateAdd(B1, B2, B3) |
這個公式將單元B1,B2,B3的當前值分別用作間隔,數目和日期的參數值。這個公式和其它公式一樣,被放置在依賴鏈中,當那些輸入的單元改變時,工作表元件會調用這個函數,傳入新的輸入值並顯示新的結果。 使用指令碼函數有它的優點和不足。指令碼是以解釋方式來執行的,這意味著他們將比編譯的代碼要慢一些。指令碼也受限於指令碼語言的功能和與用戶端機器的互動能力,以及網路的限制(因為安全限制。)不過,編譯的對象需要下載和在用戶端機器上進行安裝,在某些機構中可能不允許這樣,還有,如果對象編寫的不完善,測試不充分,可能會給用戶端機器帶來潛在的負面影響。Web頁面中的指令碼定義上是”安全”的,並且因為他們是即時解釋執行,因此他們不需要下載和安裝額外的檔案。 當然,只有在容器是IE時才會談到指令碼函數。例如,如果您在vb表單中使用工作表元件,您仍然可以使用函數外掛程式,不過這裡沒有頁面上的指令碼塊的概念。請注意AddIn方法傳入一個COM對象的引用,因此如果您在您的應用程式中使用vb,您可以使用工程中的任何公用類作為一個函數外掛程式的對象。只需建立該類的一個執行個體,然後將它的引用傳給試算表控制項的AddIn方法。
可視地區和自動調整 試算表應用程式經常只需顯示試算表介面的一部分,而不是顯示所有的行和列。例如,時間表應用程式應該顯示足夠的行和列,以便使用者在輸入他(她)的工作時間時不必看到資料周圍大量的空白行、列。試算表控制項通過ViewableRange屬性來完成這個任務,您也可以在設計階段通過屬性工具箱來設定它。 “可視地區”定義了試算表要顯示多少行、列。預設是顯示所有的行、列,不過您既可以在指令碼中改變它,也可以在設計階段通過屬性工具箱中改變它,可將它改變為任何有效地區引用。例如,將地區設定為A1:D6,使試算表只顯示四行六列。而其它部分則顯示為一個空白的灰色地區;使用者既不能在可視地區之外選擇,也不能移動到可視地區之外。可視地區之外的單元仍然存在,並可以在指令碼代碼中被引用,但是使用者看不到這些單元,也不能和它們互動。如果需要隱藏對照表(
譯者註:也就是我們常說的代碼錶,例如:資料庫多個表中都有單位ID列,那麼單位001,002,……具體代表什麼單位,就在單位代碼錶,既單位對照表中定義。)或不希望使用者看到的中間計算值,這是一個極好的方法。 可以通過代碼動態調整可視地區,因此您可以調整可視地區來響應其它使用者的事件。還要注意可視地區具有和普通地區一樣的自動調整特性――如果開發人員或使用者在可視地區中插入了一行或一列,可視地區就會增加一行或一列。例外,可視地區屬性是String類型,而不是Range物件類型,因此如果需要獲得整個可視地區的地區對象,您需要這樣寫代碼:
| Set rngViewable = Spreadsheet1.Range(Spreadsheet1.ViewableRange) |
設定可視地區不會自動調整容器中的試算表控制項的尺寸。不過,只要您定義了一個可視地區,就可將AutoFit屬性設定為True,這會使試算表自動調整大小,以便不需捲軸就能顯示整個可視地區。如果可視地區比控制項的當前尺寸小,控制項將縮短;如果比它大,它將擴大。 另外兩個屬性,MaxHeight和MaxWidth,管理試算表控制項增大的限度,可以使控制項不會變的過大。這兩個屬性定義了試算表高度和寬度的極限。如果可視地區超過了允許的最大高度和寬度,試算表將顯示內部的捲軸,使得使用者可以瀏覽整個地區。 當控制項在IE中運行時,可以將MaxHeight和MaxWidth屬性設定為百分比。例如,如果MaxWidth設定為80%,試算表將允許自身擴大到容器元素寬度80%的大小。例如,如果試算表被放在<body>元素中,試算表將允許擴大到頁面寬度的80%。如果它被放置在一個table單元中,試算表可以擴大到table單元寬度的80%。這種根據百分比來進行縮放的能力也應用在IE中標準的Width和Height屬性中。這種能力在動態HTML布局的環境中非常有用,並且可以使試算表能夠在頁面上正確的布局,而不會受視窗大小和顯示器解析度的影響。
將工作表元件用作資料來源 Excel社團中最普遍的需求之一就是希望Excel能夠成為XLS檔案中的資料的OLE DB提供者。當我們開發工作表元件時,我們意識到我們必須提供一種方法,使得圖表組件能夠從工作表元件中獲得一系列的資料,以便使用圖表來表示它們。圖表組件也必須能夠感知這些資料的變化,以便它能夠更新圖表。令人高興的是,這些需求正是OLE DB資料繫結所能提供的,因此我們決定將試算表控制項設計成一個真正的OLE DB資料來源。我會馬上從技術的角度詳細討論它的意義。不過,首先我將舉一個例子以便闡明這一點。 在IE中,您可以將眾多的HTML元素資料繫結到任何是有效資料來源的控制項上。IE5能夠將元素繫結到資料來源中某個特定的資料成員上,以適應資料來源包含一個或多個資料成員的情況。例如,如果在頁面上有一個名為tdcComposers的資料來源控制項,您可以使用下面的HTML片斷將一個html表格綁定到資料來源上。
| <table datasrc=#tdcComposers><thead> <tr style="font-weight:bold"> <td>First</td><td>Last</td> <td>Birth</td><td>Death</td><td>Origin</td> </tr></thead><tbody> <tr> <td><div datafld="compsr_first"></div></td> <td><div datafld="compsr_last"></div></td> <td><div datafld="compsr_birth"></div></td> <td><div datafld="compsr_death"></div></td> <td><div datafld="origin"></div></td> </tr></tbody></table> |
同樣,您可以將一個HTML表格綁定到工作表元件中一個地區的內容上。下面的HTML片斷是從隨書光碟片的SpreadsheetDS.htm檔案中摘抄的:
| <table datasrc=#Spreadsheet1.A2:D7 border=1><thead> <tr> <th>Salesperson</th> <th>FY98 Sales</th> <th>Projected Growth</th> <th>Est. FY99 Sales</th> </tr></thead><tbody> <tr> <td><div datafld="A"></div></td> <td><div datafld="B"></div></td> <td><div datafld="C"></div></td> <td><div datafld="D"></div></td> </tr></tbody></table> |
試算表控制項實現了IDataSource介面,這是IE和VB6及後續版本所定義和支援的標準資料來源介面。這些容器將任何實現了這個介面的控制項看作頁面或表單上其它資料繫結控制項的一個有效資料來源,一個資料來源控制項可以暴露任意數量的資料成員,每個資料成員通過一個字串來標記,並返回一個OLE DB行集合。試算表控制項幾乎暴露了無數的資料成員,因為任何有效地區引用都是一個有效資料成員。例如,前面的HTML片斷要求一個名為A2:D7的資料成員,並得到了一個兩行七列的行集合。IE5中指定資料成員的神奇的文法是<資料來源控制項名稱>.<資料成員名稱>。ID前的hash符號(#)顯示資料來源是當前頁面上的一個控制項。對於工作表元件,任何有效地區引用都能夠作為資料成員名稱。 當暴露整個地區的資料時,工作表元件也實現了簡單OLE DB提供者介面(有時被稱為OSP)。相對於實現IRowset和其它OLE DB介面而言,這個介面是被用來簡化OLE DB中暴露資料的方式的,簡單OLE DB提供者工具包將為那些需要與IRowset介面互動的資料繫結控制項,把OSP映射為一個完整的IRowset介面。實際上,IE在繫結控制項請求一個IRowset介面,而資料來源返回的是一個OSP介面時會自動使用這些映射關係。 工作表元件執行簡單OLE DB提供者介面的方式是read/write,當資料來源地區變化時,它會發出適當的通知,以便繫結控制項得知需要使用新值來重新整理它們的內容。(
譯者註:因為對簡單OLE DB提供者不瞭解,這兩段翻譯的有些似懂非懂,有沒有熟悉這方面知識的朋友幫忙校正一下這兩段?)