Issues Involving Forms

Issues Involving Forms

Let s start our discussion of forms by looking at the differences between a Visual Basic 6 form and a Visual Basic .NET form. Fortunately, these differences are relatively minor. The basic concept of a form, for example, is the same between Visual Basic 6 and Visual Basic .NET. The form serves as a surface where you can place controls to create a composite form. The way in which you create a form, set properties, and add controls to it using the Visual Basic .NET designer is also essentially the same as in Visual Basic 6. You should find yourself at home when you create and edit Visual Basic .NET forms.

Event Firing Differences

A subtle difference between Visual Basic 6 and Visual Basic .NET is that some events do not fire in the same relative order. For example, in Visual Basic 6 the Load event is the first event you expect to fire. This is not true for Visual Basic .NET, however, where the Resize event occurs before the Load event. Also, depending on the control properties you set at design time, a number of other events can occur before the Load event. Table 12-3 lists the events that can occur before the Form.Load event.

Table 12-3 Visual Basic .NET Events That Can Occur Before the Form.Load Event

Object

Event

Form

Move

Form

Resize

CheckBox

Click

ComboBox

Change

OptionButton

Click

TextBox

TextChanged

Problems generally occur when you write your code in such a way that code in one event relies on values or objects being initialized or set in another event. For example, suppose that you create an element of a control array in the Form.Load event and write code to position the control array element in the Form.Resize event. If the Resize event fires after the Load event, everything works as planned. If, on the other hand, the Resize event fires before the Load event, an error will occur in your Resize code. The reason is that you will be attempting to access a control array element that does not yet exist.

To illustrate this problem, consider the following code for a Visual Basic 6 standard EXE application, in which Form1 contains a CommandButton with the Index property set to 0.

Private Sub Form_Load() ' Load a new element of the Command1 control array Load Command1(1) Command1(1).Visible = True End Sub Private Sub Form_Resize() ' Position control array element 1 below control array element 0 Command1(1).Top = Command1(0).Top + Command1(0).Height + 3 End Sub

If you use the Upgrade Wizard to upgrade the application to Visual Basic .NET, the upgraded code will be as follows:

Private Sub Form1_Load(ByVal eventSender As System.Object, _ ByVal eventArgs As System.EventArgs) _ Handles MyBase.Load ' Load a new element of the Command1 control array Command1.Load(1) Command1(1).Visible = True End Sub 'UPGRADE_WARNING: Event Form1.Resize may fire when form is initialized. Private Sub Form1_Resize(ByVal eventSender As System.Object, _ ByVal eventArgs As System.EventArgs) _ Handles MyBase.Resize ' Position control array element 1 below control array element 0 Command1(1).Top = _ VB6.TwipsToPixelsY(VB6.PixelsToTwipsY(Command1(0).Top) + _ VB6.PixelsToTwipsY(Command1(0).Height) + 3) End Sub

When you run the upgraded Visual Basic .NET application, an error occurs on the assignment of Command1(1).Top. The error is Control array element 1 doesn t exist. It occurs because the Resize event takes place before the Load event.

You can fix this problem, and others like it, by using a class-level Boolean variable to keep track of whether the Load event has been executed. If it has not executed and is not in the process of executing, you should not execute any code in the current event. In the example just given, you can declare a new form-level variable called m_Loaded as follows:

Private m_Loaded As Boolean = False

In the Form1_Resize event procedure, check m_Loaded and do not execute any code unless it is True:

If m_Loaded Then ' Position control array element 1 below control array element 0 Command1(1).Top = _ VB6.TwipsToPixelsY(VB6.PixelsToTwipsY(Command1(0).Top) + _ VB6.PixelsToTwipsY(Command1(0).Height) + 3) End If

In the Form1_Load event procedure, set m_Loaded to True as follows:

m_Loaded = True ' Load a new element of the Command1 control array Command1.Load(1) Command1(1).Visible = True

Because the Resize event fires after the Load event in Visual Basic 6, you need to emulate this behavior in Visual Basic .NET. You do this by calling the Resize event before exiting the Form1.Load event procedure, as follows:

m_Loaded = True ' Load a new element of the Command1 control array Command1.Load(1) Command1(1).Visible = True ' Emulate Visual Basic 6 behavior by invoking the Resize event Form1_Resize(Me, New System.EventArgs())

Heed Upgrade Warnings

The Form1_Resize event procedure for the upgraded Visual Basic .NET code includes the comment UPGRADE_WARNING: Event Form1.Resize may fire when form is initialized. The Upgrade Wizard includes this type of warning for any upgraded event that is known to fire before the Load event. To be safe in situations such as this, you may want to implement the same types of changes that we applied to the Resize event in the previous example.

