Category Archives: MS Access Forms

MS Access Forms

Enumerate a List of Open but Hidden Forms

Once again, in trying to help someone in an Access forum, I came up with the following procedure(s) to be able to identify currently open, but hidden forms.

Option 1 – Print the results to the immediate window

'---------------------------------------------------------------------------------------
' Procedure : ListHiddenOpenFrms
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Generate a list of current open, but hidden forms and print their names to
'             the immediate window.
' Copyright : The following may be altered and reused as you wish so long as the
'             copyright notice is left unchanged (including Author, Website and
'             Copyright).  It may not be sold/resold or reposted on other sites (links
'             back to this site are allowed).
'
' Usage:
' ~~~~~~
' Call ListHiddenOpenFrms
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2014-Nov-19                 Initial Release
'---------------------------------------------------------------------------------------
Function ListHiddenOpenFrms()
On Error GoTo Error_Handler
    Dim DbF     As Form
    Dim DbO     As Object
    
    Set DbO = Application.Forms 'Collection of all the open forms
    
    For Each DbF In DbO    'Loop all the forms
        If DbF.Visible = False Then
            Debug.Print DbF.Name
        End If
    Next DbF

Error_Handler_Exit:
    On Error Resume Next
    Set DbF = Nothing
    Set DbO = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred." & vbCrLf & vbCrLf & _
            "Error Number: " & Err.Number & vbCrLf & _
            "Error Source: ListHiddenOpenFrms" & vbCrLf & _
            "Error Description: " & Err.Description, _
            vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

Option 2 – Return a delimited listing which can be used elsewhere

'---------------------------------------------------------------------------------------
' Procedure : ListHiddenOpenFrms
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Generate a list of current open, but hidden forms and return a delimited listing which 
'             can be used elsewhere.
' Copyright : The following may be altered and reused as you wish so long as the
'             copyright notice is left unchanged (including Author, Website and
'             Copyright).  It may not be sold/resold or reposted on other sites (links
'             back to this site are allowed).
'
' Usage:
' ~~~~~~
' Call ListHiddenOpenFrms
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2014-Nov-19                 Initial Release
'---------------------------------------------------------------------------------------
Function ListHiddenOpenFrms()
On Error GoTo Error_Handler
    Dim DbF     As Form
    Dim DbO     As Object
    Dim Frms   As Variant
    
    Set DbO = Application.Forms 'Collection of all the open forms
    
    For Each DbF In DbO    'Loop all the forms
        If DbF.Visible = False Then
            Frms = Frms & ";" & DbF.Name
        End If
    Next DbF
    
    If Len(Frms) > 0 Then
        Frms = Right(Frms, Len(Frms) - 1)   'Truncate initial ;
    End If
 
    ListHiddenOpenFrms = Frms

Error_Handler_Exit:
    On Error Resume Next
    Set DbF = Nothing
    Set DbO = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred." & vbCrLf & vbCrLf & _
            "Error Number: " & Err.Number & vbCrLf & _
            "Error Source: ListHiddenOpenFrms" & vbCrLf & _
            "Error Description: " & Err.Description, _
            vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

Hopefully this can help someone else out.

MS Access – Search with Spaces in Terms

MS Access Text Search with SpacesEvery developer at some point or another ends up needing to create a search functionality where their user(s) can enter what they know (terms, text, …) and pull up any matching records.  The problem being that you will quickly come to smack your head against your computer because once you setup a search textbox control and perform the most basic test, you will notice that Access truncates any spaces you enter making your search functionality completely useless.

Having faced this problem many years ago, and having recently been asked by fellow MVPs how to resolve this problem, I decided to create the following demo database.  In it, I demonstrate 2 similar approaches to work around Access’ native behavior and enable proper full text search.

Disclaimer/Notes:

All code samples, download samples, links, ... on this site are provided 'AS IS'.

In no event will Devhut.net or CARDA Consultants Inc. be liable to the client/end-user or any third party for any damages, including any lost profits, lost savings or other incidental, consequential or special damages arising out of the operation of or inability to operate the software which CARDA Consultants Inc. has provided, even if CARDA Consultants Inc. has been advised of the possibility of such damages.

