根据 Excel VBA 中的 ComboBox 显示/隐藏 WBS 项目

Posted

技术标签:

【中文标题】根据 Excel VBA 中的 ComboBox 显示/隐藏 WBS 项目【英文标题】:Show/Hide WBS items depending on ComboBox in Excel VBA 【发布时间】:2017-09-04 12:52:27 【问题描述】:

我希望能够使用组合框过滤工作表的 WBS 数据。

选择一个工作阶段应该导致只有具有该工作阶段的产品显示In progress 的状态是可见的。此工作阶段以及任何早期正在进行的工作阶段也应该是可见的。

此外,如果具有所选工作阶段的产品的状态为Finished,但包含正在进行的任何早期工作阶段,它也应该是可见的。那些早期的工作阶段也应该是可见的。为该产品选择的工作阶段必须可见。

如果一个产品根本没有选定的工作阶段,那么它或它的任何工作阶段都不应该是可见的。

仅当订单行具有可见产品时才可见。

选择前:

选择后:

这是我尝试做的:

Dim ws Worksheet
Set ws = ThisWorkbook.Sheets("DataSheet")

Dim lastRow As Long
lastRow = ws.Cells(Rows.Count, 4).End(xlUp).row

ws.Rows("2:" & lastRow).Select
Selection.EntireRow.Hidden = True

Dim SrchRng As Range, cel As Range
Set SrchRng = ws .Range("A5", "I" & lastRow)

For Each cel In SrchRng
    If cel.Range("C1").Text = ComboBox1.Text And cel.Range("E1").Value <> "In progress" Then
        cel.EntireRow.Hidden = False
    End If
Next

此代码仅显示正在进行的所选工作阶段。我希望产品行、订单行和所有早期进行中的工作阶段行都可见。

【问题讨论】:

您是否只需要显示Product x 而不需要显示Product y?您的示例仅显示 Product x 中的行,而 Product y 中的一些也应显示 @Zac- 是的,似乎应该显示订单 1/产品 y,因为工作阶段 2 正在进行中...? @Zac 图片有误。我已将图片编辑为正确。 【参考方案1】:

