Working with Multiple Forms

Processing multiple forms in a single application is a common requirement. This section introduces the basics of the task and demonstrates some techniques for passing values between forms that might not be obvious from the standard Visual Studio .NET documentation. The background and special techniques conveyed by this section can equip you with the knowledge to build powerful form solutions with Visual Basic .NET.

Modal vs. Modeless Forms

You can open one form from another in two ways. If you open a second form as a modal form, you cannot change the focus from the second form until you close it. A message box represents this kind of a form. This behavior is convenient for applications that require a response to the second form before actions on other forms make sense. Another way in which you can open one form from another is nonmodally . In this second style, a user can switch back and forth between the forms without closing the second form before returning to the first form. The spreadsheet windows in a Microsoft Excel workbook file demonstrate this kind of behavior. Opening forms nonmodally is suitable for applications in which users need the freedom to browse two or more forms in any order.

Visual Basic .NET offers different methods for opening one form from another based on whether you open a second form modally or nonmodally. Use the Show method to open a form nonmodally so that users can browse freely between a child form and the parent form used to open the child form. Invoke the ShowDialog method when a parent form needs to open a child form as a modal form. The method name , ShowDialog , connotes the type of form you are opening. In a dialog box, you normally complete an action before returning to the normal flow of an application.

Before you can open a second form, you must instantiate an instance of the second Form class that you want to open. Do this in a Dim statement with the New keyword. The New method requires an existing Form class on which to base its new form instance. You can add a Form class to your project with the Project, Add Windows Form command. This second Form class will not be the basis for a startup object because you want to open an instance based on this Form class from a form instance based on another Form class. In the most straightforward example, you can have two form instances ”one based on a Form1 class and the other based on a Form2 class. You can open an instance of the Form2 class with the Show method or the ShowDialog method from an instance of the Form1 class.

The next sample demonstrates the basics of modal and modeless forms with Form1 and Form2 in the MultipleFormSamples project. The startup object is set to Form1 , which means that the application opens to an automatically opened form instance based on the Form1 class. This form instance contains a couple of buttons . Clicking Button1 opens via the Show method a form instance based on the Form2 class. Clicking Button2 opens another form instance based on the Form2 class; this instance opens by means of the ShowDialog method. The Form2 class has code in its module that detects whether its instances open modally or nonmodally. The application positions modal and modeless instances of Form2 in different locations so that you can readily contrast the behavior of the two instances.

The code behind the Form1 class consists of three event procedures. The Form1_Load procedure commences by positioning the form instance in the top left corner of the screen. Next, the procedure assigns Text property settings to Button1 and Button2 . The Text property setting for Button1 designates the modeless opening of Form2 instances, and the Text property setting for Button2 specifies the modal opening of Form2 instances.

In addition, this procedure sets the width of the two buttons to 150 pixels so that each button can display its full Text property setting. The Click event procedure for Button1 begins by instantiating Form2 and representing the form instance with the frmNonModal variable. Next, the procedure assigns a Text property value for the form instance, reminding the user that it is OK to switch back to Form1 while Form2 is open. The procedure concludes by invoking the Show method for the frmNonModal variable. The Click event procedure for Button2 parallels that for Button1 , but it has some important deviations. The most significant of these is the use of the ShowDialog method for opening an instance of Form2 . You will also note that the caption for the form changes to reflect the fact that you must close this instance of Form2 before you can switch the focus back to the Form1 instance.

 Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Position Form1 in top left corner of desktop. Me.DesktopLocation = New Point(0, 0) Assign Text property for buttons. Button1.Text = "Open Modeless Form2" Button2.Text = "Open Modal Form2" Set width to show Text property setting. Button1.Width = 150 Button2.Width = 150 End Sub Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click Dim frmNonModal As New Form2() Open Form2 in modeless version. frmNonModal.Text = "Form2 - Use Form1 while I am open" frmNonModal.Show() End Sub Private Sub Button2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button2.Click Dim frmModal As New Form2() Open Form2 in modal version. frmModal.Text = "Form2 - Close Me before using Form1" frmModal.ShowDialog() End Sub 

