2009-12-16 76 views
1

我有一段時間試圖找出這一個。如何在Access 2003和VBA中使用控件集合

我想將一個Controls集合傳遞給一個函數,但我得到一個類型不匹配。這裏的函數聲明:

Public Function DoStuffToCollection(topCtlList As Controls, isLocked As Boolean) 

這裏就是我把它叫做:

Call DoStuffToCollection(myPage.Controls, isLocked) 

MYPAGE從TabControl的頁面控制。我已經瀏覽了代碼,看看myPage.Controls中是否有某些東西存在。

爲了好玩,我通過了Me.Controls(這將是Form的控件集合),而不是myPage.Controls,並且沒有類型不匹配。 Form控件集合和Page控件集合之間有區別嗎?這真讓我抓狂。

有點多挖,調試器調用myPage.Controls兒童/兒童作爲類型和Me.Controls作爲控件/控件作爲類型。爲什麼是這樣?

[編輯] 只需添加一些信息。 doStuffToCollection是一個遞歸函數,它只是應該鎖定綁定字段並在選項卡控件中禁用任何類型的按鈕。以前我只能將它鎖定在頁面級別,但後來我添加了一個頁面,其中包含一個按鈕。該按鈕沒有被頁面禁用。我知道這個http://allenbrowne.com/ser-56.html。儘管如此,我還沒有成功地適應我的需要。

回答

3

@Tim Lentine的建議是非常好的,直接回答你的問題,在我看來。

但我可能永遠不會寫一個操作整個控件集合的表單子。原因是這樣做實際上效率很低。但是否合適取決於你走路的頻率和時間。

如果您在窗體的OnLoad事件中漫遊一次(您不希望在OnOpen中執行此操作,因爲控件的數據綁定屬性無法保證在此時完全初始化 - 儘管你仍然可以對格式屬性進行操作 - 但是在OnLoad事件觸發時,所有事情都已準備就緒),這沒什麼大不了的,並且將該集合傳遞給外部子例程將是合適的。但是,如果您正在爲每條記錄散步(比如隱藏/顯示控件,或者在未綁定的逐個表單界面中初始化條件字段),那麼您將顯着提高表單的性能,方法是使用一個或多個更多自定義集合的循環數量要比任何常規窗體的控件集合中少得多。然後你可以重寫Tim的代碼來使用自定義集合,或者,對於這個問題,仍然可以使用上面的Object變量並將它傳遞給一個自定義集合(並且仍然可以將它傳遞給一個表單的控件集合)。

基本上,你要做的是在窗體的OnLoad事件中初始化集合。我通常寫一個私人子程序要做到這一點,所以我可以重新初始化應該一碼復位發生:

Private Sub SetupCollections() 
    If mcolCriteria.Count = 0 Then 
     Call PopulateCollections(Me, mcolCriteria, "Criteria") 
    End If 
    End Sub 

    Public Sub PopulateCollections(frm As Form, pcol As Collection, strTag As String) 
    Dim ctl As Control 

    For Each ctl In frm.Controls 
     If ctl.Tag = strTag Then 
     pcol.Add ctl, ctl.Name 
     End If 
    Next ctl 
    Set ctl = Nothing 
    End Sub 

在這種情況下,我確定哪些控制被添加到集合的方法是設置的標記屬性這些控件。你也可以這樣做:

Public Sub PopulateCollections(frm As Form, pcol As Collection, intControlType As AcControlType) 
    Dim ctl As Control 

    For Each ctl In frm.Controls 
     If ctl.ControlType = intControlType 
     pcol.Add ctl, ctl.Name 
     End If 
    Next ctl 
    Set ctl = Nothing 
    End Sub 

要使用此,你可以,比如,創建可空控制的這樣一個集合:

If mcolControlsNullable.Count = 0 Then 
    Call PopulateCollections(Me, mcolControlsNullable, acTextBox) 
    Call PopulateCollections(Me, mcolControlsNullable, acComboBox) 
    Call PopulateCollections(Me, mcolControlsNullable, acListBox) 
    End If 

對於布爾控件:

If mcolControlsBoolean.Count = 0 Then 
    Call PopulateCollections(Me, mcolControlsBoolean, acCheckBox) 
    End If 

對於具有默認值的選項組或其他控件:

If mcolControlsWithDefaults.Count = 0 Then 
    Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acTextBox) 
    Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acComboBox) 
    Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acListBox) 
    Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acCheckBox) 
    Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acOptionGroup) 
    End If 

    Public Sub PopulateCollectionsWithDefaults(frm As Form, pcol As Collection) 
    Dim ctl As Control 

    For Each ctl In frm.Controls 
     If Len(ctl.DefaultValue) > 0 Then 
     pcol.Add ctl, ctl.Name 
     End If 
    Next ctl 
    Set ctl = Nothing 
    End Sub 

    Private Sub SetControlValuesFromDefaults(pcol As Collection) 
    For Each ctl in pcol 
     ctl = ctl.DefaultValue 
    Next ctl 
    End Sub 

而對於其他集合:

Public Sub SetControlValues(pcol As Collection, varValue As Variant) 
    For Each ctl in pcol 
     ctl = varValue 
    Next ctl 
    End Sub 

有了這個更復雜的集藏品,你需要這樣的事情最初填充它們其中:

