I have an identical AfterUpdate event for 11 fields in my form. Is there any way to put the AfterUpdate code in a function or module and then call the function or module in each AfterUpdate event?
If a certain field is changed on the main form, I prompt the user with a message box saying they need to add a note in the subform describing the change. If the user clicks OK on the message box, it brings the focus to the note subform. If the user clicks Cancel, the change is undone in the main form.
Below is the code that is repeated for 11 fields:
Private Sub ID_AfterUpdate()
On Error GoTo StandardErrorHandling
Dim NoteReq As Integer
NoteReq = MsgBox("A change has been made to a protected field! " & _
"Please provide a note with a brief description of the change." _
, vbOKCancel, "A Note is Required!")
If Form.Dirty = True Then
If NoteReq = vbCancel Then
Me.Undo
ElseIf NoteReq = vbOK Then
'You may have to set focus to the subform then the control
[Forms]![MainForm].[SubFormNotes].SetFocus
DoCmd.GoToRecord , , acNewRec 'Go to new note record, not existing one
End If
End If
ExitStandardErrorHandling:
Exit Sub
StandardErrorHandling:
MsgBox "The following error has occured:" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & vbCrLf & _
"Error Description: " & Err.Description & vbCrLf & vbCrLf _
, vbCritical, "An Error has Occurred!"
Resume ExitStandardErrorHandling
End Sub
I know a function returns a value so if I put the above code in a function, and called the function within the AfterUpdate event of each field, how would I code it so that it returns a value? Or is there another way to cut down on the redundant code? Thanks!
Functions don't seem to need to return a value in my experience. I've made functions to do exactly this sort of thing - highlight a textbox by changing the BackColor when selected, make several buttons do a set process to another control that shares a tag, swap values between Active and Previous controls etc.
You should be able to create a function directly from that coding simply by replacing the first line with something along the lines of Function UpdateNote() and the last line with End Function, and then do a find and replace "sub" with "function"
Then you can call that function simply by putting exactly =UpdateNote() (or whatever you call the funtion) in the After Update event property (directly in the property list, not vba or macro).
You can even apply it to all the different field's properties at once, by selecting all 11 of them and pasting that =UpdateNote() in the After Update event property.
It definitely works, and is pretty neat and clean in my opinion. If there's a reason it's not ideal, I'm not aware of it...
Related
I have an error log that logs in the access table whenever a runtime error occurs for a user in the error trapper, and a particular error seems to occur for 10 random users, every hour at least.
This error appears to occur completely at random, on a random module with the Set ActiveForm code, with random users at random intervals. As far as I can see, there is no pattern between the users.
2475 - "You entered an expression that requires a form to be the active window".
This appears to occur in any of the modules that contain any of the setting of a form. I am using the following lines:
Dim af as Object
Set af = Screen.ActiveForm
I have tried using alternatives, such as declaring it as Form, and also tried the below:
Dim sstatus as String
Dim ps as String
If DLookup("[TM_UserType]", "[SD_Teams]", "[TM_username]= '" & usernm & "'") = "adj" Then
sstatus = "adj"
Else
sstatus = "tm"
End If
ps = "frmProdSubmit_" & sstatus
Then referencing the form this way:
Forms(ps).cmbTeam.Value = ""
But this still causes the same issue, even removing the ActiveForm part.
The last thing to mention (as I believe they could be factors) is that the front end is accessed through a shortcut, which minimises the Access window. Not sure if this could be the culprit, or if the user clicking another application can remove the focus.
The back-end of the database is also accessed by up to around 700 users each day.
As it stands, the error trapper pops up with the message, but the front end continues working correctly. It's just an annoying issue to resolve, but am slowly running out of ideas now, and any help would be hugely appreciated!
Error 2475 is thrown when a non-form object is the active screen object such as a table datasheet. I've encountered this error in an application that uses multiple instances of a form and needs to track whether the multiple form module is active or one of the other application module functions in which case all instances of the multiple forms (popups) need to have .visible set to false. I use the Screen.ActiveForm.Name call in the Form_Deactivate event.
You can trap the error in the procedure's error handler and take action knowing the screen's active object is not a form.
Example:
Private Sub Form_Deactivate()
On Error GoTo errHandler
If Screen.ActiveForm.Name <> "AnApplicationForm" Then 'throws 2475 if not a form object
sstatus = "status message"
End If
ExitSub:
Exit Sub
errHandler:
If Err.Number = 2475 Then Resume Next 'screen active object is not a form i.e. datasheet
MsgBox "Error in " & Me.Name & ".Form_Deactivate: " & Err.Number & " - " & Err.Description
Resume ExitSub
End Sub 'Form_Deactivate
On my form, the first field has to be entered, so I have an On Exit command. Since it also checks to see if the value exists in my table, I want it to check right away - is it blank (thus require data to be put into field) OR does it already exist. My question has to do with the first part - is it blank.
On this form, I also have a Cancel button. When the user click Cancel, I want it to close the form without saving any of the data.
Problem - If the user doesn't enter anything in the first field and clicks Cancel, it's running the On Exit code requiring data to be entered into the first field - which they don't have to because they are cancelling. I'm getting a Run-time error '2585'.
Is there anyway to have the Cancel code stop the On Exit code from running? Any other ideas?
Here is my code for Cancel:
Private Sub CancelFormButton_Click()
Me.Undo
DoCmd.OpenForm "fmuMainMenu"
DoCmd.Close acForm, "frmVetNewMainForm"
End Sub
Here is part of my On Exit code for the field:
If IsNull(Me.txtSSN) Then
strMsg = "Social Security Number Must Not Be Left Blank!" & vbCrLf
strMsg = strMsg & "Do you want to add new veteran's record?" & vbCrLf
If MsgBox(strMsg, vbQuestion + vbYesNo, "Go to Record?") = vbYes Then
Me.txtSSN.SetFocus
Exit Sub
Else
Me.Undo
DoCmd.OpenForm "fmuMainMenu"
DoCmd.Close acForm, "frmVetNewMainForm"
End If
Else
'RUNS THE REST OF CODE
End if
OnExit events occur everytime you leave the textbox, even a simple tab or click to go to another combo box or select some text will trigger it.
You may want to try the code in the AfterUpdates.
and try a ,nosave after the close form command...
In addition, any checks to ensure a field is filled/has data is usually encouraged to be performed in the Form_BeforeUpdate section... Or so I have been advised
I know just enough programming to get myself in serious trouble. I'm building a database where I want to run a function that does the same thing each time. The only problem is the button appears on various forms. I know I need to use a public function. I managed to get the function to work as a private sub on one form but when I moved it to a module and made it into a public function, it gave me an error. It states that I have entered an expression that has no value. I suspect the problem is the link between the first form (Various Forms) and the second form (Log-Memo Line Form).
In each form, the common link is the HL# (The Transaction ID#). HLCtrl refers to a text box control on one form (the various parent forms). [HL#] refers to a text box control in the [Log-Memo Line] Form. My other issue is that the HLCtrl control has different names in different forms. Should I rename the controls to be the same name?
This is the code
Public Function Memo_Line()
On Error GoTo Memo_Line_Err
Call saver
DoCmd.OpenForm "Log-Memo Line", acNormal, "", "[HL#]=" & "'" & HLCtrl & "'", , acNormal
Call ClipBoard_SetData([Forms]![Log-Memo Line]![Memo])
MsgBox ([Form_Log-Memo Line].[Memo] & "---- copied to Clipboard."), vbInformation, "Clipboard Details"
DoCmd.Close acForm, "Log-Memo Line"
Memo_Line_Exit:
Exit Function
Memo_Line_Err:
MsgBox Error$
Resume Memo_Line_Exit
End Function
I think it's in this line DoCmd.OpenForm "Log-Memo Line", acNormal, "", "[HL#]=" & "'" & HLCtrl & "'", , acNormal
You're opening the form and filtering it according to HL# = HLCtrl. Assuming HLctrl is a control on one of your forms (the form needs to be open and have a value in there for this to work), I would either pass it as a parameter to your function or if you're always going to be calling this function when the form with hlctrl is open, you could refer to hlctrl by it's whole name : [Forms]![yourFormThatYouWantToSelectaHLNumber]![hlctrl]
If you called this code from the form that contained the control HLctrl, you wouldn't need to use the full name, and you could just say HLCtrl or Me.HLctrl. Since you're calling it from a public function located outside of the form object, you need to tell it which form to look for the HLCTRL
Like DoCmd.OpenForm "Log-Memo Line", acNormal, "", "[HL#]=" & "'" & [Forms]![yourFormName]![HLCtrl] & "'", , acNormal
Also, if it's a number, you probably don't need to wrap it in single quotes.
I have a form that contains multiple partners that share a "pool". The partners are listed on a subform. After I'm done entering the information, I want a button to run a report for each of the partners with their specific information. But since it's multiple partners and I need one report for each (which I then want to email), I want to use a Loop to go through each of the partners.
EDIT1: Added entire code for review. I do have Option Explicit in and I have compiled it as well.
Private Sub btn_Run_Click()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim strSQL As String
strSQL = "Select * FROM Cobind_qryReport WHERE PartPoolName = """ & Me.TopLvlPoolName & """"
Debug.Print "strSQL: " & strSQL
Set db = CurrentDb
Set rs = db.OpenRecordset(strSQL)
On Error GoTo Err_PO_Click
If MsgBox("Do you wish to issue the cobind invites?", vbYesNo + vbQuestion, "Confirmation Required") = vbYes Then
rs.MoveFirst
Do While rs.EOF = False
DoCmd.OutputTo acOutputReport, "Cobind_rptMain", acFormatPDF,_
"K:\OB MS Admin\Postage\CoBind Opportunities\Sent Invites\" _
& rs!CatCode & "_" & rs!PartPoolName "Cobind Invite_" & _
Format(Now(), "mmddyy") & ".pdf"
DoCmd.SendObject acSendReport, "Cobind_rptMain", acFormatPDF, ,_
, , " Cobind Invite", "Please find the cobind invite attached._
Response is needed by " & [RSVP] & ". Thank you.", True
rs.MoveNext
Loop
End If
Exit_PO_Click:
MsgBox ("It didn't work")
rs.Close
Set rs = Nothing
Set db = Nothing
Exit Sub
Err_PO_Click:
MsgBox Err.Description
Resume Exit_PO_Click
End Sub
This should allow me to create a report for each record in my query, save it to my server, then open an email to send it out. Right now, it appears that the [PartPoolName] is hanging up the code because I'm getting a "Microsoft Office Access can't find the field "|" referred to in your expression." If I take out the [PartPoolName], it'll create a PDF with four pages (each page showing a partner), where I want to end up with four separate PDFs.
The first thing you should do is add Option Explicit to the Declarations section of your module.
Then, from the Visual Basic editor's main menu, select Debug->Compile [your project name here]
Fix all the problems the compiler complains about. I suspect one of the compiler's first complaints may be triggered by this section of your code:
rs.MoveFirst
Do While Recordset.EOF = False
Do you have two recordset objects open, or one?
After you fix everything the compiler complains about, try your revised code.
If you get runtime errors, show us the exact error message and which code line is highlighted.
If the part of your code you haven't shown us includes an error hander, you can disable that error handler like so:
'On Error GoTo Err_PO_Click
Error handlers are great for production to shield users from errors. However, during development you really need to be able to identify which code line causes the error.
Alternatively, you can leave your error handler active and select Tools->Options from the editor's main menu. In the Options dialog, select the General tab, then select the "Break on All Errors" radio button and click OK. You can switch that option back to "Break on Unhandled Errors" after you finish your testing.
Update: You wrote: Right now, it appears that the [PartPoolName] is hanging up the code because I'm getting a "Microsoft Office Access can't find the field "|" referred to in your expression."
What is [PartPoolName]? If it's a field in the recordset, you can reference its value as rs!PartPoolName If it's something else, perhaps a global variable, give us more information about it.
Update2: Whenever your current code completes without error, you will hit this:
Exit_PO_Click:
MsgBox ("It didn't work")
Can that be right?
Update3: This OutputTo statement is your issue now, right?
DoCmd.OutputTo acOutputReport, "Cobind_rptMain", acFormatPDF,_
"K:\OB MS Admin\Postage\CoBind Opportunities\Sent Invites\" & _
"Cobind Invite_" & Format(Now(), "mmddyy") & ".pdf"
Cobind_rptMain is a report. It has a RowSource for its data. You're calling OutputTo with that report 4 times (once for each of the 4 rows in the recordset). Yet you expect 4 different versions of that report ... a separate report for each PartPoolName value?
To finish off the fine work by HansUp visit a page on how to print a report for a single record and how to generate reports to attach to emails. See the Emailing reports as attachments from Microsoft Access page.
I'm pretty new to MS Access. I'm trying to create a simple form that will basically search for a particular record using a textbox, rather than a drop down box. Essentially a user would be able to enter an ID number and retrieve some other related Info. However, I do not want the user to be able to add any new records to the database. I've been able to get the forms to look the way I want them, but I'm not sure where to place the code (do I create a macro, insert the code into the properties of the button?) Any help is greatly appreciated!
I assume that you have bound your form to a table or a query and that you want to be able to enter the ID manually in a textbox, then press ENTER and load that record's data or display an error message if there is no such record.
As dsteele said, make sure that the form's Data property Allow Addtions is set to No to disallow users from adding records.
Then, from the AfterUpdate event of the textbox, add the following code (assuming that your textbox is named txtGoTo):
Private Sub txtGoTo_AfterUpdate()
If (txtGoTo & vbNullString) = vbNullString Then Exit Sub
Dim rs As DAO.RecordSet
Set rs = Me.RecordsetClone
rs.FindFirst "[ID]=" & txtGoTo
If rs.NoMatch Then
MsgBox "Sorry, no such record '" & txtGoTo & "' was found.", _
vbOKOnly + vbInformation
Else
Me.RecordSet.Bookmark = rs.Bookmark
End If
rs.Close
txtGoTo = Null
End Sub
Note that you will have to change the line rs.FindFirst "[ID]=" & txtGoTo to something that is adequate for your data:
"[ID]=" should be replaced by the field you want to search (it could be "[POReference]=" or something else.
if you are searching by a numeric ID, for instance because the field is an autonumber column, then the code is fine.
Otherwise, if the field you are searching on is a string (say PN12-G) then you have to change the code to:
rs.FindFirst "[ID]=""" & txtGoTo & """"
Failing to use the proper quoting (or quoting where not necessary) will result in errors of the kind Data type mismatch....
As a new user, I would recommend that you have a look at the sample NorthWind project database that is either shiped with older versions of Access or available as a template for download from Access 2007.
There a lots of techniques to learn from as a new Access developer, including other ways to implement record navigation.
Set the form property Data/'Allow Additions' to No.
Either in the AfterUpdate event of the textbox, or in the Click event of a button, you can write code or assign a macro to look up and display the record you want.