This application is interesting because it instantiates two different instances of the same form class ( Form2 ) that exhibit distinct capabilities. As mentioned earlier, modeless instances of Form2 let a user switch the focus back to Form1 without requiring the closing of Form2 . In fact, users can switch back and forth as often as necessary. Users can change the focus to a form by clicking any blank area on it. When a click to Button2 opens Form2 modally, users cannot change the focus back to Form1 until Form2 closes . To contrast the behavior of the two form instances, you will want to have them occupy different screen areas, which will make it easier for users to confirm how the form instances behave differently. Therefore, the code behind the Form2 class positions form instances on the screen in different desktop locations, depending on whether the form instance is modal or modeless.

The code behind the Form2 class consists of a pair of event procedures. The Form2_Load procedure checks the Modal property of the Form2 instance that is about to open. This property is True whenever a form instance is invoked with the ShowDialog method, but it is False otherwise . When the property is False , the code positions the Form2 instance 15 pixels to the right of the Form1 instance. This procedure assumes that the size for Form1 remains at its default setting of 300 by 300 pixels. If the Modal property is True , it means the user clicked Button2 on the Form1 instance to open Form2 modally. In this case, the Form2_Load event procedure positions the Form2 instance 15 pixels to the right and 15 pixels below the lower edge of the Form1 instance. The Click event procedure for Button1 on Form2 closes the current form instance. Users can also use the Close control in the form s control box.

 Private Sub Form2_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Position modeless version of Form2 to the right of Form1 and above modal version of Form2. If Me.Modal = False Then DesktopLocation = New Point(315, 0) Else DesktopLocation = New Point(315, 315) End If Label button. Me.Button1.Text = "Close" End Sub Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click Me.Close() End Sub 

Figure 5-5 shows the arrangement of the forms for this application. An instance of Form1 appears in the top left corner. If the user clicks the top button that opens an instance of Form2 nonmodally, the form to its right appears. At this point, a user can click back and forth freely between the two form instances. In fact, users can repeatedly click the top button in the Form1 instance and generate multiple modeless instances of Form2 . These instances stack on top of one another, but you can drag copies off the stack to show the underlying instances. In addition, you can shift the focus to any modeless instance of Form2 or back to the Form1 instance. Clicking the bottom button in Form1 creates a modal instance of Form2 . When this happens, the focus shifts to that instance of Form2 , and the user cannot change the focus to another form instance by clicking a blank area on that instance. The only option is to close the modal Form2 instance, which transfers control back to its parent Form1 instance. At that point, the user can shift the focus to any open form instances or open one or more modeless Form2 instances.

click to expand
Figure 5-5: Modeless and modal instances of Form2

Returning a Result from a Child Form

The ease of manipulating forms and the fact that the same Form class can appear in multiple instances within a single application creates some interesting possibilities. The sample code in this section demonstrates the use of a Form class for a startup form in two different ways: as a main form and as a message box. The second instance is not strictly a message box instance, but it simulates some of the behavior of a message box ”particularly when combined with the first instance based on the class for the startup form. The application shows both instances on the screen at the same time. The contents of the message in the second instance of the startup Form class derives from a child form. This child form is a modal form opened from the first instance of the startup Form class.

Demonstrating the Sample Application

This sample is the kind of application that greatly benefits from a demonstration before reviewing the code underlying it. When a user starts the application from the MultipleFormSamples project, Form3 appears by itself with two buttons. One of these buttons has a Text property assignment of Compute . Clicking this button pops up a second modal form to its right, Form4 , which contains two text boxes and a button. The Text property for the button on the modal form is Return Sum . The purpose of the modal form is for the user to input numeric values in TextBox1 and TextBox2 before clicking the button on the form. Figure 5-6 shows the startup instance of Form3 and the Form4 instance. I entered values into the text boxes on Form4 , and the right window depicts the Form4 instance just before a click to the form s button.

