Excel VBA - 创建一个带有多个组合框的动态用户窗体,并将所有组合框的值存储在一个数组中并对其进行排序

Excel VBA - Creating a dynamic userform with multiple combo boxes and storing the values of all combo boxes in one array and sorting through it

我正在 excel 2007 年创建一个用户窗体,它具有 6x6 组合框排列。最后一行和最后一列是 'all up' 框,它们的值应该基于它们各自的 rows/columns。剩下的 25 (5x5) 个组合框有 3 个值(红色、琥珀色、绿色),当用户选择一个值时,组合框显示该值并且背景用所选值着色(我通过创建一个函数来做到这一点在一个模块中并在每个 combobox_change()) 中调用它。

我在编码最后一行和最后一列框时遇到问题。基本上,如果说第 1 行,即使有一个 'Red',第 1 行 (1,6) 的最后一个框应该自动变为红色。如果没有红色,但是有一个'amber',那么最后一个框应该变成'amber'。如果有'red'和'amber',则应优先考虑'red'。列也有类似的逻辑。

到目前为止我尝试过的:

在用户表单代码中:

Private Sub Txt_Score_1_1_Change()  'This is for row 1 column 1 on the matrix'
Call ScoreChange.ScoreChange("Txt_Score_1_1")

模块内:

Public Sub ScoreChange(ctrlName As String)
If Scorecard.Controls(ctrlName).Value = "R" Then
  Scorecard.Controls(ctrlName).BackColor = vbRed
  ElseIf Scorecard.Controls(ctrlName).Value = "G" Then
  Scorecard.Controls(ctrlName).BackColor = vbGreen
  ElseIf Scorecard.Controls(ctrlName).Value = "A" Then
  Scorecard.Controls(ctrlName).BackColor = vbYellow
Else
  Scorecard.Controls(ctrlName).BackColor = vbWhite
End If

For i = 1 To 5
  For j = 1 To 5
    If Scorecard.Controls("Txt_Score_" & i & "_" & j).Value <> "" Then
        If Scorecard.Controls("Txt_Score_" & i & "_" & j).Value = "R" Then
            Scorecard.Controls("Txt_Score_" & i & "_6").Value = "R"
            Scorecard.Controls("Txt_Score_6_" & j).Value = "R"
         ElseIf Scorecard.Controls("Txt_Score_" & i & "_" & j).Value = "A"    Then
            Scorecard.Controls("Txt_Score_" & i & "_6").Value = "A"
            Scorecard.Controls("Txt_Score_6_" & j).Value = "A"
        End If
     End If
  Next j
 Next i

End Sub

上面的方法可以在更改时更改组合框的各个颜色,但对于 'total'/'all up' 框会分崩离析。

我认为要实现上述目标需要做的是,我需要编写一个代码来识别特定 row/column 的所有组合框何时都已填充,然后将这些值存储在一个数组,并在数组中识别最后一个框的值。

任何关于如何实现这一点的帮助将不胜感激。

此外,如果类似的内容已在其他地方发布,我深表歉意,但我做了很多研究但找不到任何东西。

谢谢。

我认为可能有一种更简单的方法来完成这项任务,当然还有一种更简单的方法来消耗所有 ComboBox_Change 事件。

如果我正确理解你的问题,你是说你有一个 5 x 5 'child' 组合框的矩阵。然后,您有 5 个 'parent' 控件根据行的 children 的选择而变化,还有 5 个 'parent controls' 对列的 children 进行相同的选择。

因此,您可以做的是创建两个 classes。我称它们为 clsChildclsParent。 child class 捕获更改事件,然后通知行和列 parent 已发生更改。 parent class 包含其 children 的列表,并根据 children 的选择运行着色规则。

根据规则,我创建了一个 Enum 你的颜色,其中红色最低,白色最高,所以你只需选择任何 'score' 中最低的 'score' =52=]ren 为 parent 控件着色。

对于组合框,我保留了与您的 post 相同的命名约定,但我不明白为什么 'parent' 控件是组合框 - 当然您不希望用户是能改变他们吗?然后我冒昧地使用命名约定 Lbl_Score_R1 ... R5 行和 Lbl_Score_C1 ... C5 列命名约定 Labels

