DevCity.NET - http://devcity.net
Multiple Forms in VB.NET. Part 1
http://devcity.net/Articles/94/1/multipleforms.aspx
Ged Mead

Ged Mead (XTab) is a Microsoft Visual Basic MVP who has been working on computer software and design for more than 25 years. His journey has taken him through many different facets of IT. These include training as a Systems Analyst, working in a mainframe software development environment, creating financial management systems and a short time spent on military laptop systems in the days when it took two strong men to carry a 'mobile' system.

Based in an idyllic lochside location in the West of Scotland, he is currently involved in an ever-widening range of VB.NET, WPF and Silverlight development projects. Now working in a consultancy environment, his passion however still remains helping students and professional developers to take advantage of the ever increasing range of sophisticated tools available to them.

Ged is a regular contributor to forums on vbCity and authors articles for DevCity. He is a moderator on VBCity and the MSDN Tech Forums and spends a lot of time answering technical questions there and in several other VB forum sites. Senior Editor for DevCity.NET, vbCity Developer Community Leader and Admin, and DevCity.NET Newsletter Editor. He has written and continues to tutor a number of free online courses for VB.NET developers.

 
by Ged Mead
Published on 6/28/2003
 
This article by vbCity Leader XTab is particularly aimed at VB6 Upgraders, although complete beginners will find it useful. It explains a variety of ways in which you can open and deal with multiple forms in VB.NET; techniques which are quite different from the VB.OLD ways. A downloadable Solution is included, which shows working examples of each of the seven methods covered in the article, plus an additional two methods not documented in the article.

Multiple Forms in VB.NET. Part 1

Article source code: multipleforms.zip

_________________________________

 Authors' Note February 2006:  

     This article was written in June 2003 and was aimed at users of the VB.NET 2002 and 2003 editions who were upgrading from VB6.   Now that VB2005 has arrived, much of the content has been overtaken by improvements in that new edition and won't be applicable if this is the version you are using.  

________________________________

Introduction

One of the first hurdles you're going to come up against when you move from Classic VB to VB.NET is getting to grips with the new ways of dealing with forms.

Your approach to forms in VB.NET is fundamentally different and hinges on the fact that the Framework implements forms as classes, so now you need an object variable to instantiate it. This article is aimed at helping you through some of those basic steps.

