

Introduction

Back in 2003, when VB.NET first appeared, those of us trying to move from classic VB found we had a really steep learning curve to navigate. Of the many things that had us scratching our heads, one was how to work with multiple forms in Windows Forms projects, something that had changed fundamentally at that time. So I wrote a set of four posts to help others through the initial maze. Of course, some things have changed in the 10+ years since then – one good example being the reintroduction of the default instance of each form class which had been dropped from the first versions of VB.NET. Following some posts on VBCity, I’ve decided to dust them off, update them here and there and publish them here.

The Disappearing (and Reappearing) Default Instance

As I mentioned above, the old faithful way of getting access to an instance of a Windows Form in classic VB was simply to refer to the Form by its class name. This wasn’t available in the first versions of VB.NET, probably because the team at Microsoft wanted to wean us off what was essentially a lazy way of instantiating forms, and furthermore one not in keeping with the new Object Oriented Programming(OOP) approach. As it happens, they did reintroduce the default instance later, but their original thinking was probably on the right track. It’s important to realize that a form is just an instance of a class, so it makes sense to make developers create an instance of the form class themselves and not leave it to some smoke and mirrors trick behind the scenes in Visual Studio. (In Classic VB, the Form was often thought of as being somehow different from other entities.)

So instead of using the default instance, the better way to create a new instance of a form is to – well, create a new instance of a form! Let’s assume you have a project with two forms whose class names are Form1 and Form2. To create a new instance of Form2 from a button click event in Form1 you could use code like this:

1 2 3 4 5 6 7 Private Sub Button1_Click(sender As Object , e As EventArgs) Handles Button1.Click Dim F2 As New Form2() F2.Show() ' Recommendation: Don't use the default instance ' Form2.Show() End Sub

F2 is a variable that is created in line 2 and has an instance of the Form2 class assigned to it. To refer to this instance from now on, you would do so by its variable name. You can see that this is done in line 3, where the Show method of the Form class is run against the F2 instance.

This will create a new instance of Form2, but the problem with this approach is that if the user clicks the button on Form1 several times, a new instance will be created each time. That’s not a problem in and of itself except that you will have multiple Form2 instances, all using the same variable name - F2. If that isn’t a recipe for a coding nightmare, I don’t know what is! So, let’s find some better ways.

Modal Form Instance

If you only want to create and show one instance of a second form and you want the user to work with this form and close it before returning to the original calling form, then what you need is the ShowDialog method. This creates what is known as a modal form. Personally, I’ve never been very clear why this term is used. It certainly doesn’t fit with how the term is used in math and in its standard dictionary definition it seems to mean something that isn't substantiated. Anyway, whatever the reasoning, the thing to understand about modal forms is that once one is shown, the user can’t access the caller until the modal form has been closed.

1 2 3 4 5 6 Private Sub Button1_Click(sender As Object , e As EventArgs) Handles Button1.Click Dim F2 As New Form2() F2.ShowDialog() End Sub

Now, the user can’t click the button on Form1 multiple times, because access to it is blocked until the Form2 instance is closed.

Instantiate the Form Instance at the Class Level

If you’re new to VB (and as you’re reading this beginner’s article, I assume you are) then you will sooner or later run into a problem with scope. Scope in VB.NET terms defines whether an object – such as the F2 instance of the Form2 class – can be accessed in other parts of the code. In the example above, the F2 instance has been declared (created) inside the Button1 click event handler. As a result, with this particular line of code the F2 form can only be accessed within that event handler. This might be fine for your needs and as a general rule it is recommended that you restrict scope to the lowest level that will produce the result you want.

One way to widen the scope is to declare the F2 form instance at a higher level. To enable me to demonstrate a meaningful example, I’ll need to add a second button to Form1. It now looks like this:

The Buttons are named Button1 and Button2. Here is the complete code for Form1:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Public Class Form1 Dim F2 As New Form2 Private Sub Button1_Click(sender As Object , e As EventArgs) Handles Button1.Click F2.Show() End Sub Private Sub Button2_Click(sender As Object , e As EventArgs) Handles Button2.Click F2.Close() End Sub End Class

