Solved What is the key of dynamic forms in the Forms object?

Local time
Today, 13:40
Joined
Aug 10, 2024
Messages
20
Hello,

The key in the Forms object for forms opened from the interface is the name of the form (see Case1).

When I dynamically create a form it is also added to the Forms object. However, the key used to add the dynamic form is obviously not the form's name (see Case2). It seems that Access/VBA generates a unique key to avoid collisions. Unfortunately, there seems to be no way to read out the keys in the Forms object. Does somebody know how this key is constructed? Is this somewhere documented?

Code:
Sub Case1()
    ' Form1 opened from interface.
    Debug.Assert CurrentProject.AllForms("Form1").IsLoaded
        
    Debug.Print Forms("Form1").Name
End Sub

Sub Case2()
    Debug.Assert Not CurrentProject.AllForms("Form1").IsLoaded
    
    Dim f As New Form_Form1
    Debug.Print Forms(f.Name).Name  ' Run-time error 2450
End Sub
 
i get "the same" name using both sub Cases.
you need to set Has Module on design view in order to use Case2 sub.

hasmodule.jpg
 
If you open a non-default instance of a Form, it is added to the Forms collection without a string key. This is the same behaviour as if you add something to a VBA.Collection and don't provide a key. In this case, you cannot reference the object using a string key, there is only the numeric index.
 
If you open a non-default instance of a Form, it is added to the Forms collection without a string key. This is the same behaviour as if you add something to a VBA.Collection and don't provide a key. In this case, you cannot reference the object using a string key, there is only the numeric index.
Yes, I can see that this is a possible explanation if the Forms object is indeed a VBA.Collection.
 
You can identify your dynamic form with its hWnd property

Yes, I can see that this is a possible explanation if the Forms object is indeed a VBA.Collection.
It's a collection, but it does not implement the VBA.Collection object, it's some other object.
 
This doesn't make a difference on my end. No matter whether "Has Module" is true or false Case2 errors out.
you should modify the code to:

Code:
Sub Case2()
    Debug.Assert Not CurrentProject.AllForms("Form1").IsLoaded
    
    Dim f As New Form_Form1
    Debug.Print f.Name
End Sub
 
@backcountryprogrammer

What you are saying is correct. The named index is not passed when simply instantiate its class. In fact the name is passed using the docmd method.
There is an obvious reason for this. If you instantiate a form directly you can instantiate multiple instances of the same form. However and index key in any collection has to be unique. If you would open multiple Form1 then you cannot have multiple keys "Form1". Using the docmd method you cannot instantiate multiple Form1 so it can assign the unique name index.

Here is a test to prove that.

Code:
Public Sub TestNamedIndex()
  Dim F1 As Form
  Dim i As Integer
 
 'Open f1  using lower case
 'DoCmd.OpenForm "f1"
'Open F1 using upper case
  DoCmd.OpenForm "F1"
  Set F1 = Forms("F1")
  'Instantiate F2 / open
 
  Set F2 = New Form_F2
  F2.Visible = True
 
  Debug.Print "Form Count " & Forms.Count
 
  For i = 0 To Forms.Count - 1
    Debug.Print "Form Name " & Forms(i).Name
  Next i
 
  Debug.Print "F1: " & Forms("f1").Name
  Debug.Print "F2 by index: " & Forms(1).Name
  Debug.Print "F2 by name: " & Forms("F2").Name; ""
End Sub

This shows that in fact
1. Both forms are added to the Forms collection
2. Both forms can be called by Number index.
3. Only the form opened with the docmd can be called by the Named index.
4. The name gets constructed in the Docmd call. How do I know that.
If I pass F1 in the docmd the returned name of the form is F1, if I pass f1 the returned name of the form if f1. You can test above.
5. Has nothing to do with if it has a module or not. You can test. It never gets a named index by instantiating. If it did not have a module it would have failed when you tried to instantiate Dim f As New Form_Form1, not when you tried to call it by it name.

Unfortunately the FORMS collection is read only. You can not add to it and assign a named index.
I do not know why you are opening a form this way unless you plan to open multiple instances of the same form. This requires you to persist it with a variable and set visibility. If multiple instances of the same form you manage using your own Forms collection.
Some version of this.
 
Last edited:
you should modify the code to:

Code:
Sub Case2()
    Debug.Assert Not CurrentProject.AllForms("Form1").IsLoaded
   
    Dim f As New Form_Form1
    Debug.Print f.Name
End Sub
This certainly prints the name of the form. But the question was about how a dynamically created form is keyed into the Forms object. I'd observed that it is not the forms name which is demonstrated by Case2 example. To which I added it can't be the forms name to avoid a name collision (I implied the possibility of multiple dynamic form instances in existence at the same time).
 
What you are saying is correct. The named index is not passed when simply instantiate its class. In fact the name is passed using the docmd method.
There is an obvious reason for this. If you instantiate a form directly you can instantiate multiple instances of the same form. However and index key in any collection has to be unique. If you would open multiple Form1 then you cannot have multiple keys "Form1". Using the docmd method you cannot instantiate multiple Form1 so it can assign the unique name index.
I concluded that the key can't be the forms name to avoid name collisions (in case multiple instances of the same form are instantiated which I implied).

Given all the info collected here, we have to assume that dynamically created forms either have no key in Forms (as mentioned by @MarkK) or it is unknown to us but yet we still can access it by numerical index.
 
a dynamically created form
You only open a form, creating it is something else => use design mode and take appropriate action by hand or code.

A form is a class, and opened forms are "only" instances of this class. The form name is actually the name of the class, which is then passed on as a property of the instance.

If you want to open several instances of the same form and use them at the same time, you need a variable that receives the reference to the instance for precise addressing. Or you can open one instance as a main form and other instances as subforms. Then you can reference each one individually.
 
Last edited:
Has nothing to do with if it has a module or not.
If the form does not have a module, you cannot create an instance using...
Code:
Dim frm As Form
Set frm = New Form_fNoModule
Only forms with modules are considered "Microsoft Access Class Objects". Forms without modules, for instance, do not appear in the VBE Project Explorer window.
But in the original post, if the form did not have a module the error would have occurred on the first of these two lines, not the second...
Code:
    Dim f As New Form_Form1
    Debug.Print Forms(f.Name).Name  ' Run-time error 2450
 
If the form does not have a module, you cannot create an instance using...
That was what I meant, but not said well. Clearly it was instantiated therefore it had a module, so that could not be the problem.
 
You only open a form, creating it is something else => use design mode and take appropriate action by hand or code.

A form is a class, and opened forms are "only" instances of this class. The form name is actually the name of the class, which is then passed on as a property of the instance.

If you want to open several instances of the same form and use them at the same time, you need a variable that receives the reference to the instance for precise addressing. Or you can open one instance as a main form and other instances as subforms. Then you can reference each one individually.
Yes true. The term created is ambiguous. Instantiated is the right term for an object brought into existence by the New operator.
 

Users who are viewing this thread

Back
Top Bottom