Автофильтр VBA для отмеченных элементов списка - PullRequest
0 голосов
/ 11 октября 2019

Я знаю, что я могу отфильтровать диапазон в Excel с VBA при использовании AutoFilter, например:

Sub name()
ActiveSheet.Range("$A$12:$Y$74").AutoFilter Field:=22, Criteria1:="String"
End Sub)

... это прекрасно работает. Тем не менее, я изо всех сил стараюсь сделать это немного сложнее. Кто-нибудь имеет представление о том, как следующий пример может быть реализован в VBA?

Что я хотел бы сделать, это отфильтровывать пункты с пометкой из List Box с Operator:=xlAnd между каждым элементом списка с пометкой.

Пример: если я отмечу String1 и String2 в следующих List Box, функция AutoFilter должна вернуть все строки, содержащие String1 и String2. В случае таблицы ниже это будет строка 2 и строка 4.

enter image description here

| 1 | String1                   |
| 2 | String2, String1          |
| 3 | String2                   |
| 4 | String1, String2, String3 |
| 5 | String3                   |
| 6 | String1                   |
| 7 | String3, String1          |

Ответы [ 4 ]

2 голосов
/ 15 октября 2019

Не проверял, но теоретически, это работает и с автофильтром:

Sub name()
ActiveSheet.Range("$A$12:$Y$74").AutoFilter Field:=22, Criteria1:="*String1*", _
Operator:=xlOr, Criteria2:="*String2*"
End Sub)

Если вы можете изменить String1 и String2, чтобы включить * либо в код, либоокно списка, я думаю, это должно работать, чтобы найти эти сценарии.

1 голос
/ 18 октября 2019

Автофильтровать диапазон с использованием массива

Требование: Фильтровать диапазон, чтобы показать все строки, содержащие все элементы в массиве.
т.е. для массива = («String1», «String2», «String3», «String4», «String5»)
Автофильтр должен включать все строки, содержащие «String1», «String2», «String3», «String4» и «String5» в любой позиции.
Это должно быть эквивалентно возможности выполнять что-то подобное в качестве пользовательского автофильтра:

.AutoFilter Field:=1, _
    Criteria1:=sCriteria1, Operator:=xlAnd, _
    Criteria2:=sCriteria2, Operator:=xlAnd, _
    Criteria3:=sCriteria3, Operator:=xlAnd, _
    Criteria4:=sCriteria4, Operator:=xlAnd, _
    Criteria5:=sCriteria5, Operator:=xlAnd, _
    …, _
    CriteriaN:=sCriteriaN

Решение: Этопредлагаемое решение:
1. Обработать значения массива (каждые два), чтобы сгенерировать массив отфильтрованных диапазонов
2. Получает пересечение массива отфильтрованных диапазонов
3. Скрывает все строки в целевом диапазонеи отображает все строки в диапазоне пересечений
4. Создает массив со всеми значениями на шаге 4
5. Фильтрует целевой диапазон, применяя массив, сгенерированный на шаге 4

AdvПреимущества этой процедуры:
 Он не проходит по каждой строке целевого диапазона.
 Возвращает автофильтр, поэтому дополнительные фильтры можно применять к другим полям без потери автофильтра массива.

Процедура:

Функция Range_ƒFilter_ByArray_Contains (aCriteria As Variant, rTrg As Range, sMsg As String) Как Boolean
Возвращает как логическое значение Фильтрует целевой диапазон (rTrg), применяя все значения в массиве Criteria (aCriteria), возвращая также сообщение (sMsg) в случае ошибки.

Function Range_ƒFilter_ByArray_Contains(aCriteria As Variant, _
    rTrg As Range, sMsg As String) As Boolean
