Vba 透视表:检测透视字段何时折叠

Vba 透视表:检测透视字段何时折叠,vba,excel,pivot-table,Vba,Excel,Pivot Table,对于透视表中显示的数据,我选择对数据表的某些部分应用条件格式,以突出显示特定范围内的值。找出如何突出显示第二级行数据与小计数据的不同是很有趣的,但我能够解决这个问题。My VBA使用工作表_PivotTableUpdate事件触发,这样每当用户更改数据透视表字段时,条件格式都会相应更新 当某些截面塌陷时,此方法将继续工作: 我的运行时错误发生在所有顶级部分折叠时,因此第二级行数据(位置=2)未显示 我得到以下错误: 我一直在寻找一种方法来检测是否所有的第二位置行字段都已折叠/隐藏/不可见

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

当某些截面塌陷时,此方法将继续工作:

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

我得到以下错误:

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

直接附加到工作表的事件代码为

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,我将他的支票与下面的逻辑结合起来,得出了一个解决方案


使用
PivotItems
对象的
showdeail
方法。我将其包装到一个函数中,以便更清晰地集成到代码中。所有这些都是因为您必须测试字段的每个项目

测试代码:

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
更新:下面的两种破解方法

因为您知道,在您的示例中,如果所有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 
适用于范围内的每个rng(范围(“A6”)、范围(“A6”)。结束(xlDown))
如果仪表(rng.Value,“项目”)=0,rng.Value“总计”,则
'.... 根据需要选择行范围
退出
如果结束
下一个

这是一种有趣的方法,但这是不可能的。奇怪的是,“资源”字段
pi.showdeail
即使在所有二级行都折叠时也是正确的。@彼得,问题不在于
showdeail
,而在于
pivottItems
。它返回与字段无关的项。即使在尝试了一个官方的方法之后,我也无法理解返回的值。“我想这个方法是错误的。”彼得-好的。是的,我明白你在说什么,第二关返回真(奇怪)。但是,仍然可以通过传递
Project
字段来使用该函数。传递
staffingTable.PivotFields(“项目”)
(或者不管实际名称是什么,看看它是否有效。如果第一层没有可见的项目,你知道它在高层都崩溃了。我最初也提出了这些可能有用的黑客方法。我现在正在编辑答案。这是一个困难的问题。上面显示了更新的解决方案。谢谢!
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