Passing variables from child to master form - ms-access

I have made an Access 2007 db. I will be writing some basic vba for the appropriate event of a form so that a modal form is displayed when the original form is opened. This isn't difficult, but that form will have a drop down box of IDs from a particular table. The user will select an ID, but I want the selected ID from this form to go back to the parent form. E.g.:
Car form opened
Event is fired to open a modal form
IDs for a FK to cars has to be selected from a combobox (eg ID of driver - for simplicity, lets assume one car can have many drivers but not vice versa so 1:n only)
There is a button to confirm the selection. On clicking this button, the form closes, and the selected Driver ID is automatically inserted into a DriverID textbox on the car form (probably will be read only).
The last step I am not sure about. How can that be done in VBA?

This is one of the worst Access threads I've ever seen on StackOverflow.com, because every answer is wrong in at least some crucial aspect -- not a single one of them would actually run if you pasted them into VBA in an Access database.
The key principles here:
Open the dialog form modally (with the acDialog arguments)
Don't close it when the value has been confirmed, but instead set it's .Visible property to False.
Then in the calling form, read the value out of the hidden form, and then close it.
Something like this:
DoCmd.OpenForm "dlgPickDriver", , , , , acDialog
If IsLoaded("dlgPickDriver") Then
Me!DriverID = Forms!dlgPickDriver!cmbDriver
DoCmd.Close acForm, "dlgPickDriver"
End If
[IsLoaded is a Microsoft-provided function; I posted it here on StackOverflow recently, but would assume that the vast majority of Access developers writing VBA would have been using it forever]
I would recommend against having code that runs in the dialog form poke the data into the parent form because it's then impossible to use the dialog form in multiple locations. Having the dialog form know as little as possible about the context in which it's called is good programming practice. On the other hand, it's true the calling form needs to know the control name it's getting the value from, but that's a more sensible context than the other way around, in my opinion.

To gain 'access' (get it?!) to controls on separate running forms you can use the following:
Forms("AnOpenFormName").Controls("ControlName") = value
Edit: I used the wrong brackets AMG!

You could write a function that will display the form modally and then return the user selected value as a return value of it (similar to msgbox/inputbox).
EDIT: You could write a function such as following.
Function GetUserSelectedCarID() as string
dim myPopupForm as Form
set myPopupForm = new Form
myPopupForm.Show vbModal
GetUserSelectedCarID = myPopupForm.UserSelectedCar
End Function
UserSelectedCar is a property that stored the user selection on the popup form.
EDIT2: You could also add a property on popup form to see if the user clicked OK or Cancel.
It will return blank from the above function, if user clicked Cancel.

All of the above answers are good. I especially like the third way of wrapping this kind of functionlity into reusable function calls. The only thing I would add is do not CLOSE the modal form...otherwise you cannot get the selected value. Instead do me.visible=false.
So you do somethiing like this.
Public Function GetCArKey as Integer
dim intReturn as integer
docmd.openform "MyModalForm",,,,,,acdialog
'(the click event of the okay button of this form does me.visible=false...clicking Cancel will close the form.)
if isloaded("MyModalForm") then
intReturn=Forms("MyModalForm").Controls("ControlName").Value
end if
GetCarKey=intReturn
End Function

It would seem much easier to me to create a global variable in a module (eg Public gi_Value as Integer) and then set this in the child form. The value can then be read anywhere.

Related

MS Access: Report opened with acDialog doesn't return control when made invisible