Dim blAfByAry As Boolean
Dim arAFs() As Range
Dim ws As Worksheet
Dim bDim As Byte
Dim sCriteria1 As String, sCriteria2 As String
Dim rAFs As Range, aAFcontains As Variant
Dim b As Byte

    Rem Validate Input
    If (rTrg Is Nothing) Then sMsg = "Target range is invalid": GoTo Exit_Err
    If Not (IsArray(aCriteria)) Then sMsg = "aCriteria is not an array": GoTo Exit_Err
    On Error Resume Next
    aCriteria = WorksheetFunction.Index(aCriteria, 0, 0)
    If Err.Number <> 0 Then GoTo Exit_Err
    bDim = UBound(aCriteria, 2)
    If Err.Number = 0 Then sMsg = "aCriteria is not a single dimension array": GoTo Exit_Err
    On Error GoTo Exit_Err

    With Application
        .EnableEvents = False
        .DisplayAlerts = False
        .ScreenUpdating = False
        .Calculation = xlCalculationManual
    End With

    With rTrg

        Rem Clear AutoFilter
        With .Worksheet
            On Error Resume Next
            If Not .AutoFilter Is Nothing Then .AutoFilter.Range.AutoFilter
            On Error GoTo 0
        End With

        Rem Dimensioning AutoFilters Range Array
        bDim = UBound(aCriteria)
        blAfByAry = bDim > 2
        If blAfByAry Then
            If WorksheetFunction.IsOdd(bDim) Then bDim = 1 + bDim
            bDim = (bDim / 2)
            ReDim Preserve arAFs(1 To bDim)
        End If

        For b = 1 To UBound(aCriteria) Step 2

            Rem Apply AutoFilter Criterias (2 each time)
            sCriteria1 = aCriteria(b)
            Select Case b
            Case UBound(aCriteria)
                .AutoFilter Field:=1, Criteria1:=sCriteria1
            Case Else
                sCriteria2 = aCriteria(1 + b)
                .AutoFilter Field:=1, Criteria1:=sCriteria1, _
                    Operator:=xlAnd, Criteria2:=sCriteria2
            End Select

            Rem Set AutoFilter Range Item
            If blAfByAry Then Set arAFs((1 + b) / 2) = rTrg.SpecialCells(xlCellTypeVisible)

    Next: End With

    If blAfByAry Then

        Rem Set AutoFilters Range
        Set rAFs = arAFs(1)
        For b = 2 To UBound(arAFs)
            Set rAFs = Application.Intersect(rAFs, arAFs(b))
        Next

        With rTrg

            Rem Clear AutoFilter
            rTrg.AutoFilter

            Rem Apply AutoFilters Range
            .EntireRow.Hidden = True
            rAFs.EntireRow.Hidden = False

            With ThisWorkbook

                Rem Set AutoFilter Array Criteria
                Set ws = .Sheets.Add(After:=.Sheets(.Sheets.Count))
                With ws
                    rAFs.Copy
                    .Cells(1).PasteSpecial
                    aAFcontains = .Cells(1).CurrentRegion.Value2
                    aAFcontains = WorksheetFunction.Transpose(aAFcontains)
                    ws.Delete

            End With: End With

            Rem Apply AutoFilter Array Criteria
            rTrg.AutoFilter Field:=1, _
                Criteria1:=aAFcontains, Operator:=xlFilterValues

    End With: End If

    Range_ƒFilter_ByArray_Contains = True

Exit_Err:

    With Err
        If .Number <> 0 Then sMsg = "Error: " & .Number & vbLf & vbTab & .Description
    End With

    With Application
        .Calculation = xlCalculationAutomatic
        .ScreenUpdating = True
        .DisplayAlerts = True
        .EnableEvents = True
    End With

    End Function

Процедуру следует использовать следующим образом:

Set rTrg = ThisWorkbook.Worksheets(kWsh).Range(kRng)
If Not (Range_ƒFilter_ByArray_Contains(aCriteria, rTrg, sMsg)) Then
    MsgBox sMsg, vbCritical, "Range_ƒFilter_ByArray_Contains"
End If

Примечание: Это решение обрабатывает только оператор xlAnd в соответствии свопрос оригинального ОП, тем не менее, его можно легко изменить, включив работу также с оператором xlOr.

1 голос
/ 11 октября 2019

Если у вас есть несколько значений для фильтрации по, я бы добавил их значения в массив, а затем использовал бы значения из массива для фильтрации диапазона, как показано ниже:

Sub Autofiler_Array()
Dim ws As Worksheet: Set ws = ThisWorkbook.ActiveSheet
'declare and set the worksheet you are working with
Dim myarray As Variant
myarray = Array("String1", "String2", "String3")
'declare and assign values to Array

If ws.FilterMode Then ws.Range("$A$12:$Y$74").AutoFilter
'if Worksheet already is Filtered, then remove Autofilter
ws.Range("$A$12:$Y$74").AutoFilter Field:=22, Criteria1:=myarray, Operator:=xlFilterValues
'Autofilter with Array Values on Column 22 of the applicable range
End Sub

ОБНОВЛЕНИЕ:

После прочтения ваших комментариев и обновленного вопроса, я полагаю, что следующие приведут к желаемым результатам, вместо того, чтобы использовать автофильтр, код ниже будет циклически проходить по строкам, проверьте, не ячейка лисодержит все значения из массива, если не скрывает их строки:

Sub Auto_Filter()
Dim ws As Worksheet: Set ws = ThisWorkbook.ActiveSheet
'declare and set the worksheet you are working with
Dim arrWords  As Variant
arrWords = Array("String1", "String2")
'declare and assign values to Array
ws.Cells.EntireRow.Hidden = False
'unhide all rows
LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
'get last row with data in Column A

For i = 2 To LastRow
'loop through rows
    For Each aWord In arrWords 'loop through Array values
        If Not InStr(ws.Cells(i, 22).Value, aWord) > 0 Then
            ws.Rows(i).EntireRow.Hidden = True
            'if values from Array not found in cell, then hide row
        End If
    Next
Next i
End Sub
0 голосов
/ 18 октября 2019

Вот мое решение, которое вдохновлено ответом Ксабье. Он имеет два «сценария».

1) Отображать строки, в которых строка в ячейке под проверкой содержит либо String1 , либо String2

2) Отображениестроки, в которых строка в проверяемой ячейке содержит String1 и String2

Sub AoP()

StartRow = 13
EndRow = 73
TargetColumn = 19 '(R)

LengthListBox = (ActiveSheet.ListBox1.ListCount - 1) ' Number of ListBox entries

ReDim TestXYZ(LengthListBox) As Integer 'Permanent list of checkmarked ListBox entries as ones and zeros
ReDim CheckList(LengthListBox) As String 'Permanent list of checkmarked ListBox entries as strings
ReDim Matches(LengthListBox) As Integer 'Temporary list of matches between search criteria and cell content

'''''''''''''''''''''''''''''''''''''''''''''''''
' Create arrays with information on the ListBox
'''''''''''''''''''''''''''''''''''''''''''''''''

For i = 0 To LengthListBox 'For 0 to length of ListBox
    If ActiveSheet.ListBox1.Selected(i) Then 'Loop
        TestXYZ(i) = 1 ' Checkmarked = 1
        CheckList(i) = ActiveSheet.ListBox1.List(i)
    Else
        TestXYZ(i) = 0 ' Not checkmarked = 0
    End If
Next i

'''''''''''''''''''''''''''''''''''''''''''''''''
' Hide rows that do not match a specific criteria
'''''''''''''''''''''''''''''''''''''''''''''''''

'If OR is selected as an operator
If ActiveSheet.CheckBox_AoP_Or.Value = True Then ' If "Or" is selected as an operator
    For i = StartRow To EndRow 'For each row
        ActiveSheet.Rows(i).EntireRow.Hidden = True 'Hide all rows ifnot
        For j = 0 To LengthListBox 'For 0 to length of ListBox
            If Len(CheckList(j)) > 0 Then
                If InStr(1, ActiveSheet.Cells(i, TargetColumn).Value, CheckList(j), vbTextCompare) > 0 Then 'If the cell contains the checked ListBox string
                    ActiveSheet.Rows(i).EntireRow.Hidden = False 'Unhide the row
                End If
            End If
        Next j
    Next i
'If OR is NOT selected as an operate (behave like AND)
Else ' If "Or" is NOT selected as an operator
    For i = StartRow To EndRow 'For each row
        ActiveSheet.Rows(i).EntireRow.Hidden = True 'Hide all rows ifnot
        For k = 0 To LengthListBox 'Makes sure that the matches are set to zero
            Matches(k) = 0
        Next k
        For j = 0 To LengthListBox 'Parse through all list box entries
            If TestXYZ(j) = 1 Then ' If they have been checkmarked
                If InStr(1, ActiveSheet.Cells(i, TargetColumn).Value, CheckList(j), vbTextCompare) > 0 Then ' ... and if they are contained in the string
                    Matches(j) = 1 ' Contained = 1
                Else
                    Matches(j) = 0 ' Not contained = 0
                End If
            End If
        Next j
        If Excel.WorksheetFunction.Sum(TestXYZ) = Excel.WorksheetFunction.Sum(Matches) Then 'If all are contained (all are matched so the sum of 1 is equal)
            ActiveSheet.Rows(i).EntireRow.Hidden = False '... then unhide
        End If
    Next i
End If

End Sub
...