在表单中保留 Class 模块

Preserve Class Module in form

假设我想在 Excel VBA 中制作一个自行车设计师程序。我有一个 Class 对象 (cBike),它有一些默认设置。现在我想在将自行车存储到数据库之前制作一个可用于更改这些设置的表格(如图中的表格)。存储方法(sub),位于cBike.

我可以在表单代码中将对象保存为 public 变量,如下所示:

Public objBike As cBike 
Public Sub StoreBikeToDatabase()   
 'database storing code goes here 
End Sub

虽然这可行,但我看到很多人反对使用 public(全局)变量。我不太清楚为什么,除了如果你有太多的全局变量,你的代码会很乱。

或者我可以忘记对象,使用来自不同表单控件的值,而不是 Class 模块 cBike 的属性。然而,这似乎是一个笨拙的解决方案。

我的问题是:以上哪种解决方案最好(如果有的话)?如果其中有 none 个,那么我应该怎么做呢?

更新: 我强烈建议您同时阅读已接受的答案和进一步阅读的答案。这两个答案都有一些很棒的想法,dee 的 post 还包含一些全面的代码示例,可用于其他有类似问题的人。

表单本身本质上就是一个 class,因此我建议在表单中创建一个 Private 属性 来保存您的 Bike 对象。然后,您可以通过 属性 Set 例程将现有的 Bike 对象传递到 form/class。

如果需要由表单中的多个例程访问,则在表单级别声明 Bike member/property 没有问题。 Global/Public 只有在对象需要被整个项目使用时才应使用变量(在模块中声明)。

'Private Member of this Form/Class
Private mBike As cBike

'Pass the existing object into this Form/Class
Public Property Let Bike(ByVal obj As cBike)

    Set mBike = obj

End Property

您可以通过如下声明 cBike 的属性,在表单控件和您的 class 之间有效地创建动态 link:

Private WithEvents mTextBox1 As MSForms.TextBox

Public Property Set TextBox1(ByVal obj As MSForms.TextBox)

    Set mTextBox1 = obj

End Property

这意味着如果文本框的值发生变化,您将不需要继续将其传递给 class。您将需要 Microsoft Forms 2.0 对象库的引用集

另一种方法是让 Bike 可编辑。 Bike class 将包含一个 BikeEditor 这是用于编辑自行车对象的用户表单。这是自行车类型的示例,但其他自行车属性可以用类似的方式完成。对于 BikeType,使用 class 来包装 TypeOfBikeEnum

Bike

Private m_editor As BikeEditor
Private m_bikeType As BikeType

Private Sub Class_Initialize()
    Set m_editor = New BikeEditor
    Set m_bikeType = New BikeType
End Sub

Public Property Get TypeOfBike() As BikeType
    Set TypeOfBike = m_bikeType
End Property

Public Property Set TypeOfBike(ByVal vNewValue As BikeType)
    Set m_bikeType = vNewValue
End Property

Public Sub Edit()
    m_editor.Initialize Me
    m_editor.Show
End Sub

BikeType

Public Enum TypeOfBikeEnum
    [_First]
    Unknown = 1
    MountainBike = 2
    StreetBike = 3
    OfficeBike = 4
    MoonBike = 5
    [_Last]
End Enum

Private m_type As TypeOfBikeEnum

Private Sub Class_Initialize()
    m_type = Unknown
End Sub

Public Property Get TypeValue() As TypeOfBikeEnum
    TypeValue = m_type
End Property

Public Property Let TypeValue(ByVal vNewValue As TypeOfBikeEnum)
    m_type = vNewValue
End Property

Public Function GetBikeTypeNames() As VBA.Collection
    Dim enumVal As Long, name As String
    Set GetBikeTypeNames = New VBA.Collection
    For enumVal = TypeOfBikeEnum.[_First] To TypeOfBikeEnum.[_Last]
        name = GetBikeTypeName(enumVal)
        If name <> "" Then _
            GetBikeTypeNames.Add name, CStr(enumVal)
    Next enumVal
End Function

Public Function GetBikeTypeName(typeOfBikeValue As TypeOfBikeEnum) As String
    Select Case typeOfBikeValue
        Case TypeOfBikeEnum.Unknown
            GetBikeTypeName = "Unknown"
        Case TypeOfBikeEnum.MountainBike
            GetBikeTypeName = "MountainBike"
        Case TypeOfBikeEnum.StreetBike
            GetBikeTypeName = "StreetBike"
        Case TypeOfBikeEnum.OfficeBike
            GetBikeTypeName = "OfficeBike"
        Case TypeOfBikeEnum.MoonBike
            GetBikeTypeName = "MoonBike"
        Case Else
            GetBikeTypeName = ""
    End Select
End Function

BikeEditor

Private m_bikeToEdit As Bike

Public Sub Initialize(bikeToEdit As Bike)
    Set m_bikeToEdit = bikeToEdit
    Dim bikeTypeName
    For Each bikeTypeName In m_bikeToEdit.TypeOfBike.GetBikeTypeNames
        Me.bikeTypesComboBox.AddItem bikeTypeName
    Next
    Me.bikeTypesComboBox.ListIndex = m_bikeToEdit.TypeOfBike.TypeValue - 1
End Sub

Private Sub CancelCommandButton_Click()
    Unload Me
End Sub

Private Sub SaveCommandButton_Click()
    If Me.bikeTypesComboBox.ListIndex > -1 Then
        m_bikeToEdit.TypeOfBike.TypeValue = Me.bikeTypesComboBox.ListIndex + 1
    End If
    Unload Me
End Sub

Module

Sub test()
    Dim bk As Bike
    Set bk = New Bike

    Dim bt As BikeType
    Set bt = New BikeType
    bt.TypeValue = OfficeBike

    Set bk.TypeOfBike = bt
    bk.Edit
End Sub