2009-09-02 24 views
8

Ecco il codice che applica un filtro avanzato alla colonna A nel foglio di lavoro Foglio1 (intervallo Elenco) utilizzando l'intervallo di valori su il Foglio2 (intervallo di criteri)Come ottenere l'intervallo delle righe visibili dopo l'applicazione di un filtro avanzato in Excel (VBA)

Range("A1:A100").AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:= _ 
     Sheets("Sheet2").Range("A1:A10"), Unique:=False 

Dopo l'esecuzione di questo codice, ho bisogno di fare qualcosa con le righe che sono attualmente visibili sullo schermo.

Attualmente io uso un codice come questo

For i = 1 to maxRow 
    If Not ActiveSheet.Row(i).Hidden then 
    ...do something that I need to do with that rows 
    EndIf 
Next 

C'è qualche proprietà semplice che mi può dare un intervallo di righe visibili dopo l'applicazione di un filtro avanzato?

risposta

14
ActiveSheet.Range("A1:A100").Rows.SpecialCells(xlCellTypeVisible) 

Questo produce un oggetto Range.

+1

grazie. funziona in Excel 2007. Effettua il check in Excel 2003 tommorow –

15

La soluzione di Lance funzionerà nella maggior parte delle situazioni.

Ma se gestisci fogli di calcolo grandi/complessi potresti imbatterti nello "SpecialCells Problem". In poche parole, se l'intervallo creato causa più di 8192 aree non contigue (ed è possibile), verrà generato un errore quando si tenta di accedere a SpecialCells e il codice non verrà eseguito. Se i tuoi fogli di lavoro sono abbastanza complessi, ti aspetti di incontrare questo problema, quindi ti consigliamo di seguire l'approccio ciclico.

Vale la pena notare che questo problema non è con la proprietà SpecialCells stessa, piuttosto è con l'oggetto Range. Ciò significa che ogni volta che si tenta di ottenere un oggetto intervallo che potrebbe essere molto complesso, si dovrebbe impiegare un gestore degli errori o fare come si è già fatto, il che significa far funzionare il programma su ciascun elemento dell'intervallo (dividere il estendersi).

Un altro possibile approccio sarebbe quello di restituire una serie di oggetti intervallo e quindi di eseguire il ciclo attraverso la matrice. Ho pubblicato qualche codice di esempio con cui giocare. Tuttavia, si dovrebbe notare che si dovrebbe solo preoccuparsi di questo se si prevede di avere il problema descritto o semplicemente si vuole essere certi che il codice sia robusto. Altrimenti è solo una complessità inutile.


Option Explicit 

Public Declare Function GetTickCount Lib "kernel32"() As Long 

Public Sub GenerateProblem() 
    'Run this to set up an example spreadsheet: 
    Dim row As Long 
    Excel.Application.EnableEvents = False 
    Sheet1.AutoFilterMode = False 
    Sheet1.UsedRange.Delete 
    For row = 1 To (8192& * 4&) + 1& 
     If row Mod 3& Then If Int(10& * Rnd) 7& Then Sheet1.Cells(row, 1&).value = "test" 
    Next 
    Sheet1.UsedRange.AutoFilter 1&, "" 
    Excel.Application.EnableEvents = True 
    MsgBox Sheet1.UsedRange.SpecialCells(xlCellTypeVisible).address 
End Sub 

Public Sub FixProblem() 
    'Run this to see various solutions: 
    Dim ranges() As Excel.Range 
    Dim index As Long 
    Dim address As String 
    Dim startTime As Long 
    Dim endTime As Long 
    'Get range array. 
    ranges = GetVisibleRows 
    'Do something with individual range objects. 
    For index = LBound(ranges) To UBound(ranges) 
     ranges(index).Interior.ColorIndex = Int(56 * Rnd + 1) 
    Next 

    'Get total address if you want it: 
    startTime = GetTickCount 
    address = RangeArrayAddress(ranges) 
    endTime = GetTickCount 
    Debug.Print endTime - startTime, ; 'Outputs time elapsed in milliseconds. 

    'Small demo of why I used a string builder. Straight concatenation is about 
    '10 times slower: 
    startTime = GetTickCount 
    address = RangeArrayAddress2(ranges) 
    endTime = GetTickCount 
    Debug.Print endTime - startTime 
End Sub 

