【问题标题】:Removing Duplicates through VBA still throws pop up despite displayalerts=false尽管 displayalerts=false,通过 VBA 删除重复项仍然会弹出
【发布时间】:2018-03-06 23:51:41
【问题描述】:

我的电子表格中的 C 列包含将由客户选择并经常更新的值。我希望 D 列动态应用从该列表中提取的数据验证。但是,它需要包含按字母顺序排列的唯一值。

我目前正在做的是使用以下公式按字母顺序对隐藏列 (BK) 中的这些值进行排序。 (注意:我发现这个的网站表明它应该只显示唯一值,但它没有)。

{=INDEX(List,MATCH(0,IF(MAX(NOT(COUNTIF($BK$15:BK15,List))*(COUNTIF(List,">"&List)+1))=(COUNTIF(List,">"&List)+1),0,1),0))}

要动态更新 D 列,我使用以下代码:

Dim NewRng As Range
Dim RefList As Range, c As Range, rngHeaders As Range, RefList2 As Range, msg

On Error GoTo ErrHandling


Set NewRng = Application.Intersect(Me.Range("D16:D601"), Target)
If Not NewRng Is Nothing Then

    Set rngHeaders = Range("A15:ZZ16").Find("Status List", After:=Range("E15"))
    Set RefList = Range(rngHeaders.Offset(1, 0).Address, rngHeaders.Offset(100, 0).Address)
    RefList.Copy
    RefList.Offset(0, 1).PasteSpecial xlPasteValues
    Set RefList2 = RefList.Offset(0, 1)


    Application.DisplayAlerts = False
    RefList2.RemoveDuplicates Columns:=1


    For Each c In NewRng
        c.Validation.Delete
        c.Validation.Add Type:=xlValidateList, _
                                 AlertStyle:=xlValidAlertStop, _
                                 Formula1:="=" & RefList2.Address

    Next c
End If
Application.DisplayAlerts = True
Application.EnableEvents = True

这似乎有效,除了每次我单击 D 列中的单元格时,它仍然会弹出一个名为“删除重复项”的弹出框,其中显示两个选中的复选框——“全选”和“BL 列”。它还告诉我找到了多少重复项以及将保留多少个唯一值。

我不知道为什么 displayalerts=false 没有关闭它,但绝对不是每次有人点击 D 列时都会触发此选项的选项。以前有人见过吗? (顺便说一下,我在 Excel for Mac 2016 上)。

【问题讨论】:

  • 你可以试试 Record Macro 来比较生成的代码。 RefList2.RemoveDuplicates Columns:=Array(1), Header:=xlNomsdn.microsoft.com/en-us/vba/excel-vba/articles/…
  • 我今天早上添加了 Header:=xlNo,但似乎仍然弹出。
  • 我认为问题可能是您没有将数组传递给列
  • 我不这么认为,两种方法我都试过了:/
  • 那么我的另一个猜测是纸张上是否有任何保护。我刚刚注意到 Excel 2016 for Mac 部分,因此可能值得在 Windows 上尝试以防万一。

标签: vba excel duplicates


【解决方案1】:

我仍然没有找到抑制或自动接受弹出框的方法,这会导致进一步的问题,因为这意味着我选择的 D 列中的单元格不再被选中,所以我无法选择从下拉列表中。 但是,我想知道是否有人有任何可能比我上面的方法更简单的替代想法。

基本上我需要实现两种不同的场景:

  • 上述场景,我只需要从中提取唯一值 将 C 列放入 D 列中的数据验证下拉列表中。
  • 我还需要根据另一个页面上当前不是列表格式的值创建下拉列表。例如,在下面的代码中,我正在寻找当前位于另一个页面标题中的任何值(即单元格被合并)。现在我正在查找/复制/粘贴/验证,但这似乎很复杂。当然,它会遇到与场景 1 相同的弹出问题。

    Dim EvalRng As Range
    Set ws = ThisWorkbook.Sheets("Evaluation Forms")
    Dim EvalList As Range, EvalList2 As Range, EvalHeader As Range
    
    On Error GoTo ErrHandling2
    
    Set EvalRng = Application.Intersect(Me.Range("E16:E601"), Target)
    Set EvalHeader = Range("A15:ZZ16").Find("Evaluation Forms List", 
    After:=Range("E15"))
    
    If Not EvalRng Is Nothing Then
    
    For Each c In ws.Range("A15:A105")
        If c.MergeCells Then
            c.Copy
            EvalHeader.Offset(1, 0).PasteSpecial xlPasteValues
            Set EvalHeader = EvalHeader.Offset(1, 0)
        End If
    
    
    Next c
    
    'Set EvalList = Range(EvalHeaders.Offset(1, 0).Address, EvalHeaders.Offset(100, 0).Address)
    Set EvalList = EvalHeader.Offset(1, 0).End(xlDown)
    
    EvalList.Copy
    EvalList.Offset(0, 1).PasteSpecial xlPasteValues
    Set EvalList2 = EvalList.Offset(0, 1)
    
    
    Application.DisplayAlerts = False
    Application.EnableEvents = False
    EvalList2.RemoveDuplicates Columns:=Array(1), header:=xlNo
    
    
    For Each c In ActionRng
        c.Validation.Delete
        c.Validation.Add Type:=xlValidateList, _
                                 AlertStyle:=xlValidAlertStop, _
                                 Formula1:="=" & EvalList2.Address
    
    Next c
    

    如果结束