Private Sub SetupCollections() 
    If mcolControlsNullable.Count = 0 Then 
     Call PopulateCollections(Me, mcolControlsNullable, acTextBox) 
     Call PopulateCollections(Me, mcolControlsNullable, acComboBox) 
     Call PopulateCollections(Me, mcolControlsNullable, acListBox) 
    End If 
    If mcolControlsBoolean.Count = 0 Then 
     Call PopulateCollections(Me, mcolControlsBoolean, acCheckBox) 
    End If 
    If mcolControlsWithDefaults.Count = 0 Then 
     Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acTextBox) 
     Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acComboBox) 
     Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acListBox) 
     Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acCheckBox) 
     Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acOptionGroup) 
    End If 
    End Sub 

...然後你會想子來初始化控制值:

Private Sub InitializeControls() 
    Call SetControlValues(mcolControlsNullable, Null) 
    Call SetControlValues(mcolControlsBoolean, False) 
    Call SetControlValuesFromDefaults(mcolControlsWithDefaults) 
    End Sub 

...所以,你可以一切都在你的窗體的onload事件然後設置:

Call SetupCollections() 
    Call InitializeControls() 

現在,當然,有更少的複雜方式來做到這一點。你可能希望你的初始化程序走控件集合只有一次:

Private Sub SetupCollections() 
    Dim ctl As Control 

    For Each ctl in Me.Controls 
     If Len(ctl.DefaultValue) > 0 then 
     mcolControlsWithDefaults.Add ctl, ctl.Name 
     Else 
     Select Case ctl.ControlType 
      Case acTextBox, acComboBox, acListBox 
      mcolControlsNullable.Add ctl, ctl.Name 
      Case acCheckBox 
      mcolControlsBoolean.Add ctl, ctl.Name 
     End Select 
     End If 
    Next ctl 
    Set ctl = Nothing 
    End Sub 

消除初始化程序是使用自定義屬性返回集合的一種方法,使用將被重新初始化內部靜態變量如所須。然而,這將意味着通過控件集合多個階層,所以它的效率不高:

Private Property Get colControlsNullable() As Collection 
    Static colNullable As Collection 

    If colNullable.Count = 0 Then 
     Call PopulateCollections(Me, mcolControlsNullable, acTextBox) 
     Call PopulateCollections(Me, mcolControlsNullable, acComboBox) 
     Call PopulateCollections(Me, mcolControlsNullable, acListBox)  
    End If 
    Set colControlsNullable = colNullable 
    End Property 

不幸的是,使用靜態變量,同時很好地避免了模塊級的變量,意味着你的inialization例行變得效率較低,因爲外部初始化例程無法利用這些靜態變量來通過一次遍歷控件集合來填充所有內容。

所以,我不使用這些集合的自定義屬性,即使我希望我可以。另一方面,如果我只有一個自定義控件集合,我會這樣做。

無論如何,我洋洋灑灑遠長,用了太多的卷積,大概所有的空氣代碼充滿了錯誤...

+0

我走了一個不同的方向,但這可能是理想的方式去。 – KeithA

0

一種替代方法是使用「我」將形式作爲表單對象傳遞。顯然,我不確定有什麼不同。

同時在幫助頁面上尋找頁面對象。您可以傳遞一個Page對象。

2

看起來很奇怪,您無法直接傳遞Page的控件集合。這可能與Pages集合本身是special type of control collection這一事實有關。

另一種替代方法是將「topCtlList」參數聲明爲Object而不是控件。它使得代碼不易讀,並且可能更容易出錯,但它應該消除類型不匹配錯誤。

Public Function DoStuffToCollection(topCtlList As Object, isLocked As Boolean) 
'Debug.Print TypeName(topCtlList) 
Dim ctl As Control 
For Each ctl In topCtlList 
    Debug.Print ctl.Name 
Next ctl 

End Function 
+0

不錯!我當然不知道你可以將一個表單的控件集合作爲一個對象。正如我的回答中所概述的那樣,我傾向於不要多次對整個控件集合進行操作,而是使用自定義集合。這些當然可以作爲參數傳遞給函數或子函數。 –

+0

是否有任何理由將您的代碼示例作爲函數聲明?它沒有定義返回類型(這意味着它將返回一個變體 - 如果它將爲空或空值,則不需要),並且沒有設置返回值。爲什麼不把它定義爲Sub? –

+0

我會嘗試對象路由。 @David是的,它應該可能是一個子。但是在Function和Sub之間是否有任何性能差異? – KeithA

0

我走了不同的道路了。在我的標籤控件中,我只有子窗體。所以我從OOP開始玩,併爲每個稱爲EnableForm的子表單創建一個函數。該子表單現在可以處理它自己需要做的任何事情。在包含選項卡控件的表單中,我只是遍歷選項卡控件的頁面,查看頁面是否包含子表單,然後調用EnableForm函數。

它很髒,但它的工作原理,我會在代碼中記錄它。這個東西缺少的東西(以及大部分其他訪問數據庫)都是從這個開始。

+0

加載超過需要的子窗體通常是不可取的。事實上,我經常不會在激活的選項卡上加載子窗體。 –

+0

已採取的建議。如果我需要做更多新的Access開發,我會牢記它。我只想回到我的正常預定的C#編程。 – KeithA

相關問題