click to expand
Figure 5-6: Clicking Compute in the startup instance of Form3 opens an instance of Form4 that is ready to accept quantities to sum.
Note  

The application uses Form3 as the startup object. Therefore, you will need to make sure that Form3 is set as the startup object for the project. One way to do so is to open the Property Pages dialog box for the project and select Form3 as the startup object.

Clicking the Return Sum button in Form4 creates a new instance of Form3 below the instance of Form3 to the left of Form4 and closes Form4 . (See Figure 5-7.) The new instance of Form3 has a look entirely different from its predecessor. First, it contains a label showing the text box values and their sum. Second, the new instance of Form3 shows no buttons ”not even a control box. Third, the size of the second Form3 instance is different from that for the first instance. Finally, when a user clicks the Close button in the first instance of Form3 , it closes both instances of Form3 .


Figure 5-7: After clicking the Return Sum button in Figure 5-6, the screen reconfigures to appear like this.

The Code Behind the Application

You can start creating your version of this application by adding Form3 and Form4 to your project. Form4 looks like the form on the right in Figure 5-6; it is more difficult to discern what the Form3 class looks like because it appears in Figure 5-7 in two radically different layouts. The best way to understand the layout for Form3 is probably with a picture of it in Design view. (See Figure 5-8.) As you can see, the Form3 class has a couple of buttons, a label, and a control box. In addition, the form has a standard shape and size of 300 by 300 pixels. The code behind the form causes Form3 to appear in such different instances in Figure 5-7.


Figure 5-8: The underlying Design view of the Form3 class that appears in two different layouts in the top and bottom forms in Figure 5-7

After adding controls so that your version of Form3 matches the layout shown in Figure 5-8, you can add code to the module behind Form3 . As you can see, the code consists of three short procedures. The Form3_Load event procedure includes just four simple layout statements. First, the event procedure positions the form in the screen s top left corner. Then it assigns Text property settings to Button1 and Button2 . Next the procedure makes the label invisible so that the two buttons appear to be the only controls on the form. The Button1_Click procedure instantiates Form4 and uses frm1 as a variable pointing at the Form4 instance. Then, the procedure opens the form instance by invoking the ShowDialog method; this opens Form4 as a modal form. The Button2_Click procedure closes Form3 and any child (or grandchild) form instances.

As it turns out, the second form in Figure 5-7 is a grandchild of the top form in Figure 5-7. This is because the top instance in the figure instantiates Form4 (shown in Figure 5-6), which, in turn , instantiates the bottom form. Therefore, the Form4 instance in Figure 5-6 is the child of the startup instance of Form3 . However, this Form4 instance instantiates a second instance of Form3 that appears as the bottom form in Figure 5-7. When you close a startup form instance such as the one in the top window of Figure 5-7, Visual Basic .NET closes all forms in a project and exits the application.

 Private Sub Form3_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Format Form3 for main form. DesktopLocation = New Point(0, 0) Button1.Text = "Compute" Button2.Text = "Close" Label1.Visible = False End Sub Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click Instantiate Form4. Dim frm1 As New Form4() Open Form4 modally. frm1.ShowDialog() End Sub Private Sub Button2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button2.Click Closing this instance of Form3 also closes the message from the computation window instance of Form3. Me.Close() End Sub 

The code behind Form4 contains just two procedures. The Form4_Load procedure positions the form instance to the right of the startup instance of Form3 . The Load event procedure also assigns a Text property value, Return Sum , to the button on the form.

After a user enters values in the two text boxes on Form4 , clicking Button1 opens a new instance of Form3 below the startup version that appears to the left of Form4 . The Button1_Click procedure in the module behind Form4 starts by adding the numbers represented by the Text property settings of TextBox1 and TextBox2 and then saves the result in a variable, sum , which has a Double data type. Next, the procedure instantiates a new instance of Form3 and assigns it to the frm1 variable in the procedure. Then the procedure computes a string based on the Text property settings for TextBox1 and TextBox2 as well as the sum variable, and it saves the string in the Text property setting for the Label1 control on the new instance of Form3 .

