Jvd*_*vdV 5 excel vba autofilter
背景:
我已经申请了很多次AutoFilter,但从未真正问过自己为什么有时会如此运作。有时,处理过滤后的数据的结果可能会造成混乱,尤其是在SpecialCells发挥作用时。
让我详细说明以下情形:
测试数据:
| Header1 | Header2 |
|---------|---------|
| 50 | |
| 10 | |
| 30 | |
| 40 | |
| 20 | |
Run Code Online (Sandbox Code Playgroud)
代码1-普通AutoFilter:
With Sheets("Sheet1").Range("A1:B6")
.AutoFilter 1, ">50"
.Columns(2).Value = "Check"
.AutoFilter
End With
Run Code Online (Sandbox Code Playgroud)
这将起作用(即使不使用SpecialCells(12)),但也会填充B1。
代码2-使用.Offset:
为了防止上述行为,我们可以这样实现Offset:
With Sheets("Sheet1").Range("A1:B6")
.AutoFilter 1, ">50"
.Columns(2).Offset(1).Value = "Check"
.AutoFilter
End With
Run Code Online (Sandbox Code Playgroud)
但是,这现在将填充我们的数据cell下方的行B7。
代码3-使用.Resize:
为了防止.Offset填充,B7我们现在必须包含一个.Resize:
With Sheets("Sheet1").Range("A1:B6")
.AutoFilter 1, ">50"
.Columns(2).Offset(1).Resize(5, 1).Value = "Check"
.AutoFilter
End With
Run Code Online (Sandbox Code Playgroud)
尽管现在我们既阻止B1又B7填充了我们B2:B6,但该AutoFilter机制似乎“被破坏了”。我试图用下面的截图展示它。中间的是过滤时">30"的右边,右边的是过滤时的">50"。如我所见,这与以下事实有关:引用范围现在由零个可见单元格组成。
代码4-使用.SpecialCells:
我在这里要执行的正常操作是首先Count对可见的单元格进行操作(包括范围内的标头以防止error 1004)。
With Sheets("Sheet1").Range("A1:B6")
.AutoFilter 1, ">50"
If .SpecialCells(12).Count > 2 Then .Columns(2).Offset(1).Resize(5, 1).Value = "Check"
.AutoFilter
End With
Run Code Online (Sandbox Code Playgroud)
题:
如您所见,我从.Columns(2).Value = "Check"一路走到If .SpecialCells(12).Count > 2 Then .Columns(2).Offset(1).Resize(5, 1).Value = "Check",只是为了防止B1被覆盖。
显然,该AutoFilter机制在第一种情况下可以很好地检测自身可见的行,但是为了防止标题被覆盖,我必须实现:
我在这里使事情变得过于复杂吗,会不会有更短的路线?同样,为什么一旦没有可见的细胞就会填充整个范围的不可见细胞。当实际上有一些数据被过滤时,它将很好地工作。这是什么机制(请参见代码3)?
我想出的(不是很优雅的)IMO选项是重写B1:
With Sheets("Sheet1").Range("A1:B6")
.AutoFilter 1, ">50"
Var = .Cells(1, 2): .Columns(2).Value = "Check": .Cells(1, 2) = Var
.AutoFilter
End With
Run Code Online (Sandbox Code Playgroud)
每当 Excel 在工作表上创建筛选列表时,它都会在名称管理器的后台创建一个隐藏的命名区域。如果您调用名称管理器,则此范围通常不可见。使用以下代码使您的隐藏命名范围在名称管理器中可见(在使用它之前,请在范围上设置过滤器):
Dim nvar As Name
For Each n In ActiveWorkbook.Names
n.Visible = True
Next
Run Code Online (Sandbox Code Playgroud)
在英文版的 Excel 中,隐藏的筛选范围称为 。_FilterDatabase我的解决方案使用此隐藏范围与 SpeciallCells(12) 结合来解决该问题。
更新 我的最终答案不使用隐藏的命名范围,但我将保留该信息,因为它是发现过程的一部分...
Sub test1()
Dim var As Range
Dim i As Long, ans As Long
With Sheets("Sheet1").Range("A1:C1")
.Range("B2:B6").Clear
.AutoFilter
.AutoFilter 1, ">50"
Set var = Sheet1.AutoFilter.Range
Set var = Intersect(var.SpecialCells(12), var.Offset(1, 0))
If Not (var Is Nothing) Then
For i = 1 To var.Areas.Count
var.Areas(i).Offset(0, 1).Resize(var.Areas(i).Rows.Count, 1).Value = "Check"
Next i
End If
.AutoFilter
End With
End Sub
Run Code Online (Sandbox Code Playgroud)
我用 >30 和 >50 对其进行了测试。它按预期执行。
这些问题显然源于处理表中的隐藏行,因此处理此问题的最简单方法是创建一个可以操作和查看可见单元格的表体范围。
如果您想标记可见行,它比隐藏行要容易一些,否则您需要创建一个虚拟变量,取消隐藏,填充空白,然后删除虚拟变量
例如
Sub AutoFilterTable()
Dim SrcRange As Range: Set SrcRange = Sheets("Sheet1").Range("A1:B6")
Dim BodyRange As Range: Set BodyRange = Application.Intersect(SrcRange, SrcRange.Offset(1, 0))
With SrcRange
BodyRange.Columns(2).ClearContents
.AutoFilter 1, ">30"
On Error Resume Next
BodyRange.Columns(2).SpecialCells(xlCellTypeVisible) = "Check"
.AutoFilter
End With
End Sub
Run Code Online (Sandbox Code Playgroud)
使用虚拟变量
Sub AutoFilterTable()
Dim SrcRange As Range: Set SrcRange = Sheets("Sheet1").Range("A1:B6")
Dim BodyRange As Range: Set BodyRange = Application.Intersect(SrcRange, SrcRange.Offset(1, 0))
With SrcRange
BodyRange.Columns(2).ClearContents
.AutoFilter 1, ">30"
On Error Resume Next
BodyRange.Columns(2).SpecialCells(xlCellTypeVisible) = "Dummy"
.AutoFilter
BodyRange.Columns(2).SpecialCells(xlCellTypeBlanks) = "Check"
BodyRange.Columns(2).Replace "Dummy", ""
End With
End Sub
Run Code Online (Sandbox Code Playgroud)
然后围绕代码3的问题:这取决于.Columns(2).Offset(1)是否是隐藏行(以及其他行是否隐藏)
如果可见,那么它将按预期工作;事实上,无论是否隐藏,如果存在可见的行,调整其顶部的大小最终将选择可见的单元格。如果所有行都被隐藏,则偏移范围仍处于“活动”状态,因此当调整其大小而没有可见单元格来包含该范围时,它最终会选择所有单元格。