Download

Feel free to download a copy by using the link provided below:

Download “Access - Search with Spaces in Terms Demo” TextSearchDemo.zip – Downloaded 18387 times – 89.26 KB

Count of the objects in your MS Access Database

Have you ever needed to get a simple count of the number of :

  • Tables
  • Queries
  • Forms
  • Macros
  • Reports
  • Modules

I recently needed to get some summary data regarding a database I was working on.  The following single line VBA commands will get you the count(s) you are after.

'Number of Tables
CurrentDb.TableDefs.Count 'includes system tables
'Number of Queries
CurrentDb.QueryDefs.Count
'Number of Forms
Currentproject.AllForms.Count
'Number of Macros
Currentproject.AllMacros.Count
'Number of Reports
Currentproject.AllReports.Count
'Number of Modules
Currentproject.AllModules.Count 'does not include object modules

Easy as can be!

List subforms within another form – MS Access – VBA

I was recently needing to breakdown a very complex form and amongst other things, I needed to extract a list of the subforms contained within it for further analysis.  Below is a simple function I wrote which will return to the immediate pane a listing of the subform names.

Function ListSubFrms(sFrm As String)
On Error GoTo Error_Handler
    Dim ctl             As Access.Control
    Dim frm             As Access.Form

    DoCmd.OpenForm sFrm, acDesign
    Set frm = Forms(sFrm).Form
    For Each ctl In frm.Controls
        Select Case ctl.Properties("ControlType")
            Case acSubform    ', acListBox
                ' ctl.Name 'Will return the given name of the control, not necessarily the actual object name
                ' ctl.Properties("SourceObject") 'Will return the object name
                If ctl.Properties("SourceObject") = "" Then
                    Debug.Print ctl.name
                Else
                    Debug.Print ctl.Properties("SourceObject")
                End If
        End Select
    Next ctl

Error_Handler_Exit:
    On Error Resume Next
    DoCmd.Close acForm, sFrm, acSaveNo
    Set frm = Nothing
    Set ctl = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred." & vbCrLf & vbCrLf & _
            "Error Number: " & Err.Number & vbCrLf & _
            "Error Source: ListSubFrms" & vbCrLf & _
            "Error Description: " & Err.Description, _
            vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

MS Access – VBA – Form Captions

I typically do not add form captions, mainly because I will include a label in the form header (and I find it redundant to repeat it in the form caption). That said, Access in its’ infinite (should I say finite) wisdom figures that since you did not specify a caption (left it blank) then it will help us out by simply displaying the form’s name.

Now, in many instances this is not a big deal, and it can actually be helpful. However, when deploying a database to an end-user, such information can help malicious users to hack your application since they formally know the names of your database objects! As such, I wanted a simple and fast way to solve this issue. The solution, enter a ‘space’ in each form’s caption, thus displaying nothing at all (just what I wanted originally). So I wrote a simple function which quickly loops through all the forms within a database and enters a ‘space’ as the caption for any forms that do not have a caption specified (I wanted to leave any caption I had specifically entered intact!).

 

'---------------------------------------------------------------------------------------
' Procedure : BlankFrmCaptions
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Loop through all the forms within a database and enters a 'space' as the
'             caption for any forms that do not have a caption specified but leave intact
'             any captions I had specifically entered to avoid displaying the form's
'             name by default.
' Copyright : The following may be altered and reused as you wish so long as the
'             copyright notice is left unchanged (including Author, Website and
'             Copyright).  It may not be sold/resold or reposted on other sites (links
'             back to this site are allowed).
'
' Usage:
' ~~~~~~
' Call BlankFrmCaptions
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2014-Jan-23                 Initial Release
'---------------------------------------------------------------------------------------
Function BlankFrmCaptions()
On Error GoTo Error_Handler
    Dim obj             As AccessObject
    Dim frm             As Form
    Dim sFrm            As String

    For Each obj In CurrentProject.AllForms    'Loop through all the database forms
        sFrm = obj.Name
        DoCmd.OpenForm sFrm, acDesign   'Open the current form in design mode
        Set frm = Forms(sFrm).Form
        If Len(Trim(frm.Caption)) = 0 Then frm.Caption = " "    'Create a blank caption
        DoCmd.Close acForm, sFrm, acSaveYes    'close and save the change
    Next obj    'repeat with the next form until we have processed all of them

