MS Access collection object value is being overwritten - ms-access

I have a global collection object in a form that I am using to store IDs.
Dim newCollection As New Collection
When a user hits a btn
Private Sub btn_Click()
Dim key As Variant
newCollection.ADD Me.ID, CStr(ID)
For Each key In newCollection
Debug.Print key
Next key
End Sub
When a user is at ID 1 in the forms and presses the button above I get the expected 1 returned on my immediate window
But when A user moves to another record in the form let's say from ID 1 to 2 using the forms navigation button
For Some reason the Debug.Print key returns
2
2
rather than the expected
1
2
For Some reason the key is not overwritten but the value is. What I mean is that
On my immediate console:
?newCollection(1)
returns 2
And
?newCollection(2)
also returns 2
I don't know why this is happening

Change this ...
newCollection.ADD Me.ID, CStr(ID)
to this ...
newCollection.ADD Me.ID.Value, CStr(ID)
In the first case, you're adding a field object. So when you later Debug.Print key, you're printing the current value of that field. (That may make more sense if you do Debug.Print TypeName(key) temporarily.)
You need to add the field's value instead of the field itself.

Related

MS Access 2007: Runtime error 64224: Method 'Collect' of object 'recordset2' failed

I have been searching high and low for a workaround for this error, but since I haven't, I now turn to you for help. I am not (at all) an experienced programmer, so I hope this is not too basic a question to ask here, and that others can still benefit.
I am trying to set up a form, where I can register when one of our employees lends some stuff for a given period of time. I.e. from 21-12-2015 to 10-01-2016 employee n.n. lends a laptop. The form has two unbound textboxes TxtFraDato and TxtTilDato for the dates (from and to), and two unbound listboxes containing the name of the employee and the 'stuff' to be lend. The listboxes get their data from the tables:
Medarbejder (The employee):
- Navn: text (Primary key)
Materiale:
- Id: number (Primary key)
- ModelType: Text
I know that having a table like my Medarbejder-table is not at all pretty, and I have a fealing that this could be causing the problem, but this table is linked to a .txt-file created from a bat-file, and I have not been able to figure out, if there is another way to create such a link, that will give me a chance to give the table another primary key.
Finally, I have a button that on-click should save the record to the table "Udlaan":
Id: number (Primary key)
FraDato: date
TilDato: date
Medarbejder: text (lookup column)
Materiale: number (lookup column)
However, when I click the button, I get the runtime error 64224: Method 'Collect' of object 'recordset2' failed.
Private Sub btnSaveUdlaan_Click()
Dim db As Database
Dim rs As Recordset
Dim FraDato As Date
Dim TilDato As Date
Dim Medarbejder As String
Dim Model As Integer
Set db = CurrentDb
Set rs = db.OpenRecordset("Udlaan")
FraDato = Me.TxtFraDato.Value
TilDato = Me.TxtTilDato.Value
Medarbejder = Me.MAList.Column(0)
Model = Me.ModelList.Column(0)
AddUdlaan rs, FraDato, TilDato, Medarbejder, Model
rs.Close
db.Close
End Sub
The AddUdlaan:
Private Sub AddUdlaan(rs As Recordset, FraDato As Date, _
TilDato As Date, Medarbejder As String, Model As Integer)
' Adds a new record to a Recordset using the data passed
' by the calling procedure.
With rs
.AddNew
!FraDato = FraDato
!TilDato = TilDato
**!Medarbejder** = Medarbejder ß The debugger is pointing to this field
!Materiale = Model
.Update
End With
End Sub
Perhaps this is not at all the right way to solve by problem, so any help on this is much appreciated. But as it is, I would also be happy to lean something from the encounter with this error.
This is because the lookup field is a recordset of the recordset. Only Recordset2 can handle these.
Thus, what you should assign is the (hidden) Id of Medarbejder, in pseudo code:
!Medarbejder = <Id of recordset Medarbejder>
This can be done but - as already mentioned by #Parfait - do yourself a favour and get rid of the lookup fields. How "smart" they may seem, they will cause you nothing than trouble in the future.
As a start, change table Medarbejder to have an AutoNumber Id as the primary key.

How to restrict deleting specified items from listbox using access vba

