MDI (multiple document interface) applications allow you to display multiple documents at the same time. Each document, which is actually just a set of data, is displayed in its own window within the confines of a parent window. Older versions of Word and Excel are examples of MDI applications. | In this section, we will build a small word processor. It will be an MDI version of Notepad. The program can be found in MDIDemo . | Step 1: Create an MDI Parent Form The first step in building an MDI application is to define the parent form for the application. The easiest way to do this is to configure the startup form that was provided when the application was generated. We named the form MyNotepadForm and set the Text property to MyNotepad. By setting the form's IsMDIContainer property to True, the form becomes our MDI parent form. A side effect of setting this property to True is that the background of the form changes to a dark gray. (See Figure 12-2.) Figure 12-2. Designating an MDI parent form. Our next step will be to add the main menu, as designed below. Table 12-3 summarizes the interesting properties. F ile W indow H elp N ew T ile A bout MyNotepad... O pen... C ascade S ave As... C lose ----------- E x it Table 12-3. Property Values for the MainMenu Control in MyNotepadFormDemo Text | Name | Other Properties | &File | mnuFile | | &New | mnuNew | | &Open... | mnuOpen | | &Save As... | mnuSaveAs | Enabled: False | &Close | mnuClose | Enabled: False | - | mnuSeparator1 | | E&xit | mnuExit | | &Window | mnuWindow | MDIList: True Visible: False | &Tile | mnuTile | | &Cascade | mnuCascade | | &Help | mnuHelp | | &About MyNotepad... | mnuAbout | | To build a menu separator, which is a line that separates subitems on a menu, you must set the Text property to a hyphen (-). The MDIList property, which will be discussed in an upcoming section, causes VB.NET to place a list of all MDI child windows at the bottom of the menu. Figure 12-3 shows the application if it is executed at this point. You can see that the Save As and Close menus are disabled because no document is open. Figure 12-3. An MDI form's main menu. Step 2: Create an MDI Child Form The MDI child forms are the primary forms that users interact with in MDI applications. The example shown in this section uses a RichTextBox control. However, MDI applications do not have to be word processing-type applications. Often, the child forms are reports or data entry forms. In our application, we have created a child form by adding another Windows Form to our application named DocForm . We set the Text property to MyDocument, and we added a RichTextBox control to the form with the properties summarized in Table 12-4. A RichTextBox control is similar to a textbox, except that individual characters can be formatted. Table 12-4. Property Values for the RichTextBox Control in DocForm Property | Value | Name | rtbData | Text | (blank) | Docking | Fill | The Docking property is used to determine if, and how, the control docks to the window. By setting the Docking property to Fill , the RichTextBox consumes the entire client area of the window. Each time the user chooses New from the menu, the application creates a new child window containing a RichTextBox control. At any given time, there may be zero or more of these open child windows. To achieve this behavior, we must create a new instance of the form in the mnuNew's Click event handler. We must also indicate that this form is a child of our parent MDI form by setting its MdiParent property to reference the parent form. Private Sub mnuNew_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuNew.Click Dim myDoc As New DocForm() myDoc.MdiParent = Me myDoc.Show() End Sub Figure 12-4 illustrates the application with two child forms open. Figure 12-4. MDI child forms. Step 3: Coding the Window Menu MDI applications usually have a Window menu with submenus for switching between windows or documents. We saw in step 1 that our MDIDemo application is no exception. By setting the Window menu's MDIList property to True, we are telling VB.NET to automatically keep track of the open child windows and present a list of them at the bottom of this menu (see Figure 12-5). Figure 12-5. A typical MDI Window menu. We can implement behavior to arrange the child windows using the MDI parent form's LayoutMdi method. It allows us to tile windows horizontally or vertically, cascade them, and arrange them as icons. Private Sub mnuTile_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuTile.Click Me.LayoutMdi(MdiLayout.TileVertical) End Sub Private Sub mnuCascade_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles mnuCascade.Click Me.LayoutMdi(MdiLayout.Cascade) End Sub Step 4: Interacting with the Active Child Form Within an MDI parent form, you can use the ActiveMDIChild property to determine which child form has focus. We will need this in the MyNotepadForm so that we can implement the Close menu item. In the Click event handler for mnuClose , we close the active MDI child form. Private Sub mnuClose_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuClose.Click Me.ActiveMdiChild.Close() End Sub When you have several controls on a form, you may also need to determine which control is active. In this case, the ActiveControl property is used to access the control with the focus on the active child form. Step 5: Dealing with Menu Appearance To begin with, we want to make sure that our menu items are only enabled (or visible, in the case of the Window menu) when their actions are allowable . We built two helper procedures for this. One is used when there are child forms, while the other is used when there are no child forms. Public Sub SetMenuOpenDoc() mnuSaveAs.Enabled = True mnuClose.Enabled = True mnuWindow.Visible = True End Sub Public Sub SetMenuNoDoc() mnuSaveAs.Enabled = False mnuClose.Enabled = False mnuWindow.Visible = False End Sub Within an MDI application, the MDI parent form has an MDIChildActivate event that is triggered whenever a child form is activated or closed . In the handler for this event, we will write code to change the menu options that are available based on the status of the MDI child form that is activated. Private Sub MyNotepadForm_MdiChildActivate(ByVal sender _ As Object, ByVal e As System.EventArgs) _ Handles MyBase.MdiChildActivate If Me.ActiveMdiChild Is Nothing Then SetMenuNoDoc() Else SetMenuOpenDoc() End If End Sub Step 6: Finishing the Application At this point, we know enough about MDI applications to finish our application. We can begin by coding the Click event handlers for the Close, Exit, and About menus. They are quite simple. Private Sub mnuClose_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuClose.Click Me.ActiveMdiChild.Close() End Sub Private Sub mnuExit_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuExit.Click Me.Close() End Sub Private Sub mnuAbout_Click(ByVal sender As Object, ByVal _ e As System.EventArgs) Handles mnuAbout.Click MessageBox.Show("MyNotepad v1.0", "MyNotepad", _ MessageBoxButtons.OK, MessageBoxIcon.Information) End Sub To get the Open and Save As menus working, we will use the FileOpenDialog and FileSaveDialog respectively to prompt the user for the file name. We will then use the RichTextEdit box methods LoadFile and SaveFile to actually read or write the file. Private Sub mnuOpen_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuOpen.Click dlgOpen.Filter = "Text Files*.txtAll Files*.*" Dim ans As DialogResult = dlgOpen.ShowDialog() If ans = DialogResult.OK Then Try Dim myDoc As New DocForm() myDoc.MdiParent = Me myDoc.rtbData.LoadFile(dlgOpen.FileName) myDoc.Show() Catch openErr As Exception MessageBox.Show(openErr.Message, "Error", _ MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End If End Sub Private Sub mnuSaveAs_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles mnuSaveAs.Click dlgSave.Filter = "Text Files*.txtAll Files*.*" dlgSave.FileName = "Untitled.txt" Dim ans As DialogResult = dlgSave.ShowDialog() If ans = DialogResult.OK Then Try Dim myDoc As DocForm = _ CType(Me.ActiveMdiChild, DocForm) myDoc.rtbData.SaveFile(dlgSave.FileName) Catch saveErr As Exception MessageBox.Show(saveErr.Message, "Error", _ MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End If End Sub Each of the above examples used the Filter property to specify the filters that appear in the file open and file save dialogs. A filter is defined by specifying the filter text strings and the filter's search criteria separated by the pipe () character. If the search criterion includes several file extensions, they are separated by semicolons. For example: dlgOpen.Filter = _ "Text Files (*.txt)*.txt" & _ "Image Files (*.bmp; *.ico)*.bmp;*.ico" & _ "All Files (*.*)*.*" shows three text strings in the dropdown filter list: Text Files (*.txt) Image Files (*.bmp;*.ico) All Files (*.*) The search string (ex: *.txt) does not have to appear in the text version of the filter (ex: Text Files (*.txt)). It is simply a matter of preference. |