Instead of loading and showing a form in the old way (eg. Form2.Show, what you have to do is create an instance of the form and you can then manipulate this form object.

Method 1: Show More Than One Instance of a Second Form

Here's one way. Let's assume that you have a project that contains a Form1 and a Form2. You have already set Form1 to be the startup form and it contains a button which, when clicked, will display an instance of Form2 to the user. Assuming this button is called ShowForm2Button, the code you need is as follows:

Private Sub ShowForm2Button_Click(ByVal sender As System.Object_
    ByVal e As System.EventArgsHandles ShowForm2Button.Click
    Dim F2 As New Form2()
    F2.Show()
End Sub

If you try this code out, you'll find that it works fine, as far as it goes. But you will also discover that it is possible to have several instances of Form2 displayed at the same time if you click the ShowForm2Button before you have closed down the current instance of Form2. Each click of the button does exactly what the code tells it to do - create a new instance of a Form2 Class object and show it.

Of course, this may not be what you need in your project. You may want to have only a single instance available at any one time.

This can be achieved in several ways. Each has advantages and disadvantages, and some may yield unexpected results to the OOP-unwary developer. The following way might be an acceptable fix for you in many situations:

Method 2: Show Second Form Modally

There is a potentially easy way round the problem. Show the second form modally (a concept you'll be familiar with from VB.OLD, I'm sure). The .NET syntax is:-

Private Sub ShowForm2Button_Click(ByVal sender As System.Object_
    ByVal e As System.EventArgsHandles ShowForm2Button.Click
    Dim F2 As New Form2()
    F2.ShowDialog(Me)
End Sub

Now, as you'd expect, the user can't get focus back to Form1 to click that pesky button again until they've closed down this instance of Form2.

However, there will probably be times when you want the best of both worlds, that is you want the user to only have one instance of your second form instantiated at any one time, but you want them to be able to see and use both Form1 and Form2 at the same time.

The solutions for showing forms in this way are easy. Where the problems start to kick in are that you may get side effects that are not so welcome if you don't take steps to deal with them.

Method 3: Allow Only One Instance of Second Form.

Type this into the main body of the form (i.e. outside any Subs, Events or Methods)

    Dim F2 As New Form2()

And use this code in the button's click event:

Private Sub ShowForm2Button_Click(ByVal sender As System.Object_
    ByVal e As System.EventArgsHandles ShowForm2Button.Click

    F2.Show()

End Sub

As promised, this will only show the single instance of Form2, because it isn't instantiated afresh every time you click the button. That's the up side.

The down side is that if you close Form2, then try and display it again by clicking on the button you will generate an exception.

The error message tells you why - you instantiated the Form2 object when Form1 was first created and you disposed of it when you closed it by clicking on the little x.

Trying to show it again will not succeed now because clicking on the button doesn't actually create another instance - it's simply trying to show a non-existent instance.

Let's find a couple of workarounds for this little glitch.

Method 4: Showing/Hiding a Second Form.

Instead of closing Form2, why don't we simply hide it? In many situations this may be an acceptable solution.

The code for Form1 is the same as for the above method. Your Form2 will need to be rigged so that it hides itself. Here's the easiest way:

  1. Change Form2's ControlBox Property to False. (This removes the ability of the user to close this form by using the little x).
  2. Add a button to Form2. Call this button FinishedButton.
  3. Use the following code in the button's click event:
Private Sub FinishedButton_Click(ByVal sender As System.Object_
    ByVal e As System.EventArgsHandles FinishedButton.Click
    Me.Hide()
End Sub

What we've done here is to create a situation where there is only one instance of Form2 and this instance is shown when the ShowForm2Button is clicked and hidden when the FinishedButton is clicked. The user is no wiser as to whether a form is closed or hidden.

So, does this solve all possible requirements? Of course not - this is programming; there's always another problematic scenario just around the corner!

Method 5: Showing/Hiding a Second Form (Alternative)

What if you need to allow the user to have access to the Minimize or Maximize buttons in the ControlBox? As far as I know, although you can disable them, you can't have just these two buttons visible without also showing the exit button.

My fix for this involves cutting out the middleman. If there has to be a ControlBox and it has to contain the little x, then let's short circuit that little x. Here's how:

Leave Form2 with it's ControlBox available and dispense with the FinishedButton. What we'll do is change the code that gets fired when the user clicks on that little x. This is the form's Closing event. Add this code to Form2.

Private Sub Form2_Closing(ByVal sender As Object_
    ByVal e As System.ComponentModel.CancelEventArgsHandles MyBase.Closing
    '  Bypass the instruction to Close this form
    e.Cancel = True
    '  But hide it from the user.
    Me.Hide()
End Sub

Be aware of a not so obvious knock-on effect here, though, if Form1 is not the startup form for your application. In this situation, when you close Form1 it will NOT automatically close Form2 (which is what always happens if Form1 is the startup form). So, something to keep in mind there - if you're happy to have Form2 still open once Form1 has closed, then that's fine; if not, then add this code to Form1's Closing event:

    If Not IsNothing(F2Or Not F2.IsDisposed Then F2.Close()

And it will take Form2 away with it when it closes.

Method 6: Check Current Status First

There is another way. Well, there's almost always another way, isn't there? This is based very closely on code provided by DevCity.NET member DrDave. It steps through a checklist of the possible states of the second form and takes the appropriate action depending on what it finds.

I've tried to make the steps as clear as possible with the commenting, but if you're like me you'll probably have to read it and try it a few times before it all clicks into place.

Here's the code:

Declaration outside of any methods:

    Private WithEvents F2 As Form2

And this code goes in the button's click event:

Private Sub ShowForm2Button_Click(ByVal sender As System.Object_
    ByVal e As System.EventArgsHandles ShowForm2Button.Click

'  If the instance still exists... (ie. it's Not Nothing)
    If Not IsNothing(F2Then
        '  and if it hasn't been disposed yet
        If Not F2.IsDisposed Then
            '  then it must already be instantiated - maybe it's
            '  minimized or hidden behind other forms ?
            F2.WindowState = FormWindowState.Normal  ' Optional
            F2.BringToFront()  '  Optional
        Else
            '  else it has already been disposed, so you can
            '  instantiate a new form and show it
            F2 = New Form2()
            F2.Show()
        End If
    Else
        '  else the form = nothing, so you can safely
        '  instantiate a new form and show it
        F2 = New Form2()
        F2.Show()
    End If
End Sub

Method 7: Flip-Flop Form1 and Form2

Another possible scenario might be where you need Form1 hidden while Form2 is on view, and for Form1 to be redisplayed when Form2 is closed.

This gets a bit more complicated, but only a bit. In the same way that we needed an object variable to reference our Form2s in the examples above, we will need a reference back to the instance of Form1 which we can use in Form2.

There are various ways of doing this, but we're going to overload the Form's constructor to achieve our aim. Less technically, this simply means that we'll create an alternative version of the 'New' Sub that all forms contain. This one will make a note of which form called it into existence.

Starting with Form1, add this code to the form:

Private Sub ShowForm2Button_Click(ByVal sender As System.Object_
    ByVal e As System.EventArgsHandles ShowForm2Button.Click
'  Note the "Me" in brackets.  Very important!
        Dim F2 As New Form2(Me)  ' Instantiate it
        F2.Show()   ' Show it
        Me.Hide()   ' Hide Form1
End Sub

Don't worry if you get a wiggly blue line error at this stage in Form1's code. The next bit of code in Form2 will resolve that. Form2 needs this code:

Add an additional Sub New:

Public Sub New(ByVal caller As Object)
    MyBase.New()
    InitializeComponent()
    '  Note which form has called this one
    CallingForm = caller
End Sub

Note that it doesn't replace the code already in the "Windows Form Designer generated code" region; this is an additional (overloaded) constructor.

Next, Form2 needs this declaration in the usual declarations area (ie. outside of any other code blocks) :

    Private CallingForm As Object

For this example, I have used the variation which bypasses the Closing event and simply hides Form2, but this is optional if it doesn't suit your purposes. The logic is the same if you do want Form2 to be closed, not merely hidden.

Private Sub Form2_Closing(ByVal sender As Object_
    ByVal e As System.ComponentModel.CancelEventArgsHandles MyBase.Closing
    e.Cancel = True
    Me.Hide()
    '  If the CallingForm still exists then show it now
    If Not Is Nothing(CallingFormThen CallingForm.Show()
    '  then dispose of it's reference here.
    CallingForm = Nothing
End Sub

(If you don't want to bypass the Closing event as shown above, then you can put a button on your Form2 and use the code above, but without the e.Cancel = True line.)

A happy side effect of using this approach is that it also gets round the problem of unwanted multiple instances of Form2. Obviously, because Form1 is hidden whenever Form2 is on display, the user can't get to the button to fire up another Form2 instance.

Summary

This article has only covered a sample cross-section of some of the ways you can open multiple forms. There are other techniques (MDI, 'Form within a form' and owned forms, for example) that haven't been included here. But hopefully there is enough information here for most purposes in the early days of your journey up that long and tiring .Net learning curve.

If you do want to see some working code for MDI or owned forms, then you can check out the attached solution. It includes all seven methods above, plus these two.

Of course, opening forms is only part of the story. You will also need a grasp of how to pass data between forms and also how to access one form's controls from another form. And finally you will also need to learn a few safe and trusted ways of closing down the forms you want closed, and leaving open those you still need available. Disposal of finished forms is an important part of good development technique. It's planned that these topics will be covered in later parts of this series.

Related devCity.NET articles: