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