MS Access 2016 VBA Code Reuse in Buttons - ms-access

I have an Access database frontend that houses 16 different forms. All of them have three buttons in common namely Show All, Clear and Refresh, that perform that exact same function using their respective subforms. For instance, for viewing data from a table named tbl_Students the 'On Click' event of these buttons on the Students Form have the following code:
Option Explicit
'Show all records button
Private Sub cmdShowAll_Click()
Dim task As String
task = "SELECT * FROM tbl_Students"
Me.frm_Students_subform.Form.RecordSource = task
Me.frm_Students_subform.Form.Requery
End Sub
'Clear displayed records button
Private Sub cmdClear_Click()
Dim task As String
task = "SELECT * FROM tbl_Students WHERE (StudentID) is null"
Me.frm_Students_subform.Form.RecordSource = task
Me.frm_Students_subform.Form.Requery
End Sub
'Refresh records button
Private Sub cmdRefresh_Click()
Me.frm_Students_subform.Form.Requery
End Sub
Currently, I'm using the exact same code, but with different respective subform names, in all my 16 forms. Is there a better, more efficient way to do it, with code reuse? Thanks.

Consider creating one generalized subroutine in a standard module that all 16 forms call passing needed parameters. Specifically use CurrentProject and Controls to reference objects dynamically by string.
Module save in a named standard module (not behind any form)
Option Explicit
Public Sub ProcessForms(task As String, mainform As String, subform As String)
On Error GoTo ErrHandle
CurrentProject.AllForms(mainform).Controls(subform).Form.RecordSource = task
CurrentProject.AllForms(mainform).Controls(subform).Requery
Exit Sub
ErrHandle:
Msgbox Err.Number & " - " & Err.Description, vbCritical, "RUNTIME ERROR"
Exit Sub
End Sub
Example Form single line calls
Option Explicit
'Show all records button
Private Sub cmdShowAll_Click()
Call ProcessForms("SELECT * FROM tbl_Students", _
"frm_students", "frm_Students_subform")
End Sub
'Clear displayed records button
Private Sub cmdClear_Click()
Call ProcessForms("SELECT * FROM tbl_Students WHERE (StudentID) IS NULL", _
"frm_students", "frm_Students_subform")
End Sub
'Refresh records button
Private Sub cmdRefresh_Click()
' Re-assigns same recordsource in line with others
Call ProcessForms(Me.Controls("frm_Students_subform").Form.RecordSource, _
"frm_students", "frm_Students_subform")
End Sub

Related

Button to turn change AllowEdits to true in Access using VB

I am trying create a button which changes the value of AllowEdits to False and another for true for a subform. I am using the below code. I get a Runtime error 424 each time I run it.
Option Compare Database
Private Sub Toggle_Edit_Click()
Dim strForm As String
strFormName = Me.Name
Call ToggleEdit(Me)
End Sub
and
Option Compare Database
Public strFormName As String
Sub ToggleEdit(myForm As Form)
Call Message
ctrlControl.AllowEdits = True
End Sub
and if you were interested
Sub Message()
MsgBox "Remember not to overwrite incorrect records"
End Sub
Please add Option Explicit at top of your modules!
I think AllowEdits is a Form property, not a Control property.
Option Explicit
Sub ToggleEdit(myForm As Form)
myForm.AllowEdits = Not myForm.AllowEdits
End Sub
If the code is behind the form itself, you can use Me.
Sub ToggleEdit() 'no parameter
Me.AllowEdits = Not Me.AllowEdits
End Sub
If you want to act at control level, use Locked or Enabled properties.

get Access VBA have a Sender

In Access I have a form that has about 200 text boxes on it. I Know that in C# on every on click event there is textbox1_Click(object sender).
how can I in VBA have reference the current sender without saying Me.txt_Whatever?
this is what I have so far
Private Sub txtHotMix_DblClick(Cancel As Integer, sender As Object)
Dim txt As TextBox
Set txt = sender
MsgBox txt.Name
End Sub
I think the ActiveControl property of the Screen object is what you're after (https://msdn.microsoft.com/en-us/library/bb225491(v=office.12).aspx):
In a module:
Option Compare Database
Option Explicit
Public Sub ctrl_Print(objCtrl As Control)
MsgBox objCtrl.Value, vbOKOnly, "Control Value"
End Sub
You would still have to add a click event to every control to call this sub, like this:
Option Compare Database
Option Explicit
Private Sub Text0_Click()
Call ctrl_Print(Screen.ActiveControl)
End Sub

Microsoft Access: Attempting to detect an insert triggered by a subform within the parent form