Public Function GetVisibleRows(Optional ByVal ws As Excel.Worksheet) As Excel.Range() 
    Const increment As Long = 1000& 
    Dim max As Long 
    Dim row As Long 
    Dim returnVal() As Excel.Range 
    Dim startRow As Long 
    Dim index As Long 
    If ws Is Nothing Then Set ws = Excel.ActiveSheet 
    max = increment 
    ReDim returnVal(max) As Excel.Range 
    For row = ws.UsedRange.row To ws.UsedRange.Rows.Count 
     If Sheet1.Rows(row).Hidden Then 
      If startRow 0& Then 
       Set returnVal(index) = ws.Rows(startRow & ":" & (row - 1&)) 
       index = index + 1& 
       If index > max Then 
        'Redimming in large increments is an optimization trick. 
        max = max + increment 
        ReDim Preserve returnVal(max) As Excel.Range 
       End If 
       startRow = 0& 
      End If 
     ElseIf startRow = 0& Then startRow = row 
     End If 
    Next 
    ReDim Preserve returnVal(index - 1&) As Excel.Range 
    GetVisibleRows = returnVal 
End Function 

Public Function RangeArrayAddress(ByRef value() As Excel.Range, Optional lowerindexRV As Variant, Optional upperindexRV As Variant) As String 
    'Parameters left as variants to allow for "IsMissing" values. 
    'Code uses bytearray string building methods to run faster. 
    Const incrementChars As Long = 1000& 
    Const unicodeWidth As Long = 2& 
    Const comma As Long = 44& 
    Dim increment As Long 
    Dim max As Long 
    Dim index As Long 
    Dim returnVal() As Byte 
    Dim address() As Byte 
    Dim indexRV As Long 
    Dim char As Long 
    increment = incrementChars * unicodeWidth 'Double for unicode. 
    max = increment - 1& 'Offset for array. 
    ReDim returnVal(max) As Byte 
    If IsMissing(lowerindexRV) Then lowerindexRV = LBound(value) 
    If IsMissing(upperindexRV) Then upperindexRV = UBound(value) 
    For index = lowerindexRV To upperindexRV 
     address = value(index).address 
     For char = 0& To UBound(address) Step unicodeWidth 
      returnVal(indexRV) = address(char) 
      indexRV = indexRV + unicodeWidth 
      If indexRV > max Then 
       max = max + increment 
       ReDim Preserve returnVal(max) As Byte 
      End If 
     Next 
     returnVal(indexRV) = comma 
     indexRV = indexRV + unicodeWidth 
     If indexRV > max Then 
      max = max + increment 
      ReDim Preserve returnVal(max) As Byte 
     End If 
    Next 
    ReDim Preserve returnVal(indexRV - 1&) As Byte 
    RangeArrayAddress = returnVal 
End Function 

Public Function RangeArrayAddress2(ByRef value() As Excel.Range, Optional lowerIndex As Variant, Optional upperIndex As Variant) As String 
    'Parameters left as variants to allow for "IsMissing" values. 
    'Code uses bytearray string building methods to run faster. 
    Const incrementChars As Long = 1000& 
    Const unicodeWidth As Long = 2& 
    Dim increment As Long 
    Dim max As Long 
    Dim returnVal As String 
    Dim index As Long 
    increment = incrementChars * unicodeWidth 'Double for unicode. 
    max = increment - 1& 'Offset for array. 
    If IsMissing(lowerIndex) Then lowerIndex = LBound(value) 
    If IsMissing(upperIndex) Then upperIndex = UBound(value) 
    For index = lowerIndex To upperIndex 
     returnVal = returnVal & (value(index).address & ",") 
    Next 
    RangeArrayAddress2 = returnVal 
End Function 
+1

+1 questo è il motivo per cui SO è impressionante win –

+0

[Nota: questo problema è risolto in Excel 2010 celle non contigue che possono essere selezionate in Excel 2010: 2.147.483.648 celle] (https: //www.rondebruin.nl/win/s4/win003.htm) – danieltakeshi

1

È possibile utilizzare il seguente codice per ottenere la gamma visibile di cellule:

Excel.Range visibleRange = Excel.Application.ActiveWindow.VisibleRange 

Spero che questo aiuti.

+0

Questo è sbagliato. Si riferisce all'intervallo di celle visibili nella finestra e ignora effettivamente il problema delle righe nascoste. è l'intervallo dalla cella visibile in alto a sinistra della finestra alla cella visibile in basso a destra della finestra ... – epeleg

Problemi correlati