Gráfico da macro exibindo rótulos incorretos de linhas visíveis não sequenciais na planilha filtrada

1

Esta macro exibe rótulos de texto da planilha de origem ao clicar duas vezes em um ponto em um gráfico de gráfico de dispersão. O gráfico é atualizado quando a planilha de origem é filtrada em qualquer uma das várias colunas. A macro deve detectar essa filtragem e atualizar os valores do rótulo de acordo.

Isso funciona quando os dados são classificados na coluna filtrada, mas não quando também são classificados em outra coluna na qual os dados não são classificados, o que causa linhas ocultas adicionais.

O problema é que, apesar do fato de algumas linhas estarem ocultas, a macro conta tanto linhas visíveis como ocultas, começando da primeira linha visível (como se xlCellTypeVisible não estivesse funcionando).

Para esclarecer: enquanto não houver linhas ocultas no subconjunto filtrado, os rótulos serão exibidos corretamente, começando pela primeira linha visível no subconjunto filtrado. No entanto, quando um filtro adicional é aplicado em uma coluna que não é classificada pelo valor filtrado, a etiquetagem é distorcida devido à contagem das linhas ocultas intercaladas, além das linhas visíveis.

Detalhes: - A linha inicial é calculada corretamente para qualquer que seja a primeira linha visível. - O valor Arg2 também é definido corretamente para a linha visível apropriada na série, e os valores xData e yData no rótulo estão corretos mesmo quando filtrados em linhas não ordenadas! (Assim, o Arg2 pula todas as linhas ocultas, consistente com a série exibida no gráfico).

Mas os rótulos de outras colunas estão incorretos.

Basicamente, eu preciso buscar meu texto de marcador do número de linha Arg2 do intervalo de linhas visíveis na planilha filtrada.

Acredito que o problema esteja na seção sid = .cells , em que a contagem usa todas as linhas, em vez de apenas linhas visíveis. Novamente, as contagens reais (quando exibidas com Msgbox) apontam para a linha visível correta se eu visualmente contar as linhas na planilha de origem. Mas o texto real no rótulo é baseado na aplicação dessa contagem a linhas ocultas e visíveis, portanto, surge com uma linha incorreta que é mais alta nos dados.

Eu tentei alterar sid = .cells para sid = r.cells , mas sem sorte, na verdade, ele começa a contar a partir da primeira linha nos dados, em vez da primeira linha visível. Parece que o SpecialCells(xlCellTypeVisible) está funcionando apenas como esperado ao identificar a linha visível primeiro , mas fica confuso com as linhas ocultas subseqüentes.

Qualquer ajuda seria apreciada. Eu sou novo no VBA, então seja claro / específico!

Public WithEvents myChartClass As Chart

Private Sub myChartClass_BeforeDoubleClick(ByVal ElementID As Long, ByVal Arg1 As Long, ByVal Arg2 As Long, Cancel As Boolean)
Dim ser As Series
Dim pt As Point
Dim xData As Double, yData As Double
Dim sid As String

'declare vars used for calculating row number for filtered data
Dim r As Range
Dim StartRow As Long

Cancel = True
For Each ser In Me.SeriesCollection
    ser.HasDataLabels = False
Next

If ElementID = xlSeries Then
    If Arg2 > 0 Then
        With Worksheets("MySheetName")
            Set ser = Me.SeriesCollection(Arg1)
             xData = ser.XValues(Arg2)
             yData = ser.Values(Arg2)
            Set pt = ser.Points(Arg2)

'calculate starting row when table is filtered on any variable
Set r = Worksheets("MySheetName").Range("A:A").Rows.SpecialCells(xlCellTypeVisible)
StartRow = r.Row - 1 'starting row is the first visible row minus the table header

            'grab label from the row associated with the clicked point on chart
            'the case number signifies the series of the chart in the order visible in Select Data chart properties window
            Select Case Arg1
            Case 1  'series 1
                sid = .Cells(Arg2 + StartRow, "D") & vbLf & "label1: " & .Cells(Arg2 + StartRow, "C") & vbLf & "label2: " & .Cells(Arg2 + StartRow, "L") & vbLf & "label3: " & .Cells(Arg2 + StartRow, "U")
            Case 2  'series 2
                sid = .Cells(Arg2 + StartRow, "D") & vbLf & "label1: " & .Cells(Arg2 + StartRow, "C") & vbLf & "label2: " & .Cells(Arg2 + StartRow, "L") & vbLf & "label3: " & .Cells(Arg2 + StartRow, "U")
            End Select

            pt.HasDataLabel = True
            pt.DataLabel.Characters.Font.Size = 11
            pt.DataLabel.Characters.Font.Bold = True
            pt.DataLabel.Text = sid & vbLf & "(" & xData & " , " & yData & ")"

    'MsgBox "r: " & r.Count
    'MsgBox "StartRow: " & StartRow
    'MsgBox "Arg1: " & Arg1
    'MsgBox "Arg2: " & Arg2

        End With
    End If