The new instance of Form3 is now almost ready to show its result, but the form will look odd containing only a couple of buttons that exist for the sole purpose of displaying a result. Therefore, we need to make the buttons invisible; to do so, make their Visible property settings False . You can follow the same approach for making the control box on the form instance invisible. In addition, shorten the height of the new Form3 instance to make the message stand out more in a smaller form instance. In fact, you will need to change the Visible property setting for Label1 so that it is not visible at all. You can also add a Text property setting to the form to explain that it is just a message, and you can position the new instance of Form3 below the startup instance of Form3 . Finally, you can position the new instance of Form3 so that it appears right below the startup instance of Form3 . Several of these formatting assignments require a form instance to show before they can apply to a form instance. Therefore, the procedure invokes the Show method for frm1 before making any property assignments for the new form instance.

The Button1_Click procedure concludes by invoking the Close method for the open instance of Form4 . This leaves just two instances of Form3 open ”the two that appear in Figure 5-7. When the user clicks the Close button in the startup instance of Form3 , the application closes both instances of Form3 .

 Private Sub Form4_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Position Form4 to right of Form3. DesktopLocation = New Point(315, 0) Button1.Text = "Return sum" End Sub Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click Perform computation. Dim sum As Double sum = CDbl(TextBox1.Text) + CDbl(TextBox2.Text) Instantiate new instance of Form3. Dim frm1 As New Form3() Prepare label text for new Form3. frm1.Label1.Text = _ TextBox1.Text & _ " + " & _ TextBox2.Text & _ " = " & _ sum.ToString Show Form3. frm1.Show() Then, reformat Form3 instance for showing a message. frm1.Button1.Visible = False frm1.Button2.Visible = False frm1.ControlBox = False frm1.Height = 150 frm1.Label1.Visible = True frm1.Text = "Message from Computation Window" frm1.DesktopLocation = New Point(0, 315) Close Form4. Me.Close() End Sub 

Passing Values Both Ways Between Forms

The preceding sample displays a message on a child form, but the parent of the form with the message is itself a child of another form that remains open until a user clicks its Close button to remove the startup object for the project. Although the preceding sample returns from a child form a result on a separate form instance for display along with its parent form, the sample does not pass a value back to the parent form. In addition, no communication of values from the parent form to the child form occurs. However, you will commonly need to share data between parent and child form instances. The next pair of forms, Form5 and Form6 in the MultipleFormSamples project, illustrates a couple of ways to exchange data between a parent form and its child. Exchanging values between two forms is more complicated than it might seem at first because no built-in collection of open forms exists (as exists in classic Visual Basic). Although you can create your own custom collection of form instances, many developers might prefer this alternative approach, which depends on public variables and shared variables .

Passing data values from a parent form to a child form is relatively straightforward. Declare one or more public variables in the child form. Then when a parent form creates an instance of the child form, the parent form can assign values to the child form as property values for the class instance. Creating a reference from the child form to its parent form is more challenging. You can readily create a new instance of the parent form class from the child form instance, but this new parent form instance will not be the same instance as the one used to create the child form. However, by declaring a public, shared variable in the parent form class, any child instance can update the shared variable in the original parent instance. You do not need to create a new instance of the parent form. When the child form relinquishes control to its original parent form instance, the newly updated shared value will be available to the parent form instance.

After adding Form5 and Form6 to your project, designate Form5 as the startup object for the project. Form5 has two buttons on it. Button1 instantiates Form6 and passes a pair of values to the Form6 instance in the process. Button2 displays the value of the shared variable in a message box. Form6 has an even simpler design than Form5 : Form6 contains no controls. The Load event procedure does all the work for the form, including displaying a message box with the passed values. This Load event procedure also sets the shared public value in its parent form ( Form5 ).

