Setting listboxes in MS access form - row by row - vba

I have a requirement in MS Access where a table is displayed as several rows in the form. I have created one form detail record(several fields) that will repeat for each row in the Table1. Lets say I have five columns in the Table1. Based on Column3 value, I would like to have a list of value for Column4 and Column5 during form_load. I have also created a separate Table2 to establish relationship between Column3, Column4 and Column5. I have set up Event procedure to populate the values using sub function. The challenge I have is, not being able to set up different listbox 'value list' for different rows. Any tips on populationg form fields IMRecomExIns and AmendReasonExIns by processing each row in Table1 would be a great help.
Private Sub IMRecomExIns_Click()
Dim CoverType As String
Dim ListRecomm As String
Dim ListAmend As String
Dim db As DAO.Database
Dim tablevar As Recordset
Set db = CurrentDb
Set tablevar = db.OpenRecordset("Table2")
CoverType = "*" & Me.CoverTypeExIns.Value & "*"
ListRecomm = ""
ListAmend = ""
If tablevar.EOF = False And tablevar.BOF = False Then
tablevar.MoveFirst
Do Until tablevar.EOF
If tablevar!CoverType Like CoverType Then
ListRecomm = tablevar!Recommendation
ListAmend = tablevar!AmendReason
tablevar.MoveLast
End If
tablevar.MoveNext
Loop
End If
Me.IMRecomExIns.RowSourceType = "Value list"
Me.IMRecomExIns.RowSource = ListRecomm
Me.AmendReasonExIns.RowSourceType = "Value list"
Me.AmendReasonExIns.RowSource = ListAmend
End Sub
1) I have stored all the value list in a single cell. For example tablevar!Recommendation will have all the values for Me.IMRecomExIns.RowSource, which means the output is will look like "Rec1";"Rec2";"Rec3";etc... Same applies for tablevar!AmendReason "AR1";"AR2';"AR3";ETC... Understand this is not the normalized form of storing data. I want to POC to work before building a full solution with normalized tables.
2) Answered earlier.. the rowsource will be set with all the possible values at the first match, so no point in going all the way to the end of the table
3) CoverTypeExIns is a string, Table 2 have many different possibilities such as "Mortgage Income" and "Family Income", however the Recommendation and Amendreason are same for all "Income" category that comes from Table1. Thats why the wildcard search.
My problem is not with setting the RowSource for a single row, but setting up RowSource for multiple occurrence in of the same IMRecommmendation and AmendReason in MS Access screen.
Here is the design view of the form. This form is linked to MS Access table. For multiple rows the Detail record will repeat itself as many times.
An example of two rows displayed in the screen.

I'm not sure exactly what you are asking/trying to do here.
I can see at several problems with the code that you have:
You are using tablevar.MoveLast in the loop, whic would automatically take you to the end of the recordset.
Also, you are not concatenating (joining together) ListRecomm/ListAmend, you are just setting them equal to a value, so each loop that matches will overwrite any previous value.
Finally, I am not sure what you are doing with trying to find CoverTypeExIns - you are using LIKE, which would indicate that it is text, but not wrapping the value in single quotes. If it is a numeric value, then you should be using "=".
However, rather than opening a recordset, looping it and checking for a match to build up a list of values, it is better to just set the RowSource of listboxes equal to a SQL string (effectively a query).
Something like (assuming CoverType is numeric):
Private Sub IMRecomExIns_Click()
Dim strSQL As String
strSQL = "SELECT Recommendation FROM Table2 WHERE CoverType=" & Me!CoverTypeExIns
Me!AmendReasonExIns.RowSource = strSQL
End Sub
I prefer to declare a string to hold the SQL statement rather than setting the .RowSource directly, as it makes troubleshooting easier.
Regards,