The Default Form: DefInstance

Visual Basic 6 supports the notion of a default form. A default form is the default instance of the form that Visual Basic creates for you automatically. Having a default form instance allows you to write code such as the following:

Form1.Caption = "Caption for my default form"

Visual Basic .NET does not support a default form instance. If you write the equivalent code in Visual Basic .NET, as follows:

Form1.Text = "Caption for my default form"

you will run smack into a compiler error informing you that there is no shared member called Text on Form1. One way to solve this problem is to add a shared method to your form that returns the default instance in other words, the first created instance of the form. For example, for a default Visual Basic .NET Windows application project, you could add the following code to Form1:

Public Shared m_DefInstance As Form1 Public Shared ReadOnly Property DefInstance() As Form1 'If no form instance exists, create one 'Note: if the form is the startup form it will 'already exist If m_DefInstance Is Nothing Then m_DefInstance = New Form1() End If Return m_DefInstance End Property

This code sets up the DefInstance property but does not initialize the m_DefInstance variable to the first form instance created. To accomplish this initialization, you need to initialize the variable in the Sub New constructor for the form. The Sub New constructor exists on every form and can be found in the #Region code block labeled Windows Form Designer generated code. Expand the #Region code block and insert the following assignment in Sub New at the end of the subroutine:

If m_DefInstance Is Nothing Then ' Initialize m_DefInstance to the first form created m_DefInstance = Me End If

Now you can write code that accesses the default instance of a form as follows:

Form1.DefInstance.Text

The default form is an example of a Visual Basic 6 semantic that does not exist in Visual Basic .NET. However, as we have demonstrated here, you can write code to cause your Visual Basic .NET forms to adopt the same semantic.

It is important to understand the purpose of the DefInstance property because you will likely see it used in your upgraded application. It is used when you access properties or controls using the name of the form. The Upgrade Wizard automatically implements the DefInstance shared method on all upgraded forms. However, the implementation that the wizard applies to your upgraded form is much more complicated than the simple example given here. The generated code takes into account whether the form is a startup or not. It also contains logic to initialize the default instance variable correctly when the form is a multiple document interface (MDI) form or an MDI child form. You can examine and even modify the code that the wizard generates. The DefInstance-related code is generated within the #Region blocks Windows Form Designer generated code and Upgrade Support.

Application Lifetime and Forms

Chapter 10 introduced differences in application lifetime between Visual Basic 6 and Visual Basic .NET. Let s look into this area in more detail. An application in Visual Basic 6 application terminates when the last form is unloaded. A Visual Basic .NET application, on the other hand, terminates when the startup form associated with the application is unloaded. This means that your Visual Basic .NET application can terminate when one or more forms is still showing.

note

The way you specify the startup form in Visual Basic .NET is similar to the way you do so in a Visual Basic 6 application. To set the startup form in Visual Basic .NET, right-click the project name in the Solution Explorer and select Properties from the shortcut menu. You are presented with the project settings dialog box, which groups various settings under a number of categories. Select the General category under Common Properties, and choose the desired startup form from the Startup Object list. Figure 12-3 shows where you can find the Startup Object list.

Figure 12-3.

Startup Object list in the Visual Basic .NET project settings dialog box.

When you create a new Visual Basic .NET application or upgrade a Visual Basic 6 application, the lifetime of the startup form determines the lifetime of the application. If you unload the startup form, the application terminates no matter what other forms are currently open. If the startup form is your main form, this termination will most likely not be an issue. If, on the other hand, the form that determines the application lifetime is a secondary form, such as an MDI child form, you will probably need to make some changes to your application.

The simplest way to deal with this issue is to identify a form in your application that you consider to be the main form. For example, if your application is composed of an MDI form with a number of MDI child forms, you would identify the MDI form as the main form. You should set this form as the startup form for the application.

If you want your application to terminate when the last form is closed, you need to write code that manages the lifetime of your application. Let s look at a Visual Basic 6 application that has two forms: Form1 and Form2, in which Form2 is shown from Form1 s Load event. In Visual Basic 6, the application terminates when you close both forms, regardless of the order in which you close them. Form1 contains the following code in its Form_Load event procedure to show Form2:

Private Sub Form_Load() Form2.Show End Sub

If you upgrade the project to Visual Basic .NET, the application will terminate as soon as you close Form1, even if Form2 is still showing. Suppose you want to change the behavior of the Visual Basic .NET application so that it behaves like the Visual Basic 6 application. The reason Form1 determines the lifetime for the Visual Basic .NET application is that the compiler includes an invisible piece of code that starts up your application and shows the startup form. The code the compiler includes is

