【问题标题】:Conditional formatting over huge range in excel, using VBA使用VBA在excel中进行大范围的条件格式
【发布时间】:2016-09-11 23:38:14
【问题描述】:

我有一个在给定列中有大约 30k 行的 excel 工作簿。我需要交叉验证另一个同样庞大的列表,看看是否有任何匹配项。如果是这样,那么我希望它突出显示该单元格。

按照其他线程的建议,我手动录制了宏,代码是:

Sheets("Main").Select
Columns("D:D").Select
Selection.FormatConditions.Add Type:=xlTextString, String:= _
    "=list1!$A$1", TextOperator:=xlContains
Selection.FormatConditions(Selection.FormatConditions.Count).SetFirstPriority
With Selection.FormatConditions(1).Interior
    .PatternColorIndex = xlAutomatic
    .Color = 65535
    .TintAndShade = 0
End With

此宏有效,但仅适用于另一个工作表中包含我要验证的巨大列表的第一个单元格。但是,我无法让它适用于其他 49999 行。此外,此列表在另一张纸上。

我尝试创建一个 for 循环,例如 for i = 1 to length of columnthis 但每次都失败了。

【问题讨论】:

  • 您可以创建一个公式来检查索引表中的所有行。这就是 user3598756 的答案使用 COUNTIF 公式所做的。您可以在主工作表的辅助列中使用相同的公式,将其包装在 IF 公式中以返回 TRUE(Countif 返回 1 或更多)或 FALSE(Countif 返回 0)。这就是我认为该答案中“非CF”的含义。这将使您可以按 TRUE/FALSE 而不是按颜色进行过滤,这可能更可取。

标签: vba excel macros


【解决方案1】:

编辑在 OP 关于 CF 方法与其他方法的问题之后

edited2:添加了“字典”方法

“条件格式”方法可以比“范围”方法更快,但前者也可以使工作表非常沉重缓慢在后续使用中。 更不用说我在太多 CF 单元后也有崩溃的经历

“字典”方法都是最快的

这里遵循所有上述方法的可能代码


“CF”方法

如果你真的 必须使用条件格式,并且如果我正确地理解了你的目标,那么试试这个(注释)代码:

Option Explicit

Sub main()
    Dim mainRng As Range, list1Rng As Range

    Set mainRng = GetRange(Worksheets("Main"), "D") '<--| get "Main" sheet column "D" range from row 1 down to last non empty row
    Set list1Rng = GetRange(Worksheets("list1"), "A") '<--| get "list1" sheet column "D" range from row 1 down to last non empty row

    AddCrossCountFormatCondition mainRng, list1Rng '<--| add cross validation from "Main" to "List1" worksheet
    AddCrossCountFormatCondition list1Rng, mainRng '<--| add cross validation from "List1" to "Main" worksheet

End Sub

Function GetRange(ws As Worksheet, colIndex As String) As Range
    With ws '<--| reference passed worksheet
        Set GetRange = .Range(colIndex & "1", .Cells(.Rows.Count, colIndex).End(xlUp)) '<--| set its column "colIndex" range from row 1 down to last non empty row
    End With
End Function

Sub AddCrossCountFormatCondition(rng1 As Range, rng2 As Range)
    With rng1
        Intersect(rng1.Parent.UsedRange, rng1.Resize(1, 1).EntireColumn).FormatConditions.Delete '<--| remove previous conditional formatting
        .FormatConditions.Add Type:=xlExpression, Formula1:= _
            "=COUNTIF(" & rng2.Parent.Name & "!" & rng2.Address & ",concatenate(""*""," & rng1.Resize(1, 1).Address(False, False) & ",""*""))>0"
        .FormatConditions(.FormatConditions.Count).SetFirstPriority
        With .FormatConditions(1).Interior
            .PatternColorIndex = xlAutomatic
            .Color = 65535
            .TintAndShade = 0
        End With
    End With
End Sub

“范围”方法

Option Explicit

Sub main2()
    Dim mainRng As Range, list1Rng As Range

    Set mainRng = getRange(Worksheets("Main"), "D") '<--| get "Main" sheet column "D" range from row 1 down to last non empty row
    Set list1Rng = getRange(Worksheets("list1"), "A") '<--| get "list1" sheet column "D" range from row 1 down to last non empty row

    ColorMatchingRange mainRng, list1Rng
    ColorMatchingRange list1Rng, mainRng

End Sub

