> ## Content Index
> Fetch the complete content index at: https://nolongerset.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# TryGetForm: Set a Strongly-Typed Form Object in Access VBA
- URL: https://nolongerset.com/trygetform/
- Published: 2023-04-20T17:05:12.000Z
- Updated: 2026-05-08T12:50:23.000Z
- Description: The TryGetForm function helps you convert "stringly-typed" code into "strongly-typed" code when working with Access forms in VBA.
- Author: Mike Wolfe
- Tags: Code Library, #FormFunctions, Defensive Programming, #Import 2026-05-20 02:59

Using `Forms!MyForm!MyControl` is an insidious form of ["stringly-typed" programming](https://www.hanselman.com/blog/stringly-typed-vs-strongly-typed).

## The VBA Bang (!) Operator: Hidden Stringiness

Ben Clothier [explained the situation in a comment](https://nolongerset.com/turn-runtime-errors-into-compile-errors/#commento-comment-card-959ab6d887bb4748b62b4c5d4d743807c933d2867392ac656331bfb25679af61) in my article about turning runtime errors into compile errors:

> I would argue that the average VBA code is far more stringly than might be apparent.
> 
> People will probably look at expression like:
> 
> ```
> Forms!FormA!txtThing = Forms!FormB!txtOtherThing
> 
> ```
> 
> and think it's perfectly fine. But that is a complete lie! There is a great deal of information out there that's simply *wrong* about the bang operator. A common description that's wrong is to claim that it's for indexing into a collection. Nope.
> 
> All the bang operator does is translate the code into:
> 
> ```
> Forms.Item("FormA").Controls("txtThing").Value = Forms.Item("FormB").Controls("txtOtherThing").Value
> 
> ```
> 
> Thus, holy strings, batman!

## Ben's Recommended Workaround

Ben went on to suggest a way to improve the situation: use the form's class module to convert the *str*i*ngly-typed* code to *str*o*ngly-typed* code.

Ben continues:

> If we wanted to convert those into a compile-time error whenever the forms or its controls are deleted or renamed or changed in other ways, then we need to write code like the following:
> 
> ```
> Dim frmA As Form_FormA
> Dim frmB As Form_FormB
> 
> If TryGetForm("frmA", frmA) Then
>  If TryGetForm("frmB", frmB) Then
>    frmA.txtThing.Value = frmB.txtOtherThing.Value
>  Else
>    Debug.Print "frmB isn't loaded"
>  End If
> Else
>  Debug.Print "frmA isn't loaded"
> End If
> 
> ```

## My Implementation of Ben's TryGetForm() Function

Ben didn't include an implementation of the TryGetForm function, but it is easy enough to infer based on the context. In fact, I've [written in the past about TryXxx functions](https://nolongerset.com/trysetfocus/):

> .VB.Net has several standard library methods named `TryXxx`. These functions try to perform some operation, then return a boolean to indicate whether the operation succeeded or failed.  
> ...  
> I'm going to start adopting TryXxx as my own personal naming convention for functions and methods that share this purpose.

In other words, we want a function that will:

- return a Boolean to indicate success or failure (i.e., the function itself should never fail)
- set the form object to the open form (i.e., the form argument is treated as an "Out" parameter)

Additionally, I wanted to provide some feedback to the developer to help differentiate between typos (where the form name is misspelled) and situations where the form name is spelled correctly, but it simply is not open.

## The Code

```vba
' ----------------------------------------------------------------
' Purpose   : Attempts to set a form object variable to an open form.
'               Returns True if successful, False otherwise.
' Author    : Mike Wolfe (inspired by Ben Clothier)
' Date      : 4/20/2023
' Source    : https://nolongerset.com/trygetform/
' ----------------------------------------------------------------
Public Function TryGetForm(FormName As String, _
                           Out_FormObject As Access.Form) As Boolean
    On Error Resume Next
    Set Out_FormObject = Forms(FormName)

    Select Case Err.Number
    Case 0  'Success
        Const DesignView As Integer = 0
        If Out_FormObject.CurrentView = DesignView Then
            'This won't be an issue for end users as they SHOULD NOT have
            '   the ability to edit forms, but checking for
            '   design view will save us as developers from annoying
            '   "Type Mismatch" errors in our calling code
            TryGetForm = False
            Debug.Print "TryGetForm = False: " & FormName & " open in Design View"
        Else
            TryGetForm = True
        End If
    Case 2450
        'Microsoft Access cannot find the referenced form '|'.
        'The form you referenced may be closed or may not exist in this database.
        
        'Let's determine whether it's closed OR doesn't exist
        Err.Clear
        Dim Dummy As String
        Dummy = CurrentProject.AllForms(FormName).Name
        If Err.Number = 0 Then
            Debug.Print "TryGetForm = False: " & FormName & " is not open"
        Else
            Debug.Print "TryGetForm = False: " & FormName & _
                        " does not appear to exist; check spelling for typos"
        End If
        
    Case Else
        Debug.Print "TryGetForm = False: [" & Err.Number & "] " & Err.Description
    End Select
    
    Err.Clear
    
End Function
```

## Testing and Usage

To test the function, let's create three forms in an empty database (names are in bold):

- **Form1\_Open**: a form with Has Module = Yes that will be open during testing
- **Form2\_Closed**: a form with Has Module = Yes that will be closed during testing
- **Form3\_OpenNoModule**: a form with Has Module = No that will be open during testing
- **Form4\_OpenDesignView**: a form with Has Module = Yes that will be open in Design View during testing

Additionally, our test routine will attempt to open a form named "Form5\_DoesNotExist" that does not exist so that you can see how that situation is handled.

```vba
Public Sub TestTryGetForm()
    On Error Resume Next

    DoCmd.OpenForm "Form1_Open"
    DoCmd.Close acForm, "Form2_Closed"
    DoCmd.OpenForm "Form3_OpenNoModule"
    DoCmd.OpenForm "Form4_OpenDesignView", acDesign

    'This form has a code module and is open
    Dim Form1 As Form_Form1_Open
    If TryGetForm("Form1_Open", Form1) Then
        Debug.Print "Success: "; Form1.Name
    End If

    'This form has a code module but is closed
    Dim Form2 As Form_Form2_Closed
    If TryGetForm("Form2_Closed", Form2) Then
        Debug.Print "Success: "; Form2.Name
    End If

    'There is no code module for this form
    Dim Form3 As Access.Form
    If TryGetForm("Form3_OpenNoModule", Form3) Then
        Debug.Print "Success: "; Form3.Name
    End If

    'This form is open, but it is in Design View
    Dim Form4 As Form_Form4_OpenDesignView
    If TryGetForm("Form4_OpenDesignView", Form4) Then
        Debug.Print "Success: "; Form4.Name
    End If
    
    'This form does not exist
    Dim Form5 As Access.Form
    If TryGetForm("Form5_DoesNotExist", Form5) Then
        Debug.Print "Success: "; Form5.Name
    End If

End Sub
```

![](https://storage.ghost.io/c/eb/d7/ebd732c1-5f03-4f07-b386-5d08557e15c9/content/images/2023/04/image-34.png)

## Epilogue

I'll admit, the `TryGetForm()` function ended up a lot longer than I originally anticipated it would be. 

As I wrote the `TestTryGetForm()` function, I kept running into situations that I wanted to handle better, such as:

- detecting whether a form was open but in design view
- determining whether AccessError 2450 was due to the form not being open or actually missing from the database

This goes to show that there is a lot of value in writing stand-alone single-purpose routines as that approach allows you to handle edge cases and corner cases in **one place** within your code base as they pop up.