Note that the declaration and instantiation of F2 is now on line 3 – inside the code for the class but outside the code for the Button1 click event handler. (‘Declaration’ is the term used to describe the creation of the variable F2; ‘Instantiation’ is where the keyword ‘New’ is used to instantiate or create a new instance of the form.)

Now on line 7 – inside the button click event handler – the Show method of that Form2 instance is called and the second form will become visible.

In order to show you that the F2 variable has scope outside of the first event handler, you can see that I’ve added a line of code to the click event handler of the second button. There on line 13 the Close method of the Form class is called on that F2 Form. This approach will work just fine in many situations, but not all. And the reason it won’t always be suitable hinges on whether you will need the F2 Form2 instance to be shown again for a second or subsequent time after it has been closed. In other words, if you will only ever create and show F2 once in the current session of the application then you won’t have a problem. But if the user is allowed to (in this example) close the F2 form with the button on Form1 or by clicking on the Close button at the top of Form2 and then wants to create F2 again – then you do have a problem.

And the reason for the problem is that when the F2 Form instance is closed, VB will automatically dispose of that F2 variable. You instantiate it manually in line 3 of that code snippet above, but it’s the system that disposes of it once the user has closed the form instance. And once it’s disposed, as you’d expect, its no longer available and so a runtime exception will occur and – unless you do something about it – the application will crash at that point.

Exception Handling

There’s quite an easy way around this problem, but to explain it I’ll first have to introduce the Try-Catch block. Many applications – even good, well constructed ones – can crash at runtime. A really good application will take steps to deal with the possibility of crashes (aka Exceptions) and one way of doing this is with Exception Handlers inside a Try-Catch block. I’m only going to go over this briefly, otherwise this ‘Multiple Forms’ article is going to get too lengthy. But briefly an Exception Handler is used to check if an exception has occurred and, if it has, do something about it. The skeleton of a Try-Catch Block is:

Try Catch Finally End Try

You place code that you want to run in the Try section. You can add code that will do something if an exception occurs in the Catch section. You can put code that will run no matter what happened in the two previous sections.

Here’s an example of using a Try-Catch block for the Disposed Form problem:

1 2 3 4 5 6 7 8 9 10 Try F2.Show() Catch ex As Exception If F2.IsDisposed Then Dim F2 As New Form2 F2.Show() End If Finally End Try

In the Try section an attempt is made to show the original F2 instance (the one created at the Class level). If this form has been closed by the user, it will automatically be disposed, so the test in the Catch section is to see if F2 has been disposed.

If it has then line 5 will create a brand new Form2 variable that once again is called F2. The key point to take away here is that this F2 is not the original F2 brought back to life. It’s shiny and new, but just happens to have been given the same name. To paraphrase that old saying: “F2 is dead. Long live F2”. If you’re wondering why I seem to have made things harder by creating something different with the same name, there is a reason. If I decided to use F3 as the name this time, then the code in the Button2 event handler wouldn’t work because it’s looking for a form instance named F2. In a real application there might be several references to the F2 form instance, not just this rather artificial one I’ve used for demo purposes.

So now in line 6 this new F2 form instance is shown.

I don’t have anything I want to put in the Finally section in this example, but in other applications it might be a very useful option.

Shutdown Mode

While not directly linked to how multiple instances of forms are created, I just want to divert momentarily here to talk about Shutdown Mode. In Windows Forms projects you are given two default options for closing the running application. One is that the application will automatically close when there are no more instances of any forms still live. The other is to close the application as soon as the startup form closes, even if other forms are still in existence at that point.

The default setting is for the application to end as soon as the startup form is closed. This might not be what you want in some of your projects, so let me show you how you can change this. Go to the Project menu item in Visual Studio, make sure that the ‘Application’ tab is selected and down near the bottom of this dialog box you will see the Shutdown mode combo box:

Click on the combo box to change to ‘when last form closes’ if this is what you need. (There is at least one other way you can use in code to shut down the application, but these are options built into Visual Studio).

Summary

There are enough options shown here to get you through most situations where you want to create and access multiple forms in VB.NET. Coming up in the next part are some other variations that you might want to consider if none of those above are right for your application. Later in the series, of course, we’ll need to think about how you can get two forms to talk to each other – i.e. how to read content from one form and show or use it on another form.

Posted Jan 03 2015, 10:23 AM by Ged Mead