End If
End Sub
    
por A.S 31.12.2014 / 22:55

2 respostas

1

Seu diagnóstico está correto - O VBA ignora o status oculto / não oculto ao analisar referências de células com .Cells . Um método de contagem de células de força bruta é a única coisa que eu encontrei para trabalhar:

Dim iter As Long, findCount As Long, workCel as Range

' This is okay as long as you are guaranteed only to have one header row.
Set workCel = Worksheets("MySheetName").Cells(2, 1)

' No cells found yet
findCount = 0

' Start iterator at zero
iter = 0

Do  
    ' Check row for hidden status
    If Not workCel.Offset(iter, 0).EntireRow.Hidden Then
        ' Row is visible; increment number of visible rows found
        findCount = findCount + 1
    End If

    ' Increment iterator
    iter = iter + 1

' Stop looping once the number of found rows reaches the desired count
Loop Until findCount >= Arg2

O índice necessário deve cair fora do código acima como o valor de iter :

sid = .Cells(iter + StartRow, "D") & vbLf & ...

Nenhum pós-decremento é necessário, porque .Offset(n, 0) refere-se à linha n+1 th de um intervalo começando em uma determinada célula.

Para anotar , a função .SpecialCells(xlCellTypeVisible) provavelmente está funcionando corretamente. O problema é que, como o Range é 'interrompido' pelas várias linhas ocultas, ele consiste em vários Areas (veja aqui: link ). Isso explode completamente a indexação normal .Cells(...) -tipo. Está começando na primeira linha de dados porque sua linha de cabeçalho é exibida & portanto, ancora seu intervalo r .

    
por 02.01.2015 / 15:46
1

@Brian, obrigado novamente - sua resposta e referência a Areas me levaram a procurar maneiras de obter o número da linha visível que corresponde ao valor Arg2 na série.

Fiz mais algumas pesquisas e encontrei uma solução que parece semelhante em princípio, mas conta as linhas em vez das células ( link ). Isso parece funcionar:

Public WithEvents myChartClass As Chart

Private Sub myChartClass_BeforeDoubleClick(ByVal ElementID As Long, ByVal Arg1 As Long, ByVal Arg2 As Long, Cancel As Boolean)
Dim ser As Series
Dim pt As Point
Dim xData As Double, yData As Double
Dim sid As String

'declare vars used for calculating row number for filtered data
Dim rng As Range, rngArea As Range, lRows As Long, lRow2 As Long

Cancel = True
For Each ser In Me.SeriesCollection
    ser.HasDataLabels = False
Next

If ElementID = xlSeries Then
    If Arg2 > 0 Then
        With Worksheets("MySheetName")
            Set ser = Me.SeriesCollection(Arg1)
             xData = ser.XValues(Arg2)
             yData = ser.Values(Arg2)
            Set pt = ser.Points(Arg2)

'check autofilter;
    If Not .AutoFilterMode Then
        MsgBox "Please enable autofilter on source worksheet."
        Exit Sub
    End If

'set a range = to visible cells (excluding the header)
Set rng = .AutoFilter.Range.Offset(1, 0).Resize(.AutoFilter.Range.Rows.Count - 1, 1).SpecialCells(xlCellTypeVisible)

'calculate starting row when table is filtered on any variable;
'loop through areas until row is found;
    lRows = 0
    For Each rngArea In rng.Areas
        lRows = lRows + rngArea.Rows.Count
        If lRows >= Arg2 Then
            lRow2 = rngArea.Item(Arg2 - (lRows - rngArea.Rows.Count)).Row
            Exit For
        End If
    Next rngArea

            'grab label from the row associated with the clicked point on chart
            'the case number signifies the series of the chart in the order visible in Select Data chart properties window
            Select Case Arg1
            Case 1  'series 1
            sid = .Cells(lRow2, "D") & vbLf & "label1: " & .Cells(lRow2, "C") & vbLf & "label2: " & .Cells(lRow2, "L") & vbLf & "label3: " & .Cells(lRow2, "U")
            Case 2  'series 2
            sid = .Cells(lRow2, "D") & vbLf & "label1: " & .Cells(lRow2, "C") & vbLf & "label2: " & .Cells(lRow2, "L") & vbLf & "label3: " & .Cells(lRow2, "U")
            End Select

            pt.HasDataLabel = True
            pt.DataLabel.Characters.Font.Size = 11
            pt.DataLabel.Characters.Font.Bold = True
            pt.DataLabel.Text = sid & vbLf & "(" & xData & " , " & yData & ")"


    'MsgBox "lRow2: " & lRow2

        End With
    End If
End If
End Sub
    
por 02.01.2015 / 18:03