BeforeUpdate event of a linked subform control firing multiple times - ms-access

Good afternoon,
I have to maintain an Access form which contain a linked subform (using Master and Child fields).
Basically, in the main form, the user choose a value in a combobox and the subform is automatically updated.
My issue is that I have a BeforeUpdate event on one field of my subform which is preventing to update the field (Cancel=true) when it does not meet the criteria. The alert msgbox should appear once if there is any error in the field but the BeforeUpdate event is always fired 3 times for unknown reason.
I have created a simple accdb file which reproduce my issue. It is located here: https://www.hightail.com/download/bXBhU2V0Q1JxRTFsQXNUQw.
Open the Form1, choose a value in the combobox and then try to update one of the letters in the subform by X and you will get the msgbox appearing multiple times.
Thanks in advance for your help on this issue as it's driving me crazy.
Sylvain

This happens because you do not change the value of Text0 back to what it was.
Try this
Private Sub Text0_BeforeUpdate(Cancel As Integer)
If Me.Text0 = "X" Then
MsgBox "Wrong value"
Cancel = True
Me.Text0.Undo
End If
End Sub

OK finally found the solution.
It was related to the text box used for storing the Link master fields used by my subform. This control contains the value of my combobox in order to filter out my subform.
The control source definition of this link master field used to be:
=myCombobox.column(0)
By replacing the definition by just:
=myCombobox
The beforeUpdate event in my linked subform is only triggered once and behaves as expected;
Private Sub Text0_BeforeUpdate(Cancel As Integer)
If Me.Text0 = "X" Then
MsgBox "Wrong Value"
Cancel = True
End If
End Sub
I can not explained the magic of why it is behaving that way but this is how I made it worked.
Sylvain

Related

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.

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?

Make fields visible in MS Access form as certain fields are completed

I am building a form In MS Access for users to input data but there are too many possible fields. Most of the time only about half the fields will be used.
I thus would like to have certain fields appear only depending on what the user inputted on a prior given field.
Ex: user enters project number, title, then he checks a "yes/no" engineering. since he checked it this means engineering is impacted so a dozen fields that the user will have to fill out appear.
Is this possible:
1)without VBA
2)with VBA
Probably not possible without VBA.
With VBA for example:
Ensure your form is in Design view
Right click on your Combo Box, Build Event, Code Builder
This opens the code behind your form. It drops you into the default code for the BeforeUpdate event. We want the Change event instead, so at the top right change the drop down from BeforeUpdate to Change. This will give you a bit of code like this:
Private Sub Field1_Change()
End Sub
Inside here, you want to check the value of the combo box and hide fields as required:
Assuming the name of your combo box is Field1 (yours of course will be different), you add some code so it looks like this to hide Field2:
Private Sub Field1_Change()
If Field1.Value = "Yes" Then
Me.Field2.Visible = False
End If
End Sub
Note you need to know the names of all your fields - this is in the Other tab, Name field in the properties box (F4). You should give all of your fields sensible names so you can understand what is going on in the code.
For a check box, follow exactly the same procedure, but you probably need to use the Click event. Just experiment.
Sample check box code:
Private Sub Check5_Click()
' Note: vbTrue = -1
If Me.Check5 = vbTrue Then
MsgBox ("Ticked")
Else
MsgBox ("Not Ticked")
End If
End Sub
I have a form that will show certain fields after a list box value is selected. I use the AfterUpdate function. It has worked so far. My code is below. ProjectName and ProjectNumber are fields you only want displayed if Engineering is selected. OtherName and OtherNumber are fields you only want to show if it is a "NotEngineering" project.
Insert this code by clicking on the Field that selects the project type, go to the Event tab on the property sheet, and click "After Update" and choose code builder and paste in VBA.
Private Sub ProjectType_AfterUpdate()
If ProjectType.Value = "Engineering" Then
Me.ProjectName.Visible = True
Me.ProjectNumber.Visible = True
Else
Me.ProjectName.Visible = False
Me.ProjectNumber.Visible = False
End If
If ProjectType.Value = "NotEngineering" Then
Me.OtherName.Visible = True
Me.OtherNumber.Visible = True
Else
Me.OtherName.Visible = False
Me.OtherNumber.Visible = False
End If
End Sub
There is a way to do not-quite-this without VBA. I'd definitely recommend VBA though, because you can do a lot more with it.
Rather than hiding, try disabling the unnecessary fields with conditional formatting by expression.
right-click on the control you want disabled.
go down and click on 'Conditional Formatting'
Add a new rule
Select 'Expression is'
example:
[fld1]="yes"
hit the disabled box
click ok
click ok
now the control you've selected will disable if field 1 has "yes" selected.
I have the same problem and I did the following:
Private Sub Field1_Click()
If Field1 = "Yes" Then
Me.Field2.Visible = True
Else: Me.Field2.Visible = False
End If
End Sub
but now I have other problem, when I change record, the field that I choosen to be visible in the last record is now visible on the current record, although I have not choosen any option.
Thank you,

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.