System.Windows.Forms.Application.Run(New Form1())

Application.Run is a .NET Framework function that loops internally processing messages and waits for the form instance that is passed to it to terminate. If you do not want your application to be associated with any particular form, you can substitute a version of Application.Run that takes no arguments as follows:

System.Windows.Forms.Application.Run()

This version loops until you call Application.Exit from somewhere in your program. You can control the execution of your application by calling this version of Application.Run from Sub Main. For example, you could show Form1 and then call Application.Run from Sub Main as follows:

Sub Main() Form1.DefInstance.Show() ' Equivalent to Form1.Show ' in Visual Basic 6 System.Windows.Forms.Application.Run() End Sub

Create Sub Main in a new code module called Module1, and then set Module1 as the startup object for the project.

Since the application is no longer tied to the lifetime of any particular form, you need to add code that calls Application.Exit when the last form is unloaded. One way to do this is to have each form increment a global form count every time a form instance is created and then decrement the form count when the instance is destroyed. When the application form count is decremented and reaches 0, meaning that no more forms are loaded, call Application.Exit. To increment and decrement the global form count, you can add two functions to Module1 called AddForm and RemoveForm. You also need to declare a module-level variable that will keep track of the form count. Here is an example of the code you need to add:

Dim FormCount As Integer 

Sub AddForm() FormCount += 1 'Equivalent to FormCount = FormCount + 1 End Sub Sub RemoveForm() FormCount -= 1 'Equivalent to FormCount = FormCount - 1 'At least one form remains, keep going If FormCount > 0 Then Exit Sub End If 'No more forms end the application 'Note: Make sure this call is the last statement in the Sub 'System.Windows.Forms.Application.Exit() End Sub

You then need to add calls to AddForm and RemoveForm to both Form1 and Form2. You can add the call to AddForm to the Form_Load event procedure and a call to RemoveForm to the Form_Closed event procedure. Here is the resulting code for Form1:

Private Sub Form1_Load(ByVal eventSender As System.Object, _ ByVal eventArgs As System.EventArgs) _ Handles MyBase.Load AddForm() Form2.DefInstance.Show() End Sub Private Sub Form1_Closed(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Closed RemoveForm() End Sub

Here is the code you need to add to Form2:

Private Sub Form2_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load AddForm() End Sub Private Sub Form2_Closed(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Closed RemoveForm() End Sub

As long as Module1 is set as your startup object, the application will behave like the Visual Basic 6 application. You can close Form1 and Form2 in any order. When the last form is closed, Application.Exit is called and the application terminates.

MDI Forms

Both Visual Basic 6 and Visual Basic .NET support MDI parent and child forms. The difference between the two environments lies in how you create the MDI parent form and show the MDI child form. To create an MDI application in Visual Basic 6, you add a special MDIForm to your application to act as the MDI parent form. Visual Basic .NET has no concept of an MDIForm as a separate form type. Instead, you set the IsMdiContainer property of a Visual Basic .NET form to True.

In Visual Basic 6, you specify MDI child forms by setting the MDIChild property of a form to True. When the MDI child form is shown, it always displays within the MDI parent form. Visual Basic .NET does not work this way. In Visual Basic .NET, there is no MDIChild property. Instead, you must write code to set the MdiParent property of a form to the MDI parent form. For example, if you want Form2 to be an MDI child form of MDI parent Form1, you need to write the following code:

Dim MyForm2 As New Form2 MyForm2.MdiParent = Me MyForm2.Show

In the code example given here, assume that the code is written in the module for Form1. For example, we can include this code in the Form1_Load event procedure to show the MDI child Form2 from Form1.

Lifetime of MDI Form Applications

As we discussed in the previous section, the lifetime of a Visual Basic .NET application is tied to the startup form. The same is true of MDI form applications, but with an interesting twist. Visual Basic 6 allows you to specify an MDI child form as the startup form in an MDI form application. Specifying an MDI child form as the startup form has the advantage that you don t have to write any code to show the MDI parent and MDI child form by default. When the MDI startup child form is shown, it checks to see whether the MDI parent form is showing. If it is not, the MDI parent is shown automatically and the MDI child form is displayed within the MDI parent.

If you upgrade an application in which an MDI child form is the startup form, you will find that it will terminate when you close the MDI child form. To prevent this, you need to specify the MDI parent form, not the child form, as the startup form.



Upgrading Microsoft Visual Basic 6.0to Microsoft Visual Basic  .NET
Upgrading Microsoft Visual Basic 6.0 to Microsoft Visual Basic .NET w/accompanying CD-ROM
ISBN: 073561587X
EAN: 2147483647
Year: 2001
Pages: 179

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net