Out of Stack Space & Requery Errors - ms-access

I've been modifying my Access DB to clean it up and make it more user friendly. As such, I've been changing the names on form labels and controls to include _lbl and _Ctrl instead of the generic names Access assigned them. Previously, I had three separate forms (ListingsForm, ListOffersForm, ListDetailsForm) that I could access by using buttons to call the form. Since the forms were directly related to the primary form (ListingsForm), I changed two of the forms into subforms. Now I keep getting errors when I try to add a record.
On the ListingsForm form, there is a control field for Property Address (Address_Ctrl). This control uses a combo box that is linked to a separate table called Properties. If the property address is not listed in the drop down box, it used to bring up the PropertyForm form to input new properties. After, it requeried to get the new list with the new record. After making the subforms and changing control names, I'm getting an error whenever I try to add a new property address.
Run-time error '2473':
The expression On Not in List you entered as the event property setting produced the following error. Out of stack space.
'Add Address Form Script
Private Sub Address_Ctrl_NotInList(NewData As String, Response As Integer)
DoCmd.OpenForm "PropertyForm", , , , acFormAdd
Call Address_Ctrl_AfterUpdate
End Sub
'Requery Address List Script
Private Sub Address_Ctrl_AfterUpdate()
Forms!MLSListForm.Dirty = False
Me!Address_Ctrl.Requery
End Sub
It highlights the Forms!MLSListForm.Dirty = False line. I've tried Me.Dirty = False but I get the same error. If I remove the line entirely, I get 'Run-time error '2118': You must save the current field before you run the Requery action.'
Having the forms as subforms helps with user-interface so I really do not want to go back to having three separate forms. Any help would be appreciated.

Normally, your comment Address_Ctrl_AfterUpdate() event handler,
And you can try this event handler:
Private Sub Address_Ctrl_NotInList(NewData As String, Response As Integer)
'
' add in Properties tables for NewData:
' By calling SQL Server via ADO:
' INSERT INTO Properties (City) (NewData)...
' Or
'
'
' Now NewData Exists, do this:
'....
' and then set new value and status:
'
Response = acDataErrAdded
'
' here you can then modify new city:
'
DoCmd.OpenForm "PropertyForm", , , "City='" & NewData & "'", acFormPropertySettings
'
End Sub
I've re-debugged in our Access Database VBA, with Response = acDataErrAdded,
Access will Requery automatically Address_Ctrl.Requery() After the Address_Ctrl_NotInList() returns.

So I finally got it working correctly. Turns out I had to set some default values for the controls in the form design. After that, it worked out just fine.

Related

Action on Form Save