这是我的尝试。我做了一些假设:

    A 列以Order x 格式保存订单号,其中 x 是一个数字 B 列以Product x 格式保存产品名称,其中x 为xy C 列以Work stage x 格式保存工作阶段名称,其中x 是一个数字 D 列保存工作阶段状态为FinishedIn progress ComboBox 以Work stage x 格式保存工作阶段名称,其中x 是一个数字

    没有空行

    Sub ShowHideStages()
        Dim oW As Worksheet: Set oW = ThisWorkbook.Worksheets("Sheet9")
        Dim iLR As Long: iLR = oW.Cells(Rows.Count, 4).End(xlUp).Row
        Dim oCB As OLEObject: Set oCB = oW.OLEObjects("ComboBox1")
        Dim iC As Long
        Dim iA As Integer
        Dim iFirstRow As Integer
        Dim iostartRow As Integer
        Dim iPStartRow As Integer
        Dim bStageFound As Boolean
        Dim bProdFound As Boolean
        Dim bHideRow As Boolean
        Dim sRowsToHide As String
        Dim aTemp As Variant
    
        ' Unhide all rows
        oW.Rows("2:" & iLR).EntireRow.Hidden = False
    
        ' Find the first instance of Order
        iFirstRow = Columns("A").Find(what:="*", After:=Columns("A").Cells(1, 1), LookIn:=xlValues).Row
    
        ' If dropdown value is empty then skip the process of hiding rows
        If Len(Trim(oCB.Object.Text)) > 0 Then
            With oW
    
                ' Loop through all rows
                For iC = iFirstRow To iLR
    
                    ' Lets loop through current order
                    bStageFound = False
                    iOStartRow = iC
                    Do
    
                        iC = iC + 1
                        sRowsToHide = ""
                        iPStartRow = iC
                        bProdFound = False
    
                        ' Now lets loop through each product
                        Do
                            iC = iC + 1
                            bHideRow = False
    
                            ' Check if we need to hide the current row
                            If CInt(Right(Trim(Range("C" & iC).Text), 1)) <= CInt(Right(Trim(oCB.Object.Text), 1)) And LCase(Trim(Range("D" & iC).Text)) = "finished" Then
                                bHideRow = True
                            ElseIf CInt(Right(Trim(Range("C" & iC).Text), 1)) > CInt(Right(Trim(oCB.Object.Text), 1)) Then
                                bHideRow = True
                            End If
    
                            ' Check if work stage is the expected stage
                            If Range("C" & iC).Text = oCB.Object.Text And LCase(Trim(Range("D" & iC).Text)) <> "finished" Then
                                bProdFound = True
                                bStageFound = True
                            End If
    
                            ' Set rows to hide string
                            If bHideRow Then
                                If Len(Trim(sRowsToHide)) = 0 Then
                                    sRowsToHide = CStr(iC)
                                Else
                                    sRowsToHide = sRowsToHide & "," & CStr(iC)
                                End If
                            End If
    
                        Loop While Len(Trim(Range("C" & iC).Offset(1, 0).Text)) <> 0
    
                        ' Check if product was found
                        If Not bProdFound Then
                            ' It wasn't so set the string to hide product
                            sRowsToHide = CStr(iPStartRow) & ":" & CStr(iC)
                        End If
    
                        ' Hide specified rows .. if specified
                        If Len(Trim(sRowsToHide)) > 0 Then
                            If InStr(1, sRowsToHide, ":") > 0 Then
                                Range(sRowsToHide).EntireRow.Hidden = True
                            Else
                                aTemp = Split(sRowsToHide, ",")
                                For iA = 0 To UBound(aTemp)
                                    Rows(Trim(aTemp(iA))).EntireRow.Hidden = True
                                Next
                            End If
                            sRowsToHide = ""
                        End If
    
                    Loop While Len(Trim(Range("A" & iC).Offset(1, 0).Text)) = 0 And iC < iLR
    
                    ' Check if order was found
                    If Not bStageFound Then
                        ' It wasn't so lets set the string to hide the order
                        sRowsToHide = CStr(iOStartRow) & ":" & CStr(iC)
                    End If
    
                    ' Hide the order if we need to
                    If Len(Trim(sRowsToHide)) > 0 Then
                        Range(sRowsToHide).EntireRow.Hidden = True
                    End If
    
            End With
    
        End If
    
    End Sub
    

注意:UDF 也适用于 ComboBox 中的空选择。在这种情况下,所有行都会显示

【讨论】:

这与@robinCTS 的第一次尝试一样有效,但有两个问题,我在问题中没有说清楚。 1) 如果我选择“工作阶段 3”,它仍然显示订单和产品,其中不包括工作阶段 3。 2) 早期的工作阶段是可见的,如果它们已经完成的话。它们应该被隐藏。 Zac & @MMakela 我已经编辑了问题以使其更清晰。 两件事。首先,您的代码包含在 OP 和我的第一个版本的代码中发现的相同错误。 .End(xlUp) 只找到最后一个 可见 单元格。因此,不断进行选择会逐渐隐藏所有数据行,直到可能发生错误。第二,小费。声明变量时始终使用Long 而不是Integer。有关详细信息,请参阅评论 here。 @robinCTS:对这个错误大喊大叫,我今天没有阅读 OP 的编辑。我理解使用 Long.. 背后的逻辑,但旧习惯很难改掉 :)【参考方案2】:

编辑: (v0.3.2) 添加了“显示所有”工作阶段选项。

看起来很容易做,但结果有点棘手。

在遍历行时需要保存两个行索引:当前订单行和当前产品行。只有显示任何产品,才能正确显示订单。

请注意,常量声明和长变量名使代码自记录。

v0.3.2:

Show All - 如果您将另一个项目添加到不以“工作阶段”开头的组合框中,选择它会显示所有行。此项目也可以为空。

代码:

Option Explicit
'v0.3.2
Private Sub ComboBox1_Change()

  Const l_Order_      As String = "Order "
  Const l_Work_stage_ As String = "Work stage "
  Const l_In_progress As String = "In progress"
  Const i_Orders      As Long = 1
  Const i_Products    As Long = 2
  Const i_WorkStages  As Long = 3
  Const i_Progress    As Long = 5

  Dim rngCurrentRow As Range
  Dim lngFirstDataRow As Long
  Dim lnglastDataRow As Long
  Dim lngCurrentOrderRow As Long
  Dim lngCurrentProductRow As Long
  Dim boolShowProduct As Boolean

  Application.ScreenUpdating = False
  ' No need for sheet name since Me = active sheet
  lngFirstDataRow = WorksheetFunction.Match(l_Order_ & "*", Me.Columns(i_Orders), 0)
  lnglastDataRow = WorksheetFunction.Match("*", Me.Columns(i_Progress), -1)
  With Range(Me.Rows(lngFirstDataRow), Me.Rows(lnglastDataRow))
    If Not ComboBox1.Text Like l_Work_stage_ & "*" Then
      .EntireRow.Hidden = False
      GoTo ExitSub:
    Else
      .EntireRow.Hidden = True
      Set rngCurrentRow = .Rows(1)
    End If
  End With
  ' Loop through all data rows
  Do
    If rngCurrentRow.Columns(i_Orders) <> vbNullString Then
      lngCurrentOrderRow = rngCurrentRow.Row
      Set rngCurrentRow = rngCurrentRow.Offset(1)
    End If
    If rngCurrentRow.Columns(i_Products) <> vbNullString Then
      lngCurrentProductRow = rngCurrentRow.Row
      Set rngCurrentRow = rngCurrentRow.Offset(1)
    End If
    boolShowProduct = False
    ' Loop through consecutive non-empty progress/work stage rows
    Do Until rngCurrentRow.Columns(i_Progress) = vbNullString
      If rngCurrentRow.Columns(i_WorkStages) <= ComboBox1.Text _
      And rngCurrentRow.Columns(i_Progress) = l_In_progress _
      Then
        boolShowProduct = True
        Rows(rngCurrentRow.Row).Hidden = False
      Else
        ' Ignore
      End If
      Set rngCurrentRow = rngCurrentRow.Offset(1)
    Loop
    If rngCurrentRow.Columns(i_WorkStages).Offset(-1) < ComboBox1.Text Then
      ' Re-hide previous shown work stages for this product
      Range(Rows(lngCurrentProductRow), Rows(rngCurrentRow.Row)).Hidden = True
    ElseIf boolShowProduct Then
      Rows(lngCurrentOrderRow).Hidden = False
      Rows(lngCurrentProductRow).Hidden = False
    End If
  Loop Until rngCurrentRow.Row > lnglastDataRow
ExitSub:
  Application.ScreenUpdating = True

End Sub

注意:如果你对我的变量命名约定感到好奇,它是基于RVBA。

【讨论】:

这似乎是我想要的,它只有两个问题:1) 如果我选择“Work stage 3”,它仍然显示订单和产品,在哪里工作不包括第 3 阶段。 2) 早期的工作阶段是可见的,如果它们已经完成的话。它们应该被隐藏。 @MMakela 嗯。 1)问题中并不清楚这是所需的效果。 2)没有提到在这种情况下该怎么做。 (您更新的图片也显示了其他内容!)我错误地选择了展示而不是隐藏。我的错! >>> 将更新代码以执行这些操作。 @MMakela 我已经用新版本的代码更新了答案。我觉得很奇怪,选择Work stage 3 会隐藏所有Product ys,即使它们仍有工作阶段正在进行中。不过,新代码正确地做到了这一点。 这可以正常工作!非常感谢您的帮助! @MMakela 不确定您想如何实现“全部显示”,所以我在 v0.2 中省略了它。看到 Zac 的回答,我决定添加一个可选的。我已经更新了 v0.3.2 答案中的代码。有关说明,请参阅顶部的说明。

以上是关于根据 Excel VBA 中的 ComboBox 显示/隐藏 WBS 项目的主要内容,如果未能解决你的问题,请参考以下文章

excel表VBA中用代码如何建立多级combobox下拉菜单

VBA-Excel ComboBox 具有条目列表,首先显示最后输入的值

求VBA高手 ComboBox 自动出现在单元格,选择值后自动赋值给当前单元格的问题

EXCEL VBA combobox 模糊查询触发后 退格键功能改变

EXCEL VBA combobox 模糊查询触发后 退格键功能改变

VBA-Excel如何清除组合框项目