Error_Handler_Exit:
    On Error Resume Next
    'Cleanup our variable (better safe than sorry)
    Set frm = Nothing
    Set obj = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred." & vbCrLf & vbCrLf & _
            "Error Number: " & Err.Number & vbCrLf & _
            "Error Source: BlankFrmCaptions" & vbCrLf & _
            "Error Description: " & Err.Description, _
            vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

MS Access – Enable Mouse Wheel Scrolling of Text Box

I was recently working on a database in which I was displaying a memo field in a text box on a form and was annoyed by the fact that the Mouse Wheel did not work to scroll though its’ content.   Then, I started looking for a setting, a property, to enable it, but there wasn’t one!

Searching the Internet, I found examples to enable it, but they all used SendKeys.  Any experienced programmer knows all to well that SendKeys is to be avoided at all cost, as they are unpredictable and thus very unreliable.  So I went out searching for another solution.

Continue reading

MS Access – Image Control On Click Event in Continuous Form Not Returning the Proper Record/Primary Key

I recently was creating a continuous form (using Access 2007) and wanted to use an Image control as a delete button, of sorts.  I rapidly encountered a small problem (what I personally perceive to be a glitch/bug within Access).  Access would not always delete the record on which I was clicking the Delete Image control.  So in fact, it was deleting the wrong records!  So I started performing a few tests and discovered that the Image Control was not returning the proper primary key (PK) value for the record on which the Image Control was clicked?!  The only way it deleted the proper record was to first set the focus on one of the other controls for the record I wish to delete and then click on the delete image.  So you need to manually change the focus.  This obviously was not a potential production solution for real-world usage.

I asked my fellow MVPs to see if (i) if it the behavior was reproducible, (ii) if this was a known issue and receive the following informative and confirming explanation from Dirk Goldgar:

I can confirm that, at least for Access 2010, clicking on an image control doesn’t move the focus to the control, and thus doesn’t change the current record.  It seems that, like a label, the image control can’t receive the focus, so clicking on it has no effect on what control and record are active and current.

This would explain the issue.

 

The Workaround/Solution

The only solution I came up with (and later was offered the same solution by other MVPs) was to overlay a transparent button (Format -> Transparent = Yes) over top of the Image Control and use it’s On Click event instead.  So use the image for the visual you are after, but use the button for the actual action.

MS Access – VBA – Requery a Form While Remaining on the Same Record

Have you ever wanted to requery a form after a user inserts a new record or modifies an existing record, to perhaps re-order things, but wanted to stay on the same record that you currently are on once the requery was done? Perhaps to synchronize changes made by other users, or on another form.

My Initial Idea

The fact of the matter is that it truly isn’t very complex to do.  Below is some straight forward code to do so and you’d need only add it to a Form’s After Insert event or a control’s After Update event.

    Dim rs              As DAO.Recordset
    Dim pk              As Long

    pk = Me.PrimaryKeyFieldName
    Me.Requery
    Set rs = Me.RecordsetClone
    rs.FindFirst "[PrimaryKeyFieldName]=" & pk
    Me.Bookmark = rs.Bookmark
    Set rs = Nothing

Now there is nothing wrong with the code above, but instead of putting such code inside each and every form’s After Insert event and every control’s After Update event, I thought to myself that I should be able to create a simple, re-useable function that I could call, and achieve the same desired effect. Below is that function.

