Restrict only two selections in a multi selection drop down in Excel - excel

I have a dropdown in my excel sheet and I wanted to add multiple items from that dropdown into the cell. I was able to do that and now it functions as shown in the picture below:
I also wanted the users to only be allowed to choose two items from the entire list of items from the dropdown. The users can choose any two but also only two. I was not able to do that. Can someone help me? here is the code I used.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Oldvalue As String
Dim Newvalue As String
Application.EnableEvents = True
On Error GoTo Exitsub
If Target.Address = "$C$8" Then
If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
GoTo Exitsub
Else: If Target.Value = "" Then GoTo Exitsub Else
Application.EnableEvents = False
Newvalue = Target.Value
Application.Undo
Oldvalue = Target.Value
If Oldvalue = "" Then
Target.Value = Newvalue
Else
If InStr(1, Oldvalue, Newvalue) = 0 Then
Target.Value = Oldvalue & vbNewLine & Newvalue
Else:
Target.Value = Oldvalue
End If
End If
End If
End If
If Target.Address = ("$C$8") Then
If InStr(1, Target.Value, "5", vbTextCompare) > 0 Then
Rows("9").EntireRow.Hidden = False
Else
Rows("9").EntireRow.Hidden = True
End If
End If
Application.EnableEvents = True
Exitsub:
Application.EnableEvents = True
End Sub

I had a hard time to understand your question and also the code. It is a very uncommon behavior that you have a data validation dropdown that allows to add something into that cell. This results in a very strange user experience
When I select an entry from the list, there is no way to remove it again except clearing the cell.
When I select something and then press Ctrl+Z (undo), Excel cannot undo my change.
When I select more than the limit (2) number of entries, the change is not accepted, without further notice.
When I enter something manually in that cell and the limit of 2 entries is already reached, my changes will not be accepted.
For me as a user, that strange behaviour would drive me mad. As a clever user, I will find out sooner or later that I can Paste something into that cell. That will delete your data validation - If you want to go down that road, you must add logic into the trigger that prevents this.
That said, I cleaned up your code and added a check for a maximum number of entries - the change itself is rather easy, before adding the entry, it is checked how many NewLines are already in the value.
Private Sub Worksheet_Change(ByVal Target As Range)
Const MaxEntries = 2
Dim Oldvalue As String
Dim Newvalue As String
If Target.Address <> "$C$8" Then GoTo Exitsub
On Error GoTo Exitsub
If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then GoTo Exitsub
If Target.Value = "" Then GoTo Exitsub
Application.EnableEvents = False
Newvalue = Target.Value
Application.Undo
Oldvalue = Target.Value
If Oldvalue = "" Then
Target.Value = Newvalue
ElseIf InStr(Oldvalue, Newvalue) = 0 And countCharsInStr(Oldvalue, vbNewLine) + 1 < MaxEntries Then
Target.Value = Oldvalue & vbNewLine & Newvalue
Else
Target.Value = Oldvalue
End If
Rows("9").EntireRow.Hidden = InStr(Target.Value, "5") > 0
Exitsub:
Application.EnableEvents = True
End Sub
Public Function countCharsInStr(s As String, c As String) As Long
countCharsInStr = (Len(s) - Len(Replace(s, c, ""))) / Len(c)
End Function

The code structure doesn't make much sense to me, so I rearranged it.
My solution is a separate function anyway, so it should work either way.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Oldvalue As String
Dim Newvalue As String
Application.EnableEvents = True
On Error GoTo Exitsub
If Target.Address = "$C$8" Then
If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
GoTo Exitsub
ElseIf Target.Value = "" Then
GoTo Exitsub
Else
Application.EnableEvents = False
Newvalue = Target.Value
Application.Undo
Oldvalue = Target.Value
If Oldvalue = "" Then
Target.Value = Newvalue
Else
If InStr(1, Oldvalue, Newvalue) = 0 And Not CountLines(Target) > 1 Then
Target.Value = Oldvalue & vbNewLine & Newvalue
Else
Target.Value = Oldvalue
End If
End If
End If
If InStr(1, Target.Value, "5", vbTextCompare) > 0 Then
Rows("9").EntireRow.Hidden = False
Else
Rows("9").EntireRow.Hidden = True
End If
End If
Exitsub:
Application.EnableEvents = True
End Sub
Public Function CountLines(Target As Range) As Double
Dim H1 As Double, H2 As Double
With Target
.WrapText = False
H1 = .Height
.WrapText = True
H2 = .Height
End With
CountLines = H2 / H1
End Function
Since the added values are separated by a new line, counting how many values there are can be a bit tricky. One way would be to measure the height of the cell, which is what the function does.
We call this function to check the number of lines as we are adding new values. I merged it with the If checking for duplicates.

Related

How to delete a value in a cell from a list