MS Access - Write to Table Immediately After Changing Value in Form

Is there a way to make Access write values immediately to a table when changing values in a form? Right now, I have to change a value in the form, and then click off the field onto the subform before the value is written to the corresponding table.
Sounds like no big deal, but certain controls require current data to give valid options. If the user doesn't know enough to click into the subform then the data they view could be out-of-date.
The behavior in your requirement are asking for is by default how access functions. In most typical scenarios your master form is going to be the parent table, and as a result before you can add child records to such a relational setup, then the parent record would have to be written and saved to disk.
And the reverse while not a requirement is also the default operation for access.
In other words it's not clear why when the focus moves from your parent form to a sub form, that the parent form's record is not been written to the table.
So something in your setup is incorrect here, or you'll have to expand on the behavior you're witnessing.
By default changing the focus from the main form to a sub form will cause the main form record to be written to the table, and changing the focus from a sub form tool main form as a general rule should also cause a sub form record to be written to the table.
If you are matching a combo box, you can use that as your link master field, that is, the name of the control itself. Your subform should update as soon as you select a ticket with the combo.
Link Master Fields: Combo1
Link Child Fields: Field1
You can force the save of the record when a user moves off a field in the Lost_Focus event.
Private Sub MyField_LostFocus()
DoCmd.DoMenuItem acFormBar, acRecordsMenu, acSaveRecord, , acMenuVer70
End Sub
I was also facing the same problem, but in another scenario. I am not having sub-form, but a datasheet view of the same table on a split screen, which was not updating immediately after save button. On pressing DUPLICATE button, instead of getting duplicate data of latest record, I was getting pointer shifted to first record, and then the data from the first record was getting duplicated.
By inserting
DoCmd.DoMenuItem acFormBar, acRecordsMenu, acSaveRecord, , acMenuVer70
before data duplicating code, I am now getting the data updated immediately with record pointer stick to the current record, and the same last record gets duplicated as required.
Sharing the experience.
Thanks ChrisPadgham
Just use this in your form model:
Private Sub {your textbox name her}_AfterUpdate()
Me.Refresh
End Sub
Essentially: in the AfterUpdate event of every desired control execute a acCmdSaveRecord command.
So:
In the design view of the form click on your control.
Menu > Design > Property Sheet (to bring up the property sheet for your control)
Property Sheet > Event [tab] > After Update. Choose "[Event Procedure]". Click on the elipsis "..." to take you through to the Code-Behind-Form VBA dev environment.
Supply your code as follows ...
Private Sub [Control Name]_AfterUpdate()
DoCmd.RunCommand acCmdSaveRecord
End Sub
E.g.
Private Sub cboFinancialYear_AfterUpdate()
DoCmd.RunCommand acCmdSaveRecord
End Sub
Repeat the above for every relevant control on the form.
The acCmdSaveRecord avoids an unecessary visual refresh of the current form, as when using Me.Refresh (which #medjahed mohamed has suggested).