如何组合两个 vba 代码?删除了多个列表,并显示为其他值

How do I combine two vba codes? Multiple list with item removal and a shown as other value

提问人:Pauline 提问时间:11/16/2023 最后编辑:Pauline 更新时间:11/17/2023 访问量:108

问:

我想组合两个不同的 vba 代码函数。

  1. 创建包含项目删除功能的多个列表
  2. 将所选项目显示为另一个预定义值

我有两个选项的 vba 代码,见下文。

第一个完全符合我的需求,将项目添加到列表中,并在第二次单击项目时删除项目(因此只有唯一值)。这很重要,因为列表会很长,当你点击错误的列表时,你不想重新开始。

第二个也确实创建了一个多列表并显示相应的较短值。但是这个没有删除项目的功能。由于列表很长,长值需要显示为较短值。而且下拉列表应该是长版本,所以人们不需要努力学习缩写,我仍然可以看到需要什么。

这两个 vba 代码都是由其他人编写的,而不是我写的。我只是用谷歌搜索了它们,我确实了解了一点 vba,但不足以将这两者结合起来或重写一个可以同时做这两件事的新。

是否有可能同时拥有这些功能,如果可以,如何?

额外但不是大问题: 如果我向列表中添加新值以进行选择或更改缩写,则已在下拉列表中选择的值不会更新。是否可以在代码中添加此内容?

我试图将它们结合起来,但我失败了。一个 vba 代码是用目标范围编写的,另一个是用目标范围编写的,所以我认为这是它不能一起工作的原因之一。但我也不够熟练。

第一个:

Option Explicit
Private Sub Worksheet_Change(ByVal Destination As Range)
Dim rngDropdown As Range
Dim oldValue As String
Dim newValue As String
Dim DelimiterType As String
DelimiterType = " | "
Dim DelimiterCount As Integer
Dim TargetType As Integer
Dim i As Integer
Dim arr() As String
 
If Destination.Count > 1 Then Exit Sub
On Error Resume Next
 
Set rngDropdown = Cells.SpecialCells(xlCellTypeAllValidation)
On Error GoTo exitError
 
If rngDropdown Is Nothing Then GoTo exitError
 
TargetType = 0
    TargetType = Destination.Validation.Type
    If TargetType = 3 Then  ' is validation type is "list"
        Application.ScreenUpdating = False
        Application.EnableEvents = False
        newValue = Destination.Value
        Application.Undo
        oldValue = Destination.Value
        Destination.Value = newValue
        If oldValue <> "" Then
            If newValue <> "" Then
                If oldValue = newValue Or oldValue = newValue & Replace(DelimiterType, " ", "") Or oldValue = newValue & DelimiterType Then ' leave the value if there is only one in the list
                    oldValue = Replace(oldValue, DelimiterType, "")
                    oldValue = Replace(oldValue, Replace(DelimiterType, " ", ""), "")
                    Destination.Value = oldValue
                ElseIf InStr(1, oldValue, DelimiterType & newValue) Or InStr(1, oldValue, " " & newValue & DelimiterType) Then
                    arr = Split(oldValue, DelimiterType)
                If Not IsError(Application.Match(newValue, arr, 0)) = 0 Then
                    Destination.Value = oldValue & DelimiterType & newValue
                        Else:
                    Destination.Value = ""
                    For i = 0 To UBound(arr)
                    If arr(i) <> newValue Then
                        Destination.Value = Destination.Value & arr(i) & DelimiterType
                    End If
                    Next i
                Destination.Value = Left(Destination.Value, Len(Destination.Value) - Len(DelimiterType))
                End If
                ElseIf InStr(1, oldValue, newValue & Replace(DelimiterType, " ", "")) Then
                    oldValue = Replace(oldValue, newValue, "")
                    Destination.Value = oldValue
                Else
                    Destination.Value = oldValue & DelimiterType & newValue
                End If
                Destination.Value = Replace(Destination.Value, Replace(DelimiterType, " ", "") & Replace(DelimiterType, " ", ""), Replace(DelimiterType, " ", "")) ' remove extra commas and spaces
                Destination.Value = Replace(Destination.Value, DelimiterType & Replace(DelimiterType, " ", ""), Replace(DelimiterType, " ", ""))
                If Destination.Value <> "" Then
                    If Right(Destination.Value, 2) = DelimiterType Then  ' remove delimiter at the end
                        Destination.Value = Left(Destination.Value, Len(Destination.Value) - 2)
                    End If
                End If
                If InStr(1, Destination.Value, DelimiterType) = 1 Then ' remove delimiter as first characters
                    Destination.Value = Replace(Destination.Value, DelimiterType, "", 1, 1)
                End If
                If InStr(1, Destination.Value, Replace(DelimiterType, " ", "")) = 1 Then
                    Destination.Value = Replace(Destination.Value, Replace(DelimiterType, " ", ""), "", 1, 1)
                End If
                DelimiterCount = 0
                For i = 1 To Len(Destination.Value)
                    If InStr(i, Destination.Value, Replace(DelimiterType, " ", "")) Then
                        DelimiterCount = DelimiterCount + 1
                    End If
                Next i
                If DelimiterCount = 1 Then ' remove delimiter if last character
                    Destination.Value = Replace(Destination.Value, DelimiterType, "")
                    Destination.Value = Replace(Destination.Value, Replace(DelimiterType, " ", ""), "")
                End If
            End If
        End If
        Application.EnableEvents = True
        Application.ScreenUpdating = True
    End If
 