Is it at all possible to detect an insert operation performed by a subform while still in the parent form?
To clarify: I have a series of forms for data entry, they each have a button for adding an entry to the appropriate table (using the data provided in the form). I am attempting to set each of them in turn to a subform in a 'wizard' parent form that will cycle through all the data entry forms.
My problem arises when it comes to switching between forms, as it became clear that the AfterInsert event in this parent form was not detecting the insert triggered by the form contained in the subform. I know I could move the trigger for the insert to a button in the parent form; however, to my knowledge, this would require setting the code for the click event for each of the buttons in the data entry forms as public so that they may be called from the parent form's code. I am leery to do this and was thus hoping for other options.
Create a public procedure in the parent form.
Public Sub Listener(ByVal pMsg As String)
MsgBox pMsg
End Sub
Then, in each of your subforms, call that procedure from After Insert.
Private Sub Form_AfterInsert()
Dim strMsg As String
strMsg = Me.Name & " inserted row."
Call Me.Parent.Listener(strMsg)
End Sub
If the subform may also be used stand-alone (without a parent), Me.Parent will throw error #2452, "The expression you entered has an invalid reference to the Parent property." You can create a separate function to check whether the current form has a parent, and base your code on the function's return value.
Private Sub Form_Open(Cancel As Integer)
Dim strPrompt As String
If HaveParentForm(Me) = True Then
strPrompt = "I am a subform to '" & _
Me.Parent.Name & "'."
Else
strPrompt = "I am a top level form."
End If
MsgBox strPrompt
End Sub
The function ...
Public Function HaveParentForm(ByRef frm As Form) As Boolean
Dim blnReturn As Boolean
Dim strMsg As String
On Error GoTo ErrorHandler
blnReturn = (Len(frm.Parent.Name) > 0)
ExitHere:
HaveParentForm = blnReturn
On Error GoTo 0
Exit Function
ErrorHandler:
Select Case Err.Number
Case 2452 ' The expression you entered has an invalid '
' reference to the Parent property. '
Case Else
strMsg = "Error " & Err.Number & " (" & Err.Description _
& ") in procedure HaveParentForm"
MsgBox strMsg
End Select
blnReturn = False
GoTo ExitHere
End Function

Access 2007 ReportEvents class is not firing events

