Pat, I really appreciate your continued help.
You are over controlling this.
Seems like it.
The VAST MAJORITY of the validation code belongs in the BeforeUpdate event. The only code that would be in individual control BeforeUpdate events is code that is used to hide or enable controls based on the value of the current control. Notice a pattern here - Control's BeforeUpdate Event, Form's BeforeUpdate event. NO OTHER EVENTs should ever have validation code.
My validation function fFormIsValid is now called by BeforeUpdate.
The main record ALWAYS has to be saved before you can enter a subform record. Access handles this AUTOMATICALLY if only you would let it.
Please tell me how it happens automatically. Code to save the main record is behind the AddNewLine button. I commented it out, and it broke. The main record was dirty but it wasn't saved when DoCmd.OpenForm for the sub ran and when I tried to save the line I got a run time error 3101 saying there was no record in the main table.
Access is a Rapid Application Development Tool. It does things it's own way. You can alter this to a certain degree but you will tie yourself in knots as you seem to have done.
I sometimes put Save buttons on forms but they are mostly to make people comfortable. All they do is save the record. So whether they press my save button or not, the record gets saved when Access decides it must be saved and trust me, Access KNOWS when a record is dirty.
Yes. The Save and Undo buttons seemed like a good idea at the time but I've revised my thinking and in the spirit of embracing the way Access does things I've dropped them. I'm keeping the Edit button to toggle the .enabled status of the fields but I now have a Cancel button and a Close button. The Cancel button does me.undo and closes (but with a warning if the record is dirty). Close does what it says. Access can do the save management.
The thing you need to be careful with is making sure that YOUR code doesn't dirty a record. This seems to be the problem you are trying to fix. Your ChangeDate keeps getting updated and you don't think it should. It is getting changed by your own code in an event that shouldn't be changing it. So things like the ChangedBy and ChangedDT would go in the BeforeUpdate event.
I'd already fixed this. DateAdded and DateEdited are assigned only in BeforeUpdate of the main form.
You could put it in the Dirty event but I prefer to simply put all the code in a single event so I don't have to go looking around for it.
OnDirty only had code to enable or disable three buttons. Two of them have gone so I'm left with just the AddNewLine button, enabled when the main form is dirty.
For pop up forms that are acting like subforms, you NEVER dirty the record before the user does. Your code that sets the Foreign Key goes into the BeforeInsert event. You only need to set this value for new records. The FK already exists for existing records so you don't set the FK in the Dirty event or heaven forbid the form's Open or Current events.
OK, I had the code for the FK in BeforeUpdate with a test for Me.NewRecord. I've put it in BeforeInsert and dropped the if.. line.
I'm left with just a single line of code with DoCmd.RunCommand acCmdSaveRecord, the one behind the AddNewLine button. As it is my application will not run without it. I may as well leave it in but the purist in me says if there's a way to do without it I'd like to know.
Putting the validation code in BeforeUpdate seems to have put me back in the situation of being unable to cancel execution. Here's what I'm seeing:
User clicks Close on form with invalid data. It's dirty.
BeforeUpdate is called, which calls validation code. Validation sub pops up a MsgBox to notify and does a field.SetFocus.
BeforeUpdate is cancelled.
Record doesn't save but the form closes !
I found this of yours from 2002:
Both the BeforeUpdate and Unload events can be Cancelled but the Close cannot. So do your editing in the BeforeUpdate event and cancel the event with (Cancel = True) if you don't want the record to be saved. If you also want to prevent the form from being closed if the user was trying to save bad data, you'll need a global variable that you can check from the Unload event to use so you can set the Cancel parameter if necessary.
So I put a Form_Unload event in. Added a bAllowClose = false in BeforeUpdate.
In Form_Unload, I've got: If Not bAllowClose Then Cancel = True
Now I get:
Run-time error '2169' You can't save the record at this time.
DoCmd.Close acForm, Me.Name is highlighted.
So ... how do I get the cursor in the invalid field and stop the form from closing ? An error trap ? Ugly.