Limit number of selections in a MultiSelect ListBox - vba

Is there a way to limit the number of selections a user can choose on a ListBox with MultiSelect enabled in Access 2003? Right now I have a procedure that fires on the On Click event that checks the number of choices selected and if it is over my threshold it will display a warning label.

Give this a try. It basically deselects the last item selected if its over the predefined limit:
Private Sub ListBox1_Change()
Dim counter As Integer
Dim selectedCount As Integer
selectedCount = 0
For counter = 1 To ListBox1.ListCount Step 1
If ListBox1.Selected(counter - 1) = True Then 'selected method has 0 base
selectedCount = selectedCount + 1
End If
Next counter
If selectedCount >= 4 Then 'modify # here
ListBox1.Selected(ListBox1.ListIndex) = False 'listindex returns the active row you just selected
MsgBox "Limited to 4 Choices", vbInformation + vbOKOnly, "Retry:"
End If
End Sub

You can use the listbox BeforeUpdate event to see the listbox.ItemsSelected.Count value.
If this is over your limit then you unselect (with listbox.Selectec(item)=False) the current one and cancel the event.
Here's an example :
Private Sub lstItems_BeforeUpdate(Cancel As Integer)
' Warn the user that only x items can be selected.
' ------------------------------------------------
If lstItems.ItemsSelected.Count > MAX_SELECTED_ITEM_PERMITTED Then
MsgBox "You can only select " & MAX_SELECTED_ITEM_PERMITTED & " items in this list.", vbOKOnly + vbInformation, "Error"
' Unselect previously selected item.
' ----------------------------------
lstItems.Selected(lstItems.ListIndex) = False
Cancel = True
End If
End Sub

Using the listbox ItemsSelected collection? That's the only way that I know of to limit the number of selected items. OTOH there are some truly twisted individuals out there who might have figured out an alternative so I never say never.
What's the problemw with this method?

I would suggest that a much more manageable, user-friendly and understandable UI for this would be the paired listbox approach, with ADD> and button after the user has reached the limit. You don't have to do anything difficult, just check the ListCount of the right-hand listbox and disable the ADD> button when it reaches the limit.
And you avoid many problems as it's quite clear to the user what they are doing in comparison to selecting multiple items at once in a single listbox. You could make the lefthand listbox multiselect and simply disable the ADD> button if the ItemsSelected count exceeds the limit, and notify the user appropriately.

Related

VBA Checkbox to Listbox (uncheck option to remove array)

I am struggling with a simple thing and cannot resolve it. I have a userform that user can populate from a textbox manually. I decided to add a checkbox as well to allow the user to populate the same listbox with a specific list of items. To do it,I made a simple checkbox with array. It works perfectly fine. But obviously keeps adding the items every time you check and uncheck it.
Private Sub Checkbox1_Click()
Dim mylist(7) As String
Dim i As Long
mylist(0) = "time"
mylist(1) = "hour"
mylist(2) = "how"
mylist(3) = "test"
mylist(4) = "number"
mylist(5) = "sent"
mylist(6) = "memo"
mylist(7) = "value"
For i = 0 To 7
If CheckBox1.Value = True Then
Finallist.AddItem mylist(i)
End If
Next i
End Sub
I can populate the list when the checkbox is checked with the code above, but struggling to remove the array items from the list when the user unchecks the listbox. I simply need to remove the same items from listbox when user unchecks the same checkbox.
I tried the following solution after the code, but seem to be making something very wrong with it, I understand. Just totally stuck....Could someone help me please?
If checkobx.value=false then
For i = 0 To 7
For j = 0 To FinalList.ListCount - 1
If InStr(Final.List(j), mylist(i)) > 0 Then
Finallist.RemoveItem mylist(i)
End If
Next j
Next i
end if
Try this (explanations in comments and untested):
If CheckBox1.Value Then ‘ if checkbox checked
For i = 0 To 7
NegKeyList.AddItem interrlist(i)
Next
Else ‘otherwise
Dim i As Long
For i = NegKeyList.ListCount - 1 To 0 Step -1 ‘ loop through listbox items from last one backwards
If Not IsError(Application.Match(NegKeyList.List(i), interrlist,0)) Then NegKeyList.RemoveItem i ‘ if current listbox item matches any interrlist array item then remove it from listbox
Next
End If

VBA "All" checkbox with listbox deselect loop issues