I have a listbox that contains 18 items. The selected item can be deleted from the listbox by clicking a 'Delete' button. How can I prevent a user from deleting certain items that I specify?
Private Sub cmdDelete_Click()
Dim i As Integer
With Me.listbox1
For i = .ListCount - 1 To 0 Step -1
If .Selected(i) = True Then
.RemoveItem i
End If
Next i
End With
End Sub
You could store the values in a table? Then you could have a column in the table which specified whether the field could be deleted or not, which would be easy to build into your VBA.
Edit for further explanation:
Have you listbox be unbound (i.e. no specific field as its datasource).
Create a table which contains columns for an ID (as an autonumber), the values you want to appear in your listbox (as a textbox), and a final column you can tick yes or no on (as a boolean or yes/no field). E.g.
ID Values Allow deletion?
1 Example Yes
2 Another example No
3 Yes another example Yes
In your listbox's rowsource, select that table.
Your delete sub could now run the following code.
dim qry as string
dim strLookup as string
strLookup = dlookup("[Allow deletion]","[Your Table]", YOURSELECTEDFIELDHERE & "=[Values]")
if strLookup = "No" Then
msgbox "You're not allowed to delete this field."
exit sub
else
qry = "DELETE * FROM [Your Table] WHERE [Values] = YOURSELECTEDFIELDHERE"
CurrentDB.Execute qry
Me.YourListBoxName.Requery
end if
This will look for the value you've grabbed from your listbox, match it against the Allow deletions field, delete it if that field is marked as Yes or leave it be if it's marked as No.
If you want to only temporarily delete the field and have it appear next time you load the form - in effect, hide it, you'd have to add a further column to the table called "Show", which would also be a Yes/No field, and set the default to Yes.
You'd then set your rowsource to SELECT * FROM [YOURTABLE] WHERE [Show] = "Yes".
Then qry in the above code should instead be set to:
UPDATE [YOURTABLE] SET [YOURTABLE].[Show] = "No" WHERE ((([YOURTABLE].[Values])=YOURSELECTEDFIELD));
This will then change the value of Show to No, so when the listbox is requeried, it'll no longer appear.
You'll need to run another query to change all of them back to Yes on the form exit.

how do i determine what the next record number (PK) is?

im trying to get the next number in the autonumber sequence for the primary key programatically. For instance, if the last number in the table was 10, i need it to return 11. Before, I would use something like:
docmd.RunCommand acCmdRecordsGoToNew
in order to tell the database to go to the next record, and then i'd assign it to a control on the form to show the user what record they are currently entering. The problem is, this function ceased to work when I disabled the navigation buttons by setting it's property to "No" in the properties window. How do I get the next record in vba without the nav bar being enabled?
To know what the real next value is, you have to look up the SeedValue for your Autonumber column. This code does that:
Public Function GetSeedValue(strTable As String, strColumn As String) As Long
Dim cnn As Object 'ADODB.Connection
Dim cat As Object ' New ADOX.Catalog
Dim col As Object ' ADOX.Column
Set cnn = CurrentProject.Connection
Set cat = CreateObject("ADOX.Catalog")
cat.ActiveConnection = cnn
Set col = cat.Tables(strTable).Columns(strColumn)
GetSeedValue = col.Properties("Seed")
Set col = Nothing
Set cat = Nothing
Set cnn = Nothing
End Function
If you're going to call it a lot, you'd likely want to cache the ADOX Catalog object variable, rather than re-initialize it each time you call this function.
Note that in a multiuser environment, this may or may not be accurate, since by the time you use it, it may have been updated by another user. However, it doesn't have the problem with skipping Autonumber values that Max()+1 can have.
Keep in mind, though, that if you care about the next Autonumber value, it means YOU'RE USING IT WRONG. Autonumber values are surrogate keys and you should never, ever care what the values are.
Turns out that there is a VBA function that will interact with the database and actually return a value. This is what I ended up doing to get the next record number:
Dim nextRecord As Integer
nextRecord = DMax("ID", "my_table") + 1
Sloppy, but effective for my single client situation. There is also a where clause that can be applied:
Dim nextRecord As Integer
nextRecord = DMax("ID", "my_table", "field = value")

Go To Record + Specific Tab