'---------------------------------------------------------------------------------------
' Procedure : FrmRequery
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Requery the form to apply the chosen ordering,
'               but ensure we remain on the current record after the requery
' Copyright : The following may be altered and reused as you wish so long as the
'             copyright notice is left unchanged (including Author, Website and
'             Copyright).  It may not be sold/resold or reposted on other sites (links
'             back to this site are allowed).
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' frm       : The form to requery
' sPkField  : The primary key field of that form
'
' Usage:
' ~~~~~~
' Call FrmRequery(Me, "Id")
' Call FrmRequery(Me, "ContactId")
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2012-Oct-19                 Initial Release
'---------------------------------------------------------------------------------------
Sub FrmRequery(frm As Form, sPkField As String)
    On Error GoTo Error_Handler
    Dim rs              As DAO.Recordset
    Dim pk              As Long

    pk = frm(sPkField)
    frm.Requery
    Set rs = frm.RecordsetClone
    rs.FindFirst "[" & sPkField & "]=" & pk
    frm.Bookmark = rs.Bookmark

Error_Handler_Exit:
    On Error Resume Next
    Set rs = Nothing
    Exit Sub

Error_Handler:
    MsgBox "The following error has occurred." & vbCrLf & vbCrLf & _
            "Error Number: " & Err.Number & vbCrLf & _
            "Error Source: FrmRequery" & vbCrLf & _
            "Error Description: " & Err.Description, _
            vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Sub

The beauty of the above function is that you can copy it into a standard module, and then call it with a single line of code in as many events as you choose. You could even build an event expression, thus not requiring any VBA events, if you so wished to.

A Much Simpler Solution!

Another option would be to simply requery the underlying form’s recordset directly, something like

Me.RecordSet.Requery

Or

Forms!YourFormName.Form.RecordSet.Requery

The beauty here is the screen data updates itself, but the form remains exact as is, the scrollbar doesn’t move, so it is completely transparent to the end-user.

MS Access – VBA – Export Database Objects to Another Database

I while back, I wanted to unsecure a database.  Instead of messing around with accounts….  I simply decided to export everything, all the database objects: tables, queries, forms, reports, macros, modules into a new unsecured database.  Now you can right-click on each object, one at a time, select export, browse to find the database, click ok, ok…   but this is simply a complete waste of time.

Don’t ask me why you can’t, using multiple selected objects, perform an export?!  this to me is the type of oversight made by MS’ development team, but this is another discussion altogether.

The good news is that we can easily accomplish a complete export using the power of VBA and a few very simple lines of code!

'---------------------------------------------------------------------------------------
' Procedure : ExpObj2ExtDb
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Export all the database object to another database
' Copyright : The following may be altered and reused as you wish so long as the
'             copyright notice is left unchanged (including Author, Website and
'             Copyright).  It may not be sold/resold or reposted on other sites (links
'             back to this site are allowed).
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sExtDb    : Fully qualified path and filename of the database to export the objects
'             to.
'
' Usage:
' ~~~~~~
' ExpObj2ExtDb "c:\databases\dbtest.accdb"
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2008-Sep-27                 Initial Release
'---------------------------------------------------------------------------------------
Public Sub ExpObj2ExtDb(sExtDb As String)
    On Error GoTo Error_Handler
    Dim qdf             As QueryDef
    Dim tdf             As TableDef
    Dim obj             As AccessObject

    ' Forms.
    For Each obj In CurrentProject.AllForms
        DoCmd.TransferDatabase acExport, "Microsoft Access", sExtDb, _
                               acForm, obj.Name, obj.Name, False
    Next obj

    ' Macros.
    For Each obj In CurrentProject.AllMacros
        DoCmd.TransferDatabase acExport, "Microsoft Access", sExtDb, _
                               acMacro, obj.Name, obj.Name, False
    Next obj

    ' Modules.
    For Each obj In CurrentProject.AllModules
        DoCmd.TransferDatabase acExport, "Microsoft Access", sExtDb, _
                               acModule, obj.Name, obj.Name, False
    Next obj

    ' Queries.
    For Each qdf In CurrentDb.QueryDefs
        If Left(qdf.Name, 1) <> "~" Then    'Ignore/Skip system generated queries
            DoCmd.TransferDatabase acExport, "Microsoft Access", sExtDb, _
                                   acQuery, qdf.Name, qdf.Name, False
        End If
    Next qdf

    ' Reports.
    For Each obj In CurrentProject.AllReports
        DoCmd.TransferDatabase acExport, "Microsoft Access", sExtDb, _
                               acReport, obj.Name, obj.Name, False
    Next obj

    ' Tables.
    For Each tdf In CurrentDb.TableDefs
        If Left(tdf.Name, 4) <> "MSys" Then    'Ignore/Skip system tables
            DoCmd.TransferDatabase acExport, "Microsoft Access", sExtDb, _
                                   acTable, tdf.Name, tdf.Name, False
        End If
    Next tdf

