Dynamic opening of forms

MAF

New member
Joined
Sep 11, 2007
Messages
4
Programming Experience
1-3
VBNet users:

I'm attempting to move some VB6 code to .Net 2005. The following is how we dynamically create the forms in VB6:

Set frmName = Forms.Add(strFormName)
Call frmName.DisplayForm(Me, strParameter)
' .DisplayForm is a Public Sub. strParameter may or may not be populated.

Since strParameter is sometimes very important to the function of the form being opened I must keep it. What is a good way to do the above in .Net 2005?

Any help will be appreciated.

Thanks,

MAF
 
In .Net each class is a type, so when you create a form it is inherited from base class Form, when you name it for example testForm then this is a class of its own. To create a new instance of a type from its string name you have to use reflection (you can't write: Dim x As New "testForm1"), or a Select Case with one case for each type, for simple cases with just a few cases this is easier. For example you have two forms testForm1 and testForm2, both have the DisplayForm method:
VB.NET:
    Sub createformCase(ByVal name As String)
        Select Case name
            Case "testForm1"
                Dim f As New testForm1
                f.Displayform()
            Case "testForm2"
                Dim f As New testForm2
                f.Displayform()
        End Select
    End Sub
"As New" is short for "Dim f As testForm1 = New testForm1"; declaring the variable, creating the instance and assigning it to the variable.

For reflection the instance created is returned as type Object, you can use late-binding to call the public method directly from this object, but no intellisense support and it's not safe coding. To access the method from the type you have to cast the object to that type. The above example you could have been written like this:
VB.NET:
Dim f As Object = New testForm1
f.DisplayForm() 'late-binding, Object type doesn't have a DisplayForm method, but this works too
DirectCast(f, testForm1).DisplayForm() 'casting and early-binding, testForm1 type have a DisplayForm method
You can't cast to a string literal that is supposed to represent a type. So you couldn't do this:
DirectCast(f, "testForm1").DisplayForm()
Here's where interfaces come to play, you should read up on that topic if it's new to you, it's basic OOP knowledge. In short, all your forms that need to have the DisplayForm method can implement an interface that serves as some kind of base type and provides access to this method, so they can all be casted to the interface type. Here an example of such interface:
VB.NET:
Interface IForm
    Sub DisplayForm(Optional ByVal input As String = Nothing)
End Interface
And here a form implementing it:
VB.NET:
Public Class testForm
    Implements IForm

    Public Sub DisplayForm(Optional ByVal input As String = Nothing) Implements IForm.DisplayForm
        If input IsNot Nothing Then
            Me.Text = input 'just sets the input as form title
        End If
        Me.Show()
    End Sub
End Class
and finally on to the reflection variant of the createform method:
VB.NET:
    Sub createformReflection(ByVal name As String)
        Dim f As IForm = Me.GetType.Assembly.CreateInstance(Me.GetType.Namespace & "." & name) 'implicit type casting to IForm
        f.DisplayForm("testing") 'IForm type have a DisplayForm method
    End Sub
 
To add, you don't have to do this:
VB.NET:
Set frmName = Forms.Add(strFormName)
from .NET 2.0 forward. There exists the My.Application.OpenForms property which returns all open forms, regardless of which thread opened them. Any time a form is opened, it is automatically added to the collection by the framework. See this article for more information: How to: Access All Open Forms of an Application.
And this: My.Application.OpenForms
 
I thought classic VB's Forms.Add("formname") created a new form instance from its string name. OpenForms can't do that if the instance isn't already created.
 
Source: Opening a Form by Type or Name

I was able to use this code and it works.
VB.NET:
Imports System.Reflection 

Public Class FormFunctions 
    Public Shared Function GetFormByName(ByVal FormName As String) As Form 
        'first try: in case the full namespace has been provided (as it should  ) 
        Dim T As Type = Type.GetType(FormName, False) 
        'if not found, search for it 
        If T Is Nothing Then T = FindType(FormName) 
        'if still not found, throw exception 
        If T Is Nothing Then Throw New Exception(FormName + " could not be found") 
        Return CType(Activator.CreateInstance(T), Form) 
    End Function 
#Region "Assemblies and types" 
    Public Shared Function GetAllAssemblies() As ArrayList 
        Dim al As New ArrayList 
        Dim a As [Assembly] = [Assembly].GetEntryAssembly() 
        FillAssemblies(a, al) 
        Return al 
    End Function 

    Private Shared Sub FillAssemblies(ByVal a As [Assembly], ByVal al As ArrayList) 
        If Not al.Contains(a) Then 
            al.Add(a) 
            Dim an As AssemblyName 
            For Each an In a.GetReferencedAssemblies() 
                If Not an.Name.StartsWith("System") Then FillAssemblies([Assembly].Load(an), al) 
            Next 
        End If 
    End Sub 

    Public Shared Function GetAllTypes() As ArrayList 
        Dim a As [Assembly], t As Type, al As New ArrayList 
        For Each a In GetAllAssemblies() 
            For Each t In a.GetTypes 
                If Not al.Contains(t) Then al.Add(t) 
            Next 
        Next 
        Return al 
    End Function 

    Public Shared Function FindType(ByVal Name As String) As Type 
        Dim T As Type 
        For Each T In GetAllTypes() 
            If T.Name = Name Then Return T 
        Next 
        Return Nothing 
    End Function 
#End Region 
End Class 

'example call: 
Dim f As Form = FormFunctions.GetFormByName("Form1") 
f.Show()
 
Last edited:
Back
Top