If you open a Form with acDialog (using DoCmd.OpenForm), execution of the calling routine will halt execution until the form is either closed or made invisible. Using the invisible method is useful to let the calling routine harvest data off the form.
I had hoped that Reports would work the same way. That is, opening a report using acDialog with DoCmd.OpenReport. Unfortunately, when the report is made invisible, it disappears but does not return control to the calling routine.
Is this a bug in Access? Does anyone know of a way to make this work? My alternatives to pass data back to the calling routine are all ugly. For example, use global variables or have the report insert data back to its caller before closing (this requires having the report know its caller).
As pointed out, reports generally don’t do much interaction, and they don’t generally need to return data (what data going to change in a report?).
However, you can adopt a relative “clean” means to return values to the calling form, and this works well for forms, and would also work reasonable well for reports (but as noted, it seems rather rare that a report would return data since they tend to not change anything).
So what you do is in the forms on-load (or report) is grab a reference to the calling form like this:
Option Compare Database
Option Explicit
Public frmPrevious As Form
Private Sub Command67_Click()
' this is our report close button code
' we can set, call, or do ANYTHING to the calling
' form - including setting controls or running code
frmPrevious!Text18 = Now()
frmPrevious.Refresh
Call frmPrevious.MyCodeToRun
DoCmd.Close
End Sub
Private Sub Report_Open(Cancel As Integer)
Set frmPrevious = Screen.ActiveForm
End Sub
So the above is the code in the report. Note that we can set any value in the calling form, set any control, run any code, or even “refresh” the calling form.
So you are free to grab values from the previous form, you are free to return values, set controls, or run code, or use ANY property or method in that calling form.
In fact, as a coding standard, I very often create an frmPrevious value for “most” forms. And thus now in my code for a form (or report), you can always referance, or do anything to the previous calling form.
Eg:
frmPrevous.Requery ‘ requery data in calling form
‘ force write of data in previous form
if frmPrevious.Dirty = true then
frmPrevious.Dirty = false
End if
‘ get name of form that called the previous form.
frmPrevious.frmPrevious.Name
So this approach provides a nice clean way to do ANYTHING to the calling form, and the name of the calling form is not hard coded.
In addition to the above, this also means you do NOT have to launch the form as “dialog” which of course not only stops calling code, but prevents use of ribbon or custom menus etc.
So what this means you have to place the “additional” code you wanted to run in the calling form into a separate sub, and call that code from the report on the button you press to either close, or do whatever action is that you want.

How to partially clear userform in ms access

I have a bound userform with lot of fields in it, I use submit button to let user make entries in shared form. But whenever user clicks on submit button it requery whole form. However, there are around 4-5 fields which should not be cleared on submit button, they should retain the value and rest of the fields should get cleared on every submit button click.
Can this be done in access and how ? below is the simple code is use to submit and requery the record.
Private Sub SaveBtn_Click()
RunCommand acCmdSaveRecord
Me.Requery
Me.DataForm.Requery
End Sub
When you have a "Bound Form" in Access, this means that the Access Form is tied to the underlying data source, which is specified by, and stored in, the "Record Source" property for the form. In the "SaveBtn_Click()" method you provided above, this code does the following things:
RunCommand acCmdSaveRecord - Saves the current record in the form.
Me.Requery - Requeries the entire form.
Me.DataForm.Requery - Requeries the subform named "DataForm" on your main form.
So when you "Requery" the entire form (depending on how the "Record Source" and other form property settings are setup), the requery operation in some cases moves the cursor to the new record in the data source (...its the default settings for the drag and drop form designer tools in later versions of Access), and I suspect that is why you see the form being "cleared" when you call "Requery." A couple of items I would point out, just for clarity:
Note 1: "Requery" actually saves the record, so explicitly calling "RunCommand acCmdSaveRecord" should not be necessary if you're going to call "Requery" too.
Note 2: Depending on how the Record Source (and other) form properties are set, Requery in some cases just saves and refreshes the currently selected record, but it doesn't sound like that is how your form is working (based on what you said above), so I'm assuming that is not the case in your form.
Note 3: The subform will also be requeried when you call "Requery" for the main form, so that line of code may also be redundant here. The reason to call "Me.DataForm.Requery" is if you only want to requery the subform and NOT requery the entire main form.
Note 4: The "DataForm" subform (on your main form) will have a separate data source for it's own "Record Source" property, which is separate from the parent (main) form, so it is important to be aware of that fact, depending on which field values you want to save.
So, that said, there are a couple of options I might suggest, depending on exactly how you want your form to behave:
If you want to keep some of the field values and use those for the NEW RECORD when you hit the requery button, you could always save them off in a variable and then set those controls again after requerying from your variables. Just create a seperate variable for each value you want to save, copy the TextBox control values into each of variables respectively, call requery on the form, and then copy those values back into your TextBox controls after you requery. That code would be something like the following (depending on the exact names of your TextBox controls, I used the fictitious name "txtMyTextBox" for this example):
Private Sub SaveBtn_Click()
Dim vValue1 as Variant
vValue1 = txtMyTextBox
Me.Requery
txtMyTextBox = vValue1
End Sub
Or, if you're just trying to create "Default Values" for certain controls, TextBox controls have a "DefaultValue" property you can set to any value you would like to use for the default. Those can be set from the Property Sheet on the right side of the Access window when the form is opened in Design mode or Layout mode and the TextBox control is selected. (Press F4 to open the Property Sheet if it's not already open).
But, if you really want to Requery and then go back to the same record you were previously on, you could try the following code:
Private Sub SaveBtn_Click()
Dim iRecord as Integer
iRecord = Me.CurrentRecord
Me.Requery
DoCmd.GoToRecord , , acGoTo, iRecord
End Sub
Anyway, I hope this all makes sense and you find it helpful. Please let me know if you have any further questions about this answer.

