MS Access 2007 VBA - Reusable list box code - ms-access

I have a MS Access 2007 application which has several forms where I've used the same list box design. I have two list boxes, one of which gets values from a table with a query like:
SELECT id, value FROM table
And the second which is initially empty. In between these two list boxes are add and remove buttons, which are disabled by default. Clicking a value in the first list box enables the add button, and clicking a value in the second list box enables the remove button. Clicking the add button adds the selected item to the second list, and clicking the remove button removes an item for the second list.
The code I have for the add button is as follows ("ALLLIST" refers to the list with the query values, "SELECTEDLIST" is the one that's initially empty) :
Dim selectedId, selectedValue, safeValue As String
Dim existing As Boolean
Dim index As Integer
existing = False
selectedId = Me.ALLLIST.Value
index = Me.ALLLIST.ListIndex
selectedValue = Me.ALLLIST.Column(1,index)
'Loop through the list of selected values and see if this one has already been added to the list
For i = 0 To (Me.SELECTEDLIST.ListCount)
If (Me.SELECTEDLIST.Column(0, i) = selectedId) Then
existing = True
End If
Next i
'Only add the value if it's not already on the list
If (existing) Then
MsgBox "This list can't contain duplicate values", vbOKOnly + vbInformation, "Error"
Else
safeValue = Replace(selectedValue & "", "'", "''")
Me.SELECTEDLIST.AddItem (selectedId & ";'" & safeValue & "'")
Me.SELECTEDLIST.Value = Null
Me.REMOVEBUTTON.Enabled = False
End If
And the code for the remove button is:
Dim numItems, index As Integer
index = Me.SELECTEDLIST.ListIndex
'Remove the selected item and move to the top of the list
Me.SELECTEDLIST.RemoveItem (index)
Me.SELECTEDLIST.Selected(0) = True
numItems = Me.SELECTEDLIST.ListCount
'Cosmetic feature, select the row above the one we're removing
If (index > 0) Then
Me.SELECTEDLIST.Selected(index - 1) = True
Else
Me.SELECTEDLIST.Selected(0) = True
End If
'If the list is empty now, disable the remove button
If (numItems = 0) Then
Me.ALLLIST.SetFocus
Me.REMOVEBUTTON.Enabled = False
Me.SELECTEDLIST.Selected(-1) = True
End If
What I would like to do is, rather than copy and paste this code all over the place, have this template stored somewhere and then in my form code write something like:
hookUpLists(allListName, selectedListName, addButtonName, removeButtonName)
How can I do this? Can I write a module to do this some how? I'm also open to any improvements I can make on the above code.
Thanks

Thanks for the hint on using class modules, HK1, I'd never used them before.
To solve the original question, I created a new class module "MultiSelectListBox", which has the following code:
Option Compare Database
Private WithEvents allList As ListBox
Private WithEvents selectedList As ListBox
Private WithEvents addBtn As CommandButton
Private WithEvents removeBtn As CommandButton
Private numColumns As Integer
Public Sub hookUpLists(numberOfColumns As Integer, allValuesList As ListBox, selectedValuesList As ListBox, addButton As CommandButton, _
removeButton As CommandButton)
'Grab all the controls passed in
Set allList = allValuesList
Set selectedList = selectedValuesList
Set addBtn = addButton
Set removeBtn = removeButton
numColumns = numberOfColumns
'Tell Access we want to handle the click events for the controls here
allList.OnClick = "[Event Procedure]"
selectedList.OnClick = "[Event Procedure]"
addBtn.OnClick = "[Event Procedure]"
removeBtn.OnClick = "[Event Procedure]"
End Sub
Private Sub allList_Click()
addBtn.Enabled = True
End Sub
Private Sub selectedList_Click()
removeBtn.Enabled = True
End Sub
Private Sub addBtn_Click()
Dim selectedId As String
Dim existing As Boolean
Dim index As Integer
existing = False
selectedId = allList.Value
index = allList.ListIndex
'Loop through the list of selected values and see if this one has already been added to the list
For i = 0 To (selectedList.ListCount)
If (selectedList.Column(0, i) = selectedId) Then
existing = True
End If
Next i
'Only add the value if it's not already on the list
If (existing) Then
MsgBox "This list can't contain duplicate values", vbOKOnly + vbInformation, "Error"
Exit Sub
End If
Dim item As String
item = selectedId & ";"
'Loop over all of the columns and add them to the second list box
For i = 1 To numColumns - 1
item = item & "'" & Replace(allList.Column(i, index) & "", "'", "''") & "'"
'Don't add a trailing semicolon
If (i <> numColumns - 1) Then
item = item & ";"
End If
Next i
selectedList.AddItem (item)
selectedList.Value = Null
removeBtn.Enabled = False
'Select the next row
If (index <> allList.ListCount - 1) Then
allList.Selected(index + 1) = True
allList.Value = allList.Column(0, index + 1)
End If
End Sub
Private Sub removeBtn_Click()
Dim numItems, index As Integer
index = selectedList.ListIndex
'Remove the selected item and move to the top of the list
selectedList.RemoveItem (index)
selectedList.Selected(0) = True
numItems = selectedList.ListCount
'Cosmetic feature, select the row above the one we're removing
If (index > 0) Then
selectedList.Selected(index - 1) = True
Else
selectedList.Selected(0) = True
End If
'If the list is empty now, disable the remove button
If (numItems = 0) Then
allList.SetFocus
removeBtn.Enabled = False
selectedList.Selected(-1) = True
End If
End Sub
Most of the above is identical to what I was already using, one important thing to note for anyone stumbling across this is the use of "WithEvents" when declaring the variables. This tells Access to look in the class module for event handlers. Finally, from my form I can do the following:
Private contactList As MultiSelectListBox
Private Sub Form_Open(Cancel As Integer)
Set contactList = New MultiSelectListBox
contactList.hookUpLists 3, Me.allContactsList, Me.selectedContactsList, Me.addContactBtn, Me.removeContactBtn
End Sub

Related

Refer to multiple selected Items in a listbox in ms-access

How do I select multiple items in the list box, then refer to the Items I have selected?
You will need to use a variation of the following steps:
create a list box on a form
populate the list box using the row source.
go to the other tab and change the multiselect property to extended
I then used the following VBA
Option Compare Database
Private Item_IDs as string
Private Sub List_item_id_Click()
Dim i As Integer, count As Integer
Dim Item_IDs As String
count = 1
For i = 0 To Me.List_item_id.ListCount - 1
If Me.List_item_id.Selected(i) = True Then
Item_IDs = Item_IDs & ", " & Me.List_item_id.ItemData(i)
count = count + 1
End If
Next i
Item_IDs = Mid(Item_IDs, 3)
Debug.Print Item_IDs
End Sub
Now every time I click on a value in the list, it will return the a comma separated value string (Item_IDs) of the things I have selected. Use CTRL+G in the VBA window to open the immediate window and see the fruits of your labors.
Something like . . .
Private Sub OKButton_Click()
Dim Msg As String
Dim i As Integer
Msg = "You selected" & vbNewLine
For i = 0 To ListBox1.ListCount - 1
If ListBox1.Selected(i) Then
Msg = Msg & ListBox1.List(i) & vbNewLine
End If
Next i
MsgBox Msg
Unload UserForm1
End Sub

Find next record based on certain criteria

I am trying to use the .FindNext (and .FindPrevious) function on an update form "next button" to find the record that meets certain criteria.
Private Sub NextRecord_Click()
Dim foundmatch As Boolean
For x = 0 To 3 Step 1
With Me.RecordsetClone
.FindNext "[Sensitivity] = " & [TempVars]![AccessLevel] + x
If .NoMatch Then
foundmatch = False
Else
Me.Bookmark = .Bookmark
foundmatch = True
Exit For
End If
End With
Next
If foundmatch = False Then
MsgBox "No More Records"
End If
End Sub
Upon a user entering the database the users accesslevel is assigned to a temp variable (1 to 4), and each project has a sensitivity rating of 1 to 4. The code below was used and worked for both next and previous only in finding records when the sensitivity and accesslevel were equal but not for sensitivities below the users access level which they are qualified to see.
Private Sub PrevRecord_Click()
Dim Stringy As String
Stringy = "[Sensitivity] = " & [txtaccess]
With Me.RecordsetClone
.FindPrevious Stringy
If .NoMatch Then
MsgBox "No More Records"
Else
Me.Bookmark = .Bookmark
End If
End With
End Sub
Note: The form is pulled from a query with Sensitivity one of the fields, and [txtaccess] is a text box on the field with the default value set at [TempVars]![AccessLevel]. I've also tried changing it to:
Stringy = "[Sensitivity] >= " & [txtaccess]
but that doesn't work either
I was able to fix the problem by setting applying a filter for sensitivity on the actual forms On_Load event rather than the command button. It now works using a next record command button added with the default code/settings!

MS Access - How to Add Items to List Box and Select only Items that are found in Recordset

I have a list box that is populated by a recordset. I am trying to then select the items in that list box based on the values in another recordset. I am able to populate the list box, but when I try to select the values based on another recordset the list box Me.ToolUsed1 is Null. I call another function for selecting the values because I plan on using the same procedure for other list boxes. I really appreciate any help that you can provide.
'Populate the tool list box
While Not rsToolList.EOF
Me.ToolUsed1.AddItem Item:=rsToolList.Fields(0)
rsToolList.MoveNext
Wend
matchKey = "MatchKey = """ & rsActivities.Fields(0) & """"
If rsTools.RecordCount > 0 Then
rsTools.MoveFirst
rsTools.FindFirst (matchKey)
toolIndex = rsTools.Fields(2)
While Not rsTools.EOF
If (rsTools.Fields(2) = toolIndex) Then
SelectListValues Me.ToolUsed1, rsTools.Fields(1)
End If
rsTools.MoveNext
Wend
End If
Private Sub SelectListValues(tempListBox As Object, selectString As String)
Dim i As Integer
Dim found As Boolean
i = 0
found = False
'select the value in the listbox
While i < tempListBox.ListCount And Not found
If tempListBox.Value(i) = selectString Then
tempListBox.Selected(i) = True
found = True
End If
i = i + 1
Wend
'if the string wasn't found, add it
If Not found Then
tempListBox.AddItem (selectString)
End If
End Sub
Consider using a query recordsource for your listbox instead of value items to add. Listboxes like comboxes maintain the RowSource property, allowing for Table/Query sources which you can set to the first recordset, rsToolList. Then, just open one recordset, rsTools, and loop through it to decide selected items. Do note, with table/query sources the bound column is the value of the listbox, not any of the other columns.
' POPULATE TOOL LIST BOX TO QUERY
Me.tempListBox.RowSource = "ToolList" ' OR USE SELECT SQL STATEMENT HERE
Me.tempListBox.RowSourceType = "Table/Query"
Me.tempListBox.Requery
' LOOP THROUGH LISTBOX AND RECORDSET FOR SELECTED ITEMS
Dim rsTools As Recordset, i As Integer
Set rsTools = CurrentDb.OpenRecordset("Tools", dbOpenDynaset)
rsTools.MoveLast
rsTools.MoveFirst
If rsTools.RecordCount > 0 Then
While Not rsTools.EOF
i = 1
While i < Me.tempListBox.ListCount
' CHANGE C FUNCTION HERE TO NEEDED TYPE: CLng, CInt, CStr, CDate, ...
If CLng(Me.tempListBox.ItemData(i)) = rsTools.Fields(1) Then
Me.tempListBox.Selected(i) = True
End If
i = i + 1
Wend
rsTools.MoveNext
Wend
End If
rsTools.Close
Set rsTools = Nothing

Me.Recordset.AbsolutePosition can not move to current record in the form

i have a form with data sheet view i want to use this form as a list box (multi select extended), so when i click on each record/field in the form it uses (clear selection function) for unselected other records and just select record i have focused, now problem is when click on each field after running clear selection function it goes to the first record and did not moves cursor to current focused record.
it seems below code
Me.Recordset.AbsolutePosition = Pos1
does not work and will not be moved to current focused record.
the complete code is like below:
Private Sub P_Click()
On Error Resume Next
Dim ct As Control
Dim Cnt As Long, Rws As Long
Dim Pos1 As Long, Pos2 As Long
Pos1 = Me.Recordset.AbsolutePosition
Set ct = ActiveControl
' Clear other selections if Ctrl or Shift key
' is not simultaneously pressed.
If CtrlPressed = 0 And ShiftPressed = 0 Then
P_ClearSelections
Me.Recordset.AbsolutePosition = Pos1
Me.IsSelected = True
ct.SetFocus
GoTo ExitPoint
End If
If ShiftPressed > 0 Then
Rws = Me.SelHeight
If Rws > 1 Then
Pos2 = Me.SelTop - 1
For Cnt = Pos2 To Pos2 + Rws - 1
Me.Recordset.AbsolutePosition = Cnt
Me.IsSelected = True
Next
End If
GoTo ExitPoint
End If
Me.IsSelected = True
ExitPoint:
' Save the status
Me.Dirty = False
' Update display in SF_Selected
Me.Parent("SF_Selected").Requery
ActiveControl.SelLength = 0
Set ct = Nothing
On Error GoTo 0
End Sub
function clear selection is like below;
Public Sub P_ClearSelections()
On Error Resume Next
DoCmd.Echo False
' Clear all check boxes
CurrentDb.Execute "UPDATE tblItems " & _
"SET IsSelected = False;", dbFailOnError
Me.Requery
DoCmd.Echo True
On Error GoTo 0
End Sub
You are requerying the underlying recordset in your P_ClearSelections() procedure. From the Microsoft documentation:
There is also no assurance that a given record will have the same
AbsolutePosition if the Recordset object is requeried or reopened.
Bookmarks are still the recommended way of retaining and returning to
a given position and are the only way of positioning across all types
of Recordset objects.
MSDN AbsolutePosition
I suggest that you investigate the use of Bookmarks instead (and remove the On Error Resume Next statements).

VBA code that changes the background color of a form after update

I need some code that when a check box is unchecked it will change the background color of my form and return it back to its original color when checked. The code i have for the check box currently locks a combo box when a value is chosen. Example below
Private Sub AccessKeyNo_AfterUpdate()
If MsgBox("Do you want to assign Access Key " & Me.AccessKeyNo & "?", _
vbYesNo) = vbYes Then
Me.GuestAccessKeyID = Me.AccessKeyNo
If Me.Dirty Then Me.Dirty = False
Me.AccessKeyNo.Requery
Me.AccessKeyNo = Null
Me.MyCheckBox = IsNull(Me.GuestAccessKeyID)
End If
End Sub
In a standard module (not the form module -- the scope of the constants would be limited to form, thus you wouldn't be able to reuse them):
Public Const colorBlue_Cornflower = "15714765"
Public Const colorTan_Encarnacion = "11398133"
Now in the module for the form:
Dim colorThis as String, booWhatever as Boolean
booWhatever = Me.MyCheckBox ''Use of the variable can prevent problems
If booWhatever Then
colorThis = colorBlue_Cornflower
Else
colorThis = colorTan_Encarnacion
End If
subFrm.Form.Section(acDetail).BackColor = colorThis
subFrm.Form.Section(acHeader).BackColor = colorThis
subFrm.Form.Repaint