【讨论】:

  • 您可能应该编辑您的原始问题,而不是发布此后续问题作为答案。无论如何,如果您可以将所有这些不同的列表聚合到一个可能是新的工作表上,您可能会发现您可以简化此过程。在该工作表上,您可以随意使用任意数量的辅助列,这将使您更容易弄清楚如何排序、过滤唯一性以及创建用于数据验证的单个列表。在单个数组公式中尝试所有这些是痛苦的秘诀,而 VBA 的所有这些下游问题可能会通过另一种方法消失。
  • 我想我没有看到差异。我目前没有使用数组,我正在将数据复制到同一张表中的列表中。我可以将它们添加到不同的表中,但似乎“删除重复项”问题将仍然存在,无论它是在当前表上还是一个不同的。我认为我得到的是那些我正在使用的值 .Find 在其他工作表上拉入,如果我可以将它们添加到某种变量(数组?)中会更好,哪个然后将是数据 val 的公式 1,而不是先将它们粘贴到列表中。
  • 我试图表明您应该找到一种不使用 RemoveDuplicates 的方法,因为它似乎会造成麻烦。您在上面提到尝试使用公式仅返回唯一项目;该技术应该有效。上面的公式可能不起作用,但有一个公式可以做到这一点。如果您找不到一个公式,请在帮助表上将一对串在一起。或者,您可以将项目推送到 VBA 端的 Dictionary 中,以防止重复,然后从那里输出。
【解决方案2】:

我找到了一种使用 RemoveDuplicates 来实现所需结果的方法。感谢 Jean-Francois Corbett 和 SJR 提供了一些构建此解决方案的代码。见下文:

Public varUnique As Variant

Public ResultingStatus As Range
Public WhenAction As Range
Public EvalForm As Range



'Remove Case Sensitivity
  Option Compare Text

Private Sub Worksheet_SelectionChange(ByVal Target As Range)

Application.ScreenUpdating = False
Application.EnableEvents = False

'Prevents users from deleting columns that would mess up the header box
If Selection.Rows.Count = ActiveSheet.Rows.Count Then
    If Not Intersect(Target, Range("A:H")) Is Nothing Then

        Range("A1").Select
    End If

End If


Call StatusBars(Target)

Dim rngIn As Range
Dim varIn As Variant
Dim iInCol As Long
Dim iInRow As Long
Dim iUnique As Long
Dim nUnique As Long
Dim isUnique As Boolean
Dim i As Integer
Dim ActionRng As Range
Dim EvalRng As Range
Dim ActionList As Range, c As Range, rngHeaders As Range, ActionList2 As Range, msg
Dim ws As Worksheet


Set ResultingStatus = Range("A15:Z15").Find("Resulting Status")
Set WhenAction = Range("A15:Z15").Find("When can this action")
Set EvalForm = Range("A15:Z15").Find("Evaluation Form")


'When can action be taken list

    'On Error GoTo ErrHandling



Set ActionRng = Application.Intersect(Me.Range("D16:D601"), Target)
    If Not ActionRng Is Nothing Then
        Set rngIn = Range(ResultingStatus.Offset(1, 0).Address, ResultingStatus.Offset(1000, 0).End(xlUp).Address)
        varIn = rngIn.Value

        ReDim varUnique(1 To UBound(varIn))

        nUnique = 0
        For i = LBound(varIn) To UBound(varIn)
            isUnique = True
            For iUnique = 1 To nUnique
                If varIn(i, 1) = varUnique(iUnique) Then
                    isUnique = False
                    Exit For
                End If
            Next iUnique
            If isUnique = True Then
                nUnique = nUnique + 1
                varUnique(nUnique) = varIn(i, 1)
            End If
        Next i

        '// varUnique now contains only the unique values.
        '// Trim off the empty elements:
        ReDim Preserve varUnique(1 To nUnique)

        QuickSort varUnique, LBound(varUnique), UBound(varUnique)


        myvalidationStr = ""
        For Each x In varUnique
            myvalidationStr = myvalidationStr & x & ","
        Next x

        myvalidationStr = Left(myvalidationStr, Len(myvalidationStr) - 1)

            With ActionRng.Validation

                .Delete
                .Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:= _
                xlBetween, Formula1:=myvalidationStr
                .IgnoreBlank = True
                .InCellDropdown = True
                .InputTitle = ""
                .ErrorTitle = ""
                .InputMessage = ""
                .ErrorMessage = ""
                .ShowInput = True
                .ShowError = True
            End With

    End If


