问题
I have an MS Access form in which selection of a list box item determines how many other aspects of the application behave.
I have a table containing an ID and description and several other fields which correspond to settings for other aspects of the application; this table is the Row Source
of the list box.
The list box is configured such that the first two columns (ID & Description) are visible, with the widths of the remaining columns set to 0
- the Column Widths
property of the list box is therefore something like:
1cm;5cm;0cm;0cm;0cm;0cm;0cm;0cm;0cm;0cm;0cm;0cm;0cm
Within my application I then reference the remaining fields using expressions such as:
MyListBox.Column(n)
However, this relies heavily on the order of the fields in the table, and if I then decide to add another field to my table, I have to:
- Increment the list box Column Count
- Add another zero to the semi-colon delimited Column Widths
- Potentially change the identifiers used in the various
Column(n)
statements, depending on where the new field was added in the table.
My original reason for using this approach was to avoid the need to repeatedly open a RecordSet to obtain the values of the additional fields associated with the item selected from the list box.
Is there a way to reference list box fields by field name rather than by column number?
(Or is there an entirely better way to approach this task?)
回答1:
While I'm hearing a lot of No, not possible, this actually is possible for list boxes that have a row source type of Table/Query, by using the ListBox.RecordSet
property.
For combo boxes, this is easy, since the recordset moves along with the selected value. For list boxes, it doesn't, so you will have to know the name and position of the ID column (or, if the ID column is the bound column, just use the .Value
property)
Example:
Dim rs As DAO.Recordset
Set rs = Me.List1.Recordset.Clone
rs.FindFirst "ID = " & Me.List1.Value
Debug.Print rs.Fields!Field1
Add code to check if a value is selected, if the recordset property is set, and if a match is found if needed.
回答2:
Get the value from a list box/combo box by referencing its field name
I have a listbox that contains 30+ columns but only a few are visible. When a row in the listbox is selected, I need to grab values from columns on the selected row and display them in other fields or use them to enable/disable other controls. Over the years there have been various people supporting this application and sometimes a column gets added or re-positioned in a new order. This causes havoc for references like this:
txtFlow_Peak = lstPOC_ID.Column(27)
txtFlow_Avg = lstPOC_ID.Column(29)
It's hard to debug code written like this and keep track of each column position.
I had an idea to reference each column by name instead of by its column position and I created a function to make the code more readable and writable.
txtFlow_Peak = GetColumnValueFromName(lstPOC_ID, "Flow_Peak")
txtFlow_Avg = GetColumnValueFromName(lstPOC_ID, "Flow_Avg")
Is there a way to reference list box fields by field name rather than by column number?
I believe this function or the method used within it addresses your question. The error handler will show you the available field names if you try to address a column name that is not present.
Public Function GetColumnValueFromName(ctl As Control, ColumnName As String) As String
' Pass in a listbox/combobox control and a column name and return the value from that column.
' This lets you address a column by its name instead of the index which can be awkward
' if there are a lot of columns.
' This code is expecting a control that is bound to a Table/Query.
' Ben S. - 11/8/2019
On Error GoTo ErrorHandler
Dim strMsg As String
Dim fld As Field
If ctl.Recordset Is Nothing Then
' Recordset is not loaded.
ElseIf ctl.ListIndex = -1 Then
' No current row.
Else
'Debug.Print ctl.Name; Tab(20); "Column " & ColumnName & " (" & ctl.Recordset.Fields(ColumnName).OrdinalPosition & ") = '" & ctl.Column(ctl.Recordset.Fields(ColumnName).OrdinalPosition) & "'"
GetColumnValueFromName = ctl.Column(ctl.Recordset.Fields(ColumnName).OrdinalPosition)
End If
Exit_Function:
Exit Function
ErrorHandler:
Debug.Print "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "
strMsg = "Error #" & err.Number & " - " & err.Description & vbCrLf & "in procedure GetColumnValueFromName" & vbCrLf
strMsg = strMsg & vbCrLf & "Control name = " & ctl.Name _
& vbCrLf & "Passed column name = '" & ColumnName & "'" & vbCrLf
If err.Number = 3265 Then ' Item not found in this collection.
' Build a list of columns in the recordset to help the developer pick the correct column name.
strMsg = strMsg & vbCrLf & "Available Columns:" & vbCrLf
For Each fld In ctl.Recordset.Fields
strMsg = strMsg & fld.OrdinalPosition & " - " & fld.Name & vbCrLf
Next
End If
Debug.Print strMsg
MsgBox strMsg, vbExclamation, "GetColumnValueFromName()"
GoTo Exit_Function
Resume Next
Resume
End Function
As a bonus, I have a simpler function that can return the name of a column if you pass in the index.
Public Function GetColumnNameFromIndex(ctl As Control, ColumnIndex As Long) As String
' Pass in a listbox/combobox control and a column number and return
' the field name of that column.
' Ben S. - 11/8/2019
If ctl.Recordset Is Nothing Then
' Recordset is not loaded.
ElseIf ctl.ListIndex = -1 Then
' No current row.
Else
'Debug.Print ctl.Name; Tab(20); "Column " & ctl.Recordset.Fields(ColumnIndex).Name & " (" & ColumnIndex & ") = '" & ctl.Column(ColumnIndex) & "'"
GetColumnNameFromIndex = ctl.Recordset.Fields(ColumnIndex).Name
End If
End Function
来源:https://stackoverflow.com/questions/48853790/reference-list-box-column-by-field-name