創(chuàng )建數據驅動(dòng)窗體
所謂數據驅動(dòng)窗體就是根據所請求的數據的不同生成相應的窗體。舉例來(lái)講,假設你現在有一個(gè)數據庫,其中有些字段必須根據登錄者的身份加以顯示,授權級別高的用戶(hù)可以瀏覽并修改這些字段的內容;授權級別一般的用戶(hù)只能瀏覽這些字段中的數據;授權級別低的用戶(hù)則不能瀏覽這些字段中的內 容。要做到這一點(diǎn)就得利用VB動(dòng)態(tài)添加控件的功能。
動(dòng)態(tài)創(chuàng )建控件
無(wú)論你想要創(chuàng )建何種類(lèi)型的數據驅動(dòng)窗體,你必須知道如何在運行時(shí)動(dòng)態(tài)地創(chuàng )建控件。你可以通過(guò)控件數組做到這一點(diǎn),但 VB6的Controls集合所提供的Add方法,功能更強大,靈活性更高。使用該方法,你不需要在設計時(shí)將控件的實(shí)例放在窗體上。實(shí)際上,用Add方法 你甚至可以創(chuàng )建程序在編譯時(shí)根本不存在的控件。這種方法的用法也很簡(jiǎn)單:
‘ 聲明一個(gè)窗體級的變量
Dim WithEvents txtTotal As TextBox
Sub CreateTextbox()
‘ 創(chuàng )建新的Textbox控件
Set txtTotal = Controls.Add("VB.TextBox", "txtTotal")
‘ 將控件移動(dòng)到你所需要的地方
txtTotal.Move 1000, 800, 1200, 300
‘ 創(chuàng )建時(shí),所有的控件都是不可見(jiàn)的
txtTotal.Visible = True
End Sub
請注意Add方法的第二個(gè)參數:分配給控件的名稱(chēng)。從代碼可讀性出發(fā),這個(gè)名稱(chēng)一般都與變量名相同。你可以用這個(gè)名稱(chēng)從Controls集合中獲取相應的控件或移除該控件。例如:
Controls.Remove "txtTotal"
在變量聲明時(shí)加上WithEvents關(guān)鍵字,即使在設計時(shí)窗體不存在該控件,你也可以為該控件的事件編寫(xiě)代碼。
上面所講的方法只適合解決VB內置的控件。例如,當你要添加一個(gè)TreeView控件時(shí),VB會(huì )要求你證明你已經(jīng)得到了合法的授權來(lái)創(chuàng )建該控件的實(shí)例。換句話(huà)說(shuō),VB要證明這個(gè)控件是買(mǎi)來(lái)的,而不是從其它附有該控件的程序中借來(lái)的。
要證明你經(jīng)過(guò)了合法的授權有下面幾種方法:
在窗體上放置一個(gè)控件。這也是最簡(jiǎn)單的方法。你完全沒(méi)有必要將這個(gè)窗體顯示出來(lái)。
將相關(guān)的控件添加到工具箱中,然后在“工程屬性”對話(huà)框的“生成”標簽頁(yè)中取消選擇“刪除有關(guān)未使用的ActiveX控件的信息”這一項。
向Licenses集合添加一個(gè)元素。例如:Debug.Print Licenses.Add("MSMask.MaskedEdBox")
僅僅創(chuàng )建了控件并不足夠
要 創(chuàng )建一個(gè)數據驅動(dòng)窗體,僅僅知道動(dòng)態(tài)創(chuàng )建控件還不夠。例如:現在你要創(chuàng )建一個(gè)能根據數據庫中表的不同字段自動(dòng)生成控件的窗體。該窗體可能會(huì )創(chuàng )建單行文本 框,其長(cháng)度隨字段長(cháng)度不同而不同;也可能會(huì )創(chuàng )建單選按鈕或復選按鈕以顯示布爾型字段;甚至可能創(chuàng )建一個(gè)多行文本框顯示備注型字段。
你需要解 決的第一個(gè)問(wèn)題是:文本框控件的Multiline屬性在運行時(shí)是只讀的,只在設計時(shí)可用。幸好,微軟的Microsoft Windowless Controls 6.0可以解決這個(gè)問(wèn)題。這組控件集包括了輕量級的TextBox,ComboBox,ListBox,CheckBox,OptionButton, CommandButton和兩個(gè)scrollbar控件。這些控件與VB內置的相應的控件最大的區別在于:這些控件的所有屬性在運行時(shí)是可讀寫(xiě)的。在 VB的安裝光盤(pán)中的Common\Tools\VB\WinLess文件夾中可以找到這個(gè)控件組。用下面的代碼可以創(chuàng )建一個(gè)多行文本框:
Dim WithEvents txtEditor As MSWLess.WLText
Private Sub CreateEditor()
Set txtEditor = Controls.Add( "MSWLess.WLText", "txtEditor")
txtEditor.Move 0, 0, 4000, 4000
txtEditor.MultiLine = True
txtEditor.ScrollBars = wlBoth
txtEditor.Visible = True
End Sub
另 外一個(gè)問(wèn)題比較復雜:在事先不知道要創(chuàng )建多少個(gè)控件的情況下,如何給每個(gè)對新創(chuàng )建的控件的引用分配唯一的帶WithEvents關(guān)鍵字的變量。換句話(huà)說(shuō)就是要對新創(chuàng )建的控件的事件進(jìn)行編程,前提是你在設計時(shí)不知道程序會(huì )創(chuàng )建多少個(gè)控件。使用對象數組顯然不行,因為不能用WithEvents關(guān)鍵字聲明一個(gè)對象數組;更壞的情況是,資一個(gè)變量定義為As Control或As Object也不行,因為還是不能用WithEvents。
問(wèn)題源自于我們無(wú)法在運行時(shí)捕獲一個(gè)對象數組事件。所以我們只能采取曲線(xiàn)救國的辦法。所要的編寫(xiě)的代碼可能比你想象的多,不過(guò)這個(gè)解決方法很有趣,值得我們這樣去做。
我 們需要兩個(gè)輔助類(lèi)模塊來(lái)捕獲事件,分別取名為ControlItems和ControlItem。ControlItems是一個(gè)集合類(lèi),其中保存了 ControlItem對象及其數量。該數量等于你所要對之編程的控件的數量。ControlItem類(lèi)的每一份實(shí)例捕獲控件產(chǎn)生的事件,然后調用在其所屬的ControlItems集合類(lèi)中的過(guò)程,最后由ControlItems在窗體中觸發(fā)事件并執行事件中的代碼。整個(gè)過(guò)程如下圖所示:
捕獲多個(gè)控件的事件
為簡(jiǎn)單起見(jiàn),假設你要捕獲來(lái)自所有的動(dòng)態(tài)添加到窗體上去的控件的Validate事件。為完成這個(gè)工作,ControlItems集合類(lèi)必須向父窗體展示該事件,并隨時(shí)準備接收來(lái)自其子ControlItem類(lèi)的通知以觸發(fā)事件。代碼如下:
Event Validate(CtrlItem As ControlItem, Cancel As Boolean)
Private m_ControlItems As New Collection
‘ 向集合中添加一個(gè)新的ControlItem項目
Function Add(ctrl As Control) As ControlItem
Dim newItem As New ControlItem
newItem.Init ctrl, Me
‘ 添加到私有類(lèi)
m_ControlItems.Add newItem
‘ 返回新項目給調用者
Set Add = newItem
End Function
Friend Sub Notify_Validate(Item As ControlItem, Cancel As Boolean)
RaiseEvent Validate(Item, Cancel)
End Sub
ControlItem 類(lèi)必須捕獲來(lái)自動(dòng)態(tài)添加到窗體中的控件的事件,并通知其所屬的ControlItems集合類(lèi)。很顯然,ControlItem類(lèi)必須有一個(gè)用 WithEvents關(guān)鍵字定義的變量來(lái)引用真正的控件。這意味著(zhù)你不能將變量聲明為As Control或As Object。如果你決定在窗體中所動(dòng)態(tài)添加的控件不使用VB內置的控件的話(huà),這個(gè)問(wèn)題的解決辦法相當的簡(jiǎn)單。你只需要將變量聲明為 VBControlExtender類(lèi)型就行了。對于創(chuàng )建數據驅動(dòng)窗體來(lái)講,不使用VB內置的控件并不是一件大不了的事。
將變量聲明為 VBControlExtender,并加上WithEvents關(guān)鍵字,你就能直接捕獲Validate,GotFocus,LostFocus, DragDrop和DragOver這幾個(gè)事件了。如果要捕獲其它更多的事件,你可以使用ObjectEvent。下面是ControlItem類(lèi)模塊中 的代碼:
Public WithEvents Ctrl As VBControlExtender
‘ 所屬的ControlItems對象
Dim m_Parent As ControlItems
Sub Init(ctl As Object, parnt As ControlItems)
Set Ctrl = ctl
Set m_Parent = parnt
End Sub
Private Sub Ctrl_Validate(Cancel As Boolean)
‘ 通知所屬的ControlItems類(lèi)
m_Parent.Notify_Validate Me, Cancel
End Sub
將下面的代碼放入窗體中,就可以捕獲動(dòng)態(tài)添加的控件所產(chǎn)生的事件了:
Dim WithEvents CtrlItems As New ControlItems
Private Sub cmdCreateControls_Click()
Dim ctrl As Control
‘ 創(chuàng )建兩個(gè)文本框并將它們添加到ControlItems集合? Set ctrl = Controls.Add("MSWLess.WLText", "One")
ctrl.Move 100, 200, 1000, 300
ctrl.Visible = True
CtrlItems.Add ctrl
‘ 注意你可以使用同一個(gè)變量
Set ctrl = Controls.Add("MSWLess.WLText", "Two")
ctrl.Move 100, 800, 1000, 300
ctrl.Visible = True
CtrlItems.Add ctrl
End Sub
Private Sub CtrlItems_Validate( CtrlItem As ControlItem, Cancel As Boolean)
‘ 拒絕空字符串 - 注意如何引用控件的屬? If CtrlItem.Ctrl.Text = ""
Then Cancel=True
End Sub
現在解決了最困難的部分,要創(chuàng )建一個(gè)數據驅動(dòng)窗體就變得簡(jiǎn)單了
**************************************************************
****************************************************************
動(dòng)態(tài)添加控件
VB6有一個(gè)新功能,可以動(dòng)態(tài)添加控件,不用控件數組:
object.Add (ProgID, name, container)
參數說(shuō)明
Object 必需的。一個(gè)對象表達式,其值是“應用于”列表中的一個(gè)對象。
ProgID 必需的。一個(gè)標識控件的字符串。大多數控件的 ProgID 都可通過(guò)查看對象瀏覽器來(lái)決定??丶?span lang="EN-US"> ProgID 是由控件的庫和類(lèi)組成的。
例如,CommandButton 控件的 ProgID 是 VB.CommandButton。在ProgID 與對象瀏覽器中所顯示的不一樣的情況下,Visual Basic
將顯示一個(gè)包括正確 ProgId 的錯誤信息。
name 必要的。一個(gè)字符串,用來(lái)標識集合的成員。
container 可選的。一個(gè)對象引用,它指定控件的容器。如果沒(méi)有指定或為NULL,缺省值為 Controls 集合所屬的容器。通過(guò)指定該參數,可以把一個(gè)控件放置在任何現存的容器控件(如 Frame 控件)中。用戶(hù)控件或 ActiveX 文檔也可以作為一個(gè)容器。
舉例: //在picture1上面添加一個(gè)commandbutton
Private Sub Form_Load()
Form1.Controls.Add "VB.CommandButton", "cmdOk", Picture1
With Form1!cmdOk
.Visible = True
.Width = 500
.Caption = "確認(&Y)"
End With
End Sub
重點(diǎn):當您添加一個(gè)未引用的需要許可證的控件到一個(gè)現存的(已部署好的)應用程序時(shí),在使用 Add 方法之前您必須也添加這個(gè)控件的許可證關(guān)鍵字。
在運行時(shí)添加未引用的控件:
您也可以利用 Add 方法來(lái)動(dòng)態(tài)添加一個(gè)在工程中沒(méi)有被引用的控件。(“未引用的”控件是不出現在 Toolbox 中的控件)。為此,您必須也把控件的License 關(guān)鍵字添加到 Licenses 集合中。下面的示例中在添加控件本身之前添加了控件的許可證關(guān)鍵字:
Option Explicit
Private WithEvents extCtl As VBControlExtender
Private Sub Form_Load()
Licenses.Add "prjWeeks.WeeksCtl", "xydsfasfjewfe"
Set extCtl = Form1.Controls.Add("prjWeeks.WeeksCtl", "ctl1")
extCtl.Visible = True ‘ The control is invisible by default.
End Sub
但是,為了編程這樣一個(gè)未引用控件的事件,您必須使用 WithEvents 關(guān)鍵字聲明一個(gè)對象變量為VBControlExtender 對象(如上),并且設置該對象變量到Add 方法返回的引用上。然后,利用VBControlExtender 對象的 ObjectEvent事件來(lái)編程該控件的事件。下面是一個(gè)簡(jiǎn)單的例子。
Option Explicit
Dim WithEvents objExt As VBControlExtender ‘聲明 Extender 變量
Private Sub LoadControl()
Licenses.Add "Project1.Control1", "xydsfasfjewfe"
Set objExt = Controls.Add("Project1.Control1", "myCtl")
objExt.Visible = True
End Sub
Private Sub extObj_ObjectEvent(Info As EventInfo)
‘使用 Select Case 編程控件的事件。
Select Case Info.Name
Case "Click"
‘這里處理 Click 事件。
‘現在顯示其他的 case
Case Else ‘未知事件
‘這里處理未知事件。
End Select
End Sub
Note: 不能把一個(gè)固有的控件指定給這個(gè) VBControlExtender 變量; 任何這種試圖將引起類(lèi)型不匹配錯誤。
但是,您也可以通過(guò)使用 WithEvents 關(guān)鍵字聲明一個(gè)對象變量,并且設置該方法返回的引用為該變量,從而編程一個(gè)動(dòng)態(tài)添加控件的事件,如下所示。
Option Explicit
‘聲明對象變量為 CommandButton 。
Private WithEvents cmdObject As CommandButton
Private Sub Form_Load()
Set cmdObject = Form1.Controls.Add("VB.CommandButton", "cmdOne")
cmdObject.Visible = True
cmdObject.Caption = "Dynamic CommandButton"
End Sub
Private Sub cmdObject_Click()
Print "This is a dynamically added control"
End Sub
如果希望添加一個(gè)用戶(hù)控件或任何 ActiveX 控件到您的窗體,必須或者把這個(gè)控件添加到“工具箱”,或者把控件的 License 關(guān)鍵字添加到 Licenses集合中。有關(guān)詳細信息請參閱“增加方法 (Licenses 集合)”。
注意:如果您添加一個(gè) ActiveX 或用戶(hù)控件到您的工程,但是沒(méi)有在窗體中使用它,您也必須不要選定“工程屬性”對話(huà)框的“生成” 選項卡上的“刪除有關(guān)未使用的 ActiveX 控件”選項。如果您的應用程序試圖添加該控件,那么該 Add 方法將失敗,因為必需的信息已經(jīng)被丟棄。
聯(lián)系客服