背景:
我已经多次申请AutoFilter
,但从未真正问过自己为什么它有时会起作用。有时,处理过滤后的数据的结果可能会造成混乱,尤其是当SpecialCells
发挥作用时。
让我详细说明以下情况:
测试数据:
| Header1 | Header2 |
|---------|---------|
| 50 | |
| 10 | |
| 30 | |
| 40 | |
| 20 | |
代码1-普通AutoFilter
:
With Sheets("Sheet1").Range("A1:B6")
.AutoFilter 1, ">50"
.Columns(2).Value = "Check"
.AutoFilter
End With
这将起作用(即使不使用SpecialCells(12)
也可以),但是将填充B1
。
代码2-使用.Offset
:
为防止上述行为,我们可以像这样实现Offset
:
With Sheets("Sheet1").Range("A1:B6")
.AutoFilter 1, ">50"
.Columns(2).Offset(1).Value = "Check"
.AutoFilter
End With
但是,这现在将填充我们数据下方的行,单元格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
尽管现在我们都阻止了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
问题:
如您所见,我从.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
答案 0 :(得分:4)
每当Excel在工作表上创建一个过滤列表时,它就会在名称管理器的后台创建一个隐藏的命名范围。如果调用名称管理器,此范围通常是不可见的。使用以下代码使隐藏的命名范围在名称管理器中可见(使用它之前,请在范围上设置过滤器):
Dim nvar As Name
For Each n In ActiveWorkbook.Names
n.Visible = TrueRange
NextWith nSheets("Sheet1")
在英文版的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
我用> 30和> 50进行了测试。它按预期执行。
答案 1 :(得分:1)
问题显然是由于处理表中的隐藏行而引起的,因此处理此问题的最简单方法是创建一个表体范围,您可以操纵该表体范围并查看可见单元格。
如果要标记可见行,则它比隐藏行要容易一些,否则需要创建一个虚拟变量,取消隐藏,填充空白,然后删除虚拟变量
例如
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
使用虚拟变量
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
然后围绕代码3的问题:它取决于.Columns(2).Offset(1)是否为隐藏行(以及其他是否为隐藏行)
如果可见,它将按预期工作;实际上,无论是否存在隐藏的行(如果存在可见行),在其顶部进行调整大小最终都会选择可见的单元格。但是,如果所有行都被隐藏,则“偏移”范围仍处于“活动”状态,因此在调整其大小而没有可见的单元格可以包含该范围时,最终将选择所有单元格。
答案 2 :(得分:1)
这与手动执行这些步骤时的行为完全相同:
现在,如果您在键入之前按下向下箭头(与.Offset(1)
相同),则会选择下一个可见的单元格(B7
)。
如果您在应用自动过滤器之前手动选择了范围B2:B6
(因此所有单元格都被过滤掉了),然后使用 Ctrl + Enter 插入值,单元格将受到影响-我猜这是手动自动筛选器的一个未处理的边缘情况(人们并没有试图仅将值插入隐藏的单元格),即使使用VBA自动筛选器时也不理想。
答案 3 :(得分:1)
Range.AutoFilter method (Excel)的效果符合预期,将过滤条件应用于某个范围。 Range.SpecialCells method (Excel)在返回 union范围以及在其应用范围内遇到的可见单元格时,它的性能也达到了预期。
将using Newtonsoft.Json;
public class Item
{
public int Id { get; set; }
[JsonProperty("ServiceName")]
public string ItemName { get; set; }
}
方法应用于整个范围SpecialCells
时会产生意外结果,因为标头可见,然后将其包含在结果范围中。
由于Range("A1:B6")
方法可能会返回一个并集范围(几个区域),建议将其应用于“目标” SpecialCells
,我们需要使用值“ Check”进行更新,即Range
。
我们还需要使用Column(2)
来管理On Error statement
不返回可见行的情况。
以下过程将应用两个过滤器来更新各自的值。
AutoFilter
这与其他答案没什么不同,只是由于方法的应用方式而要添加有关“意外结果”的说明。
答案 4 :(得分:1)
这里有很多聪明的方法。矿山有些陈旧,但似乎可以正常工作(我已经使用问题中提供的表格对其进行了测试)
Sub SetFilteredCell()
Dim oWS As Worksheet: Set oWS = ThisWorkbook.Worksheets("Sheet4") ' Change sheet reference
Dim iLRow As Long: iLRow = oWS.Range("A" & oWS.Rows.Count).End(xlUp).Row ' Presuming that first row is the header
Dim oRng As Range: Set oRng = oWS.Range("A1:B" & iLRow) ' Set range here
Dim rFilteredRng As Range
Dim oCRng As Range
' Clear any existing filter
oWS.AutoFilterMode = False
' Set autofilter
oRng.AutoFilter Field:=1, Criteria1:=">20"
' Check if autofilter returned any rows
If oWS.AutoFilter.Range.Columns(1).SpecialCells(xlCellTypeVisible).Cells.Count - 1 > 0 Then
' Set filtered range
Set rFilteredRng = oRng.Cells.SpecialCells(xlCellTypeVisible)
' Loop through all filtered rows
For Each oCRng In rFilteredRng.Rows
' Skipping first row as the presumption is that its the header row
If oCRng.Row > 1 Then
oWS.Cells(oCRng.Row, 2).Value = "Check"
End If
Next
End If
' Clear filter
oWS.AutoFilterMode = False
End Sub
参考: This question
答案 5 :(得分:1)
如果要在过滤后更改范围的数据主体,则应使用原始范围偏移1行的 Intersection
< / strong>(省去标题)和SpecialCells(xlCellTypeVisible)
,然后通过 Areas
。
线索在此示例中:
Option Explicit
Sub MoreThan50()
MoreThanValue "50"
End Sub
Private Sub MoreThanValue(Optional Amount As String = "")
Dim oRng As Range, oRngArea As Range, oRngResult As Range
Set oRng = Sheets(1).Range("A1:B6")
' Clear Previous data on 2nd column
With Intersect(oRng, oRng.Offset(1))
.Columns(2).ClearContents
End With
With oRng
' Apply AutoFilter
.AutoFilter 1, ">" & IIf(Len(Amount) = 0, "50", Amount)
' Update 2nd Column of resulting data
Set oRngResult = Intersect(oRng.Offset(1), .SpecialCells(xlCellTypeVisible))
If Not oRngResult Is Nothing Then
With oRngResult
If .Areas.Count > 0 Then
For Each oRngArea In .Areas
oRngArea.Columns(2).Value = "check"
Next
End If
End With
Set oRngResult = Nothing
End If
.AutoFilter
End With
Set oRng = Nothing
End Sub
答案 6 :(得分:1)
我认为(在受限测试下,第2列中的单元格必须为空)可以使用.FindNext。 不需要SpecialCells和错误语句,
我在您的代码3中添加了此代码:
With Sheets("Sheet1").Range("A1:B6")
.AutoFilter 1, ">50"
If Not .FindNext(.Cells(1)) Is Nothing Then .Columns(2).Offset(1).Resize(5, 1).Value = "Check"
.AutoFilter
End With
编辑: 假设您的FilterTable旁边有一个空白列
With Sheets("Sheet1").Range("A1:B6")
.AutoFilter 1, ">30"
If Not .Offset(, 1).FindNext() Is Nothing Then .Columns(2).Offset(1).Resize(5, 1).Value = "Check"
.AutoFilter
End With
答案 7 :(得分:0)
这是将SpecialCells
xlCellTypeConstants
和xlCellTypeVisible
链接起来以调整目标范围的另一种变化形式。
With Range("A1:B6")
.Offset(1).Columns(2).ClearContents
.AutoFilter 1, ">50", , , True
On Error Resume Next
.Offset(1).SpecialCells(xlCellTypeConstants).SpecialCells(xlCellTypeVisible).Columns(2) = "Checked"
On Error GoTo 0
.AutoFilter
End With