I have a check box that is used for "ALL" (selected by default) selections in a muliselect listbox that is deselected when an item in the listbox is selected. I also coded it so when "ALL" is selected, then it clears the listbox selections and checks the box. It ends up looping through the different subs and makes it annoying for the user.
For instance, when I click an item in the listbox it selects that value, then deselects the checkbox. Since the checkbox is deselected, it goes back through the listbox and deselects the selected item. It loops between the two subs a couple times and ends up only working correctly half the time.
Can I prevent entering the other sub?
Is there better logic so it won't loop as it does?
or maybe an better method to achieve this?
Multiselect listbox:
Private Sub Mkts_Change()
If Me.cheMkts.Value = True Then
Me.cheMkts.Value = False
End If
End Sub
Checkbox:
Private Sub cheMkts_Click()
Dim i As Integer
For i = 0 To Mkts.ListCount - 1
If Me.Mkts.Selected(i) = True Then
Me.Mkts.Selected(i) = False
End If
Next
End Sub
What about adding an If statement around your cheMtks_Click()?
This way when your code deselects it automatically it shouldn't trigger the loop.
Private Sub cheMkts_Click()
If Me.cheMkts.Value = True Then
Dim i As Integer
For i = 0 To Mkts.ListCount - 1
If Me.Mkts.Selected(i) = True Then
Me.Mkts.Selected(i) = False
End If
Next
End If
End Sub
Thanks for your help, Ruben. That corrects the error on the one end, but I am still having issues on the other side. When I have a selection and click the "ALL" box it deselects the check.
I came up with this code, which works beautifully in combo to your suggestion, but only when I have one item selected. If there is anything more, then it still goofs up. Figured I would post to see if you or someone else could advise a solution for multiple selections.
Private Sub Mkts_Change()
Dim i As Integer, count As Integer
For i = 0 To Mkts.ListCount - 1
If Me.Mkts.Selected(i) = False Then
count = count + 1
End If
Next
If Me.cheMkts.Value = True And count <> Mkts.ListCount Then
Me.cheMkts.Value = False
End If
End Sub

Why is my listbox removing all items instead of what's selected?

