【问题标题】:Pivot Table: Detect When Pivot Field is Collapsed数据透视表:检测数据透视字段何时折叠
【发布时间】:2018-03-18 10:05:49
【问题描述】:

对于数据透视表中显示的数据,我选择将条件格式应用于数据表的某些部分,以突出显示某些范围内的值。弄清楚如何以不同于小计数据的方式突出显示第二级行数据很有趣,但我能够解决这个问题。我的 VBA 使用 Worksheet_PivotTableUpdate 事件触发,因此每当用户更改数据透视表字段时,条件格式都会相应更新。

当某些部分折叠时,此方法继续有效:

我的运行时错误发生在所有顶级部分都折叠时,因此第二级行数据(位置=2)不显示。

我收到以下错误:

我一直在寻找一种方法来检测所有第二个位置行字段是否已折叠/隐藏/不可见/未钻孔,以便识别该条件并跳过格式化部分。但是,我还没有发现 PivotFieldPivotItemPivotTable 的哪个方法或属性会为我提供该信息。

直接附在工作表上的事件代码是

Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable)
    ColorizeData
End Sub

所以在一个单独的模块中,ColorizeData 的代码是

Option Explicit

Sub ColorizeData()
    Dim staffingTable As PivotTable
    Dim data As Range
    Set staffingTable = ActiveSheet.PivotTables(PIVOT_TABLE_NAME)
    Set data = staffingTable.DataBodyRange
    '--- don't select the bottom TOTALS row, we don't want it colored
    Set data = data.Resize(data.rows.count - 1)

    '--- ALWAYS clear all the conditional formatting before adding
    '    or changing it. otherwise you end up with lots of repeated
    '    formats and conflicting rules
    ThisWorkbook.Sheets(PIVOT_SHEET_NAME).Cells.FormatConditions.Delete
    ThisWorkbook.Sheets(PIVOT_SHEET_NAME).Cells.ClearFormats
    staffingTable.DataBodyRange.Cells.NumberFormat = "#0.00"
    staffingTable.ColumnRange.NumberFormat = "mmm-yyyy"

    '--- the cell linked to the checkbox on the pivot sheet is
    '    supposed to be covered (and hidden) by the checkbox itself
    If Not ThisWorkbook.Sheets(PIVOT_SHEET_NAME).Range("D2") Then
        '--- we've already cleared it, so we're done
        Exit Sub
    End If

    '--- capture the active cell so we can re-select it after we're done
    Dim previouslySelected As Range
    Set previouslySelected = ActiveCell

    '--- colorizing will be based on the type of data being shown.
    '    Many times there will be multiple data sets shown as sums in
    '    the data area. the conditional formatting by FTEs only makes
    '    sense if we colorize the Resource or TaskName fields
    '    most of the other fields will be shown as summary lines
    '    (subtotals) so those will just get a simple and consistent
    '    color scheme

    Dim field As PivotField
    For Each field In staffingTable.PivotFields
        Select Case field.Caption
        Case "Project"
            If field.Orientation = xlRowField Then
                If field.Position = 1 Then
                    staffingTable.PivotSelect field.Caption, xlFirstRow, True
                    ColorizeDataRange Selection, RGB(47, 117, 181), RGB(255, 255, 255)
                End If
            End If
        Case "WorkCenter"
            If field.Orientation = xlRowField Then
                If field.Position = 1 Then
                    staffingTable.PivotSelect field.Caption, xlFirstRow, True
                    ColorizeDataRange Selection, RGB(155, 194, 230), RGB(0, 0, 0)
                End If
            End If
        Case "Resource"
            If field.Orientation = xlRowField Then
                If field.Position = 1 Then
                    staffingTable.PivotSelect field.Caption, xlFirstRow, True
                Else
===> ERROR HERE-->  staffingTable.PivotSelect field.Caption, xlDataOnly, True
                End If
                ColorizeConditionally Selection
            End If
        Case "TaskName"
            If field.Orientation = xlRowField Then
                If field.Position = 1 Then
                    staffingTable.PivotSelect field.Caption, xlFirstRow, True
                Else
                    staffingTable.PivotSelect field.Caption, xlDataOnly, True
                End If
                ColorizeConditionally Selection
            End If
        End Select
    Next field

    '--- re-select the original cell so it looks the same as before
    previouslySelected.Select
End Sub

表格的具体设置是当用户选择行数据为时

以防万一您想知道,为了完整起见,我在此处包含了两个私有子调用:

Private Sub ColorizeDataRange(ByRef data As Range, _
                              ByRef interiorColor As Variant, _
                              ByRef fontColor As Variant)
    data.interior.Color = interiorColor
    data.Font.Color = fontColor
End Sub

