Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
(ebook) Visual Studio .NET Mastering Visual Basic.pdf
Скачиваний:
120
Добавлен:
17.08.2013
Размер:
15.38 Mб
Скачать

846 Chapter 19 THE MULTIPLE DOCUMENT INTERFACE

When menu items are merged, the parent form’s menu items appear first, followed by the child form’s menu items. To change the order in which two items are merged, set their MergeOrder property. This property is an integer value that determines whether an item appears in front of or after another item. The menu items being merged need not have consecutive MergeOrder values. Those with a smaller value appear in front of others with larger MergeOrder values. The MergeOrder property of the main menu’s items determines the order of the top-level menu. The MergeOrder property of the items in a specific menu determines the order of the commands in this menu.

The Window item, for example, is always the last menu in an MDI application, with the exception of the Help menu. To make sure it is the last item in the merged menu structure, set its MergeOrder property to 99. If your application has a Help menu, set the Help item’s MergeOrder property to 100. You need not do anything about the items of the Window menu; they follow their parent item.

Let’s go through the settings of the menus of the MDI form and its child form of the application you’re building. The Window menu is unique to the MDI form. To make sure it is the last menu on the form, set its MergeType property to Add and its MergeOrder property to 99. The File menu of the MDI form must be merged with the File menu of the child form, so you must set the MergeType property of both items to MergeItems. The MergeOrder property doesn’t make any difference.

Then you must set the MergeOrder property of each item in the File menu of both forms. Switch to the MDI form, select the New item under the File menu, and set its MergeOrder property to 0. We want this item to be the first one in the File menu, even when the two menus are merged. The Exit item must be the last one, so set its MergeOrder property to 9. The items of the File menu on the child form should be merged with the commands of the File menu of the MDI form. Set their MergeType property to MergeItems. The MergeOrder property of the File menu’s items need not change. We’ve already specified that the New command must appear at the top of the menu and the Exit command must appear at the bottom of the menu. The remaining commands will appear between them, and they’ll have the same order as they do in the child form’s File menu.

Then select the Edit and Format menus on the child form. These menus must appear after the File menu, in this order, so set their MergeType property to Add. Again, you don’t have to do anything about their order. The two menus that are unique to the child form (Edit and Format) are placed on the merged menu in the order in which they appear in the menu of their own form. Figure 19.5 shows the parent and child menus, as well as the merged menu. The child menu is shown in design mode, because this menu can’t be displayed on its own.

At this point you can run the application and check out how an MDI application handles the menus. Notice how the parent form’s menu changes when you open new child forms and how it shrinks back to the items of the parent form after you close the last child form.

Built-In Capabilities of MDI Applications

You’ve just created an MDI application. It doesn’t do much, but if you run the project now, you’ll see two forms, one inside the other, as shown in Figure 19.6. To properly start the application, make sure that the MDI form is the application’s startup form. To do this, open the project’s Properties window and set the MDI form (whatever you have named it) as the startup object. If the startup object is the child window, it won’t be displayed by default; you must load it from within the application code. Notice that the child form is contained entirely within the parent form and exists only in that context. If you close or minimize the parent form, the child form also will be closed or minimized.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

MDI APPLICATIONS: THE BASICS 847

Figure 19.5

Parent Menu

Child Menu

The parent menu (top left), the child menu (top right) and the merged parent/ child menu (bottom)

Merged Menu

Figure 19.6

The framework

of an MDI application with a single child form

Use the mouse to move the child form around and change its size. If you click the child form’s Maximize button, the two forms are combined into one, as shown in Figure 19.7.

You can also move the child form outside the parent form, in which case the appropriate scroll bars will be attached to the parent form. In addition, both the child window and the MDI form have a Control menu (which you can open by clicking the icon in the top-left corner) and their own Minimize, Restore, and Close buttons (in the top-right corner).

Note Later in this chapter, you’ll see that the way you name parent and child windows is important to maintaining the Windows graphical user interface (GUI) guidelines. The most important rule for parent and child windows is that the parent form’s caption should be the name of the application and the captions of the child forms should be the names of the documents in each of them.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

848 Chapter 19 THE MULTIPLE DOCUMENT INTERFACE

Figure 19.7

The MDI application from Figure 19.6 after the child window has been maximized