MS Access - Data Entered in a Form Automatically saves when i close the form

I've been tasked with making updates to an MS Access Database and its forms.
Each form seems to be linked to a query. If I enter data into a text box on the form and then close the form without pressing the Save Record button a new record is still added to the Database which makes no sense to.
Any insight would be great, I'm a programmer but have little experience working with Access Forms and Databases.
Thanks.
Microsoft Access binds forms to data by default, and will automatically save data as soon as you either move between records or close the form you're working on. For the average user, this is actually a good thing because it makes it very difficult to lose data, even if you accidentally close the form after making an edit.
If this functionality isn't what you're looking for, I'd suggest removing the binding from the form, that is, set the Record Source property to blank, then manipulate all the data in code using unbound controls. It's a lot more fiddly, but it gives you a lot more control.
The other option would be to use the form's BeforeUpdate event to ask the user if they want to save their changes before allowing them to go through.
If your main concern is accidentally adding new records, set the Allow Additions property to false, and create new records programmatically.
A sample of a BeforeUpdate event procedure (the same code would work for the BeforeInsert event):
Private Sub Form_BeforeUpdate(Cancel As Integer)
Dim intAns As Integer
intAns = MsgBox("Are you sure you want to save this record?", vbQuestion + vbYesNo, "Save Record")
If intAns = vbNo Then Cancel = 1
End Sub
If you were to use the above method, and the user clicked "No", they would have to then click the ESC key to undo the changes they had entered. Otherwise it would keep showing the dialogue when they moved off the record.

How to open another form within a vba function and return to the function

I have a MS Access form with a button that calls a function that starts by checking a date and asks the user if he needs to manually change the date. If this is the case, another form is opened where the date is entered and validated. Is there a possible way to load the selected date into a variable and return to the first form into the function that is running to use it further down the procedure?
The following way is working for me although I assume there is a better one.
First of all I inserted a module and declared a global variable:
Global globalText As String
Then you need to make sure the other form (here frmEntry) is called "modal", so that your code will wait until the form is closed again. You can achieve this by:
DoCmd.OpenForm "frmEntry", WindowMode:=acDialog
In frmEntry you need to write whatever value was selected then to the global variable and then close the form, which would look something like this:
Private Sub btnClose_Click()
globalText = Me.txtEntry.Value
DoCmd.Close acForm, "frmEntry"
End Sub
And then it is basically done and your code in your basic form will go on running and you can use the value from globalText anywhere or can write it to some hidden textBox to use it later in other functions.