Here:
'Eval forms

Set ws = ThisWorkbook.Sheets("Evaluation Forms")
Dim EvalList As Range, EvalList2 As Range, EvalHeader As Range

On Error GoTo ErrHandling2
Set EvalRng = Application.Intersect(Me.Range("E16:E601"), Target)
Dim cUnique As Collection
Dim vNum As Variant
Set cUnique = New Collection

If Not EvalRng Is Nothing Then
    On Error Resume Next
    For Each c In ws.Range("A15:A105")
            If c.MergeCells Then
                cUnique.Add c.Value, CStr(c.Value)
            End If
    Next c

QuickSort2 cUnique, 1, cUnique.Count


        myvalidationStr = ""
        For Each x In cUnique
            myvalidationStr = myvalidationStr & x & ","
        Next x

        myvalidationStr = Left(myvalidationStr, Len(myvalidationStr) - 1)

            With EvalRng.Validation

                .Delete
                .Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:= _
                xlBetween, Formula1:=myvalidationStr
                .IgnoreBlank = True
                .InCellDropdown = True
                .InputTitle = ""
                .ErrorTitle = ""
                .InputMessage = ""
                .ErrorMessage = ""
                .ShowInput = True
                .ShowError = True
            End With

    End If





Here2:

Application.ScreenUpdating = True
Application.DisplayAlerts = True
Application.EnableEvents = True
Exitsub:
Application.EnableEvents = True

    Exit Sub

ErrHandling:
    If Err.Number <> 0 Then
        msg = "Error # " & Str(Err.Number) & " was generated by " & _
            Err.Source & Chr(13) & "Error Line: " & Erl & Chr(13) & Err.Description
        Debug.Print msg, , "Error", Err.HelpFile, Err.HelpContext
    End If
    Resume Here

ErrHandling2:
    If Err.Number <> 0 Then
        msg = "Error # " & Str(Err.Number) & " was generated by " & _
            Err.Source & Chr(13) & "Error Line: " & Erl & Chr(13) & Err.Description
        Debug.Print msg, , "Error", Err.HelpFile, Err.HelpContext
    End If
    Resume Here2


End Sub



'Sort array
Sub QuickSort(varUnique As Variant, first As Long, last As Long)

  Dim vCentreVal As Variant, vTemp As Variant

  Dim lTempLow As Long
  Dim lTempHi As Long
  lTempLow = first
  lTempHi = last

  vCentreVal = varUnique((first + last) \ 2)
  Do While lTempLow <= lTempHi

    Do While varUnique(lTempLow) < vCentreVal And lTempLow < last
      lTempLow = lTempLow + 1
    Loop

    Do While vCentreVal < varUnique(lTempHi) And lTempHi > first
      lTempHi = lTempHi - 1
    Loop

    If lTempLow <= lTempHi Then

        ' Swap values
        vTemp = varUnique(lTempLow)

        varUnique(lTempLow) = varUnique(lTempHi)
        varUnique(lTempHi) = vTemp

        ' Move to next positions
        lTempLow = lTempLow + 1
        lTempHi = lTempHi - 1

    End If

  Loop

  If first < lTempHi Then QuickSort varUnique, first, lTempHi
  If lTempLow < last Then QuickSort varUnique, lTempLow, last

End Sub

'sort collections
Sub QuickSort2(cUnique As Collection, first As Long, last As Long)

  Dim vCentreVal As Variant, vTemp As Variant

  Dim lTempLow As Long
  Dim lTempHi As Long
  lTempLow = first
  lTempHi = last

  vCentreVal = cUnique((first + last) \ 2)
  Do While lTempLow <= lTempHi

    Do While cUnique(lTempLow) < vCentreVal And lTempLow < last
      lTempLow = lTempLow + 1
    Loop

    Do While vCentreVal < cUnique(lTempHi) And lTempHi > first
      lTempHi = lTempHi - 1
    Loop

    If lTempLow <= lTempHi Then

      ' Swap values
      vTemp = cUnique(lTempLow)

      cUnique.Add cUnique(lTempHi), After:=lTempLow
      cUnique.Remove lTempLow

      cUnique.Add vTemp, Before:=lTempHi
      cUnique.Remove lTempHi + 1

      ' Move to next positions
      lTempLow = lTempLow + 1
      lTempHi = lTempHi - 1

    End If

  Loop

  If first < lTempHi Then QuickSort cUnique, first, lTempHi
  If lTempLow < last Then QuickSort cUnique, lTempLow, last

End Sub

【讨论】:

    猜你喜欢
    • 2019-11-04
    • 2018-09-16
    • 2013-08-20
    • 2015-03-21
    • 1970-01-01
    • 1970-01-01
    • 2021-07-30
    • 2015-04-18
    相关资源
    最近更新 更多