2009-09-02 12 views
8

Oto kod, który stosuje zaawansowany filtr do kolumny A w arkuszu Arkusz1 (zakres listy) przy użyciu zakresu wartości Arkusz2 (zakres kryteriów)Jak uzyskać zakres widocznych wierszy po zastosowaniu zaawansowanego filtru w programie Excel (VBA)

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

Po uruchomieniu tego kodu, muszę zrobić coś z wierszami, które są obecnie widoczne na ekranie.

Obecnie używam kodu jak ten

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

Czy istnieje prosty właściwość, że może dać mi szereg wierszy widoczne po zastosowaniu filtru zaawansowanego?

Odpowiedz

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

To daje obiekt Range.

+1

dziękuję. działa w programie Excel 2007. Sprawdza w programie Excel 2003 jutro –

15

Rozwiązanie Lance działa w większości sytuacji.

Ale jeśli masz do czynienia z dużymi/złożonymi arkuszami kalkulacyjnymi, możesz natknąć się na "SpecialCells Problem". W skrócie, jeśli utworzony zakres powoduje więcej niż 8192 nieciągłych obszarów (i może się zdarzyć może być), to program Excel rzuci błąd, gdy spróbujesz uzyskać dostęp do SpecialCells, a twój kod nie zostanie uruchomiony. Jeśli Twoje arkusze są wystarczająco złożone, możesz napotkać ten problem, dlatego zaleca się trzymać z pętlą.

Warto zauważyć, że ten problem nie dotyczy samej właściwości SpecialCells, a raczej dotyczy obiektu Range. Oznacza to, że w dowolnym momencie, gdy próbujesz uzyskać obiekt zakresu, który może być bardzo skomplikowany, powinieneś albo obsługiwać moduł obsługi błędów, albo zrobić to, co już zrobiłeś, co spowoduje, że twój program będzie działał na każdym elemencie zakresu (podziel zasięg w górę).

Innym możliwym podejściem byłoby zwrócenie tablicy obiektów zakresu, a następnie przejrzenie jej w pętli. Wysłałem przykładowy kod do zabawy. Jednak należy zauważyć, że naprawdę powinieneś zawracać sobie tym głowę, jeśli spodziewasz się opisanego problemu lub po prostu chcesz mieć pewność, że twój kod jest solidny. W przeciwnym razie jest to po prostu niepotrzebna złożoność.


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 dlatego SO jest niesamowite wygrać –

+0

[Uwaga: Ten problem został rozwiązany w programie Excel 2010 Niezłączne komórki, które można wybrać w programie Excel 2010: 2 147 483 648 komórek] (https: //www.rondebruin.nl/win/s4/win003.htm) – danieltakeshi

1

Można użyć następującego kodu, aby uzyskać widzialny zakres komórek:

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

nadzieję, że to pomaga.

+0

To jest złe. Odnosi się do zakresu komórek widocznych w oknie i faktycznie ignoruje problem ukrytych wierszy. to jej zakres od komórki widocznej w lewym górnym rogu okna do komórki widocznej w prawym dolnym rogu okna ... – epeleg

Powiązane problemy