I'm having an issue with report events that I haven't
encountered before in Access prior to Access 2007.
I'm using using Access 2007 for a Front-end to a SQL Back-end.
I have a Class, ReportEvents, that I use for the reports.
In a report's Report_Open event I instantiate and then use this class to
handle events like activate, Close, NoData and I also put common code
such as exporting data to excel instead of a report.
This code was working fine in a previous Access 2003 application (mdb) I was using,
but it isn't working as expected in 2007 (accdb). In my tests the call to a non event public sub, ProvideXLOption works like a charm, but none of the events are being fired
from the ReportEvents class. I didn't change the code I just imported it into the
project. I set up break points but they aren't being hit. I changed all of them to
public events and then called them within the test reports event and they worked fine.
I set up another report in Access 2007 with the same results. I've checked
the startup settings in Access and they are fine. I even removed and re-added
the database location from the trusted locations without any luck.
Has Microsoft modified the Events Code or is this just a simple code error on my part that I'm not seeing? It's gotta be something simple. My brain is just toast (my son decided to stay awake after last night's feeding).
Class ReportEvents Code:
Option Compare Database
Option Explicit
Private WithEvents rpt As Access.Report
Const strEventKey As String = "[Event Procedure]"
Public Property Set Report(Rept As Access.Report)
Set rpt = Rept
With rpt
.OnActivate = strEventKey
.OnClose = strEventKey
If LenB(.OnNoData) = 0 Then
.OnNoData = strEventKey
End If
End With
End Property
Public Sub Terminate()
On Error Resume Next
Set rpt = Nothing
End Sub
Private Sub rpt_Activate()
LoadPrintRibbon
End Sub
Private Sub rpt_Close()
Me.Terminate
End Sub
Private Sub rpt_NoData(Cancel As Integer)
Dim strMsg As String
strMsg = "No Records were found that match your criteria."
MsgBox strMsg, vbInformation, rpt.Name & _
": No Match. Report Cancelled"
Cancel = True
End Sub
Private Sub LoadPrintRibbon()
#If AccessVersion >= 12 Then
If rpt.RibbonName <> "PrintPreview" Then
rpt.RibbonName = "PrintPreview"
End If
#End If
End Sub
';;Provides user with option to send data to Excel instead of a report
Public Sub ProvideXLOption(Cancel As Integer)
'... some XLOption Code
End Sub
In the Test Report Code:
Private Sub Report_Open(Cancel As Integer)
Dim rptEvts As ReportEvents
Set rptEvts = New ReportEvents
Set rptEvts.Report = Me
';;Let User User Choose to Export Data to Excel or Display the report
rptEvts.ProvideXLOption Cancel
End Sub
I figured it out. It was a scope issue The ReportEvents class variable rptEvts, was inside the Report_Open sub. Because of this it wouldn't exist when the other events happened. It should be at the module level and not within the procedure.
Dim rptEvts As ReportEvents
Private Sub Report_Open(Cancel As Integer)
Set rptEvts = New ReportEvents
Set rptEvts.Report = Me
';;Let User User Choose to Export Data to Excel or Display the report
rptEvts.ProvideXLOption Cancel End Sub
End Sub
It's amazing what a little rest will do for you.

Does it degrade performance to use subforms in MS Access?

I am considering the use of a tab control on a parent form for which I would like to have around 20 tabs. Each tab I am considering the use of one or two separate sub forms. Each sub form will have varied complexity in coded logic. By taking this approach will I severally reduce the performance of my application? I am currently using this in MS Access 2003. I will expect an average of 15 users at any given time on the various forms.
Thoughts?
Yes, performance will be degraded slightly for each subform. One or three isn't too bad but twenty is definitely going to cause you performance issues.
Once you have the subform working to your satisfaction either save the Record Source as a query and give it a name or save the query SQL string. Then either paste the query name or the query SQL string in the VBA code in the tab control change event.
Private Sub TabCtl_Change()
On Error GoTo TabCtl_Change_Error
Select Case Me.TabCtl.Value
Case Me.pagPartsConsumed.PageIndex
If Me.PartsConsumedsbf.Form.RecordSource <> "Equipment - Parts Consumed sbf" Then _
Me.PartsConsumedsbf.Form.RecordSource = "Equipment - Parts Consumed sbf"
....
Now just to ensure that I don't accidentally leave some subform recordsources filled in slowing down the app on startup I check to see if the file the code is running is an MDB (instead of an MDE. The function is below) then display a message telling me I have to remove the recordsource.
If Not tt_IsThisAnMDE Then
If Me.PartsConsumedsbf.Form.RecordSource <> "" Then _
MsgBox "Record source of Equipment - Parts Consumed sbf not empty"
...
End If
Public Function tt_IsThisAnMDE()
On Error GoTo tagError
Dim dbs As Database
Set dbs = CurrentDb
Dim strMDE As String
On Error Resume Next
strMDE = dbs.Properties("MDE")
If Err = 0 And strMDE = "T" Then
' This is an MDE database.
tt_IsThisAnMDE = True
Else
tt_IsThisAnMDE = False
End If
Exit Function
tagError:
Call LogError(Application.CurrentObjectName, "")
Exit Function
End Function
Also in the form unload event I clear the Recourd Source as well.
Private Sub Form_Unload(Cancel As Integer)
On Error GoTo Form_Unload_Error
Me.PartsConsumedsbf.Form.RecordSource = ""
....
BTW I almost always would put each subform on a seperate tab. Also that many tab entries gets visusally unwieldy. When I had a similar question my fellow Access MVPs suggested using a listbox along the left hand side to control which subform is viewable.
Also each combo box and list box will also slightly degrade the performance. So if you have those on a subform then consider similar logic.
In addition to adding recordsets at runtime, I would generally only use one or two tabs and a number of controls to load various subforms into a subform control.
The text for the On Click event of the control might be:
=WhichPage([Form],"lblLocations")
Where WhichPage is a function with the following lines, amongst others:
Function WhichPage(frm, Optional LabelName = "")
<..>
Select Case LabelName
Case "lblLocations"
frm("sfrmAll").SourceObject = "sfrmLocations"
<...>
If necessary, the link child and link master fields can be changed at runtime. The link master field is best set to the name of a control, rather than a field, to avoid errors.
Me.sfrmAll.LinkChildFields = "LocationKey"
Me.sfrmAll.LinkMasterFields = "txtLocationKey"
To expand on Remou's answer...here is a sub I wrote that dynamically loads a form into a subform control. You pass in the name of the form in the call and it will load it into the subform of the Main form. The arguments map to the arguments of Docmd.OpenForm method of Access. If the main form that is hosting the subform control is not open...it just does a regular open of the form. Otherwise it loads it into the subform control. If a where clause was passed in it is used to filter the subform.
Public Sub MyOpenForm(FormName As String, _
Optional View As AcFormView = acNormal, _
Optional FilterName As String = vbNullString, _
Optional WhereCondition As String = vbNullString, _
Optional DataMode As AcFormOpenDataMode, _
Optional WindowMode As AcWindowMode, _
Optional OpenArgs As String)
On Error GoTo PROC_ERR
Dim frm As Form
Dim strNewForm As String
Dim strCurrentForm As String
Dim strNewTable As String
Dim fDoNotFilter As Boolean
Dim strActionText As String
Dim strID As String
If Not IsLoaded("frmMain") Then
DoCmd.OpenForm FormName:=FormName, View:=View, FilterName:=FilterName, WhereCondition:=WhereCondition, DataMode:=DataMode, WindowMode:=WindowMode, OpenArgs:=OpenArgs
Else
strCurrentForm = Forms![frmMain]![sfrMyForm].SourceObject
If strCurrentForm <> FormName Then
Forms![frmMain]![sfrMyForm].SourceObject = vbNullString
Forms![frmMain]![sfrMyForm].SourceObject = FormName
End If
If WhereCondition <> vbNullString Then
Forms![frmMain]![sfrMyForm].Form.Filter = WhereCondition
Forms![frmMain]![sfrMyForm].Form.FilterOn = True
End If
End If
PROC_EXIT:
Exit Sub
PROC_ERR:
MsgBox Err.Description
Resume PROC_EXIT
End Sub