We have an Access 2010 database that acts as a front-end to a MS SQL database. When we edit data in the form there is a procedure that needs to run in order to properly save certain data back to SQL.
Our DB programmer added a "Save Button" to do this. But that causes another problem - there are multiple ways in Access by which to save a form -
Navigate to the next record
Click on the Confirmation bar on the left
Create a new record
Search for a new record
Use commands in the ribbon
Is there any way to attach a procedure the actual save action so that no matter how a person moves to a next form that the procedure gets run?
[update]
Here is the code behind the scenes: the first sub is attached to the "Save" Button. Of course, the second is attached to the form BeforeUpdate.
Private Sub SaveRecord_Click()
'From NAME form
Form_BeforeUpdate False
End Sub
Private Sub Form_BeforeUpdate(Cancel As Integer)
'used by NAME form
[Last_Update] = Now
'*********************
Save_Record
'*********************
MName_ID = Me.Name_ID
Me.Undo
Cancel = True
If Not IsNull(MName_ID) Then
Jump_to_Name_ID MName_ID, True
Else
End If
End Sub
I guess I just don't understand what the button is for.
So I installed an MS Access 2010 trial and finally managed to figure out a way to solve your problem. It includes data macros and a hidden gem that took me quite a while to find.
Here's how you run VBA when a table changes:
Create an ordinary module (haven't tried class modules) with public functions:
Module name: EventHandlers
Public Function InsertEvent(ByVal id As Integer)
MsgBox "inserted: " + CStr(id)
End Function
Open the table that, when modified, should run VBA and go to "Table" in the ribbon.
Click on "After Insert"
In the "Add New Action"-select box, choose SetLocalVar (or select it from the Action Catalog).
In the Name-field, insert the name of the module (in this case, EventHandlers, as we created earlier)
In the Expression-field, write the name of the function: InsertEvent([id]) (where [id] is an actual column in the table you're adding a data macro for)
Save and close
Whenever something is inserted to the table, a messagebox will be shown with the id.
You could do the same with the update event. The function could be something like this:
Public Function UpdateEvent(ByVal oldValue As String, ByVal newValue As String)
MsgBox oldValue + " changed to: " + newValue
End Function
and the data macro would be
Action: SetLocalVar
Name: EventHandlers
Expression: UpdateEvent([Old].[your_column_name];[your_column_name])
Note: Executing DoCmd.RunSQL with update, insert or delete will execute data macros and THEN ask the user if he or she actually WANTS to update/insert/delete the row. If the user clicks cancel, nothing is changed but your data macro executed anyway. If you haven't already, you should probably disable this check before implementing data macros.
Well, I was not able to use Mr. Sommer's solution because it was not possible to add an event handler to a linked table on account of their being read-only. But, I did work out a simple procedure that seems to work well enough.
So, I was actually already using the BeforeUpdate event, so I'm catching the right event here - this is the event that traps the save, whether it be on change of navigation or the save-record bar on the left. However, there were a few issues that resulted from using Application.Echo False to keep Access from posting back the old data to the control whilst the Me.Undo takes place.
So we use cancel=true to prevent the BeforeUpdate event from doing its normal processing, and we use Me.Undo to prevent Access from trying to save data to the linked tables.
Private Sub Form_BeforeUpdate(Cancel As Integer)
Cancel = True
[Last_Update] = Now
'*********************
Save_Record '-->This will save the data back to SQL
'*********************
MName_ID = Me.Name_ID
Application.Echo False 'don't show the undo in the controls
Me.Undo
If Not IsNull(MName_ID) Then 'used for the navigation buttons
Jump_to_Name_ID MName_ID, True
Else
End If
Application.Echo True 'turn the control refresh back on
Me.Repaint
End Sub

Allowing blank records despite validation

Many of my database forms use a listbox with 4 columns and several textbox/combobox controls which are mapped to their respective fields using the ‘control source’ in the property sheet.
I validate these using ‘If Trim(control.value & vbnullstring) = vbnullstring then’ and then ask the user to enter their ID number in a textbox (also mapped using control source) if the controls are not null.
An issue was raised with my database last night where a user managed to enter a record into the database with just their user number (my time field also contained an entry as its set to default = Now()) but every other field was blank.
The code below is what I’ve been using to prevent accidental blank records:
Private Sub Form_BeforeUpdate(Cancel As Integer)
If Not blnSave Then
Cancel = True
Me.Undo
End If
End Sub
Then my Submit_Click() button event contains this to save the record…
blnSave = True
DoCmd.GoToRecord , , acNewRec
blnSave = False
I can’t recreate the problem myself as my validation won’t allow you to even enable the ‘User ID’ control until something is selected in the listbox and all textboxes contain a value. What could have caused this? Would it be better to add records to the table using a query rather than relying on the Control Source property or am I focusing on the wrong thing?
Maybe the user circumvented your form by accessing the database table directly? Could that be possible?

"Operation not supported in transactions" While Copy/Paste Records in Access

When copy / pasting cells from Excel into an Access Subform, when no parent record has been created, will result in an error - as expected.
The problem, is that after this error occurs access gets locked in a state where all subsequent data that is entered results in an Operation not supported in transactions error. When you open the table, you can see that the newly added data is not yet in the table - so it appears as if Access is in fact engaged in some sort of transaction.
I have tried hitting 'Save' .. 'Refresh' .. and even adding an AfterInsert event to force a commitTrans() but have had no luck - and Access claims there is no transaction underway
There is no error if the records are entered manually. The problem only seems to occur when pasting records. I'm guessing that Access is creating a transaction to handle the multiple record operations and is not properly recovering from the error.
I can remove the 'Required' flag and it will work - but then you have orphan records. I was thinking that perhaps an After Insert Data Macro could be used to add a new Batch with a default batch Name, and auto-fill the new BatchID into the Items table. I am not certain how that would be done however.
I did try to remove the 'Required' flag and trap for the error using a Before Change Data Macro - but while it cut down on the errors - it still produced the same Operation not supported in transactions error.
I have looked up the error on the Microsoft Knowledgebase, but did not find anything specific to my circumstances. I searched stackoverflow for the error message and found nothing.
I created a new database and was able to replicate the issue.
Steps to Replicate
Set up the Database
Create a new ACCDB database in Access 2010
Create a Table called 'Batches', with the following fields:
BatchID (AutoNumber) (Primary Key)
BatchName (Text)
Create a Table called 'Items', with the following fields:
RecordID (AutoNumber) (Primary Key)
BatchID (Long Integer)
Set Required attribute to True
Data - Text
Create a Relationship, linking Batches.BatchID to Items.BatchID
Include all Records from Batches, and matching records from Items
Enforce Referential Integrity
Cascade Updates / Deletes
Create a Form called 'Form'
Set the Recordsource to Batches
Add in the BatchID and Batch name Textboxes
Add in a SubForm/SubReport Control
Set Source Object to "Table.Items"
Set Link Master Fields to "BatchID"
Set Link Child Fields to "BatchID"
Set "Filter On Empty Master" = Yes
Create sample data (Using the Form)
Create a New Record in Batches.
Set BatchName = "Test"
Create a New Record in Items.
Reference the Batch Record.
Set Data = "Test"
As you can see, by hand this works fine.
Copy and Paste Data From Excel
In Excel
From A1-A10 enter one letter per cell running down: A,B,C,D,E,F,G,H,I,J
Highlight the cells A1-A10
Copy (Control+C)
In Access, using the Form:
Add a New Batch Record. It should say "(New)" in BatchID TextBox
Do NOT enter a Batch Name
In the Sub-Form, click the record selector (*) for the new record to select the entire row. Type Control+V to paste.
Click OK for "You must enter a value in the 'Data.BatchID' field. error
This error may repeat. Keep Clicking OK
If it asks "Do you want to suppress further error messages..." answer Yes
Click OK for the "Records that Microsoft Access was unable to paste have been inserted into a new table called 'Paste Errors.' notification
Fill in the Batch Name Textbox with "TestName"
Try to gracefully recover. Hit Escape. Change the Record.
At this point - you should see the BatchID entered, the Batch Name, and the New Data. Everything appears to be working as expected. If you try to refresh or navigate to another batch record - you will get the error Operation not supported in transactions. Access will continue to display this error message until we close and reopen the form. The data you pasted will not have made it into the database.
Normally someone with a bit of tech savvy will realize something isn't going well and close out of the database and re-open ... but unfortunately I have users that play "whack-a-mole" with any popup boxes and then try to continue on - so I'd like to make things as bulletproof as possible.
Desired Solution
I'd like a workaround to the problem, that won't eventually lead to other quirks with access, duplicate values, etc.
In my own experience, using VBA to 'fix-up' keys isn't reliable. Data macros seem to be a lot more reliable - but they can be tricky to set up - they aren't very mainstream yet (I'd say there should be a ms-access-data-macros tag on stackoverflow but there isn't yet)
Suggested workaround:
In the [Batches] table, set the Required property of the [BatchName] field to Yes.
Change the Tab Stop property of the [BatchID] text box to "No". That will give the [BatchName] text box the default focus when the form opens.
Have the On Current event of the form give the [BatchName] text box the focus for new records (IsNull(Me.BatchID) = True).
Make the form dirty when the [BatchName] text box loses focus.
Option Compare Database
Option Explicit
Dim DeletePending As Boolean
Private Sub Form_Load()
DeletePending = False
Me.ItemSubForm.Enabled = False ' Disable Subform by default
End Sub
Private Sub Form_Current()
If IsNull(Me.BatchID) Then
Me.BatchName.SetFocus
' Disable Subform if BatchID is NULL
Me.ItemSubForm.Enabled = False
Else
' Enable SubForm Otherwise
Me.ItemSubForm.Enabled = False
End If
End Sub
Private Sub Form_BeforeDelConfirm(Cancel As Integer, Response As Integer)
DeletePending = True
End Sub
Private Sub Form_AfterDelConfirm(Status As Integer)
DeletePending = False
End Sub
Private Sub BatchName_LostFocus()
If IsNull(Me.BatchID) And Not DeletePending Then
Me.Dirty = True
End If
End Sub
When the user clicks on the subform (and off the [BatchName] text box) they make the form dirty and BatchID gets a value. Then they can paste and they don't get the "You must enter a value..." message for [BatchID]. If they haven't entered a [BatchName] value they now get prompted for it (because it is now Required), but at least they can gracefully recover from that.
Updated 2013-11-09 18:40 UTC:
In playing around with this a bit more I discovered two quirks:
If you deleted the last parent record Access would do it and then immediately create another new one in its place.
If you navigated into the "new" parent record and then immediately backed out (e.g., via the PageDown and PageUp keys) Access would create a new record and then leave you on that record with the form dirty. Hitting Esc and then moving off the "new" record worked, and it didn't cause any errors, but it certainly could be confusing to the user.
I have updated the answer to try and address these issues. I added a bit more VBA code to track "DeletePending" status. I also added the requirement that [BatchName] be set to Required = Yes in the [Batches] table. The latter makes it slightly more intuitive (albeit slightly more annoying) if the user move into the "new" record and then immediately moves back out again.
I struggled with this for a long until I finally understood what is happening to cause this error. It would take an article of considerable length to go into the details rather than a blog response to explain it all. If anyone is interested they can leave a contact method and I will contact them and explain it in detail.
However, for those who want to figure this out, I can save you a lot of time by giving you the idea behind the issue:
When you are performing a data transaction in a bound sub-form, you cannot reference other objects. The internal code of the transaction process does not allow this. For example, if you have code in the Form_BeforeUpdate event that attempts to open another form while in the midst of a sub-form data transaction, you will get error 3246. You can have code that creates variables, set values, references the sub-form controls, etc. but you cannot go out to another object.
This makes sense if you think about it. Who knows what the user or code might do once it gets itself into another form or object. It may never come back or get involved in other errors that leave the transaction hanging. That's why the transaction must complete first.
This means that you must trap and dismiss error 2115 that is caused when a user tries to click on an area outside the sub-form while in the midst of a transaction. This usually occurs during large copy and paste where the user becomes inpatient or starts to proceed to another record while still in the midst of the sub-form transaction.
I know that this is an old storry, I was also strugling with this.
My solution was to re-desing the process so the user closes the form receiving the data in order to save the records inserted. Is nor elegant but efficient and saved me from guessing each and every event which could occure.
to avoid the undesired internal transaction is enough to
code the sub-form Error event with:
Private Sub Form_Error(DataErr As Integer, Response As Integer)
Response = acDataErrContinue
End Sub
A general approach for intercepting sub-forms events is
' parent form code
' ---------------------------------------------------
Private WithEvents subFormObj As [Form_Sottomaschera items]
Private Sub Form_Open(Cancel As Integer)
Set subFormObj = Me.Sottomaschera_items.Form
End Subcode here
' asynchronous way
Private Sub subFormObj_isInserting()
Me.batchName = vbNullString ' this resolves the new ID issue
End Sub
' synchronous way
Public Sub subFormInserting()
Me.batchName = vbNullString
End Sub
' sub-form code
' ---------------------------------------------------
Public Event isInserting() ' for asynchronous way
Dim parentFormObj As Form_Maschera1 ' for synchronous way
Private Sub Form_BeforeInsert(Cancel As Integer)
parentFormObj.subFormInserting
RaiseEvent isInserting
' Cancel = True
End Sub
Private Sub Form_Error(DataErr As Integer, Response As Integer)
Response = acDataErrContinue
End Sub
Private Sub Form_Open(Cancel As Integer)
Set parentFormObj = Me.Parent
End Sub
where [Maschera1] is the main form and [Sottomaschera items] the sub-form.
Unfortunately it doesn't resolve the paste issue.
To definitely resolve issue you need to save parent record + a SetFocus trick, either synchronous or asynchronous:
Private Sub subFormObj_isInserting()
Me.batchName = vbNullString
DoCmd.RunCommand acCmdSaveRecord
' DoEvents
Me.batchName.SetFocus
End Sub
Public Sub subFormInserting()
Me.batchName = vbNullString
DoCmd.RunCommand acCmdSaveRecord
' DoEvents
Me.batchName.SetFocus
End Sub
I don't understand what exactly do you want to achive, so this answer may be inadequate.
You can
set your subform property .Visible = False when Me.NewRecord = True to prevent entering data into it
force saving record of the main form to the table after adding Batch name by setting .Dirty = False in After Update event triggered by pressing Enter. It allows also to avoid not saving records of a subsform to a table after adding few records to a main form in some databases, at least with dynamical subform .Recordsource.
set your subform property .Visible = True
The code below works for Form View and perhaps should be extended (develop) somehow for other Views.
Set .Tag of the subform Child and all other controls you want to hide / show to "a".
Private Sub Form_Current()
If Me.CurrentView = 1 Then
If Me.NewRecord = True Then
ShowControls False
ElseIf Me![Items subform Child].Visible = False Then
ShowControls True
End If
End If
End Sub
Private Sub BatchName_Text_AfterUpdate()
Dim NewRecordx As Boolean
If Me![Items subform Child].Visible = False And Me.CurrentView = 1 Then ShowControls True
NewRecordx = Me.NewRecord
If Me.Dirty Then Me.Dirty = False 'save the new record to the table
If Me.CurrentView = 1 And NewRecordx Then Me![Items subform Child].Form.Requery
End Sub
Private Sub ShowControls(bVisible As Boolean)
Dim ctl As Control
For Each ctl In Me.Controls
If ctl.Tag = "a" Then ctl.Visible = bVisible
Next ctl
End Sub
I reported this as a bug through Microsoft Premier Support a number of years ago with a concise standalone repro case. It appears this was finally resolved in October 2021 with KB5001978.

MS Access - opening a form with a new record and inserting a value from a previous form

I have a form in an MS Access database which lists Orders with an Order Number with one order per page. At the bottom of the form there is a button which opens another form, to add an item for the order.
I am trying to use vb in MS Access to take the order number and automatically put it in a field in the details form for the new item. I have tried different ways but using OpenArgs seems to be recommended. But the detail form won't open and I get run-time errors.
Here are the details of the problem - advice will be much appreciated:
The forms and field concerned are:
Form with orders is frmPedidoAvifiFind
Form with order-lines for one order is frmPedidoAvifi-dtlAdd (a separate form for adding details but not for viewing existing ones).
Field on both forms for Order Number is PedidoAvifiNo. This is a numeric field in both tables which are linked by a one-to-many relation via this field.
Main form: Button bring up detail form, code as follows:
Code on main form button:
Sub AddDetails_Click()
Dim strDocName As String
strDocName = "frmPedidoAvifi-dtlAdd"
' Open frmPedidoAvifi-dtl form in data entry mode and store PedidoAvifiNo in the form's OpenArgs property.
DoCmd.OpenForm strDocName, , , , acFormAdd, , [frmPedidoAvifiFind]![PedidoAvifiNo]
End Sub
Detail form: On Open property
Private Sub Form_Open()
If Me.OpenArgs <> vbNullString Then
Me.PedidoAvifiNo = Me.OpenArgs
End If
End Sub
Test 1: select an order number on main form so that record shows.
Press button to add orderline. - run-time error '2465' can't find the field "|" referred to. Debug highlights the DoCmd line.
Test 2:
Change openform line to:
DoCmd.OpenForm strDocName, , , , acFormAdd, , Me.PedidoAvifiNo
result: - run-time error 2501 the openForm action was canceled.
Thank you,
Mike Gunner
Reus, Spain
The error you are getting means that you have misspelled the name of the control PedidoAvifiNo.
When you type Me. intellisense will give you a list of available fields, see what you have that is similar to PedidoAvifiNo, or check the properties. It can be very easy to switch one letter and not notice.
As for the second part, you should use the Load event, rather than the Open event on frmPedidoAvifi-dtlAdd, because the controls are not yet available in the Open event.

How do I chain forms in Access? (pass values between them)

I'm using Access 2007 and have a data model like this...
Passenger - Bookings - Destinations
So 1 Passenger can have Many Bookings, each for 1 Destinations.
My problem...
I can create a form to allow the entry of Passenger details,
but I then want to add a NEXT button to take me to a form to enter the details of the Booking (i.e. just a simple drop list of the Destinations).
I've added the NEXT button and it has the events of
RunCommand SaveRecord
OpenForm Destination_form
BUT, I cant work out how to pass accross to the new form the primary key of the passenger that was just entered (PassengerID).
I'd really like to have just one form, and that allow the entry of the Passenger details and the selection of a Destination, that then creates the entries in the 2 Tables (Passenger & Bookings), but I can't get that to work either.
Can anyone help me out please?
Thanks
Jeff Porter
Actually the best suggestion I can give here is to not actually pass parameters. Simple in your form's on-open event, or even better is to use the later on-load event is to simply to pick up a reference in your code to the PREVIOUS calling form. The beauty of this approach is that if overtime you go from one parameter to 10 parameters then you don't have to modify the parameter parsing code, and you don't even have to modify the calling code. In fact there's no code to modify AT ALL if you decide to examine previous values from the calling form.
So, keep in mind using open args is only a one way affair. You can not use it to return values to the calling form. Furthermore all of the open args params will have to be strings. So, you lose any ability of dating typing such as real integers or even date and time formatting which can be quite problematic to parse out. And as you can see the example here the code to parse out strings can get a little bit messy anyway.
The simple solution is that in each form where you want values from the PREVIOUS from, simply declare a module level variable as follows
Dim frmPrevous as form.
Then in your forms on load an event, simply pick up the name of the previous form as follows:
Set frmPrevious = screen.ActiveForm
That is it. We are done!
We only written one line of code here. (ok two if you include the declaration statement). At this point on words ANY place in your form's current code you can reference the events properties and any field or value in the previous form by doing the following
Msgbox "PK id of previous form = " & frmPrevious.ID
And let's say for some reason that you want the previous form to re-load records as in a continues form. Then we can go:
frmPrevious.Requery
Or, force a record save:
frmPrevious.Dirty = false
So, the above becomes almost as natural and handy as using "ME" in your current code. I find this so simple and easy I think this should have been part of access in the first place.
And as mentioned the uses are endless, I can inspect ANY column or value from the calling form. You can even declare variables and functions as public, and then they can be used.
And, note that this works BOTH WAYS. I can stuff and change values in the calling form. So I can update or change the value of any value/column/control from the calling form.
And there is absolutely no parsing required. Furthermore the above code step even works if the same existing form is called by different forms. In all cases the forms calling ID can be picked up without modifying your code.
And even in the case of that I have many different forms launching and calling this particular form, you can still pull out the ID column. And, in the case that columns could be different from different forms, you can simply declare public variables or public functions in the calling form of the SAME name. So, if I wanted to call a form that needs the DateCreate, but each form did NOT have a consistent column name of DateCreate (maybe invoiceDateCreate and inventory Date Create), then you simply declare a public function in the calling forms with a constant name. We then can go:
Msgbox "Date created of calling form record = " & frmPrevious.DateCreated
So, Date created can be a public variable or public function in the previous form that could be any conceivable column from the database.
So don't pass values between forms, simply pass a reference to the calling form, not only is this spectacularly more flexible than the other methods showing here, it's also an object oriented approach in which you're not limited to passing values.
You can ALSO return values in the calling form by simply setting the value of any control you want (frmPrevous.SomeContorlName).
And as mentioned, you not limited to just passing values, but have complete use of code, properties such as dirty and any other thing that exists in the calling form.
I have as a standard coding practice adopted the above for almost every form. I simply declare and set up the form previous reference. This results in a handy previous form reference as useful as "ME" reference when writing code.
With this coding standard I can also cut and paste the code between different forms with different names and as a general rule my code will continue to run without modification.
And as another example all of my forms have a public function called MyDelete which of course deletes the record in the form, therefore if I want to delete the record in the previous calling form for some reason, then it's a simple matter to do the following
frmPrevious.MyDelete
So I can save data in the previous form. I can requery the previous form, I can run code in the previous form, I can return values to the previous form, I can examine values and ALL columns all for the price of just ONE measly line of code that sets a reference to the calling form.
I do this by defining properties in the form with Property Let and Property Get and passing values to those properties after opening the form, like this:
in the destination form:
Dim strCallingForm As String
Dim lngKey As Long
Public Property Get callingform() As String
callingform = strCallingForm
End Property
Public Property Let callingform(NewValue As String)
strCallingForm = NewValue
End Property
Public Property Let PrimaryKey(NewValue As Long)
lngKey = NewValue
End Property
in the calling form:
Sub btnGo_Click()
Const cform As String = "frmDestinationForm"
DoCmd.OpenForm cform
Forms(cform).callingform = Me.Name
Forms(cform).PrimaryKey = Me.PrimaryKey
Me.Visible = False
End Sub
(end)
I would use the openargs method of the form. That way you can pass one piece of data to the new form from any other form. You can also expand of this by sending a delimited string of arguments and then splitting them out. For example I have a form for editing agent activity that is passed the date, the agents name, agents ID and team in the open args
DoCmd.OpenForm "frmEdit_agent_activity", , , , , acDialog, Item & "|" & Me.txtDate & "|" & Item.ListSubItems(1) & "|" & Item.ListSubItems(2)
The form then uses this to pre populate the controls
Private Sub Form_Load()
If IsMissing(Me.OpenArgs) = True Or IsNull(Me.OpenArgs) = True Then
'no args, exit the form
DoCmd.Close acForm, "frmEdit_agent_activity", acSaveNo
Else
'this form has 4 open args
'1 Staff ID
'2 Date
'3 Team_ID
'4 Staff Name
Me.txtStaff_ID = GetDelimitedField(1, Me.OpenArgs, "|")
Me.txtDate = GetDelimitedField(2, Me.OpenArgs, "|")
Me.txtTeam_ID = GetDelimitedField(3, Me.OpenArgs, "|")
Me.txtStaff_name = GetDelimitedField(4, Me.OpenArgs, "|")
End If
End Sub
Ohh and here is the GetDelimitedField function
Function GetDelimitedField(FieldNum As Integer, DelimitedString As String, Delimiter As String) As String
Dim NewPos As Integer
Dim FieldCounter As Integer
Dim FieldData As String
Dim RightLength As Integer
Dim NextDelimiter As Integer
If (DelimitedString = "") Or (Delimiter = "") Or (FieldNum = 0) Then
GetDelimitedField = ""
Exit Function
End If
NewPos = 1
FieldCounter = 1
While (FieldCounter < FieldNum) And (NewPos <> 0)
NewPos = InStr(NewPos, DelimitedString, Delimiter, vbTextCompare)
If NewPos <> 0 Then
FieldCounter = FieldCounter + 1
NewPos = NewPos + 1
End If
Wend
RightLength = Len(DelimitedString) - NewPos + 1
FieldData = Right$(DelimitedString, RightLength)
NextDelimiter = InStr(1, FieldData, Delimiter, vbTextCompare)
If NextDelimiter <> 0 Then
FieldData = Left$(FieldData, NextDelimiter - 1)
End If
GetDelimitedField = FieldData
End Function
Have you considered subforms? There are ideal for one to many relationships. The Link Child Field(s) will be automatically completed from the Link Master Field(s).
If you need an example of subforms in actions, the Northwind database ships with all versions of Access, or you can download which ever is relevant.
2007
http://office.microsoft.com/en-us/templates/TC012289971033.aspx?CategoryID=CT102115771033
2000
http://www.microsoft.com/downloads/details.aspx?familyid=c6661372-8dbe-422b-8676-c632d66c529c&displaylang=en
You can use OpenArgs.
But I would also suggest that you also consider using a tab control.
That allows you to have different sets of controls on the same "screen real estate", using one single recordset, or using sub forms to show child recordsets.
In that case, you could use the "Next" button to just switch to the next page of your tab control.