Home Page
Policies
Policies
About
Tips And Tricks
Helpful Links
e-mail me

Picture of building from outside Tips And Tricks

I seem to run into things from time to time that I want to share with the world, usually things that I had to find out the hard way. Sometimes it will just be something that I had to dig for, and want to make more readily accessible. This page is intended to make life a little easier for other programmers. If it meets your need, then that's what it's here for.



Call A Form In Another MDB Through A Reference Using MS Access

Ok, you've started moving code from places where it was cloned into a central library, usually code which will be called from several different places. You've set up a reference to the library, and you have the ability to call functions in it. Next, you try moving a form into the new library, but the Access doesn't seem to know where the form is . . . How do we get Access to bring up a form which resides in a central library?

We first tackled this in ADPs, where the problem was actually simpler. Only recently did I try it from an MDB type of central library. In ADPs, you set up a connection to a SQL Server style database. In any ADP which has had the connection set, all tables and queries in the SQL Server backend are available. So, we connect both our satellite module and our central library to the same back end database, and we don't have a problem with forms getting to the data.

In a central library of the MDB type, it gets a bit more complicated, in that you would either have to have links pointing to the back end database (MDB) or else you would have to have some dynamic way of pointing the form to the data. If you only have one project, with one back end database, this might not seem like much of an issue. However, I had several projects which had both front ends and back ends for each one. In my MDB central library, I elected not to have table links of any kind, and to have my forms connect to the data at run time, rather than trying to manage links which would have to be set up to point to either development data or production data. There are probably several ways to do this, among them the use of disconnected recordsets, and dynamic connections made "on the fly" at run time using ADO. At the time of this writing, at least, I chose the latter method.

We had used something like this in our ADPs to connect to a local MDB on the user's local computer. To base a form in an ADP on a recordset in an MDB, you can establish an ADO recordset at run time using a connection string. This is done using code in the form's OnOpen event. For example:

'use a local temp file in an MDB from an ADP to allow each user to have unique local data
Private Sub Form_Open(Cancel As Integer)
 On Error GoTo Err_Form_Open
 Dim ConnStr As String
 Dim MDBConn As New ADODB.Connection
 Dim rstmyform As New ADODB.Recordset
    
 ConnStr = MDBConnStr(LocPAFile)
 MDBConn.Open (ConnStr)

 rstmyform.Open ("MyFormTempFile"), MDBConn, adOpenKeyset, adLockOptimistic
 If Not rstmyform.EOF Then
  Set Me.Recordset = rstmyform
 Else
  MsgBox "There is no data in the temp file"
 End If

Exit_Form_Open:
 Exit Sub

Err_Form_Open:
 MsgBox "Error #" & Err.Number & ": " & Err.Description
 Resume Exit_Form_Open
End Sub

Function MDBConnStr(pMDB) As String
 On Error GoTo Err_MDBConnStr
 Dim strConnection As String

 'Build a connection string for an MS Access database.
 strConnection = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source='" & pMDB & "'"
 MDBConnStr = strConnection
Exit_MDBConnStr:
 Exit Function

Err_MDBConnStr:
 MsgBox "Error #" & Err.Number & ": " & Err.Description
 Resume Exit_MDBConnStr
End Function
Once you know how to do this, it's not too terribly different to open up a recordset in any MDB from a central library using ADO. You don't need the elaborate Jet connection string. The code to open a table in the MDB your code is running in at run time ends up looking something like this:

Private Sub Form_Open(Cancel As Integer)
 On Error GoTo Err_Form_Open
 Dim con As Object
 Dim rstUsr As New ADODB.Recordset
 
 'run as normal -set up the data for the form
 Set con = Application.CurrentProject.Connection
 rstUsr.Open ("Users"), con, adOpenKeyset, adLockOptimistic
 Set Me.Recordset = rstUsr

Exit_Form_Open:
 Exit Sub

Err_Form_Open:
 MsgBox "Error #" & Err.Number & ": " & Err.Description
 Resume Exit_Form_Open
End Sub
That code establishes the recordset dynamically, at run time, for a login form that checks to see if you are in the list of valid users. With an ADO recordset pointing to the "Users" table, the login form, which runs in the central library, is able to validate users attempting to use the program.

Having called this form through a reference, then, it still has access to the records in the Users table, even though there are no table links or tables in the central library. And that brings us back to the original question of how to call the form in the first place.

Because the form is now off in another MDB, any buttons or toolbar menu options which try to call the form the usual way won't work. The form is not located in the current MDB. What we have to do is run code which DOES reside in the same MDB where the form is located. This can be accomplished by calling a function through the reference. The function is in the same MDB (or ADP) where the form is, and therefore has the form in its scope, so that it can run it.

Originally, when my associate first found a way to do this, we had a module with over a hundred functions with the form name "hard coded" twice in each one. Each function would each bring up a particular form. Some of them had the form name hard coded three times, with an extra reference to allow a where clause to be passed in. Well, that got old in a hurry, because an excess of cloned code can lead to problems. Since this module consisted of so many smaller functions, each of which opened a specific form, I came along and wrote a function which allows us to pass in a parameter for the form name. See the following:

Function GenericFormOpen(pFormName, Optional pWhereClause, Optional pViewMode)
 On Error GoTo Err_GenericFormOpen
 
 If IsMissing(pViewMode) Then
  pViewMode = 0
 End If
 
 If IsMissing(pWhereClause) Then
  pWhereClause = ""
 End If
 
 If Len(pWhereClause) = 0 Then
  DoCmd.OpenForm pFormName, pViewMode
 Else
  DoCmd.OpenForm pFormName, pViewMode, , pWhereClause
 End If
 
Exit_GenericFormOpen:
 Exit Function

Err_GenericFormOpen:
 If Err.Number = 438 Then Resume Next
 If Err.Number = 2501 Then Resume Next
 MsgBox "Error #" & Err.Number & ": " & Err.Description
 Resume Exit_GenericFormOpen
End Function
This allows us to call up almost any form in the central library, which is a separate MDB or ADP. We can even call up a form for a specific record or set of records using the optional where clause. A call to this function can be placed in the code for a click event of a button, or can even be placed in a toolbar menu option using the equal sign calling convention. For example the following toolbar contains =GenericFormOpen("Department"). To bring this up this dialogue, you right-click on a menubar or toolbar and choose customize. For further details, see the following:

Image of MS Access Toolbar customization screen


Right click again and choose properties:


Image of MS Access Toolbar item properties screen

These pictures illustrate how a form which is located in a central library can be brought up by a function which is located in a central library, from a toolbar in an application. Neither the form or the function are found in the application, and the only way that the application can get to these is through a reference which has been established between the subsystem and the library (normally, prior to run time).


If you found this information to be useful, and would like to donate via PayPal to help offset the cost of maintaining this web site, use the following button:



|Home Page| |Custom Software| |Policies| |About| |Tips And Tricks| |Helpful Links|