VB下Add-Ins的編程
發表于:2007-07-14來源:作者:點擊數:
標簽:
華工 姜新 汪秉文 摘要 :本文從一個具體例子出發闡述了在Visual Basic 5.0環境下進行外接程序(Add-Ins) 開發 的原理、關鍵技術和注意事項,并對其相關技術,如ActiveX、多態性與接口、事件變量等 VB 5新引進的編程概念也做了必要的分析和描述。 關鍵詞:
華工 姜新 汪秉文
摘要 :本文從一個具體例子出發闡述了在Visual Basic 5.0環境下進行外接程序(Add-Ins)
開發的原理、關鍵技術和注意事項,并對其相關技術,如ActiveX、多態性與接口、事件變量等
VB5新引進的編程概念也做了必要的分析和描述。
關鍵詞:外接程序(Add-Ins),ActiveX,接口,多態,事件變量
一、 概述
Visual Basic下的Add-Ins,即外接程序,是擴展VB編程環境的非常有用的工具。作為一個開發者,我們發現在使用Visual Basic集成開發環境(IDE)時經常需要重復地干同一件簡單的工作,如設置所有或一類控件的字體,前景及背景顏色,改變控件的Tab次序等。這些簡單、單調而重復性的勞動如果由程序自動完成,將變得非常方便,而Add-Ins正為實現這一功能提供了可能。與Visual Basic的早期版本不同,Visual Basic5.0下的Add-Ins是一種模塊化的ActiveX部件,可以作為ActiveX DLL或EXE文件進行編譯。另外,跟以前的版本相比,VB5下Add-Ins無論從編程思想還是從其擴展模型的對象與結構來說,變化都比較大,因此VB5下編寫Add-Ins程序與早期版本將會有很大不同。在這篇文章中,我們將從一個具體例子出發,闡述VB5下Add-Ins的編程。
二、 問題的提出
在Visual Basic下進行窗體設計時,我們一般希望自己的程序具有風格一致的界面,如同類控件具有相同的外觀、相同的字體等。另外,我們在設計時還應該考慮到屏幕分辨率的變化對程序的影響,例如我們經常發現出現這樣的問題:即在某一分辨率(如800*600)下看起來比較好的字體,當在另一分辨率下(如640*480)運行時將變得非常難看;或者在中文環境下看起來比較舒服,但在英文環境下卻很不清晰。這種種原因都使得我們必須調整窗體控件的字體,以達到最佳效果。但如果表單或控件比較多時,手工調整將會非常繁瑣,而且容易出錯,因此編寫一個Add-Ins以實現字體的自動調整將會非常實用。本文中的例子即是用來實現此功能的。
三、 編程方法
編寫Add-Ins的最簡單方法是首先利用VB5的編程模板生成一個Add-Ins的框架,然后在此框架上進一步實現自己的代碼。具體方法是在VB5的“文件”菜單下選擇“新建工程”,然后在彈出的窗口中選擇“外接程序”(英文版中是“Add-Ins”)即可。這時VB5將生成一個名為MyAddin的工程,該工程中包括一個窗體frm AddIn,一個模塊AddIn,以及一個類模塊Connect。下面我們對這些部分分別做一簡單分析。
1. 類模塊Connect
整個類模塊Connect的代碼均是由模板自動生成的,大大方便了
程序員編程,但其源代碼中有一些比較重要的概念與方法,對我們理解VB Add-Ins甚至VB
面向對象編程均有很大好處。下面我們對如下幾點做一簡單介紹(源代碼由于是VB自動產生,這里就沒有列出了)。
(1)實現IDTExtensibility接口
所有的VB外接程序都必須實現IDTExtensibility接口,該接口包含了當外接程序與Visual Basic連接時Visual Basic調用的一些方法,無論是通過外接程序管理器,還是其它一些手段。接口是VB5引入的新概念,是Visual Basic提供多態性的一種重要手段。我們知道,面向對象的語言一般都提供多態性。如C++語言,通過類的繼承關系,子類重載父類的方法以實現其不同的特性,或父類僅提供方法框架,即無函數體的虛函數,而子類具體實現其代碼。簡單地說,多態意味著許多類可以提供同樣的屬性或者方法,而且調用者在調用這些屬性或方法之前,不必知道某個對象屬于什么類。嚴格來說,Visual Basic不是完整意義上的面向對象編程語言,但它也能實現多態性,不過這種多態性不是通過繼承來實現的,而是通過多重ActiveX接口來提供的。多態性具體實現方法簡單來說,首先是建立一個類模塊,然后在該模塊中聲明一些子程序(Sub),但不實現其代碼,這樣該類模塊可以說是一個抽象類,也即接口;然后建立其它的類模塊,在這些類模塊的聲明部分中,使用Implements語句表明該類實現了上述接口,接下來就可以實現接口代碼(也即接口的方法)了。這種用Implements語句的方法可以說與
Java語言比較相似。至于這方面的詳細信息,可參看VB的幫助或聯機手冊。
在類模塊Connect中,使用Implements IDTExtensibility語句聲明該類實現了IDTExtensibility接口。因此類模塊Connect中實現了該接口包括的四個方法:OnAddInsUpdate,OnConnection,OnDisconnection,OnStartupComplete的代碼,例如一條語句、過程調用、注釋等等。如果過程是空的話,它會被編譯器刪掉。如果實在沒有代碼可往這些過程里加的話,就插入注釋。這是因為既然實現了接口,就必須提供接口的所有方法的代碼。這四種方法中,比較關鍵的是OnConnection和OnDisconnection方法,其中OnConnection方法當一個外接程序通過“外接程序管理器”對話框或另一個個接程序與Visual BasicIDE連接時被調用。我們一般在該方法中保存當前Visual Basic會話期實例,掛接VB菜單,工具欄,或顯示初始窗口,以便用戶使用該外接程序。而OnDisconnection方法當外接程序通過編程或“外接程序管理器”對話框與Visual BasicIDE分離時被調用。我們一般在該方法中卸掉菜單、工具欄、程序窗體,以及做保存設置等工作。
(2)掛接Visual BasicIDE菜單
在接口IDTExtensibility的OnConnection方法代碼中,一般來說大多數情況下需要掛接Visual BasicIDE菜單(當然也可以是工具欄),以便用戶可隨時使用該程序。掛接菜單的代碼如下:
Set MCBmenuCommandBar = AddToAddinCommandBar ("My Addin")
'sink the event
Set Me.MenuHandler=
VBinst.Events.CommandBarEvents (mcbMenuCommandBar)
其中AddToAddInCommandBar 函數的代碼如下:
Function AddToAddInCommandBar (sCaption As String)As-Office.CommandBarControl
Dim cbMenuCommandBar As Office.CommandBarControl
Dim cbMenu As Object
On Error Go To AddToAddInCommandBarErr
'see if we can find the Add-ins menu
Set cbMenu=Vbinstance.CommandBars ("Add-ins")
If cbMenu is Nothing Then
'not available so we fail
Exit Function
End if
'add it to the command bar
Set cbMenuCommandBar=cbMenu.Controls.Add(1)
'set the caption
cbMenuCommandBar.Caption =sCaption
Set AddToAddInCommandBar = cbMenuCommandBar
Exit Function
AddToAddInCommandBarErr:
End Function
寫上面的代碼中,mcbMenuCommandBar變量定義為:
Dim mcbMenuCommandBar As Office.CommandBarControl
MenuHandler變量定義為:
Public WithEvents MenuHandler As CommandBarEvents
這里需要指出的是,MenuHandler是WithEvents變量,凡是WithEvents變量均表明該變量是一個用來響應由ActiveX對象觸發的事件的對象變更。也就是說,該變量是擁有事件的,因此可以編寫該變量的事件代碼。在上述代碼中將事件對象VBInst.Events.Comman-dBarEvents(mcbMenuCommandBar)賦予MenuHandler變量,則表示當發生有關mcbMenuC-ommandBar菜單項的事件時,由該變量的代碼進行處理。在類模塊中,我們發現,就存在MenuHandler-Click事件代碼,該代碼用以顯示窗體對話。
在AddToAddInCommandBar函數中,VBInstance是在OnConnection方法保存的會話期實例,所有Add-Ins的調用都需要此實例。使用VBInstance. CommandBars(“Add-Ins”)將獲得Add-Ins菜單條對象(在中文VB5下是“外接程序”菜單條),請注意:該“Add-Ins”是不會
本地化的,因此無論是在中文還是英文VB5下抱歉獲得上菜單條對象。代碼Set cbMenuCommandBar=cbMenu.Controls.Add(1)在該菜單下添加一新的菜單項“My AddIn”,使用了cbMenu.Controls的Add方法。關于菜單條的層次關系及相應的屬性及方法,可以參看VB關于外接程序的幫助或聯機手冊。但通過筆者的實踐認為,最好的方法是使用F2打開VB的瀏覽器,其類之間的層次關系以及對象事件,方法,屬性一目了然。
關于類Connect中的其它方面的代碼,一般來說比較容易理解,這里就不敘述了。
3. 模塊AddIn
模塊AddIn比較簡單,僅包含一個子程序:
Sub AddToINI ()
Dim ErrCode As Long
ErrCode=WritePrivateProfileString ("Add-Ins32","My AddIn.Connect","0","
vbaddin.ini")
End Sub
該程序可以在立即窗口中運行,以便將關于此外接程序的信息添加到
Windows目錄下的vbaddin.ini文件中去,這樣VB的外接程序管理器就可以認識該程序,并可將其掛接到VB的IDE環境中。該程序所做的工作很簡單,只是在vbaddin.ini文件的[Add-Ins32]下添加一行My AddIn.Connect=0,表明My AddIn沒有掛接到IDE環境中,若My AddIn.Connect=1,則VB啟動時自動掛接該程序。
4. 窗體frm AddIn
窗體frm AddIn中的代碼是由用戶編寫的,模板僅給出了一個框架。針對我們上面提出的具體問題,我們設計了一個簡單的用戶界面,包括如下控件:
(1)標簽Label1:Caption="All controls In SelectedForm:"
(2)列表框List1:用于顯示表單中的所有控制。
(3)命令按鈕fontButton (0):Caption ="&Select Font",用于將選中的窗體上所有控制的字體設為缺省字體。(注:缺省字體為宋體9號字,這種字體在中文環境不同分辨率下效果都比較好)。
(4)命令按鈕fontButton (1):Caption ="&Select Font",選擇該按鈕后將彈出選擇字體,然后程序將選中的窗體上所有控制的字體均設為用戶選擇的字體。
(5)命令按鈕ExitButton:Caption="&Exit",用于退出此程序,實際上的工作只是隱藏了窗體frm AddIn。外接程序的真正退出還是需要外接程序管理器中刪去。
(6)通用對話框CommonDialog1:用于彈出選擇字體的通用對話框,便于用戶從中選擇所需字體。
下面是窗體代碼:
Public VBInstance As vbide.VBE
Public Connect As Connect
Dim mcmpCurrentForm As VBComponent
Option Explicit
Private Sub ChangeFont (NewFontName As String,NewFontSize As Integer,NewFontBold As Boolean,NewFontltalic As Boolean)
Dim control As VBControl
On Error Resume Next
List1.Clear
Set mcmpCurrentForm =VBInstance.SelectedVBComponent
If (mcmpCurrentForm.Type<>vbext-ct-VBForm) And-
(mcmpCurrentForm.Type<>vbext-ct-UseControl) And-
(mcmpCurrentForm.Type<>vbext-ct-DocObject) And-
(mcmpCurrentForm.Type<>vbext-ct-PropPage) Then
Exit Sub
End if
For Each control in mcmpCurrentForm.Designer.VBControls
List.AddItem control.ControlObject.Name
control.ControlObject.FontName= NewFontName
control.ControlObject.FontSize= NewFontSize
control.ControlObject.FontBole= NewFontBole
control.ControlObject.FontItalic= NewFontItalic
Next
End Sub
Private Sub ExitButton-Click (Index As Integer)
Connect.Hide
End Sub
Private Sub FontButton-Click (Index As Integer)
Select Case Index
Case 0
ChangeFont "宋體",9,False,False
Case 1
With CommonDialog1
.Flags = cdlCFBoth
.ShowFont
ChangeFont.FontName,.FontSize,.FontBold,.FontItalic
End With
End Select
End Sub
上面的代碼比較簡單,關鍵的代碼在于獲取屏幕上的所有控制,在Visual Basic5.0下,與早期版本不同,它利用了VBComponent對象的Designer屬性獲取屏幕上的設計器,它可以是窗體,也可以是ActiveX控件或ActiveX文檔等,然后通過該設計器來獲得設計器上的用戶控制。上述代碼中,首先將mcmpCurrenForm變量被設為當前選中的構件,然后判斷它是否是窗體,用戶控件,ActiveX文檔或屬性頁,如果是,則獲取其設計器即Designer對象,然后通過Designer對象的VBControls屬性獲得其上所有控制的集合,這樣就可以操作該集合中的每一個VBControls控制了。對每一個控制,通過其ControlObject 屬性可以獲得其相應的屏幕上的控制對象,然后就可以設置該對象的任意屬性了。在上述代碼中,也可以使用control.Properties!Fontname取代control.ControlObject.FontName,其結果是一樣的。另外,為了防止對沒有Font屬性的控制進行該屬性設置將導致錯誤發生,在程序的開頭使用了On Error Resume Next語句以忽略這些錯誤。下面是該程序的用戶界面:
四、 進一步的改進
對上述程序還可做進一步的改進,下面列出幾種改進思想及編程方案。
1、 設置控制的其它屬性
上述代碼主要用來設置控制的字體,但基于同樣的原理,我們只要對代碼稍加改變就可以設置控制的其它屬性,如前景色,背景色,可見或不可見等。
2、 為當前工程中的所有窗體設置控制屬性
在上述代碼中,是對當前選中的窗體里的控制設置字體,但有時我們需要將工程中的所有窗體中的所有控制一次設置其屬性,而不是每次選擇一個窗體進行設置。這時可以采用如下方法:首先用VBE(即當前會話期實例)的ActiveVBProject屬性獲得當前的工程,然后通過其VBComponents屬性獲得當前工程中的所有組件集合,針對每一組件判斷它是否為窗體或 ActiveX控件等,剩下的代碼就和上面基本一樣了。
3、 僅為選中的控制設置屬性
若只是需要為選中的控制設置屬性則可以利用VBForm 對象的SelectedVBControls屬性獲得當前選中的所有控制的集合,然后對其包含的控制設置屬性即可。也可利用VBControl的InSelection屬性判斷該控制是否被選中,然后僅對選中的控制設置屬性即可。
4、 當控件被添加到窗體時,自動為它設置屬性
要實現當控件被添加到窗體時,自動為它設置屬性的功能需要用到VBE的事件對象,即與上面提到的為菜單提供事件代碼的方法一樣。首先聲明一個事件變量:Public WithEvents CtlHandler As VBControlsEvents,然后在接口IDTExtensibility的OnConnection方法代碼中掛接控制事件處理:Set Me.CtlHandler=VBInstance.Events.VBControlsEvents (Nothing,Nothi-ng),這樣就可以進行事件處理了。而進行事件處理的代碼可寫在CtlHandler變量的ItemAdded代碼中,如下:
Private Sub CtlHandler-ItemAddec (ByVal VBControl As VBIDE.VBControl)
'這里可加入所需要的代碼。
End Sub
當然,除上述改進外,我們還可以提出更多的改進方案,以豐富原程序的功能??傊?,由于VB顯露其IDE包括窗口、工程、控制,甚至事件、代碼等各個部分的接口,使得我們能非常簡單而有效地控制自己的編程活動和定制自己的界面,從而節省了編程時間,提高了工作效率,也減少了錯誤的發生。
參考文獻
1、[美]鮑(Boyle,D)等著·Visual Basic 4 開發人員指南·薛萬鵬等譯·機械工業出版社
2、Visual Basic 5聯機手冊·Microsoft Corporation
原文轉自:http://www.anti-gravitydesign.com