提问人:Pauline 提问时间:11/16/2023 最后编辑:Pauline 更新时间:11/17/2023 访问量:108
如何组合两个 vba 代码?删除了多个列表,并显示为其他值
How do I combine two vba codes? Multiple list with item removal and a shown as other value
问:
我想组合两个不同的 vba 代码函数。
- 创建包含项目删除功能的多个列表
- 将所选项目显示为另一个预定义值
我有两个选项的 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 文件以使其更清晰。
答:
尝试这种解决方法,一个接一个地调用两个潜艇
将第二个 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
评论
请注意,这非常简单,仅作为示例:
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,因此您不必更改代码本身的任何内容。
评论
Application.EnableEvents
firstChange Target
firstChange Target
firstChange Target
首先,感谢你们俩抽出时间接受采访!即使是聊天 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 代码培训......
再次感谢!
评论