I have created a search criteria form that allows the user to search for specific branches that our company deals with. Now - I have created a button that displays with each entry found by the search.
I want this button to open to the specific 'ID' that goes with each record, but here is my problem: this search criteria form (Tab1) updates the records information (Tab2,Tab3,Tab4) But doesnt jump to the tab after being pressed - Simply because I'm not sure how to code it so that it automatically jumps to Tab2 (the first tab of data).
Here is my code below:
Module1
Public Function viewDetails(frmID As Integer)
Dim rs As Object
DoCmd.OpenForm "Form1"
'Directs to the selected record
Set rs = Forms!Form1.RecordsetClone
rs.FindFirst "ID = " & frmID
Forms!Form1.Bookmark = rs.Bookmark
Set rs = Nothing
and
Private Sub Command46_Click()
viewDetails (Me.ID)
End Sub
Thanks :)
The .Value property of the TabControl is a read/write property that you can use to get the currently selected page or set the currently displayed page. It is the default property of the tab control so you do not need to explicitly refer to it. Note that the page index is a zero-based array so the second tab is 1 in the index:
Forms!Form1.TabCtl0 = 1 'Jump to the second tab; form must already be open

How To Refer To Continuous Subform Contol

I have an Access 2003 form with one subform, set to continuous forms, in a subform control. For one record in the main form, 1 to many records will appear in the sub form. The data displays properly.
The main form is named Widgets and the sub form is named Transactions. There are 5 textbox controls that display the data in the subform. The one in question is ReceiptDate.
What I would like to do is look at the values and determine if there was a receipt for the year 2009, and if so then change the background of that row to yellow so it stands out when the user encounters that condition. Maybe even change the date field's font to boldface..
I tried many ways of referencing the subform's controls. When I have tried Me.Transactions.ReceiptDate I have only received the first record in that subform. I'd like to be able to loop through them and see if the condition is met. I tried Me.Transactions.ReceiptDate(1) and Me.Transactions.ReceiptDate(0) and so forth.
I tried the For Each ctl In Form.Controls route as well. It worked for a few iterations and then I received a run-time error 2455 "You entered an expression that has an invalid reference to the property Form/Report".
I had the subform in "datasheet" mode but thought that was causing me not to be able to read through an array of subform controls. So I changed it to "continuous" mode. I get the same errors for either.
Is there a way to reference specific "rows" in the subform and do something based on a value found? Also, I am performing this in the On Current event as I dont know where else to put the code. The subform loads before the parent form so its possible that these controls arent even fully "there" but then I do get the first row's date when I try it in the Immediate Window.
UPDATE 12.23.2010:
With the gracious help of #Remou I am able to debug.print the ReceiptDate fields from the RecordSet. This is great because now I can evaluate the data and do certain things based on the values.. The #Remou's code helped me put this into the OnCurrent event:
Dim i As Long
Dim frm As Form
Dim rs As DAO.Recordset
' Get the form and its recordset.
Set frm = Me.Form
Set rs = frm.RecordsetClone
' Move to the first record in the recordset.
rs.MoveFirst
' Move to the first selected record.
rs.Move frm.SelTop - 1
' Enumerate the list of selected records presenting the ReceiptDate field
For i = 1 To rs.RecordCount
Debug.Print rs![ReceiptDate]
rs.MoveNext
Next i
So now that I am able to know which row in my subform has a receipt from 2009, I need to be able to highlight the entire row or rows as I come across them in that for loop. How can I reference the actual row? Datasheet view or Continuous Forms view - I have tried both.
Conditional Formatting is great but it only allows me to highlight one particular record and I'd much rather be able to do this via VBA because...... from here I will want to give the use the ability to click on any record in the subform and get the receipt details and potentially print them.
Any ideas?
In this situation, it is best to use conditional formatting.
To refer to a control on a subform, refer to the subform control by name, then the form property to get the form contained, then the control by name:
Me.MySubformControlName.Form.MyControl
See: http://www.mvps.org/access/forms/frm0031.htm
I have finally got it. The frm.SelTop = x will set the selected record and from there I can set the background or font style, etc.. Very cool. A simple test for 2009 and setting the selected record:
Dim i As Long
Dim frm As Form
Dim rs As DAO.Recordset
' Get the form and its recordset.
Set frm = Me.Form
Set rs = frm.RecordsetClone
' Move to the first record in the recordset.
rs.MoveFirst
' Move to the first selected record.
rs.Move 0
' Enumerate the list of selected records presenting
' the CompanyName field in a message box.
For i = 1 To rs.RecordCount
If Year(rs![ReceiptDate]) = 2009 Then
frm.SelTop = i '<-----------------------------
End If
rs.MoveNext
Next i
In order for me to get the email off the bottom of my continuous form, I used this much simpler code (as I avoided the RecordsetClone code)
Me.[email subform].Form.SelTop = Me.[email subform].Form.Count 'selects the last row
str = Me.[email subform].Form.Email 'capture the value of the last row
MsgBox str