Clicking the child form’s Minimize button reduces the child form to an icon, but always within the parent form. You’ll never see a child form’s icon on the Desktop’s status bar, even if its ShowInTaskbar property is set to True. To restore the minimized form to its original size, double-click the icon. A child window is usually minimized instead of being closed to make room for other documents. In short, the child form behaves like a regular form on the Desktop, only its “desktop” is the parent window.

I have just demonstrated the basic operations of an MDI application with just a few lines of codes. These capabilities are built into the language and are available to your applications through the settings of certain properties. We’re almost ready to build a functional MDI application. But first, let’s see how we can access the controls on the child forms.

Accessing Child Forms

There are two different methods to access the child forms. The first method, and the most common one, is to use the Me keyword. This keyword refers to the form in which the code resides, and since the bulk of the code is on the child form, you can use the Me keyword to access the controls on the child form. The MDI child forms of a text editor, for example, contain a TextBox control where the user can enter and edit text. The following expression returns the text on the active child form:

Me.TextBox1.Text

To select all the text on the active child window, call the TextBox control’s SelectAll method with the following statements:

Me.TextBox1.SelectAll

To access the child form from within the MDI parent form’s code, you can use the ActiveMdiChild property, which represents the active child form. The following statement returns the caption of the active child form:

Me.ActiveMdiChild.Text

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

MDI APPLICATIONS: THE BASICS 849

To access the contents of a TextBox control on the child form from within the MDI form’s code, use the following statement:

Me.ActiveMdiChild.TextBox1.Text

In the last two examples, the Me keyword refers to the MDI parent form, not to the child forms, because the code resides in the MDI form.

If the various child forms weren’t identical, you’d have to insert additional code to make sure that the active form contains the control you want to access. If some of the child forms contain a TextBox control while others don’t, you can’t use the last statement without some error-trapping code. MDI applications that deploy more than a single type of child form are more complicated to code and rather uncommon.

You can also access all child windows through the MdiChildren property of the parent form. This property returns an array whose elements represent the child forms open on the MDI form at any one time. To find out the number of open child forms, read the Length property of this array:

Console.WriteLine(“There are “ & Me.MdiChildren.Length & “ child Forms open”)

This statement works only if it appears in the MDI parent form’s code. To access a child form from within another child form’s code, you must first access the parent form (Me.ParentForm) and then the parent form’s MdiChildren property. The following statements return the values shown in bold if executed from within a child form’s code:

Console.WriteLine(Me.ParentForm.MdiChildren.GetLength(0))

3

Console.WriteLine(Me.ParentForm.ActiveMdiChild)

MDIProject.ChildForm, Text: Child window # 1

Tracking the Active Child Form

The child form you design is the prototype. An instance of a form is a copy that inherits all the properties of the original but exists in your application independently of the original. On an MDI form, all child forms are usually instances of one basic form. All forms all have the same behavior, but the operation of each one doesn’t affect the others. When a child form is loaded, for example, it will have the same background color as its prototype, but you can change this from within your code by setting the form’s BackColor property. No other child form will be affected by that change.

Each child form is totally independent of any other child form, and you can access it from within your code through the Me keyword. This keyword identifies the current form, as long as you program it from within its own form. When you program a command of the child form’s menu, for example, you can access the active child form with the Me keyword.

Sometimes, we want to access an MDI child form from within another form that’s neither a parent nor a child form. In a text-editing application, like the one you’re going to develop in the following section, we want to be able to access the active child form from within the Search & Replace dialog box. The simplest method is to maintain a variable that keeps track of the active MDI child form, and access the child form through this variable. You will see this technique in action in the following section.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

850 Chapter 19 THE MULTIPLE DOCUMENT INTERFACE

VB.NET at Work: The MDIPad Project

In Chapter 5, you built the TextPad application, which is a simple text editor based on the TextBox control. Now you’re going to convert it to an MDI application. An MDI application lets you open and edit multiple documents simultaneously. You can also copy information from one window and paste it into another, and you can arrange multiple documents on-screen so that you can view any other document while editing the active one. All this is possible without invoking multiple instances of the application. Figure 19.8 shows the TextPad application, and Figure 19.9 shows the MDIPad application.

Figure 19.8

The TextPad application

Figure 19.9

The MDIPad application

Start a new project, name it MDIPad, and change the name of its form to MDIForm. Then set its form’s IsMdiContainer property to True. Add another form and name it DocumentForm. This is the MDI child form.