Can't set field value in Access form with new record

When I open my access form ActivityTracker to a new record, I want it to auto-populate the field *start_time* with the current time now().
Private Sub Form_Open(Cancel As Integer)
If IsNull(Form_ActivityEntry.Start_time) And IsNull(Form_ActivityEntry.id) Then
Form_ActivityEntry.Start_time = Now()
End If
End Sub
This throws an error "You can't assign a value to this object" and stops execution.
I can fix the error by going explicitly to a new record
Private Sub Form_Open(Cancel As Integer)
If IsNull(Form_ActivityEntry.Start_time) And IsNull(Form_ActivityEntry.id) Then
DoCmd.RunCommand acCmdRecordsGoToNew
Form_ActivityEntry.Start_time = Now()
End If
End Sub
or by
Private Sub Form_Open(Cancel As Integer)
If IsNull(Form_ActivityEntry.Start_time) And IsNull(Form_ActivityEntry.id) Then
Me.Recordset.AddNew
Form_ActivityEntry.Start_time = Now()
End If
End Sub
but either of these causes a popup warning, "You can't go to the specified record."
I've tried to suppress the warning with this
DoCmd.SetWarnings False
DoCmd.RunCommand acCmdRecordsGoToNew
DoCmd.SetWarnings True
or by setting up error handling,
On Error GoTo Err_SomeName
but I still get the warning.
AllowAdditions is set to True. Recordset type is Dynaset.
Otherwise, everything works fine with this form.
What am I doing wrong? Is there an event called "opening new record" as opposed to "open form"?
Thanks for your help!
The problem here is still no one correctly answered why the code as posted does not work.
The REASON why is that you are using the on-open event.
Unlike .net and most systems access has a GREAT design in which you have two events occur when you an open form event (which can be canceled) and an on-load event.
This great design of Access means that code to test conditions and prevent the form load can be placed in the on-open event. If you look CLOSE you will see that the on-open even has a cancel. If you set cancel = true, then the FORM WILL NOT LOAD, AND WILL NOT DISPLAY.
So, you can test for no data, or for user rights or whatever, and huts cancel the form loading. If you can cancel the form loading, then it would make little sense to allow modifying the value of bound controls – as such all bound controls are READ ONLY.
You are NOT ALLOWED to change the values of BOUND controls in the on-open event. This is TOO soon and is by DESIGN and intention of the product.
So, testing of conditions to prevent the form load goes in on-open.
This thus suggests that setup of variables,, setup of controls, setting values of controls and your basic form startup code belongs in the ON-LOAD event. On-open is too soon.
And of Couse if your code is going to cancel a form load, then it is logical that all of your forms startup and setup code SHOULD NOT and does not need to run.
In summary
On-open event:
Can set cancel = true
Code goes here to test if you wish to prevent the form from loading and being seen by the user.
On-LOAD event:
All your startup code, setting of variables, setting of controls etc. is now recommended and possible.
And this simple design in Access ALSO means that as a developer you now know where to look for the code that will prevent and cancel the form load. Of course without this basic understanding of the difference and WHY two events exist in Access then the confusing of the poster and answers given becomes obvious.
I suggest you set the default value of the control to Now() or Date(). The default value only applies to new records and once you complete any other field, the value will be assigned.
In this particular case, there is even an argument for setting the default value of the field in the table.
Please do not use Set Warnings: What's the difference between DoCmd.SetWarnings and CurrentDB.Execute
I am not a fan of moving to a new record on a form. There are too many data holes that can occur and you run into issues like yours.
I reccomend a blank, unbound form, with a textbox, calander conrol, number up down... etc for each of the fields that you want to add. This way you can type check each of the fields or do other checks against what you want. Then, when the user is happy, then add the record with an insert query.
However, for the question that you asked. It looks like you are trying to assign a value to the bound field. Try assigning the value to the object the field is bound to.