I have a set of ListBoxes that pulls data from a report that the rest of the encompassing macro generates. These ListBoxes are displayed in a UserForm adjacent to one another to compare their data together and allow the user to remove data if it matches (refer to picture). Unfortunately, when I press the remove button for the right list (BWListBox), the entire list gets removed rather than just a single item.
I suspect this is due to the dynamic nature of the ListBox (if a Bank Wire report is generated, the list remains single-select and single-column; if a Credit Card Report is generated, it changes to multi-select and double-column, as well as marking a "Total" TextBox as visible, just as the left side)
When I run the Credit Card Report, and use the mark the list (while it's multi-select), the remove button works just fine. Oddly enough, in the single-select form, it removes the entire list.
Here is the block of code for the Remove Button:
Private Sub RemoveButton2_Click()
Dim bwTotal As Double
'If CCReport Then `Currently not functioning due to testing
'bwTotal = BWTotalTextBox.Value 'Throwing error - Double to String ""
'End If
For lItem = BWListBox.ListCount - 1 To 0 Step -1
If BWListBox.Selected(lItem) Then
'GPListBox.RemoveItem GPListBox.ListIndex
BWListBox.RemoveItem lItem
If CCReport Then 'if CCReport since it will be multi-select and multi-column
bwTotal = bwTotal - BWListBox.List(lItem, 1)
BWTotalTextBox.Value = Format(bwTotal, "$#,##0.00")
End If
End If
Next
End Sub
Here is the BWListBox_Change event:
Private Sub BWListBox_Change()
If CCReport Then 'This only exists for the sole purpose of the BWTotalTextbox, if it's not visible (Such as during a Bank Wire Report), there is no need
Dim bwTotal As Double
For lItem = 0 To BWListBox.ListCount - 1
If BWListBox.Selected(lItem) = True Then
bwTotal = bwTotal + BWListBox.List(lItem, 1)
Debug.Print BWListBox.List(lItem, 1)
End If
Next
BWTotalTextBox.Value = Format(bwTotal, "$#,##0.00")
End If
End Sub
EDIT: I recently found it only removes the entire list if the LAST item is selected prior to pressing the remove button.
In case of single select, the loop keeps on running because when you delete one item at the bottom, the immediate item above that gets selected and the loop gets validated.
So, all you need to do is jump out of the loop after deleting the selected element.
For lItem = Me.ListBox1.ListCount - 1 To 0 Step -1
If ListBox1.Selected(lItem) Then
ListBox1.RemoveItem lItem
If Me.ListBox1.MultiSelect = fmMultiSelectSingle Then
Exit For
End If
End If
Next

Select combobox if wrong item selected

I have an MS-Word 2013 document with several (legacy) formfields; some text boxes, some comboboxes. The first list item on all of the comboboxes is "(select one)" to let the end user know they need to make a selection (I did not draft or design this document, I've just been asked to write the VBA code for it). So I coded each to give a simple VBA message box, ran on exit, if that first selection was not changed, for example:
Public factor1 As Integer
Sub MyFormFieldFactor1()
If ActiveDocument.FormFields("cbofactor1").Result = "(select one)" Then
MsgBox "You must select either Yes or No."
Exit Sub
End If
If ActiveDocument.FormFields("cbofactor1").Result = "Yes" Then
factor1 = 1
Else
factor1 = 0
End If
End Sub
The word document automatically goes to the next formfield when you click ok on the message box. Through VBA, I want it to stay on the current formfield when "(select one)" is chosen. Bonus points if it stays on the current field and pulls up the list selection automatically.
Will this work:
If ActiveDocument.FormFields("cbofactor1").Result = "(select one)" Then
MsgBox "You must select either Yes or No."
ActiveDocument.FormFields("cbofactor1").SetFocus()
Exit Sub
End If
You can auto drop the list with something like:
SendKeys "%{down}", True
DoEvents
Full code:
If ActiveDocument.FormFields("cbofactor1").Result = "(select one)" Then
MsgBox "You must select either Yes or No."
ActiveDocument.FormFields("cbofactor1").SetFocus()
SendKeys "%{down}", True
DoEvents
Exit Sub
End If

Populating a dropdown box with already-existing options in VBA?

I'm making an add records form for a spreadsheet of mine, and let's say that I want one of the controls to be a dropdown that is populated by unique entries under a certain column "type". However, I want to also make it such that the dropbox always has a initial option to "add new type" and upon such selection, it becomes a regular text box. How would I do this in VBA?
You cannot change a control type at run time. The easiest thing to do is create a combo box and a text box. Set the text box visibility to false. Then in the onchange event of the combo box your code will unhide the text box and hide the combo box. You will also need a save button so that when it is clicked it will add the option to the drop down, clear the text box, hide the text box, hide the button and unhide the drop down.
Okay, so here's my idea of how to tackle this.
Create 2 hidden elements (Visibility = False), one a TextBox and one a CommandButton.
Populate your ComboBox with the values from the sheet under column "type"
Add one more entry AddItem with wording such as "Add new item..."
When the user selects "Add new item...", change the Visibility of the TextBox & CommandButtons to True
When the user clicks the CommandButton, add the phrase to the column and add a new element to the ComboBox
I have created a mockup UserForm and code that does a little more than just this; it also styles the user entry to sentence case (consistency purposes) and checks to make sure the value isn't already in the column.
Excel Sheet with "type" column
UserForm with name labels
UserForm Code
Private Sub bAdd_Click()
Dim str As String
Dim rng As Range
Dim ro As Integer
'Makes sure there is an entry, adds it to the Sheet and then updates the dropdown
If Len(Me.tbNew) > 0 Then
'Converts user entry to "Sentance Case" for better readability
str = StrConv(Me.tbNew, vbProperCase)
'Finds out if the entry already exists
Set rng = Sheets(1).Range(Sheets(1).Cells(2, 1), Sheets(1).Cells(Sheets(1).Cells(Sheets(1).Rows.Count, 1).End(xlUp).Row, 1))
On Error Resume Next
Err.Number = 0
'Searches for duplicate; if found, then ListIndex of cbColor is modified without inserting new value (prevents duplicates)
ro = rng.Find(str, LookIn:=xlValues, LookAt:=xlWhole).Row
Debug.Print Err.Number
'Ensures a user doesn't add the same value twice
If Err.Number > 0 Then
Sheets(1).Cells(Sheets(1).Cells(Sheets(1).Rows.Count, 1).End(xlUp).Row + 1, 1) = str
Me.cbColor.AddItem StrConv(Me.tbNew, vbProperCase), Me.cbColor.ListCount - 1
Me.cbColor.ListIndex = Me.cbColor.ListCount - 2
Else
Me.cbColor.ListIndex = ro - 2
End If
'Resets and hides user form entries
Me.tbNew = vbNullString
Me.tbNew.Visible = False
Me.bAdd.Visible = False
End If
End Sub
Private Sub bClose_Click()
Unload Me
End Sub
Private Sub cbColor_Change()
'Visibility is toggled based on if the user selected the last element in the dropdown
Me.bAdd.Visible = Me.cbColor.ListIndex = Me.cbColor.ListCount - 1
Me.tbNew.Visible = Me.cbColor.ListIndex = Me.cbColor.ListCount - 1
End Sub
Private Sub UserForm_Initialize()
'Populate from the sheet
For a = 2 To Sheets(1).Cells(Cells(Sheets(1).Rows.Count, 1).End(xlUp).Row, 1).Row
Me.cbColor.AddItem Sheets(1).Cells(a, 1)
Next
'Add option for new type
Me.cbColor.AddItem "Add new type..."
End Sub