Design two menus, one for the MDI form and another one for the MDI child form. The two menus of the application are identical to the menus of the MDIProject sample, discussed earlier (see Tables 19.1 and 19.2). Then add a TextBox control on the child form. Set its name to Editor and its Dock property to Fill, so that it takes up all the available space on the child form. Add a ListBox control to the child form, too, and set its Visible property to False. We’ll use this control to store the file’s name so that we can quickly retrieve it from within the Save command’s event handler. We’re ready to implement the commands of the two menus, starting with the MDI form’s menu. If you’re in doubt as to the settings of the MergeType and MergeOrder properties of the menu items, open the MDIPad on the CD and examine their settings.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

MDI APPLICATIONS: THE BASICS 851

Programming the New Command

The New command of the MDI form’s File menu opens a new child form. Its implementation is shown in Listing 19.3.

Listing 19.3: The New Command

Private Sub FileNew_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles FileNew.Click Dim child1 As New DocumentForm()

child1.MdiParent = Me child1.Show() child1.Text = “Untitled”

End Sub

The New command doesn’t clear the contents of the active child window, as was the case with the TextPad application. Instead, it opens a new, blank child window. Every time you click the New command, a new child form is displayed and its initial caption is set to “Untitled”.

Programming the Open Command

The Open command’s code is longer but not drastically different from the Open command of the TextPad application. It prompts the user to select a text file through the Open dialog box and then displays the text in the active child window. Listing 19.4 shows the code of the Open command.

Listing 19.4: The Open Command

Private Sub FileOpen_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles FileOpen.Click

OpenFileDialog1.DefaultExt = “*.txt”

OpenFileDialog1.ShowDialog()

If OpenFileDialog1.ShowDialog() = DialogResult.OK Then

Dim fname As String = OpenFileDialog1.FileName

Me.Text = fname.Substring(fname.LastIndexOf(“\”) + 1)

Dim StrReader As System.IO.StreamReader

StrReader = New System.IO.StreamReader(fname)

Editor.Text = StrReader.ReadToEnd

Editor.SelectionStart = 0

Editor.SelectionLength = 0

ListBox1.Items.Add(fname)

ListBox1.Items.Add(Now())

End If

End Sub

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

852 Chapter 19 THE MULTIPLE DOCUMENT INTERFACE

The Open command displays the Open dialog box and retrieves the name of a text file selected by the user. The caption of the active child form is the name of the file. Because the OpenFileDialog control returns the full pathname, we extract the last section of the pathname, which is the filename. Then the file is read and displayed on the Editor TextBox control.

Most of the information you need about each child form can be retrieved through the Me keyword. You will also have to maintain some information that can’t be retrieved directly from the child form. The filename of each open document, for example, isn’t stored on the child form—unless you want to display the file’s pathname on the form’s title bar. You may also want to maintain additional information about each document (like the date and time it was opened, the current magnification for an image, and so on). This information is stored in the invisible ListBox control. You can add all kinds of information to the ListBox control and access it at any time from within your code. The only requirement is that you must decide the order of the item in which each piece of information is stored. The Open command’s event handler stores the path of the file in the first item of the ListBox control and the time the file was opened in the second item of the control. The pathname will be used later by the Save command.

Programming the Save Commands

New documents are saved with the Save As command. The code of the Save As command, shown in Listing 19.5, is the counterpart of the Open command.

Listing 19.5: The Save As Command

Private Sub FileSaveAs_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles FileSaveAs.Click

SaveFileDialog1.DefaultExt = “*.txt”

If SaveFileDialog1.ShowDialog() = DialogResult.OK Then

Dim saveFileName As String

Dim StrWriter As System.IO.StreamWriter

StrWriter = New System.IO.StreamWriter(saveFileName)

StrWriter.Write(Editor.Text)

Me.Editor.Modified = False

ListBox1.Items.Add(saveFileName)

End If

End Sub

The Save As command prompts the user for a filename. This filename is then stored in the first item of the hidden ListBox control, to be used later in saving the document. The Save command’s code retrieves the file’s path from the hidden control and saves the document, as shown in Listing 19.6. If the user attempts to save a new document, the first item of the list will be empty, and the program invokes the SaveAs command automatically.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

MDI APPLICATIONS: THE BASICS 853

Listing 19.6: The Save Command