exitError:
  Application.EnableEvents = True
End Sub
 
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
 
End Sub

第二个:

Private Sub Worksheet_Change(ByVal Target As Range)

    Dim oldValue As String
    Dim newValue As String
    Dim selectedNum As Variant
    Application.EnableEvents = True
    On Error GoTo Exitsub
    If Target.Column = 2 Then
        If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
            GoTo Exitsub
        Else: If Target.Value = "" Then GoTo Exitsub Else
            Application.EnableEvents = False
                     
            
            newValue = Target.Value
            selectedNum = Application.VLookup(newValue, Worksheets("PPE").Range("ShowAs"), 2, False)
            newValue = selectedNum
            Application.Undo
            oldValue = Target.Value
            If oldValue = "" Then
                Target.Value = newValue
            Else
                If InStr(1, oldValue, newValue) = 0 Then
                    Target.Value = oldValue & ", " & newValue
                Else:
                    Target.Value = oldValue
                End If
            End If
        End If
    End If
    Application.EnableEvents = True
Exitsub:
    Application.EnableEvents = True
End Sub

我仍在努力弄清楚如何添加我的 excel 文件以使其更清晰。

Excel VBA

评论

0赞 Black cat 11/16/2023
错过附加代码片段。没有他们,几乎什么都说不出来。
0赞 Notus_Panda 11/16/2023
OP 在您尝试编辑帖子@Blackcat刷新;)时编辑了帖子
0赞 Black cat 11/16/2023
@Notus我刷新了,但看不见。
0赞 Notus_Panda 11/16/2023
无论出于何种原因,社区机器人都批准了您的编辑,然后 Mayukh 在此基础上进行了编辑。我不知道为什么您的编辑获得批准,但我会纠正代码的删除。
0赞 Black cat 11/16/2023
谢谢。我只删除 Vlookup,它是 VBA

答:

0赞 Black cat 11/16/2023 #1

尝试这种解决方法,一个接一个地调用两个潜艇

将第二个 sub 重命名为 testsub

Private Sub Worksheet_Change(ByVal target As Range)

'here is your code

testsub target

End Sub


Private Sub testsub(ByVal target As Range)

Debug.Print "called from worksheet change"; target.Address

'here is the second code

End Sub

评论

0赞 Pauline 11/17/2023
我应该从哪里放到哪里?
0赞 Black cat 11/17/2023
从第一行到最后一行,都没有 Sub 和 End Sub 语句的代码。重命名只是因为一张纸上不能有两个更改事件子。并且可以注释掉用于测试目的的 Debug.Print 行。
0赞 Pauline 11/17/2023
我遇到了与@Notus_Panda解决方案相同的问题。代码不会给出错误,但也不会执行它们。我只是有带有数据验证列表的单元格。
0赞 Black cat 11/17/2023
您是否在即时窗口中收到消息?
0赞 Black cat 11/17/2023
您不会收到错误消息,因为代码中有一个 On Error Goto 语句。将其注释掉以检查较短的潜艇中的错误,并在较长的潜艇中进行第二次检查。
0赞 Notus_Panda 11/16/2023 #2

请注意,这非常简单,仅作为示例:

Option Explicit

Sub firstChange(ByVal Destination As Range)
    If Destination.Count > 1 Then Exit Sub
    Destination.Offset(, 1).Value = "Yes"
End Sub

Sub secChange(ByVal Target As Range)
    If Target.Column = 2 Then
        Target.Offset(, 2).Value = "Nice"
    End If
End Sub

Private Sub Worksheet_Change(ByVal Target As Range)
    Application.EnableEvents = False
    firstChange Target
    secChange Target
    Application.EnableEvents = True
End Sub

我在第一手陈述 firstChange(Target) 时犯了一个错误,很抱歉误导了。

“First One”和“Second One”中的逻辑进入 firstChange 和 secChange,因此您不必更改代码本身的任何内容。

评论