Private Sub ColorizeConditionally(ByRef data As Range)
    '--- light green for part time FTEs
    Dim dataCondition As FormatCondition
    Set dataCondition = data.FormatConditions.Add(Type:=xlCellValue, _
                                                  Operator:=xlBetween, _
                                                  Formula1:="=0.1", _
                                                  Formula2:="=0.5")
    With dataCondition
        .Font.ThemeColor = xlThemeColorLight1
        .Font.TintAndShade = 0
        .interior.PatternColorIndex = xlAutomatic
        .interior.ThemeColor = xlThemeColorAccent6
        .interior.TintAndShade = 0.799981688894314
        .SetFirstPriority
        .StopIfTrue = False
    End With

    '--- solid green for full time FTEs
    Set dataCondition = data.FormatConditions.Add(Type:=xlCellValue, _
                                                  Operator:=xlBetween, _
                                                  Formula1:="=0.51", _
                                                  Formula2:="=1.2")
    With dataCondition
        .Font.ThemeColor = xlThemeColorLight1
        .Font.TintAndShade = 0
        .Font.Color = RGB(0, 0, 0)
        .interior.PatternColorIndex = xlAutomatic
        .interior.Color = 5296274
        .SetFirstPriority
        .StopIfTrue = False
    End With

    '--- orange for slightly over full time FTEs
    Set dataCondition = data.FormatConditions.Add(Type:=xlCellValue, _
                                                  Operator:=xlBetween, _
                                                  Formula1:="=1.2", _
                                                  Formula2:="=1.85")
    With dataCondition
        .Font.Color = RGB(0, 0, 0)
        .Font.TintAndShade = 0
        .interior.PatternColorIndex = xlAutomatic
        .interior.Color = RGB(255, 192, 0)
        .SetFirstPriority
        .StopIfTrue = False
    End With

    '--- red for way over full time FTEs
    Set dataCondition = data.FormatConditions.Add(Type:=xlCellValue, _
                                                  Operator:=xlGreater, _
                                                  Formula1:="=1.85")
    With dataCondition
        .Font.Color = RGB(255, 255, 255)
        .Font.TintAndShade = 0
        .interior.PatternColorIndex = xlAutomatic
        .interior.Color = RGB(255, 0, 0)
        .SetFirstPriority
        .StopIfTrue = False
    End With
End Sub

编辑:感谢@ScottHoltzman,我将他的检查与以下逻辑结合并得出了解决方案

    Case "Resource"
        If field.Orientation = xlRowField Then
            If (field.Position = 2) And PivotItemsShown(staffingTable.PivotFields("Project")) Then
                staffingTable.PivotSelect field.Caption, xlDataOnly, True
                ColorizeConditionally Selection
            ElseIf field.Position = 1 Then
                staffingTable.PivotSelect field.Caption, xlFirstRow, True
                ColorizeConditionally Selection
            End If
        End If

【问题讨论】:

    标签: vba excel pivot-table


    【解决方案1】:

    使用PivotItems 对象的ShowDetail 方法。我包装了一个函数,以便更清晰地集成到您的代码中。这一切都是因为您必须测试该领域的每一项。

    测试代码:

    If field.Orientation = xlRowField Then
        If PivotItemsShown(field) Then
            If field.Position = 1 Then
                staffingTable.PivotSelect field.Caption, xlFirstRow, True
            Else
                staffingTable.PivotSelect field.Caption, xlDataOnly, True
            End If
            ColorizeConditionally Selection
        End If
    End If
    
    Function PivotItemShown(pf as PivotField) as Boolean
    
        Dim pi as PivotItem
    
        For each pi in pf.PivotItems
            If pi.ShowDetail Then 
                PivotItemsShown = True
                Exit For
            End If
        Next
    
    End Function
    

    更新:以下两种 hack 方法

    既然您知道,在您的示例中,如果所有 3 个项目都折叠,则单元格 A10 将为空白,您可以这样检查:

    If Len(Range("A10") Then ... `skip this section
    

    或者,如果您随时可能有动态项目列表,请使用:

    For each rng in Range(Range("A6"),Range("A6").End(xlDown))
        If Instr(rng.Value,"Project") = 0 and rng.Value <> "Grand Total" Then 
            '.... select the row range as needed
            Exit For
        End If
    Next 
    

    【讨论】:

    • 有趣的方法,但这是不行的。奇怪的是,“资源”字段pi.ShowDetail 为真,即使所有第二级行都已折叠。
    • @PeterT,问题不在于ShowDetail,而在于PivotItems。它返回与该字段无关的项目。即使在尝试了官方example 之后,我也无法理解返回的值。我猜这个方法坏了。
    • @PeterT - 好的。是的,我明白你所说的第二级返回真(奇怪)。但是,仍然可以通过传递 Project 字段来使用该函数。通过staffingTable.PivotFields("Project")(或任何实际名称,看看它是否有效。如果第一层没有可见项目,你知道它在高层全部折叠。
    • 我最初也想出了这些可能有用的黑客方法。我现在正在编辑答案。
    • 这是一个艰难的过程。上面显示的更新解决方案。谢谢!
    猜你喜欢
    • 2012-11-05
    • 2016-11-09
    • 2015-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多