Private Sub FileSave_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles FileSave.Click Dim saveFileName As String

saveFileName = ListBox1.Items.Item(0) If saveFileName = “” Then

FileSaveAs_Click(sender, e) Else

Dim StrWriter As System.IO.StreamWriter

StrWriter = New System.IO.StreamWriter(saveFileName) StrWriter.Write(Editor.Text)

Me.Editor.Modified = False End If

End Sub

The Save and Save As commands also set the Modified property of the TextBox control on the active child form to False. The reason for this is that the TextBox isn’t aware of the save operation and doesn’t reset the Modified property. We want the Modified property to be up to date, because we’ll examine it when the user attempts to close the application and prompt him accordingly. See the section “Closing an MDI Application” for more information.

Finally, the Close command closes the active child form (and the current document, of course). Its code is shown in Listing 19.7.

Listing 19.7: The Close Command

Private Sub FileClose_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles FileClose.Click

Me.Close()

End Sub

The application doesn’t examine the status of the document and prompt the user if they’re about to close a document that has been edited. As you will see, this action takes place in the child form’s Closing event, which is discussed later in this chapter.

Programming the Format Menu

Let’s add some code behind the Text Color and Page Color commands of the Format menu. These are really trivial operations compared to the Open and Save commands. To implement the two color commands, you must add an instance of the ColorDialog control on the child form and then insert the statements shown in Listing 19.8 in the Click event handlers of the Text Color and Page Color commands.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

854 Chapter 19 THE MULTIPLE DOCUMENT INTERFACE

Listing 19.8: Setting the Text and Page Colors

Private Sub TextColor_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles TextColor.Click

ColorDialog1.Color = Me.Editor.ForeColor

ColorDialog1.ShowDialog()

Me.Editor.ForeColor = ColorDialog1.Color

End Sub

Private Sub PageColor_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles PageColor.Click

ColorDialog1.Color = Me.Editor.BackColor

ColorDialog1.ShowDialog()

Me.Editor.BackColor = ColorDialog1.Color

End Sub

This is the same code you’d use to set the background and foreground colors of a TextBox control on a regular SDI application. You use the Me keyword to access the active child form. If you run the application now, you can open any number of child forms and change their colors. Each child form behaves independently of all others, and it maintains its settings.

The code that sets the font for the active child form is also quite trivial. We display the Font dialog box and then assign the font selected by the user to the Font property of the Editor control, as shown in Listing 19.9.

Listing 19.9: Setting the Editor’s Font

Private Sub EditFont_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs)

FontDialog1.Font = Me.Editor.Font

If FontDialog1.ShowDialog() = DialogResult.OK Then

Me.Editor.Font = FontDialog1.Font

End If

End Sub

For some operations, we need to know when the user switches to another child form. This action is signaled by the MdiChildActivate event, which is an event of the MDI parent form, and you can use it to update internal variables in your code. In this event’s handler, we display the title of the currently active child form on the MDI form’s title bar. Listing 19.10 displays the title of the currently active child form on the MDI form’s caption. In addition, it sets the activeChildForm variable to the currently active child window. This variable points always to the current child window and is used by the Find command, which acts on the document of the active child window.

Listing 19.10: Keeping Track of the Active Child Form

Private Sub MDIForm_MdiChildActivate(ByVal sender As Object, _

ByVal e As System.EventArgs) Handles MyBase.MdiChildActivate

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

MDI APPLICATIONS: THE BASICS 855

If Me.ActiveMdiChild Is Nothing Then Me.Text = “MDIPad - no document”

Else

Me.Text = Me.ActiveMdiChild.Text activeChildForm = Me.ActiveMdiChild

End If End Sub

The activeChildForm variable is declared as Public Shared on the MDI form with the following statement:

Public Shared activeChildForm As DocumentForm

Every time another child form is activated, this variable is updated to reflect the currently active child form. You’ll see shortly how this variable is used in the discussion of the Find command.

Programming the Edit Menu

The Edit menu’s commands handle the selected text on the Editor TextBox control. They’re straightforward: The Copy command copies the selected text to the Clipboard. The Cut command does the same, and then it deletes the current selection. The Paste command extracts the text from the Clipboard and uses it to replace the current selection on the TextBox control. All commands use the expression Me.Editor to access the TextBox control on the active child form, and their code is shown in Listing 19.10.