0赞 Pauline 11/16/2023
我更改了代码的最后一部分,但现在它不执行两个代码中的任何一个。我的单元格现在只是普通的数据验证列表。有没有可能与我的两个子代码中也有关系?Application.EnableEvents
0赞 Notus_Panda 11/17/2023
在 处放置一个停止标记,然后使用 F8 逐步执行代码,看看发生了什么/错误。(您需要通过更改某些内容来触发事件)firstChange Target
0赞 Pauline 11/17/2023
就像我需要创建一个宏并一行一行地运行它一样?
0赞 Notus_Panda 11/17/2023
查看此链接,了解如何调试。如链接所示,在代码中的“更改事件”中设置断点,然后在更改工作表中的某些内容后单步执行代码。firstChange Target
0赞 Pauline 11/17/2023
我搞砸了一些东西,所以我再次添加了您建议的代码。它确实执行了第一个代码,所以我确实得到了一个多列表,只是没有缩写。所以第二个代码似乎什么都没做。但是没有错误,所以我不明白如何调试它。此外,在通过将其设置为注释来关闭后,它不会给出错误,但它会停止制作多个列表,这通过关闭它来是有意义的。firstChange Target
0赞 Pauline 11/17/2023 #3

首先,感谢你们俩抽出时间接受采访!即使是聊天 gpt 3,5 也无法像你一样走得那么远,但我可以使用某人的第 4 个版本。经过 20 分钟的与聊天 gpt 的讨论和很多失败,它终于找到了一个完全符合我想要的代码:)尽管我仍然很好奇你的工作原理。

我现在有一个多重列表,从列表中选择一个值/字符串后,它只向我显示我创建的缩写,当我选择错误的值时,我可以在多重列表中再次单击它,然后它就消失了。单元格也受到保护,不会出现无效输入。

对于其他有相同问题的人,这段代码为我解决了问题。

Private Sub Worksheet_Change(ByVal Target As Range)
    Dim rngDV As Range
    Dim oldVal As String
    Dim newVal As String
    Dim arr
    Dim abbreviation As String
    Dim i As Long

    On Error Resume Next
    Set rngDV = Cells.SpecialCells(xlCellTypeAllValidation)
    On Error GoTo CleanUp

    If rngDV Is Nothing Then Exit Sub
    If Intersect(Target, rngDV) Is Nothing Then Exit Sub
    If Target.Count > 1 Then Exit Sub

    Application.EnableEvents = False
    newVal = Target.Value
    Application.Undo
    oldVal = Target.Value
    Target.Value = newVal

    ' Controleer of de nieuwe waarde een geldige selectie is
    If Not IsError(Application.Match(newVal, Worksheets("PPE").Range("List"), 0)) Then
        abbreviation = GetAbbreviation(newVal)
    Else
        GoTo CleanUp
    End If

    If Target.Column = 2 And InStr(Target.Validation.Formula1, "List") > 0 Then
        If oldVal = "" Then
            ' Eerste selectie
            Target.Value = abbreviation
        Else
            arr = Split(oldVal, "|")
            If IsInArray(abbreviation, arr) Then
                ' Item verwijderen
                Target.Value = JoinArrayWithout(arr, abbreviation)
            Else
                ' Item toevoegen
                Target.Value = Trim(oldVal & " | " & abbreviation)
            End If
        End If
    End If

CleanUp:
    Application.EnableEvents = True
End Sub

Function GetAbbreviation(ByVal value As String) As String
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("PPE")
    For Each cell In ws.Range("ShowAs")
        If cell.Value = value Then
            GetAbbreviation = cell.Offset(0, 1).Value
            Exit Function
        End If
    Next cell
    GetAbbreviation = value
End Function

Function IsInArray(ByVal value As String, arr As Variant) As Boolean
    Dim i As Long
    For i = LBound(arr) To UBound(arr)
        If Trim(arr(i)) = Trim(value) Then
            IsInArray = True
            Exit Function
        End If
    Next i
    IsInArray = False
End Function

Function JoinArrayWithout(arr As Variant, ByVal value As String) As String
    Dim result As String
    Dim i As Long
    For i = LBound(arr) To UBound(arr)
        If Trim(arr(i)) <> Trim(value) Then
            result = result & arr(i) & "|"
        End If
    Next i
    If Len(result) > 0 Then
        JoinArrayWithout = Left(result, Len(result) - 1)
    Else
        JoinArrayWithout = ""
    End If
End Function

也许有一天,如果我从中恢复过来,我会尝试找出在我更改缩写后或当我向具有可用值的列表中添加新值时,如何自动更新单元格中多个列表中已选择的值。并按字母顺序排列选定的值/缩写(我认为这应该不难)。也许我需要 vba 代码培训......

再次感谢!

评论

0赞 Notus_Panda 11/17/2023
若要在更新缩写时进行更新,可以执行类似的操作,在该 EPP 工作表上使用 Change 事件,然后对具有验证列表的其他工作表中的每个单元格检查以前的缩写是否在其中,如果是,请更改它。