The following code listing shows the custom code behind Form5 . Critically, the listing commences with a shared public variable named input1 . This is the variable to which the child form instance ( Form6 ) assigns a value. After the variable declaration, the listing contains three short event procedures. The Load event procedure merely formats the two buttons on Form5 . The Click event procedure for Button1 demonstrates the syntax for passing values from a parent form to a child form. First, you instantiate an instance of the child form ( Form6 ). The sample assigns this instance to the frm1 variable. Second, you assign values to the public variables for the child form. The public variables in Form6 have the names input1 and input2 . Third, you invoke the Show (or ShowDialog ) method for the variable representing the child form to display the child form for this parent form instance. The third event procedure, Button2_Click , merely presents in a message box the value of the parent form s shared public variable ( input1 in Form5 ). This value is initially 0, but the child form assigns a new value to the variable.

Note  

The sample uses the variable name, input1 , in both Form5 and Form6 . However, Visual Basic .NET is smart enough to track separate values for both variables and avoid any name conflicts between the two.

 Expose input1 in Form5 as a public, shared field. Public Shared input1 As Double Private Sub Form5_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Format buttons on form. Button1.Text = "Pass values to second form" Button1.Width = 175 Button2.Text = "Displayed Shared input1 value" Button2.Width = 175 End Sub Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click Instantiate an instance of Form6. Dim frm1 As New Form6() Assign value to public fields in the form. frm1.input1 = 10 frm1.input2 = 20 Open the form. frm1.Show() End Sub Private Sub Button2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button2.Click Display current value of input1 on Form5. MsgBox(input1.ToString) End Sub 

The code behind Form6 is even more basic. Its listing includes a couple of public variable declarations followed by a single event procedure. The public variable declarations correspond to the variables getting assignments in the code behind Form5 . This correspondence is the trick that enables a parent form to pass values to a child form. The form s Load event procedure performs three tasks . First, it opens a message box to display the passed values, which allows you to confirm the success of the operation. Second, the procedure assigns a value to the shared public variable in the parent form by creating a new instance of the parent form ( frm2 ) and assigning a value to the shared variable ( frm2.input1 ). Because Form5 assigns the value 10 to input1 in Form6 , the expression in Form6 for frm2.input1 assigns the value 10.5 to input1 in Form5 . Notice that the code never creates an instance of Form5; this is not necessary because Form5 exposes its input1 field as a shared, public variable. Third, the event procedure closes the child form. This operation returns control to the parent form instance ( Form5 ). After control returns to the parent form, you can click Button2 to display the value returned from the child form to the parent form.

 Declare input1 and input2 as public fields from Form6. Public input1 As Double Public input2 As Double Private Sub Form6_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Display passed values for input1 and input2 in a comma-delimited format. MsgBox(input1.ToString & ", " & input2.ToString) Reference Form5 to make its shared, public variable available and assign a value to it. Dim frm2 As Form5 frm2.input1 = input1 + 0.5 Close the form instance after you no longer need it. frm2.Close() Close this form to return focus to its parent form. Me.Close() End Sub 

Figure 5-9 shows the sample application in operation. The top version of Form5 shows the form instance just after the application starts. A click to Button1 causes the display of the message box to its right. The message box actually results from the Load event procedure for Form6 . Initially clicking Button2 before clicking Button1 presents a message box with a value of 0. This value reflects the current value for the shared, public variable: input1 from Form5 . However, the second display of Form5 shows the result of clicking Button2 after clicking Button1 . At this point, Form6 updated the default value of 0 for input1 to 10.5. This value appears in a message box to the right of Form5 just after a click to Button2 .

click to expand
Figure 5-9: The application for Form5 and Form6 demonstrates approaches to passing values between parent and child form instances.
 


Programming Microsoft Visual Basic. NET for Microsoft Access Databases
Programming Microsoft Visual Basic .NET for Microsoft Access Databases (Pro Developer)
ISBN: 0735618194
EAN: 2147483647
Year: 2006
Pages: 111
Authors: Rick Dobson

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