Error_Handler_Exit:
    On Error Resume Next
    Set qdf = Nothing
    Set tdf = Nothing
    Set obj = Nothing
    Exit Sub

Error_Handler:
    MsgBox "The following error has occurred." & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: ExpObj2ExtDb" & vbCrLf & _
           "Error Description: " & Err.Description, _
           vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Sub

Voilà, nothing to it (once you do it once).

MS Access – VBA – Convert Form SQL into Queries Automatically

When I develop, I always, initially, build my SQL statements directly within my form’s Record Source. Now, it is a best practice to always create an independent query and base form’s off of the query rather than use an embedded SQL Statement. This is all fine a dandy when your db has 5-10 forms, but when you are working on databases with 10s or 100s of forms, this can become quite a tedious task to convert all of the SQL statements into queries and then reassign the newly created queries to their respective forms. So, I thought about it for a couple minutes and quickly realized that this could actually all be automated quite easily. As such, I developed the following procedure.

'---------------------------------------------------------------------------------------
' Procedure : ConvertSQL2QRY
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Save recordsource to queries and update recordsource to use the query
' Copyright : The following may be altered and reused as you wish so long as the
'             copyright notice is left unchanged (including Author, Website and
'             Copyright).  It may not be sold/resold or reposted on other sites (links
'             back to this site are allowed).
' Dependencies: CreateQry()
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2011-04-30              Initial Release
' 2         2022-11-18              Fixed Error Handler Issue
'---------------------------------------------------------------------------------------
Sub ConvertSQL2QRY()
On Error GoTo Error_Handler
    Dim db               As DAO.Database
    Dim qdf              As DAO.QueryDef
'    Dim sSQL             As String
    Dim ctl              As Control
    Dim frm              As AccessObject
    Dim dbo              As AccessObject
    Dim dbp              As Object
    Dim sFrmName         As String
    Dim sFrmRS           As String
    Dim sQryRS As String

    Set db = CurrentDb
    Debug.Print "ConvertSQL2QRY Begin"
    Debug.Print "================================================================================"

    'Check Forms
    For Each frm In CurrentProject.AllForms
        sFrmName = frm.Name
        DoCmd.OpenForm sFrmName, acDesign
        sFrmRS = Forms(sFrmName).RecordSource
        Debug.Print "Processing Form: " & sFrmName
        If Len(sFrmRS) > 0 And Left(sFrmRS, 4) <> "qry_" Then
            Debug.Print "   Converting Form: " & sFrmName
            If Left(sFrmRS, 7) = "SELECT " Then
                sQryRS = sFrmRS
            Else
                sQryRS = "SELECT [" & sFrmRS & "].* FROM [" & sFrmRS & "];"
            End If
            'Create a query based on the active RS and name it based on the form name for
            '   traceability
            CreateQry "qry_" & sFrmName, sQryRS
            'Change the form RS to use the newly created query
            Forms(sFrmName).RecordSource = "qry_" & sFrmName
        End If
        DoCmd.Close acForm, frm.Name, acSaveYes
    Next frm

    Debug.Print "================================================================================"
    Debug.Print "ConvertSQL2QRY End"

Error_Handler_Exit:
    On Error Resume Next
    Set qdf = Nothing
    Set db = Nothing
    Set ctl = Nothing
    Set frm = Nothing
    Set dbp = Nothing
    Set dbo = Nothing
    Exit Sub

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Source: ConvertSQL2QRY" & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Sub

What does this procedure do? Well, it will go through all the forms in your database and create a query using the current record source and then reassign the newly created query as the form’s record source. I run this query just before deploying a database. It is also intelligent enough to only process those forms that don’t already have a query created, so it can be rerun whenever needed should I add new forms, etc…

Please note this procedure is dependent on my CreateQry procedure, so be sure to get it as well!