Based on the new information given, below is some VBA code that opens up a recordset based on the information entered in "Cover", and then sets the .RowSource property of the two combo boxes to be the value lists. In my example, don't bother setting the .RowSourceType, as this should be done at design time:
Private Sub IMRecomExIns_Click()
Dim db As DAO.Database
Dim rsData As DAO.Recordset
Dim strSQL As String
Set db = DBEngine(0)(0)
strSQL = "SELECT Recommendation, AmendReason FROM Table2 WHERE CoverType LIKE '*" & Me!cboCover & "*';"
Set rsData = db.OpenRecordset(strSQL)
If Not (rsData.BOF And rsData.EOF) Then
Me!IMRecomExIns.RowSource = rsData!Recommendation
Me!AmendReasonExIns.RowSource = rsData!AmendReason
End If
rsData.Close
Set rsData = Nothing
Set db = Nothing
End Sub
As I have previously stated, you should really normalize the design of your database now, rather than getting in so far that it requires a major re-write.
Regards,

Related

Return Query Value Using VBA function in Access

I'm currently working on a project and I've been having trouble trying to get a function that is able to return the value of a query, which I do need in order to display it on a textbox.
The current code is like this:
Public Function rubrieknaamSQL() As String
Dim rst As DAO.Recordset
Dim strSQL As String
strSQL = "SELECT T_Train.trainPlate, T_Category.categoryName FROM T_Category INNER JOIN T_Train ON T_Category.id = T_Train.category_id WHERE (((T_Train.trainPlate)=[Forms]![F_Comboio]![Combo_Search_Comboio]));"
Set rst = CurrentDb.OpenRecordset(strSQL)
rubrieknaamSQL = rst!categoryName
rst.Close
End Function
I should say that the code is copied from other publisher and I do not own its rights. However, it still won't work when I try to run it and the error displayed goes like this:
Run-Time Error 3061 : Too few parameters. Expected 1
and it happens in Set rst command.
For a SELECT query to set a recordset object, concatenate variable:
" ... WHERE T_Train.trainPlate=" & [Forms]![F_Comboio]![Combo_Search_Comboio]
If trainPlate is a text field, need apostrophe delimiters (date/time field needs # delimiter):
" ... WHERE T_Train.trainPlate='" & [Forms]![F_Comboio]![Combo_Search_Comboio] & "'"
For more info about parameters in Access SQL constructed in VBA, review How do I use parameters in VBA in the different contexts in Microsoft Access?
There are ways to pull this single value without VBA.
make combobox RowSource an SQL that joins tables and textbox simply references combobox column by its index - index is 0 based so if categoryName field is in third column, its index is 2: =[Combo_Search_Comboio].Column(2)
include T_Category in form RecordSource and bind textbox to categoryName - set as Locked Yes and TabStop No
build a query object that joins tables without filter criteria and use DLookup() expression in textbox
=DLookup("categoryName", "queryname", "trainPlate='" & [Combo_Search_Comboio] & "'")

Access subtract form value from another query

In MS Access using VBA trying to subtract a calculated value (time between dates) from a total amount of time in another table.
The query holding the total amount of time is QryScsBatchHandler the field I want to subtract the value from is FreezerLifeUsed and the match condtion is BatchID
The query holding the value I want to subtract is QrySCSMaterialFreezerLog or the form is Frm_MaterialFreezerLog.. the value is AccumilatedTime and the Match condition is Batch ID
Private Sub BkInFrzr_Click()
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim strSQL As String
Set db = CurrentDb()
strSQL = "SELECT FreezerLifeUsed FROM QryScsBatchHandler WHERE [BatchID] = BatchID"
Set rst = db.OpenRecordset(strSQL, dbOpenDynaset)
With rst
.MoveFirst
.Edit
FreezerLifeUsed = -AccumilatedTime
.Update
End With
End Sub
I can’t seem to get this simple subtraction to work… any suggestions on what’s not right?
Any help will be greatly appreciated..
At least 3 issues with code.
concatenate reference to form field/control for dynamic criteria in SQL:
strSQL = "SELECT FreezerLifeUsed FROM QryScsBatchHandler WHERE [BatchID] = " & Me.BatchID
The VBA needs to use dot or bang when referencing controls or fields and qualifying with prefix is advised:
!FreezerLifeUsed
Me.AccumilatedTime
Nothing is subtracted from anything, so consider:
!FreezerLifeUsed = !FreezerLifeUsed - Me.AccumilatedTime
However, if query is expected to return only one record, don't bother with loop. Also, instead of opening a recordset, an UPDATE action would work.

In MS Access, is it possible to add an updatable blank field to a query to calculate it's values programmatically in VBA?

In MS Access, in order to prevent nesting queries, because it has become really slow, I'm trying to calculate programmatically in VBA all calculated fields for my queries using a Loop. In general, the idea is to stop using calculated fields on queries at all, to perform all calculations from the backend.
I am having problems showing the values in a TextBox in a Continuous Form. I'm trying to create a query in VBA with an empty field and then update the field with the calculated values, so then I can set the ControlSource of the textbox to that field for it to show properly. The problem is that since the empty field is a calculated field, I cannot update it's value. Is there any other way to add a field to a query that remains empty and updatable until I can calculate it's values?
An alternative solution would be to create a temporal table instead of a query and update the values, but I would really prefer not to create temporal tables every time an user needs to do a query with calculated fields (especially nested ones).
Is there a way to accomplish this?
Here is what I tried:
Private Sub Form_Load()
Dim SQLQ As string
Dim rs As DAO.Recordset
Dim dbs As DAO.Database
Dim qrydef As DAO.QueryDef
Dim i As Integer
'calcField1 is not a member of Items, and it's intended to be updated later on
SQLQ = "SELECT Items.Price, calcField1 FROM Items;"
Set dbs = CurrentDb()
Set qrydef = dbs.CreateQueryDef("testqry", SQLQ)
'Field is prompted for a value when the query is called and has no source
qrydef!calcField1 = vbNullString
Set rs = qrydef.OpenRecordset()
i = 0
Do Until rs.EOF
rs.Edit
rs!calcField1 = rs!Price *2 'Error here because calcField1 is a Calculated Field
rs.Update
i = i + 1
rs.MoveNext
Loop
Me.TextBox1.ControlSource = "=Price"
Me.TextBox2.ControlSource = "=calcField1"
Set Me.Recordset = rs
Me.Refresh
End Sub
I think it is better to create another table where you can insert the new calculated record with a reference to the original table so they are linked.
for example:
table 1:
item_id, item_desc, price
table 2:
item_id, calculated_value
then, the calculated value can be inserted or modified depending on the purpose of your calculation process.
Can you add the calculated field into a query and use that as the Record Source?
Private Sub Form_Load()
Dim strSQL As string
'calcField1 is calculated
strSQL = "SELECT Items.Price, (Items.Price * 2) AS calcField1 FROM Items;"
Me.TextBox1.ControlSource = "=Price"
Me.TextBox2.ControlSource = "=calcField1"
Set Me.RecordSource = SQLQ
Me.Refresh
End Sub
or if the logic is more complex you can put it into a function
'in the form
Private Sub Form_Load()
Dim strSQL As string
'calcField1 is computed by a function
strSQL = "SELECT Items.Price, MyFunction(Items.Price) AS calcField1 FROM Items;"
Me.TextBox1.ControlSource = "=Price"
Me.TextBox2.ControlSource = "=calcField1"
Set Me.RecordSource = strSQL
Me.Refresh
End Sub
'in a module
Public Function MyFunction(dbl As Double) As Double
MyFunction = dbl * 2
End Function

Is it possible to make a dynamic sql statement based on combobox.value in access?

I made a form in access with 2 different comboboxes. The user of
This tool can choose in combobox1: the table (which has to be filtered) and the second combobox2 is the criteria to be filtered( for example Language= “EN”) and the output of this query has to be located in tablex.
The problen what I have is that i cant find a solution for passing the value of the combobox1 to the sql statement. The second one is just like: where [language] = forms!form!combo2.value, but the part where i cant find a solution for is: select * from (combobox1 value)? How can i pass the combobox value as table name to be filtered? Can anyone please help me?
You can't have the table name in the WHERE clause of your query (there might be a hacky way to do it, but it should be discouraged at any case).
If you want to select data from 1 of a number of tables, your best bet is to generate SQL dynamically using VBA. One way to do this (especially if you want/need your query to open in Datasheet View for the end user) is to have a "dummy" query whose SQL you can populate using the form selections.
For example, let's say we have 2 tables: tTable1 and tTable2. Both of these tables have a single column named Language. You want the user to select data from either the first or second table, with an optional filter.
Create a form with 2 combo boxes: One for the tables, and one with the criteria selections. It sounds like you've already done this step.
Have a button on this form that opens the query. The code for this button's press event should look something like this:
Private Sub cmdRunQuery_Click()
Dim sSQL As String
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
If Not IsNull(Me.cboTableName.Value) Then
sSQL = "SELECT * FROM " & Me.cboTableName.Value
If Not IsNull(Me.cboFilter.Value) Then
sSQL = sSQL & vbNewLine & _
"WHERE Language=""" & Me.cboFilter & """"
End If
Set db = CurrentDb
Set qdf = db.QueryDefs("qDummyQuery")
qdf.SQL = sSQL
DoCmd.OpenQuery qdf.Name
End If
End Sub
Note how the SQL is being generated. Of course, using this method, you need to protect yourself from SQL injection: You should only allow predefined values in the combo box. But this serves as a proof of concept.
If you don't need to show the query results, you don't need to use the dummy query: You could just open a recordset based on the SQL and process that.
If you run the code in the afterupdate event of the combobox you can set an SQL statement like this:
Private Sub combobox2_AfterUpdate()
someGlobalVar = "Select * FROM " & me.combobox1.value & " WHERE language = " & _
me.combobox2.value
End Sub
And then call the global with the SQL string wherever you need it.

Table and form based off a different table - see if a common record exists in the table and insert a "yes" or "no" value in the form

I have an access database that my team uses to track projects. We use it as a VCB.
On the main form (Projects) we view it and make updates on the form for the projects - which in turn updates the table linked to that form.
However, we also have a separate table (Comments) where we store comments related to the projects. The Primary Key for both tables is ProjectID.
There is only 1 Project entry on the Projects form but there can be 0 to Many on Comments on the comment table.
What I am trying to achieve is have the Project Form (our VCB) look at the comments table and see if there is a record in the table with the same ProjectID - if there is insert a "Yes" value into my textbox on the project form but if there is not then insert "NO".
Its just an easy visual aid to see if comments exist for the projects on the VCB (we handle comment entry and viewing in a separate form)
I am stumped on which route to take. Ive tried a few things and gotten stuck in loops. Any help is appreciated
Dim strSQL As String
Dim rs As Dao.Recordset
Dim db As Dao.Database
strSQL = "SELECT * FROM COMMENTS WHERE [PROJECTID] = " & Me.PROJECTID & ""
Set db = CurrentDb
Set rs = db.OpenRecordset("COMMENTS")
rs.MoveFirst
Do Until rs.EOF = True
Set rs = db.OpenRecordset(strSQL)
If rs.RecordCount = 0 Then
Me.CommentTxtBox.SetFocus
Me.CommentTxtBox.Text = "NO"
Else
Me.CommentTxtBox.SetFocus
Me.CommentTxtBox.Text = "YES"
End If
rs.MoveLast
Loop
rs.Close
Set rs = Nothing
I'm getting every entry a NO in the field and it errors out because the ProjectID is not in the comments table(which it might not be) so it says no record found.
DCount() could make your code simpler.
Dim lngComments As Long
Dim strComments As String
lngComments = DCount("*", "COMMENTS", "[PROJECTID] = " & Me.PROJECTID)
If lngComments > 0 Then
strComments = "YES"
Else
strComments = "NO"
End If
Me.CommentTxtBox.Value = strComments
Notice by assigning the value to the text box's .Value property (instead of its .Text property), you don't have to bother about SetFocus.
I think that is a simpler version of your current approach. However I'm uncertain whether that is the best approach. Perhaps you could get what you need by setting the text box's Control Source property to an expression which uses IIf to evaluate DCount:
=IIf(DCount("*", "COMMENTS", "[PROJECTID] = " & [PROJECTID]) > 0, "YES", "NO")