I am trying to figure out how to delete a value coming from a list in a cell.
What i have is a list where you can select multiple values. The problem with this is that i needed to open the list for each choice.
And the second problem is that if i want to delete one value, i have to delete them all and then choose again.
If someone has any ideas on how to do to improve what i have i will appreciate.
For reference : this is the VBA code that i add in my previous excel sheet :
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Oldvalue As String
Dim Newvalue As String
Application.EnableEvents = True
On Error GoTo Exitsub
If Target.Column = 5 Then
If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
GoTo Exitsub
Else: If Target.Value = "" Then GoTo Exitsub Else
Application.EnableEvents = False
Newvalue = Target.Value
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
Please, test the next updated code. It checks if the new selected string already exists in the list and if so, asks for exclusion. Pressing Yes, it will be excluded:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Oldvalue As String, Newvalue As String, ans As VbMsgBoxResult
If Target.cells.count > 1 Then Exit Sub 'if more than one cell changed (by copying, for example) code exists
If Not hasLValidation(Target) Then Exit Sub 'if no List validtion code exits
If Target.Value = "" Then Exit Sub
On Error GoTo Exitsub
If Target.Column = 5 Then
Application.EnableEvents = False
Newvalue = Target.Value: Application.Undo
Oldvalue = Target.Value
If Oldvalue = "" Then
Target.Value = Newvalue
Else
If InStr(1, Oldvalue, Newvalue) = 0 Then
Target.Value = Oldvalue & ", " & Newvalue
Else
ans = MsgBox("Do you like excluding """ & Newvalue & """ from the list?" & vbCrLf & _
"For excluding, please press ""Yes""!", vbYesNo, "Exclusion confirmation")
If ans <> vbYes Then
Target.Value = Oldvalue
Else
Dim arr, mtch
arr = Split(Oldvalue, ", ") 'place the list in an array
mtch = Application.match(Newvalue, arr, 0) 'match the array element
If Not IsError(mtch) Then 'if a match exists:
arr(mtch - 1) = "##$%&" 'replace that element with a strange string different from all existing
arr = filter(arr, "##$%&", False) 'eliminate that specific element
Target.Value = Join(arr, ", ") 'place back the list by joining the array
End If
End If
End If
End If
End If
Exitsub:
Application.EnableEvents = True
End Sub
Function hasLValidation(T As Range) As Boolean
Dim vType As Long
On Error Resume Next
vType = T.Validation.Type
On Error GoTo 0
If vType = 3 Then hasLValidation = True 'only for LIST validation type!
End Function
Please, send some feedback after testing it.

VBA multiple values from drop down in excel removing items selected in error

I have a piece of code which I am using which was written by Sumit Bansal which is working as I need it to
Dim Oldvalue As String
Dim Newvalue As String
Application.EnableEvents = True
On Error GoTo Exitsub
If Target.Address = "$D$26" Or Target.Address = "$D$38" Or Target.Address = "$C$4" Then
If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
GoTo Exitsub
Else: If Target.Value = "" Then GoTo Exitsub Else
Application.EnableEvents = False
Newvalue = Target.Value
Application.Undo
Oldvalue = Target.Value
If Oldvalue = "" Then
Target.Value = Newvalue
Else
If InStr(1, Oldvalue, Newvalue) = 0 Then
Target.Value = Oldvalue & vbNewLine & Newvalue
Else:
Target.Value = Oldvalue
End If
End If
End If
End If
Application.EnableEvents = True
Exitsub:
Application.EnableEvents = True
End Sub
However I have tried to amend the code to allow the user to remove a value if it has been selected in error, i.e. if they select
1
2
3
these appear in the cell on separate lines.
However 2 should not have been selected and needs to be removed so what appears in the cell is
1
3
and to appear on separate lines, not separated by a comma
The order of the selections should be the same as in the drop down list.

Using Target.Column every nth column

Private Sub Worksheet_Change(ByVal Target As Range)
'Code by Sumit Bansal from https://trumpexcel.com
' To allow multiple selections in a Drop Down List in Excel (without repetition)
Dim Oldvalue As String
Dim Newvalue As String
Application.EnableEvents = True
On Error GoTo Exitsub
If Target.Column = 6 Then
If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
GoTo Exitsub
Else: If Target.Value = "" Then GoTo Exitsub Else
Application.EnableEvents = False
Newvalue = Target.Value
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••••ˇˇˇˇ
I am trying to use Target.Column to start at the 6th column and use the same code for every 3 columns after as well so basically 6, 9, 12, 15, 18, etc.
If Target.Column = 6 Then
How would I go about implementing that here
You could use a function and check the correct column number via If correctCol(target.Column) Then:
Function correctCol(ByVal col As Long, Optional floor As Long = 6) As Boolean
correctCol = (col Mod 3 = 0) * (col >= floor)
End Function

excel multi-select pick list VBA doesnt run on password protect sheet

Any thoughts on how to change this to allow multiselect to run on password protected sheet without having to key in the password?
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Oldvalue As String
Dim Newvalue As String
Dim num As Integer
On Error GoTo Exitsub
If Target.Address = "$H$29" Or Target.Address = "$H$33" Or Target.Address = "$H$37" Or Target.Address = "$H$42" Or Target.Address = "$H$58" Or Target.Address = "$H$59" Or Target.Address = "$H$60" Or Target.Address = "$H$63" Or Target.Address = "$H$65" Or Target.Address = "$M$29" Or Target.Address = "$M$33" Or Target.Address = "$M$37" Or Target.Address = "$M$42" Or Target.Address = "$M$58" Or Target.Address = "$M$59" Or Target.Address = "$M$60" Or Target.Address = "$M$63" Or Target.Address = "$M$65" Then
If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
GoTo Exitsub
Else: If Target.Value = "" Then GoTo Exitsub Else
Application.EnableEvents = False
Newvalue = Target.Value
Application.Undo
Oldvalue = Target.Value
If Oldvalue = "" Then
Target.Value = Newvalue
Else
num = InStr(Oldvalue, Newvalue)
If num = 0 Then ' If the element selected isnt already on the selected list
Target.Value = Oldvalue & ", " & Newvalue
ElseIf num = 1 Then ' If the element is the first on the list
If Len(Oldvalue) = Len(Newvalue) Then ' If the element is the only element selected
Target.Value = Replace(Oldvalue, Newvalue, "")
Else ' If the element is not the only element selected
Target.Value = Replace(Oldvalue, Newvalue & ", ", "")
End If
ElseIf num > 1 Then ' If the element is not the first
Target.Value = Replace(Oldvalue, ", " & Newvalue, "")
End If
End If
End If
End If
Application.EnableEvents = True
Exitsub:
Application.EnableEvents = True
End Sub
Option 1 unprotect the sheet, run your code, then protect it again using VBA (but this can be insecure when the macro is stopped in the middle)
Option 2 protect the sheet using this code
ActiveSheet.Protect "password", UserInterfaceOnly:=True
that way the sheet is protected only from user changes, not macro changes.
SpecialCells(xlCellTypeAllValidation) throws an error on a protected sheet
This will work on a protected sheet:
Private Sub Worksheet_Change(ByVal Target As Range)
Const SEP As String = ","
Dim c As Range, NewValue, OldValue, arr, v, lst, removed As Boolean
On Error GoTo Exitsub
If Target.CountLarge > 1 Then Exit Sub '<< only handling single-cell changes
'is the changed cell in our monitored range?
Set c = Application.Intersect(Target, Me.Range("B5,B7,B9,B11")) ' for example
If Not c Is Nothing Then
If Len(c.Value) > 0 And Not c.Validation Is Nothing Then
Application.EnableEvents = False
NewValue = c.Value
Application.Undo
OldValue = c.Value
If OldValue = "" Then
c.Value = NewValue
Else
arr = Split(OldValue, SEP)
'loop over previous list, removing newvalue if found
For Each v In arr
If v = NewValue Then
removed = True
Else
lst = lst & IIf(lst = "", "", SEP) & v
End If
Next v
'add the new value if we didn't just remove it
If Not removed Then lst = lst & IIf(lst = "", "", SEP) & NewValue
c.Value = lst
End If
End If 'has validation and non-empty
End If 'handling this cell
Exitsub:
If Err.Number <> 0 Then MsgBox Err.Description
Application.EnableEvents = True
End Sub

How Apply Visual Basic Multi-Select to Column?

I am not familiar with programming Excel codes, but I was able to find one code to apply the Multi-Select in a DropDown List without repetition. However, it only applies to that one cell, so I am left with going through each cell to apply that rule to each separate cell. I would like to apply this rule-coding to the column if that's possible!
Private Sub Worksheet_Change(ByVal Target As Range)
'Code by Sumit Bansal from https://trumpexcel.com
' To allow multiple selections in a Drop Down List in Excel (without repetition)
Dim Oldvalue As String
Dim Newvalue As String
Application.EnableEvents = True
On Error GoTo Exitsub
If Target.Address = "$E$13" Then
If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
GoTo Exitsub
Else: If Target.Value = "" Then GoTo Exitsub Else
Application.EnableEvents = False
Newvalue = Target.Value
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
Instead of checking Target.Address, you want to check Target.Column.
If Target.Column = 5 Then
You might also want to check that Target is just one column. For example, Range("E5:G5").Column will still return 5.
You can do this with:
If Target.Columns.Count = 1 Then
Or together:
If Target.Columns.Count = 1 And Target.Column = 5 Then

Resources