Sub ColorMatchingRange(rng1 As Range, rng2 As Range)
    Dim unionRng As Range, cell As Range, f As Range

    Set unionRng = rng1.Offset(, rng1.Columns.Count).Resize(1, 1)
    For Each cell In rng1
        If WorksheetFunction.CountIf(rng2, "*" & cell.Value & "*") > 0 Then Set unionRng = Union(unionRng, cell)
    Next cell
    Set unionRng = Intersect(unionRng, rng1)
    If Not unionRng Is Nothing Then
        With unionRng.Interior
            .PatternColorIndex = xlAutomatic
            .Color = 65535
            .TintAndShade = 0
        End With
    End If
End Sub

Function getRange(ws As Worksheet, colIndex As String) As Range
    With ws '<--| reference passed worksheet
        Set getRange = .Range(colIndex & "1", .Cells(.Rows.Count, colIndex).End(xlUp)) '<--| set its column "colIndex" range from row 1 down to last non empty row
    End With
End Function

“字典”方法

Option Explicit

Sub main3()
    Dim mainRng As Range, list1Rng As Range
    Dim mainDict As New Scripting.Dictionary, list1Dict As New Scripting.Dictionary

    Set mainRng = getRange(Worksheets("Main"), "D") '<--| get "Main" sheet column "D" range from row 1 down to last non empty row
    Set list1Rng = getRange(Worksheets("list1"), "A") '<--| get "list1" sheet column "D" range from row 1 down to last non empty row

    Set mainDict = GetDictionary(mainRng)
    Set list1Dict = GetDictionary(list1Rng)

    ColorMatchingRange2 mainRng, mainDict, list1Dict
    ColorMatchingRange2 list1Rng, list1Dict, mainDict

End Sub

Sub ColorMatchingRange2(rng1 As Range, dict1 As Scripting.Dictionary, dict2 As Scripting.Dictionary)
    Dim unionRng As Range
    Dim vals As Variant
    Dim i As Long

    vals = Application.Transpose(rng1.Value)

    Set unionRng = rng1.Offset(, rng1.Columns.Count).Resize(1, 1)
    For i = LBound(vals) To UBound(vals)
        If dict2.Exists(vals(i)) Then Set unionRng = Union(unionRng, rng1(i, 1))
    Next i

    Set unionRng = Intersect(unionRng, rng1)
    If Not unionRng Is Nothing Then
        With unionRng.Interior
            .PatternColorIndex = xlAutomatic
            .Color = 65535
            .TintAndShade = 0
        End With
    End If
End Sub

Function GetDictionary(rng As Range) As Scripting.Dictionary
    Dim dict As New Scripting.Dictionary
    Dim vals As Variant
    Dim i As Long

    vals = Application.Transpose(rng.Value)

    On Error Resume Next
    For i = LBound(vals) To UBound(vals)
        dict.Add vals(i), rng(i, 1).Address
    Next i
    On Error GoTo 0
    Set GetDictionary = dict
End Function

Function getRange(ws As Worksheet, colIndex As String) As Range
    With ws '<--| reference passed worksheet
        Set getRange = .Range(colIndex & "1", .Cells(.Rows.Count, colIndex).End(xlUp)) '<--| set its column "colIndex" range from row 1 down to last non empty row
    End With
End Function

【讨论】:

  • 感谢您的宝贵时间。我将很快测试这种可能性。您说必须,但是如果匹配,还有其他方法可以突出显示单元格吗?问题是我无法创建一个条件格式规则来遍历索引表中的所有行。它必须是一个接一个,很好...将有 30k 行要一个接一个...
  • 查看编辑后的答案。如果我的回答满足了您的问题,请将其标记为已接受。谢谢
  • CF 方法:它还更改并突出显示第一行,这应该是表头。此外,它还在另一张纸上突出显示。只需要它来标记“主”选项卡。 范围方法:它不会改变表头,但会突出显示另一张表。 字典方法:这一行Sub ColorMatchingRange2(rng1 As Range, dict1 As Scripting.Dictionary, dict2 As Scripting.Dictionary 输出错误User-defined type not defined。谢谢!!
  • 顺便说一句,如果您能帮我解决双重突出显示问题,我将不胜感激。
  • 我不是PC所以有些几乎是盲目的。 1) CF 方法:注释掉第二个“AddCrossCountFormatCondition”调用 2) 字典方法:注释掉第二个“ColorMatchingRange2”调用并将“Microsoft Scripting Library”(或非常相似的)库引用添加到您的项目
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-06
  • 1970-01-01
相关资源
最近更新 更多