Listing 19.10: Programming the Text Editing Commands

Private Sub EditCopy_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles EditCopy.Click

If Me.Editor.SelectedText.Length > 0 Then

Me.Editor.Copy()

End If

End Sub

Private Sub EditPaste_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles EditPaste.Click

If Clipboard.GetDataObject.GetData(DataFormats.Text) Then

Me.Editor.Paste()

Else

MsgBox(“no text to paste”)

End If

End Sub

Private Sub EditCut_Click(ByVal sender As Object, _

ByVal e As System.EventArgs) Handles EditCut.Click

If Me.Editor.SelectedText.Length > 0 Then

Clipboard.SetDataObject(Me.Editor.SelectedText)

Me.Editor.Cut()

End If

End Sub

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

856 Chapter 19 THE MULTIPLE DOCUMENT INTERFACE

The Find Command

This is the most interesting part of the application. The Find command must display a Find & Replace dialog box, shown in Figure 19.10. This dialog box must remain on top of the application’s main form, even when it doesn’t have the focus. Design a form like the one shown in Figure 19.10, and set its TopMost property to True.

Figure 19.10

The Find & Replace dialog box of the MDIPad application

To invoke the Find & Replace dialog box from within a child form’s code, you must create an instance of the dialog box and then call its Show method. Insert the following declaration in the child form’s code window, outside any procedure’s definition:

Dim extForm As Form = New FindForm()

Then add the code in Listing 19.11 to the Find command’s Click event handler. This code displays the FindForm dialog box by calling the extForm object’s Show method.

Listing 19.11: Displaying the Find & Replace dialog box

Private Sub EditFind_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles EditFind.Click extForm.Show()

End Sub

Once the dialog box is displayed, users can search for a word or replace one or more instances of a word with another one. The search can be case-sensitive or not, depending on the status of the Case-Sensitive Search check box. The code of the Find button is shown in Listing 19.12.

Listing 19.12: The Find Button

Private Sub bttnFind_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles bttnFind.Click Dim selStart As Integer

Dim srchMode As CompareMethod srchMode = SetSearchMode()

selStart = InStr(MDIForm.activeChildForm.Editor.Text, _ srchWord.Text, srchMode)

If selStart = 0 Then MsgBox(“Can’t find word”) Exit Sub

End If

MDIForm.activeChildForm.Editor.Select(selStart - 1, srchWord.Text.Length) MDIForm.activeChildForm.Editor.ScrollToCaret()

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

MDI APPLICATIONS: THE BASICS 857

bttnFindNext.Enabled = True bttnReplace.Enabled = True bttnReplaceAll.Enabled = True

End Sub

To access the document of the active child form, the code uses the activeChildForm variable of the MDI parent form. This variable references the active child form, and we can use it to access the TextBox control with the document. The code uses the InStr() function because it provides an argument that allows us to perform case-sensitive and case-insensitive searches. The search mode is specified by the srchMode argument, which is calculated by the SetSearchMode() function based on the status of the check box on the dialog box. The SetSearchMode() function examines the value of the check box and returns a member of the CompareMethod enumeration:

Function SetSearchMode() As CompareMethod

If chkCase.Checked = True Then

Return CompareMethod.Binary

Else

Return CompareMethod.Text

End If

End Function

The code behind the Find Next button, shown in Listing 19.13, is quite similar. It uses the InStr() function as well, except that it specifies the index of the character in the text where the search will begin. This index is the location of the character following the current selection.

Listing 19.13: The Find Next Button

Private Sub bttnFindNext_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles bttnFindNext.Click Dim selStart, srchStart As Integer

Dim srchMode As CompareMethod srchMode = SetSearchMode()

srchStart = MDIForm.activeChildForm.Editor.SelectionStart + _ MDIForm.activeChildForm.Editor.SelectionLength

selStart = InStr(srchStart + 1, MDIForm.activeChildForm.Editor.Text, _ srchWord.Text, srchMode)

If selStart = 0 Then

MsgBox(“There are no more instances of the specified word”) bttnFindNext.Enabled = False

Exit Sub End If

MDIForm.activeChildForm.Editor.Select(selStart - 1, srchWord.Text.Length) MDIForm.activeChildForm.Editor.ScrollToCaret()

bttnFindNext.Enabled = True bttnReplace.Enabled = True bttnReplaceAll.Enabled = True

End Sub

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com