6. ActiveX控制項
現在Visual Basic更進一步地讓發展者可以自行創造自己的自訂控制項(稱為ActiveX控制項)。用Visual Basic發展出來的ActiveX控制項和用C發展出來的控制項在外觀上和行為上完全一樣,事實上,使用Microsoft C++ 的程式設計師也可以使用Visual Basic的ActiveX控制項。
ActiveX控制項應用的範圍極廣,幾乎到處都可以使用,例如,用Microsoft Internet Explorer瀏覽器的網頁、Microsoft Excel 97和Word 97的文件、Microsoft Access和Visual FoxPro資料庫應用程式、Visual Basic、Visual J++ 和Visual C++ 的檔案等等,都可以使用Visual Basic的ActiveX控制項。
本章要告訴你如何建立ActiveX控制項,並且要討論關於ActiveX控制項的程式設計課題。我們把本章裡的範例程式放在隨書光碟裡,以便利讀者隨時參閱。
如何建立ActiveX控制項?
「ActiveX控制項界面精靈」可以讓你以現有的控制項為基礎,幫你自動建立一個新的控制項。但是經由這種方式所產生的控制項,初學者對它的程式碼可能很難了解,因此,在這裡我們教你如何自己一步一步地建立控制項,一旦你了解了ActiveX控制項的細節後,你必定更能掌握「ActiveX控制項界面精靈」所產生的程式。
設計ActiveX控制項的步驟
要建立ActiveX控制項,請按照下列的步驟進行:
首先,建立一個ActiveX控制項專案。
接下來,我們要藉著建立一個叫做Blinker(BLINKER.VBD)的示範控制項,來詳細地討論這幾個步驟。偵錯和編譯會在接下來的兩節中討論。
這個Blinker控制項被用來使想要強調的視窗或控制項在螢幕上產生閃爍的效果。
建立ActiveX控制專案
要建立一個ActiveX控制項專案有以下幾個步驟:
描繪使用者界面
Blinker控制項的功能並不複雜,在Blinker控制項裡,我們只需要放三個Visual Basic提供的控制項:上下控制項(UpDown Control)、文字方塊控制項和計時器控制項﹛﹜imer Control﹜,如圖6-1。
圖6-1 Blinker控制項的視覺界面,包括一個ActiveX控制項和兩個基本控制項 |
以下的五個步驟告訴你如何建立Blinker控制項的視覺界面:
調整控制項的大小
當使用者把Blinker控制項放在他們設計的表單上時,他們可以拉大或縮小Blinker控制項,因此,Blinker控制項本身必須能調整它的視覺界面,以免Blinker控制項看起來像變形了一樣。這個調整Blinker控制項外觀的工作,必須要靠我們自己寫程式才可以達成,沒有其他簡便的方法可以替代。
改變Blinker控制項大小時,UserControl_Resize事件程序會被驅動,因此,我們要把調整外觀的程式碼放在這裡事件程序中,以下就是這個程式。
`Code for control's visual interface Private Sub UserControl_Resize() `Be sure visible controls are `positioned correctly within the `UserControl window updnRate.Top = 0 txtRate.Top = 0 txtRate.Left = 0 `Resize visible controls `when user control is resized updnRate.Height = Height txtRate.Height = Height `Adjust UpDown control's width up `to a maximum of 240 twips If Width > 480 Then updnRate.Width = 240 Else updnRate.Width = Width \ 2 End If `Set width of text box txtRate.Width = Width - updnRate.Width `Move UpDown control to right edge of `text box updnRate.Left = txtRate.Width End Sub
從程式裡你可以看到,我們用了一點小技巧來調整UpDown控制項的大小。我們不把UpDown控制項的大小固定,如果Blinker的寬度小於480 Twips的時候,我們讓UpDown控制項隨著Blinker控制項的大小變化按比例調整寬度,當Blinker寬度超過480 Twips時,則UpDown控制項的寬度會固定在240 Twips。
加入Blinker控制項的屬性、物件方法和事件
除了一般控制項都有的尺寸、位置和可見性等標準屬性外,Blinker控制項有兩個較特別的屬性:TargetObject和Interval。TargetObject用來設定Blinker控制項閃爍的目標物件,而Interval用來設定目標物件在每秒鐘內該閃爍幾次。在事件方面,Blinker控制項包括了一個Blinked事件,它發生在目標物件閃爍的動作結束之後,另外,在物件方法方面,Blinker控制項有一個Blink物件方法,它用來設定TargetObject和Interval屬性。
以下就是TargetObject、Interval、Blinked和Blink的定義:
Option Explicit `Windows API to flash a window Private Declare Function FlashWindow _ Lib "user32" ( _ ByVal hwnd As Long, _ ByVal bInvert As Long _ ) As Long `Blinked event definition Public Event Blinked() `Internal variables Private mobjTarget As Object Private mlngForeground As Long Private mlngBackground As Long Private mblnInitialized As Boolean `Public error constants Public Enum BlinkerErrors blkCantBlink = 4001 blkObjNotFound = 4002 End Enum `Code for control's properties and methods `~~~.TargetObject Public Property Set TargetObject(Setting As Object) If TypeName(Setting) = "Nothing" Then Exit Property `Set internal object variable Set mobjTarget = Setting End Property Public Property Get TargetObject() As Object Set TargetObject = mobjTarget End Property `~~~.Interval Public Property Let Interval(Setting As Integer) `Set UpDown control--updates TextBox and `Timer controls as well updnRate.Value = Setting End Property Public Property Get Interval() As Integer Interval = updnRate.Value End Property `~~~.Blink Sub Blink(TargetObject As Object, Interval As Integer) `Delegate to TargetObject and Interval properties Set Me.TargetObject = TargetObject Me.Interval = Interval End Sub
TargetObject Property Set屬性程序把閃爍的目標物件指定給一個內部的物件變數mobjTarget,而Interval屬性用來設定和讀取UpDown控制項的設定值,當這個值改變時,文字方塊控制項txtRate裡面的值也會跟著改變,同時也因而改變了計時器控制項tmrBlinker的Interval屬性值。Blinked事件在這裡定義,但事實上它在Timer事件裡被觸發,我們接下來要介紹計時器控制項和文字方塊控制項的事件程序。
撰寫程式定義控制項的行為
到目前為止,我們還沒有談到Blinker控制項會做哪些事,Blinker的行為──閃爍另一個控制項或視窗──被定義在三個程序裡:UpDown控制項的Change事件程序、計時器控制項的Timer事件程序和文字方塊控制項的Change事件程序。以下就是這兩個程序的內容。
`Code for control's behavior Private Sub updnRate_Change() `Update the text box control txtRate.Text = updnRate.Value End Sub Private Sub tmrBlink_Timer() On Error GoTo errTimer `Counter to alternate blink Static blnOdd As Boolean blnOdd = Not blnOdd `If the object is a form, use FlashWindow API If TypeOf mobjTarget Is Form Then FlashWindow mobjTarget.hwnd, CLng(blnOdd) `If it's a control, swap the colors ElseIf TypeOf mobjTarget Is Control Then If Not mblnInitialized Then mlngForeground = mobjTarget.ForeColor mlngBackground = mobjTarget.BackColor mblnInitialized = True End If If blnOdd Then mobjTarget.ForeColor = mlngBackground mobjTarget.BackColor = mlngForeground Else mobjTarget.ForeColor = mlngForeground mobjTarget.BackColor = mlngBackground End If Else Set mobjTarget = Nothing GoTo errTimer End If `Trigger the Blinked event RaiseEvent Blinked Exit Sub errTimer: If TypeName(mobjTarget) = "Nothing" Then Err.Raise blkObjNotFound, "Blinker control", _ "Target object is not valid for use with this control." Else Err.Raise blkCantBlink, "Blinker control", _ "Object can't blink." End If End Sub Private Sub txtRate_Change() `Set Timer control's Interval property `to match value in text box If txtRate = 0 Then tmrBlink.Interval = 0 tmrBlink.Enabled = False mblnInitialized = False `If blinking is turned off, be sure object `is returned to its original state If TypeOf mobjTarget Is Form Then FlashWindow mobjTarget.hwnd, CLng(False) ElseIf TypeOf mobjTarget Is Control Then mobjTarget.ForeColor = mlngForeground mobjTarget.BackColor = mlngBackground End If Else tmrBlink.Enabled = True tmrBlink.Interval = 1000 \ txtRate End If End Sub
UpDown控制項的Change事件程序只是單純地把UpDown控制項的值複製到文字方塊控制項中。計時器控制項的Timer事件程序在處理目標物件或視窗的閃爍動作;如果閃爍的對象是一個視窗,那麼Timer事件程序會呼叫FlashWindow API函式;如果閃爍的對象是一個控制項,那麼它用交換其前後景顏色的方式來達到閃動的目的。文字方塊控制項的Change事件程序負責處理閃爍的頻率。
參考資料:
請參閱 第十二章"對話方塊、視窗和其他表單" 中對FlashWindow API函式的討論。
如何對控制項進行偵錯?
建好ActiveX控制項後,下一步就是測試它。從「執行」功能表中選取「開始」時,Visual Basic通常對ActiveX偵錯測試的方式是把它顯示在Internet Explorer裡,如圖6-2所示。透過這種方式,你可以看到這個控制項執行的情形,但這並不是個偵錯的好方法,因為不能很容易地測試控制項的屬性和物件方法。
圖6-2 Visual Basic將執行的ActiveX控制項顯示在Internet Explorer中 |
若要能夠完全地對ActiveX控制項進行偵錯,請按照以下幾個步驟建立一個包含兩個專案的專案群組,其中一個是ActiveX控制項專案,另一個則是測試用的專案:
以下這段程式碼測試一個名為blnkText的Blinker控制項,這個控制項和另一個叫txtStuff的文字方塊在同一張表單上。
Option Explicit Private Sub Form_Load() `Set the object to blink blnkTest.Blink txtStuff, 1 End Sub Private Sub Form_Click() `Stop the blinker blnkTest.Interval = 0 End Sub Private Sub blnkTest_Blinked() Static intCount As Integer intCount = intCount + 1 Caption = "Blinked " & intCount & " times" End Sub
圖6-3中顯示的是測試中的Blinker控制項。
圖6-3 測試中的Blinker控制項 |
在程式執行中進行偵錯時,你可以從測試專案裡追蹤程式的執行,一直追蹤到ActiveX控制項裡的程式碼。用「偵錯」功能裡的「逐行」、「逐程序」來設定中斷點和使用「監看式」可以幫助你進行偵錯,如圖6-4。
在測試程式執行以前,Blinker控制項中的部份程式碼就已經被執行了,這些程式碼在Blinker控制項的UserControl_Resize事件程序裡。如果要看到這些程式碼被執行的情況,你可以在製作測試用的表單之前,在UserControl_Resize程序裡設定幾個中斷點。當你在表單上產生一個Blinker控制項時,Visual Basic會把程式執行停留在中斷點上。
圖6-4 對Blinker控制項進行偵錯 |
ActiveX控制項中的程式碼可以隨時修改,但是當你打開了UserControl設計視窗時,Visual Basic就不讓你動用表單上以及工具箱中的ActiveX控制項了,你必須關掉UserControl設計視窗才能夠繼續使用ActiveX控制項。
如果在測試用的表單上產生了一個ActiveX控制項,然後又在這個控制項上加入一些設計階段的屬性(稍後會討論如何產生設計階段屬性),那麼Visual Basic會禁止你動用表單上的這個控制項。結束ActiveX控制項中正在執行的程式,也會有相同的結果。你可以藉由執行測試專案來使ActiveX控制項恢復可用狀態。
注意:
當ActiveX控制項被初始化(Initialize)時,在表單上某些其他控制項的屬性可以被ActiveX控制項中的程式使用,有些屬性則不行。如果在表單上先拉出一個控制項,然後才拉出ActiveX控制項,那麼在ActiveX控制項的程式中引用前面這個控制項的屬性,可能會造成程式在執行時沒有任何反應,這是Visual Basic本身的一個缺陷。
如何對控制項進行編譯和註冊?
在編譯一個ActiveX控制項之前,你應該決定好要把這控制項編譯成機器碼還是虛擬碼。機器碼的執行速度比較快,而含虛擬碼的OCX檔比較小。
以Blinker控制項為例,Blinker控制項的行為靠Timer事件程序執行,因此,執行速度不是個重要的考慮因素。另一方面,Blinker控制項的機器碼OCX檔和虛擬碼OCX檔的檔案大小差別也只有5K而已,因此,從速度與檔案大小看來,機器碼OCX與虛擬碼OCX兩者並沒有什麼差別。
不過,如果這個OCX檔是放在Internet上面讓使用者下載的話,5K就是個很大的差別了。一般而言,要放在Internet上的ActiveX控制項,應該要編譯成虛擬碼。
設定編譯選項和進行程式編譯的步驟如下:
一旦控制項被編譯完畢之後,Visual Basic自動會向系統註冊這個ActiveX控制項。控制項經過註冊之後,Visual Basic會把它加到「設定使用元件」對話方塊中的「控制項」頁籤下的選項中。(不過必須在另一個新專案裡才看得到這個控制項在選項中)。請看圖6-5。
如果你的ActiveX控制項包含在預備要散發的應用程式中,那麼當你在另一部機器上安裝應用程式時,「封裝暨部署精靈」會在待安裝的機器上向系統註冊你的ActiveX控制項。如果散發的應用程式不附帶安裝程式,你必須安裝Visual Basic的執行階段動態連結程式庫(Runtime DLL)、MSCOMCT2.OCX和BLINKER.OCX,然後用REGOCX32.EXE公用程式來對MSCOMCT2.OCX和BLINKER.OCX進行註冊。你可以在Visual Basic光碟中的 \COMMON\TOOLS\VB\REGUTILS目錄下找到REGOCX32.EXE。以下這一行就是向系統註冊Blinker控制項的命令:
REGOCX32.EXE BLINKER.OCX
圖6-5 「設定使用元件」對話方塊列出所有已註冊的控制項 |
如何產生設計階段的屬性?
Blinker控制項的TargetObject和Interval屬性可以在程式執行階段加以設定,我們也可以讓這兩個屬性在設計階段就被設定,也就是說,我們可以在「屬性視窗」裡看到他們。如果在「屬性視窗」裡設定這些屬性項目,我們要在UserControl的ReadProperties和WriteProperties事件程序中,使用PropertyBag物件和它的兩個物件方法── ReadProperty及WriteProperty,如下所示:
'Make Interval a design-time property Private Sub UserControl_ReadProperties(PropBag As PropertyBag) updnRate.Value = PropBag.ReadProperty("Interval", 0) End Sub Private Sub UserControl_WriteProperties(PropBag As PropertyBag) PropBag.WriteProperty "Interval", updnRate.Value, 0 End Sub
這段程式把Interval屬性放進了Blinker控制項的「屬性視窗」裡,如圖6-6,接下來我們需要加入Property_Changed陳述式到Blinker控制項的Interval Property Let程序裡;這樣Visual Basic才會把在設計階段取得的Interval屬性內容存起來。以下就是修改後的Property Let Interval程序:
`~~~.Interval Public Property Let Interval(Setting As Integer) `Set UpDown control--updates TextBox and `Timer controls as well updnRate.Value = Setting `Update design-time setting PropertyChanged "Interval" End Property
圖6-6 用PropertyBag物件在「屬性」視窗中加入屬性項目 |
最後,我們需要確定在設計階段對Interval所作的改變不會引起Timer事件(回頭看一下txtRate_Change程序,看看我們原來是如何在執行階段觸發Timer事件的)。我們可以用UserControl物件的Ambient.UserMode屬性來判斷控制項是在執行階段還是設計階段;如果UserMode是False,那麼表示控制項正處於設計階段,反之,則在執行階段。以下是已經修改過的txtRate_Change程序。如果我們在設計階段把Interval設定成非零的值,這個修改後的程序就不會產生錯誤。
Private Sub txtRate_Change() `Exit if in design mode If Not UserControl.Ambient.UserMode Then Exit Sub `Set Timer control's Interval property `to match value in text box If txtRate = 0 Then tmrBlink.Interval = 0 tmrBlink.Enabled = False mblnInitialized = False `If blinking is turned off, be sure object `is returned to its original state If TypeOf mobjTarget Is Form Then FlashWindow mobjTarget.hwnd, CLng(False) ElseIf TypeOf mobjTarget Is Control Then mobjTarget.ForeColor = mlngForeground mobjTarget.BackColor = mlngBackground End If Else tmrBlink.Enabled = True tmrBlink.Interval = 1000 \ txtRate End If End Sub
注意:
在ActiveX控制項的Initialize事件中,UserMode屬性無法被使用。
要讓TargetObject成為設計階段屬性,我們可要花費多一點心思了,因為Visual Basic的「屬性視窗」目前並不能以物件作為屬性的設定值,也就是說,Visual Basic不能把物件當作設定值選項讓使用者選擇,因此,我們要用一個新屬性來接收一串字串,然後利用這字串來設定TargetObject屬性內容。這個新屬性TargetString可以在「屬性」視窗中出現,以下是它的程序內容:
`~~~.TargetString Public Property Let TargetString(Setting As String) If UserControl.Parent.Name = Setting Then Set TargetObject = UserControl.Parent ElseIf Setting <> "" Then Set TargetObject = UserControl.Parent.Controls(Setting) End If End Property Public Property Get TargetString() As String If TypeName(mobjTarget) <> "Nothing" Then TargetString = mobjTarget.Name Else TargetString = "" End If End Property
我們還需要把PropertyChanged陳述式加入到控制項的TargetObject屬性裡,如下所示:
'~~~~~~.TargetObject Public Property Set TargetObject(Setting As Object) If TypeName(Setting) = "Nothing" Then Exit Property 'Set internal object variable Set mobjTarget = Setting 'Property has changed PropertyChanged "TargetObject" End Property
要把TargetString加到屬性視窗中,請修改UserControl控制項的ReadProperties和WriteProperties事件程序:
`Get design-time settings Private Sub UserControl_ReadProperties(PropBag As PropertyBag) updnRate.Value = PropBag.ReadProperty("Interval", 0) TargetString = PropBag.ReadProperty("TargetString", "") End Sub `Save design-time settings Private Sub UserControl_WriteProperties(PropBag As PropertyBag) PropBag.WriteProperty "Interval", updnRate.Value, 0 PropBag.WriteProperty "TargetString", TargetString, "" End Sub
如何顯示「屬性頁」對話方塊?
「屬性頁」對話方塊讓你不用透過「屬性」視窗就可以設定ActiveX控制項的設計階段屬性。這樣有什麼好處?你可以把控制項中相關的重要的屬性集合在一張「屬性頁」裡,方便地修改其內容,而不必在「屬性」視窗中眾多的屬性項目裡辛苦地尋找你要設定的屬性。
你也可以利用「屬性頁」列出可供使用者選擇的屬性設定值。例如Blinker控制項的「屬性頁」列出了可供TargetString設定的屬性設定值(表單上的物件),如圖6-7。
先確定你在「專案」視窗中選到的是這個ActiveX控制項的專案,而不是測試專案,然後再增加一個「屬性頁」到ActiveX控制項專案裡。首先,在「專案」功能表裡點選「新增屬性頁」,然後點選兩下「屬性頁」圖像。在「屬性」視窗中把這個新屬性頁物件的Name屬性和Caption屬性加以修改,屬性頁的Caption屬性內容會在執行時顯示在「屬性頁」的頁籤標題上。
圖6-7 Blinker控制項的「屬性頁」 |
接下來,我們要把這個屬性頁和ActiveX控制項連結在一起。第一步先打開UserControl設計視窗並且選擇這個控制項,在「屬性」視窗裡點選兩下PropertyPages屬性,這時會有一個「連結至屬性頁」對話方塊出現,如圖6-8,選擇你要的屬性頁,按下「確定」。
當你把ActiveX控制項和屬性頁連結在一起之後,把UserControl設計視窗關掉,點選在測試表單上的Blinker控制項。你可以看到Blinker的「屬性」視窗中的屬性項目裡多了一個「自訂」屬性。點選兩下「自訂」屬性,你就可以看到你剛剛增加屬性頁了。
圖6-8 利用「連結至屬性頁」對話方塊將屬性頁和控制項連結在一起 |
在屬性頁的設計階段,你可以在屬性頁上面加入各種控制項以及屬性頁的事件程序,就好像在設計一張表單一樣。屬性頁中有一個內建的SelectedControls集合物件,透過這個集合物件你可以取得屬性頁所連結的ActiveX控制項,進而取得這個ActiveX控制項(Blinker)裡的屬性。另外,可以利用屬性頁的SelectionChanged事件程序來對屬性頁上的控制項作資料初值化的工作。以下的程式利用屬性頁設定Blinker屬性頁上的Interval和TargetObject屬性。
Blinker屬性頁包含了一個文字方塊控制項txtInterval和一個下拉式清單方塊(ComboBox Control) cmbTargetString。
Private Sub PropertyPage_SelectionChanged() `Set property page interval to match `control's setting txtInterval = SelectedControls(0).Interval `Build a list of objects for TargetString Dim frmParent As Form Dim ctrIndex As Control Dim strTarget As String `Get form the control is on Set frmParent = SelectedControls(0).Parent strTarget = SelectedControls(0).TargetString If strTarget <> "" Then `Add current property setting to `combo box cmbTargetString.List(0) = strTarget End If If frmParent.Name <> strTarget Then `Add form name to combo box cmbTargetString.AddItem frmParent.Name End If `Add each of the controls on the form to `combo box For Each ctrIndex In frmParent.Controls `Exclude Blinker control If TypeName(ctrIndex) <> "Blinker" Or _ ctrIndex.Name = strTarget Then cmbTargetString.AddItem ctrIndex.Name End If Next ctrIndex `Display current TargetString setting cmbTargetString.ListIndex = 0 End Sub
注意:
SelectedControls集合物件在屬性頁的Initialize事件中不能被使用。
在上面的程式中,有幾點要特別討論一下。SelectedControls(0) 會傳回目前選到的物件── Blinker控制項。另外,我們必須加上一個Parent屬性到Blinker控制項裡,這樣才能存取Blinker控制項收納器的資訊。以下是Blinker控制項的Parent屬性程序:
`~~~.Parent Public Property Get Parent() As Object Set Parent = UserControl.Parent End Property
接下來工作是:用屬性頁的ApplyChanges事件程序把屬性頁裡的設定內容寫到ActiveX控制項的屬性裡加以儲存。以下這段程式告訴你如何完成這件工作:
Private Sub PropertyPage_ApplyChanges() `Save settings on the property page `in the control's properties SelectedControls(0).Interval = txtInterval.Text SelectedControls(0).TargetString = _ cmbTargetString.List _ (cmbTargetString.ListIndex) End Sub
如果使用者更動了任何一項屬性頁上的設定,我們必須通知屬性頁本身。這要靠Visual Basic裡一個內建的Changed屬性來達成。當使用者在「屬性頁」上按下了「確定」或「套用」按鈕時,Changed屬性會告訴Visual Basic去套用目前屬性頁上的設定,我們在屬性頁中控制項的事件程序裡直接使用Changed屬性,達到相同的效果。以下的程式告訴你,屬性頁上任何一個設定被更動時,我們如何通知屬性頁。
Private Sub txtInterval_Change() Changed = True End Sub Private Sub cmbTargetString_Change() Changed = True End Sub
使用者可以用三種方式將屬性頁顯示出來:把滑鼠游標到移表單的ActiveX控制項上,按下滑鼠左鍵,然後選擇「屬性」;按兩下「屬性」視窗中的(自訂)屬性項目;按二下(自訂)屬性內容空格旁邊的"... "按鈕(Ellipsis)。
以下的幾個步驟教你如何加上一個"..."按鈕到屬性視窗的某個屬性項目上:
圖6-9 使用「程序屬性」對話方塊將屬性頁與某個屬性連結 |
現在你可以在「屬性」視窗中按下TargetString屬性項目的"..."按鈕,就可以看到在圖6-10裡的屬性頁。當使用者以上述方式顯示屬性頁時,屬性頁應該要把駐點(Focus)放在相關的控制項上。以下屬性頁的EditProperty事件程序告訴你如何在屬性頁的控制項上設定駐點。
Private Sub PropertyPage_EditProperty(PropertyName As String) `Set focus on the appropriate control Select Case PropertyName Case "TargetString" cmbTargetString.SetFocus Case "Interval" txtInterval.SetFocus Case Else End Select End Sub
圖6-10 按下"..."按鈕以呼叫「屬性頁」 |
如何以非同步的方式載入屬性?
在網頁中使用的ActiveX控制項需要用非同步(Asynchronous)方式載入屬性的設定值,這樣可以讓網頁瀏覽器一邊顯示網頁的內容,一邊下載圖形和其他較大的資料項。
圖6-11裡是一個AsynchronousAnimation控制項,它是由Microsoft Windows Common Control-2 6.0 (MSCOMCT2.OCX)裡的Animation控制項改良而來的。
圖6-11 AsynchronousAnimation正在播放一個AVI檔 |
AsynchronousAnimation控制項的AVIFile屬性可以接受一個代表檔名或URL的字串,而AsyncRead方法則用來傳輸檔案,把讀入的檔案存放在Windows的Temp目錄下,檔名由Visual Basic自動指定。以下就是AVIFile的屬性程序。
Option Explicit Dim mstrAVISourceFile As String Dim mstrTempAVIFile As String `~~~.AVIFile Property Let AVIFile(Setting As String) If UserControl.Ambient.UserMode _ And Len(Setting) Then AsyncRead Setting, vbAsyncTypeFile, "AVIFile" mstrAVISourceFile = Setting End If End Property Property Get AVIFile() As String AVIFile = mstrAVISourceFile End Property
當檔案傳輸完畢後,AsyncRead方法會驅動AsyncReadComplete事件,我們可以把所有非同步執行的事件放在AsyncReadComplete事件程序中處理。我們把AsyncProp.PropertyName的值放在Select Case陳述式裡作條件判斷,讓每一個非同步的屬性執行它們所應執行的程式碼。在我們以下的範例中,AsyncReadComplete事件程序會利用一個叫作aniControl的Animation控制項開啟一個AVI檔。
`General event handler for all async read complete events Private Sub UserControl_AsyncReadComplete _ (AsyncProp As AsyncProperty) Select Case AsyncProp.PropertyName `For AVIFile property Case "AVIFile" `Store temporary filename mstrTempAVIFile = AsyncProp.Value `Open file aniControl.Open mstrTempAVIFile `Play animation aniControl.Play Case Else End Select End Sub
當ActiveX控制項結束它們所有的動作後,要確定把暫存檔清理掉。一個好的Internet應用程式不應該在使用者的機器上留下任何暫存檔。以下的程式用來關閉aniControl檔並且刪除掉暫存檔。
Private Sub UserControl_Terminate() `Delete temporary file If Len(mstrTempAVIFile) Then aniControl.Close Kill mstrTempAVIFile End If End Sub
要使用AsynchronousAnimation控制項,只要在表單上拉出這個控制項,並在事件程序中設定AVIFile屬性即可。以下的例子在用這個控制項開啟播放Visual Basic光碟裡的"檔案搜尋"動畫。
Private Sub Form_Load() aaniFindFile.AVIFile = _ "d:\common\graphics\avis\findfile.avi" End Sub
參考資料:
請參閱
第八章"建立Internet元件" ,本章告訴你如何把AsynchronousAnimation控制項內嵌在網頁裡。
如何建立一個與資料庫連結的控制項?
如果ActiveX控制項的屬性提供資料連結(Data Binding)功能,我們就可以用這個ActiveX控制項顯示資料錄中的資料。所謂 " 資料連結 " 是指控制項屬性與資料錄或查詢的資料欄之間的關係。例如,我們可以把前一節中的Blinker控制項的Interval屬性連上資料庫中的某個數值型別的欄位,這種屬性與資料欄之間的關係就是資料連結。如果要在控制項的屬性上加上資料連結功能,請遵循以下這幾個步驟:
如圖6-12,Employee控制項是一個很簡單的資料連結控制項,它連結Visual Basic提供的NWIND資料庫。
圖6-12 Employee控制項用來顯示NWIND資料庫的Employee資料表 |
Employee控制項(DatCtl.VBP)包含了幾個標籤和文字方塊,用以顯示NWIND資料庫中的Employee資料表,其中FirstName、LastName、HireDate和Notes等屬性的程式碼如下:
`Properties section Public Property Get FirstName() FirstName = lblFirstName.Caption End Property Public Property Let FirstName(Setting) lblFirstName.Caption = Setting PropertyChanged FirstName End Property Public Property Get LastName() As String LastName = lblLastName.Caption End Property Public Property Let LastName(Setting As String) lblLastName.Caption = Setting PropertyChanged LastName End Property Public Property Get HireDate() As String HireDate = lblHireDate.Caption End Property Public Property Let HireDate(Setting As String) lblHireDate.Caption = Setting PropertyChanged HireDate End Property Public Property Get Notes() As String Notes = txtNotes.Text End Property Public Property Let Notes(Setting As String) txtNotes.Text = Setting PropertyChanged Notes End Property
以上的每個Property Let屬性程序都有一行PropertyChanged指令,根據Visual Basic文件所述,你應該在所有資料連結的屬性中加入這一行,儘管這些屬性只能在執行時期才能使用。
如果要使用Employee控制項,請看以下幾個步驟:
圖6-13 利用「資料連結」對話方塊把屬性與資料欄加以連結 |
圖6-14 以Employee控制項和Data控制項顯示NWIND.MDB中的資料錄 |
如何使用DataRepeater控制項?
DataRepeater控制項是一種收納器(Container),用以收納具備資料連結功能的ActiveX控制項。要使用DataRepeater控制項,你必須先建好編譯過的資料連結控制項,就像前一節中的Employee控制項。
在顯示資料庫中的資料時,DataRepeater控制項讓你可以用捲動式的清單取代一般表單(一次一筆)的界面,有點像FlexGrid控制項,但卻可以在格子內包含其他的控制項。從圖6-15中你可以看到一般表單界面、FlexGrid界面和DataRepeater界面的差異。
注意:
DataRepeater控制項必須使用相容的資料來源,如ADO Data控制項;DataRepeater控制項無法與Data控制項一起使用。
請按照以下步驟使用DataRepeater控制項:
圖6-15 一般表單、FlexGrid控制項和DataRepeater控制項提供三種不同的界面 |
圖6-16 利用DataRepeater的「屬性頁」對話方塊將重複的控制項屬性連結至資料欄 |
執行這個專案時,你可以用ADO Data控制項的箭號按鈕或是DataRepeater控制項的捲軸來移動資料錄。
如何建立一個收納器控制項?
如果你把某個使用者控制項的ControlContainer屬性設為True,所有放在這個控制項上的物件可以一起被移動或改變大小,Microsoft Tabbed Dialog控制項和DataRepeater控制項就是屬於收納器(Container)控制項。圖6-17所顯示的是一個由Shape和Label控制項所建立的簡單收納器控制項。
圖6-17 這個Container控制項的ControlContainer屬性被設為True,它具備類似Frame控制項的功能 |
以下這段程式告訴你Container控制項(Container.VBP)如何處理其形狀大小的改變:
`User interaction section Private Sub UserControl_Resize() `Resize the frame to match control shpFrame.Width = UserControl.ScaleWidth - shpFrame.Left shpFrame.Height = UserControl.ScaleHeight - shpFrame.Top End Sub
Container的Caption屬性初設值由Extender物件設定。Extender物件讓所有的物件可以存取Visual Basic或收納器控制項的內建屬性,它可以被使用在InitProperties程序裡,但是如果你在更早的階段(如控制項的Initialize事件中)使用Extender物件,Visual Basic會產生一個執行階段的錯誤。當使用者在表單上建立一個Container控制項時,以下這段程式碼會把Visual Basic自動指定的名稱顯示在Container控制項的標題上。
`Control maintenance section Private Sub UserControl_InitProperties() `Display appropriate caption lblTitle.Caption = Extender.Name End Sub
Container的Caption屬性可以設定或是傳回lblTitle的Caption屬性。在設計階段,Container的Caption屬性是一個可讀可寫的屬性,因此,它會有Property Let、Property Get、Read Properties和Write Properties等程序,如下所示:
Private Sub UserControl_ReadProperties(PropBag As PropertyBag) lblTitle.Caption = PropBag.ReadProperty("Caption") End Sub Private Sub UserControl_WriteProperties(PropBag As PropertyBag) PropBag.WriteProperty "Caption", lblTitle.Caption End Sub `Properties section Public Property Get Caption() As String Caption = lblTitle.Caption End Property Public Property Let Caption(Setting As String) lblTitle.Caption = Setting End Property
Container的Controls屬性會傳回Container物件所包含的集合物件,這是一般的Frame控制項所欠缺的功能,我們特別把它放在範例中,告訴你如何使用ContainedControls集合物件。當你把Container的ControlContainer屬性設為True時,Visual Basic就會自動建立一個ContainedControls集合物件,其他種類的控制項不會有這樣的集合物件。以下就是唯讀的Controls屬性的程式內容:
`Read-only property Public Property Get Controls() As Collection Set Controls = UserControl.ContainedControls End Property