这种方法的美妙之处在于,你只需要将children和parent之间的关系绑定一次,然后简单地传递它们之间的控制权objects。这将避免每次发生更改事件时都必须进行笨拙的字符串操作。

那么,代码...

我。插入一个新的 class 并将其命名为 clsChild。添加以下代码:

Option Explicit

Private WithEvents mCtrl As MSForms.ComboBox
Private mMum As clsParent
Private mDad As clsParent
Private mLight As Lights

Public Property Set Mum(val As clsParent)
    Set mMum = val
    Set mMum.ChildInLine = Me
End Property

Public Property Set Dad(val As clsParent)
    Set mDad = val
    Set mDad.ChildInLine = Me
End Property

Public Property Set Ctrl(val As MSForms.ComboBox)
    Set mCtrl = val
    With mCtrl
        .List = Array("R", "A", "G", "W")
        .ListIndex = 3
    End With
End Property

Public Property Get Light() As Lights
    Light = mLight
End Property

Private Property Let Light(val As Lights)

    mLight = val
    With mCtrl
        Select Case mLight
            Case Lights.Red: .BackColor = vbRed
            Case Lights.Amber: .BackColor = vbYellow
            Case Lights.Green: .BackColor = vbGreen
            Case Lights.White: .BackColor = vbWhite
        End Select
    End With

    If Not mMum Is Nothing Then mMum.ConsumeChildChanged
    If Not mDad Is Nothing Then mDad.ConsumeChildChanged
End Property

Private Sub mCtrl_Change()
    Select Case mCtrl.Value
        Case Is = "R": Light = Red
        Case Is = "A": Light = Amber
        Case Is = "G": Light = Green
        Case Else: Light = White
    End Select
End Sub

二。插入另一个新的 class 并将其命名为 clsParent 并添加以下代码:

Option Explicit

Private mCtrl As MSForms.Label
Private mChildren As Collection
Private mLight As Lights

Public Property Set Ctrl(val As MSForms.Label)
    Set mCtrl = val
    Set mChildren = New Collection
End Property

Public Property Set ChildInLine(val As clsChild)
    mChildren.Add val
End Property

Public Sub ConsumeChildChanged()
    Dim lowest As Lights
    Dim oChild As clsChild

    lowest = White
    For Each oChild In mChildren
        With oChild
            If .Light < lowest Then
                lowest = .Light
            End If
        End With
    Next
    Light = lowest
End Sub
Private Property Get Light() As Lights
    Light = mLight
End Property
Private Property Let Light(val As Lights)
    mLight = val
    With mCtrl
        Select Case mLight
            Case Lights.Red: .BackColor = vbRed
            Case Lights.Amber: .BackColor = vbYellow
            Case Lights.Green: .BackColor = vbGreen
            Case Else: .BackColor = vbWhite
        End Select
    End With
End Property

三。在任何 Module 的顶部添加以下内容:

Public Enum Lights
    Red
    Amber
    Green
    White
End Enum

四。最后将以下内容添加到您的 UserForm 代码中:

Option Explicit
Private mMum(1 To 5) As clsParent
Private mDad(1 To 5) As clsParent
Private mChild(1 To 5, 1 To 5) As clsChild

Private Sub UserForm_Initialize()

    Dim i As Integer, j As Integer

    For i = 1 To 5
        Set mMum(i) = New clsParent
        Set mMum(i).Ctrl = Me.Controls("Lbl_Score_R" & i)
        Set mDad(i) = New clsParent
        Set mDad(i).Ctrl = Me.Controls("Lbl_Score_C" & i)
    Next

    For i = 1 To 5
        For j = 1 To 5
            Set mChild(i, j) = New clsChild
            With mChild(i, j)
                Set .Ctrl = Me.Controls("Txt_Score_" & i & "_" & j)
                Set .Mum = mMum(i)
                Set .Dad = mDad(j)
            End With
        Next
    Next

End Sub