- •Using Your Sybex Electronic Book
- •Acknowledgments
- •Contents at a Glance
- •Introduction
- •Who Should Read This Book?
- •How About the Advanced Topics?
- •The Structure of the Book
- •How to Reach the Author
- •The Integrated Development Environment
- •The Start Page
- •Project Types
- •Your First VB Application
- •Making the Application More Robust
- •Making the Application More User-Friendly
- •The IDE Components
- •The IDE Menu
- •The Toolbox Window
- •The Solution Explorer
- •The Properties Window
- •The Output Window
- •The Command Window
- •The Task List Window
- •Environment Options
- •A Few Common Properties
- •A Few Common Events
- •A Few Common Methods
- •Building a Console Application
- •Summary
- •Building a Loan Calculator
- •How the Loan Application Works
- •Designing the User Interface
- •Programming the Loan Application
- •Validating the Data
- •Building a Math Calculator
- •Designing the User Interface
- •Programming the MathCalculator App
- •Adding More Features
- •Exception Handling
- •Taking the LoanCalculator to the Web
- •Working with Multiple Forms
- •Working with Multiple Projects
- •Executable Files
- •Distributing an Application
- •VB.NET at Work: Creating a Windows Installer
- •Finishing the Windows Installer
- •Running the Windows Installer
- •Verifying the Installation
- •Summary
- •Variables
- •Declaring Variables
- •Types of Variables
- •Converting Variable Types
- •User-Defined Data Types
- •Examining Variable Types
- •Why Declare Variables?
- •A Variable’s Scope
- •The Lifetime of a Variable
- •Constants
- •Arrays
- •Declaring Arrays
- •Initializing Arrays
- •Array Limits
- •Multidimensional Arrays
- •Dynamic Arrays
- •Arrays of Arrays
- •Variables as Objects
- •So, What’s an Object?
- •Formatting Numbers
- •Formatting Dates
- •Flow-Control Statements
- •Test Structures
- •Loop Structures
- •Nested Control Structures
- •The Exit Statement
- •Summary
- •Modular Coding
- •Subroutines
- •Functions
- •Arguments
- •Argument-Passing Mechanisms
- •Event-Handler Arguments
- •Passing an Unknown Number of Arguments
- •Named Arguments
- •More Types of Function Return Values
- •Overloading Functions
- •Summary
- •The Appearance of Forms
- •Properties of the Form Control
- •Placing Controls on Forms
- •Setting the TabOrder
- •VB.NET at Work: The Contacts Project
- •Anchoring and Docking
- •Loading and Showing Forms
- •The Startup Form
- •Controlling One Form from within Another
- •Forms vs. Dialog Boxes
- •VB.NET at Work: The MultipleForms Project
- •Designing Menus
- •The Menu Editor
- •Manipulating Menus at Runtime
- •Building Dynamic Forms at Runtime
- •The Form.Controls Collection
- •VB.NET at Work: The DynamicForm Project
- •Creating Event Handlers at Runtime
- •Summary
- •The TextBox Control
- •Basic Properties
- •Text-Manipulation Properties
- •Text-Selection Properties
- •Text-Selection Methods
- •Undoing Edits
- •VB.NET at Work: The TextPad Project
- •Capturing Keystrokes
- •The ListBox, CheckedListBox, and ComboBox Controls
- •Basic Properties
- •The Items Collection
- •VB.NET at Work: The ListDemo Project
- •Searching
- •The ComboBox Control
- •The ScrollBar and TrackBar Controls
- •The ScrollBar Control
- •The TrackBar Control
- •Summary
- •The Common Dialog Controls
- •Using the Common Dialog Controls
- •The Color Dialog Box
- •The Font Dialog Box
- •The Open and Save As Dialog Boxes
- •The Print Dialog Box
- •The RichTextBox Control
- •The RTF Language
- •Methods
- •Advanced Editing Features
- •Cutting and Pasting
- •Searching in a RichTextBox Control
- •Formatting URLs
- •VB.NET at Work: The RTFPad Project
- •Summary
- •What Is a Class?
- •Building the Minimal Class
- •Adding Code to the Minimal Class
- •Property Procedures
- •Customizing Default Members
- •Custom Enumerations
- •Using the SimpleClass in Other Projects
- •Firing Events
- •Shared Properties
- •Parsing a Filename String
- •Reusing the StringTools Class
- •Encapsulation and Abstraction
- •Inheritance
- •Inheriting Existing Classes
- •Polymorphism
- •The Shape Class
- •Object Constructors and Destructors
- •Instance and Shared Methods
- •Who Can Inherit What?
- •Parent Class Keywords
- •Derived Class Keyword
- •Parent Class Member Keywords
- •Derived Class Member Keyword
- •MyBase and MyClass
- •Summary
- •On Designing Windows Controls
- •Enhancing Existing Controls
- •Building the FocusedTextBox Control
- •Building Compound Controls
- •VB.NET at Work: The ColorEdit Control
- •VB.NET at Work: The Label3D Control
- •Raising Events
- •Using the Custom Control in Other Projects
- •VB.NET at Work: The Alarm Control
- •Designing Irregularly Shaped Controls
- •Designing Owner-Drawn Menus
- •Designing Owner-Drawn ListBox Controls
- •Using ActiveX Controls
- •Summary
- •Programming Word
- •Objects That Represent Text
- •The Documents Collection and the Document Object
- •Spell-Checking Documents
- •Programming Excel
- •The Worksheets Collection and the Worksheet Object
- •The Range Object
- •Using Excel as a Math Parser
- •Programming Outlook
- •Retrieving Information
- •Recursive Scanning of the Contacts Folder
- •Summary
- •Advanced Array Topics
- •Sorting Arrays
- •Searching Arrays
- •Other Array Operations
- •Array Limitations
- •The ArrayList Collection
- •Creating an ArrayList
- •Adding and Removing Items
- •The HashTable Collection
- •VB.NET at Work: The WordFrequencies Project
- •The SortedList Class
- •The IEnumerator and IComparer Interfaces
- •Enumerating Collections
- •Custom Sorting
- •Custom Sorting of a SortedList
- •The Serialization Class
- •Serializing Individual Objects
- •Serializing a Collection
- •Deserializing Objects
- •Summary
- •Handling Strings and Characters
- •The Char Class
- •The String Class
- •The StringBuilder Class
- •VB.NET at Work: The StringReversal Project
- •VB.NET at Work: The CountWords Project
- •Handling Dates
- •The DateTime Class
- •The TimeSpan Class
- •VB.NET at Work: Timing Operations
- •Summary
- •Accessing Folders and Files
- •The Directory Class
- •The File Class
- •The DirectoryInfo Class
- •The FileInfo Class
- •The Path Class
- •VB.NET at Work: The CustomExplorer Project
- •Accessing Files
- •The FileStream Object
- •The StreamWriter Object
- •The StreamReader Object
- •Sending Data to a File
- •The BinaryWriter Object
- •The BinaryReader Object
- •VB.NET at Work: The RecordSave Project
- •The FileSystemWatcher Component
- •Properties
- •Events
- •VB.NET at Work: The FileSystemWatcher Project
- •Summary
- •Displaying Images
- •The Image Object
- •Exchanging Images through the Clipboard
- •Drawing with GDI+
- •The Basic Drawing Objects
- •Drawing Shapes
- •Drawing Methods
- •Gradients
- •Coordinate Transformations
- •Specifying Transformations
- •VB.NET at Work: Plotting Functions
- •Bitmaps
- •Specifying Colors
- •Defining Colors
- •Processing Bitmaps
- •Summary
- •The Printing Objects
- •PrintDocument
- •PrintDialog
- •PageSetupDialog
- •PrintPreviewDialog
- •PrintPreviewControl
- •Printer and Page Properties
- •Page Geometry
- •Printing Examples
- •Printing Tabular Data
- •Printing Plain Text
- •Printing Bitmaps
- •Using the PrintPreviewControl
- •Summary
- •Examining the Advanced Controls
- •How Tree Structures Work
- •The ImageList Control
- •The TreeView Control
- •Adding New Items at Design Time
- •Adding New Items at Runtime
- •Assigning Images to Nodes
- •Scanning the TreeView Control
- •The ListView Control
- •The Columns Collection
- •The ListItem Object
- •The Items Collection
- •The SubItems Collection
- •Summary
- •Types of Errors
- •Design-Time Errors
- •Runtime Errors
- •Logic Errors
- •Exceptions and Structured Exception Handling
- •Studying an Exception
- •Getting a Handle on this Exception
- •Finally (!)
- •Customizing Exception Handling
- •Throwing Your Own Exceptions
- •Debugging
- •Breakpoints
- •Stepping Through
- •The Local and Watch Windows
- •Summary
- •Basic Concepts
- •Recursion in Real Life
- •A Simple Example
- •Recursion by Mistake
- •Scanning Folders Recursively
- •Describing a Recursive Procedure
- •Translating the Description to Code
- •The Stack Mechanism
- •Stack Defined
- •Recursive Programming and the Stack
- •Passing Arguments through the Stack
- •Special Issues in Recursive Programming
- •Knowing When to Use Recursive Programming
- •Summary
- •MDI Applications: The Basics
- •Building an MDI Application
- •Built-In Capabilities of MDI Applications
- •Accessing Child Forms
- •Ending an MDI Application
- •A Scrollable PictureBox
- •Summary
- •What Is a Database?
- •Relational Databases
- •Exploring the Northwind Database
- •Exploring the Pubs Database
- •Understanding Relations
- •The Server Explorer
- •Working with Tables
- •Relationships, Indices, and Constraints
- •Structured Query Language
- •Executing SQL Statements
- •Selection Queries
- •Calculated Fields
- •SQL Joins
- •Action Queries
- •The Query Builder
- •The Query Builder Interface
- •SQL at Work: Calculating Sums
- •SQL at Work: Counting Rows
- •Limiting the Selection
- •Parameterized Queries
- •Calculated Columns
- •Specifying Left, Right, and Inner Joins
- •Stored Procedures
- •Summary
- •How About XML?
- •Creating a DataSet
- •The DataGrid Control
- •Data Binding
- •VB.NET at Work: The ViewEditCustomers Project
- •Binding Complex Controls
- •Programming the DataAdapter Object
- •The Command Objects
- •The Command and DataReader Objects
- •VB.NET at Work: The DataReader Project
- •VB.NET at Work: The StoredProcedure Project
- •Summary
- •The Structure of a DataSet
- •Navigating the Tables of a DataSet
- •Updating DataSets
- •The DataForm Wizard
- •Handling Identity Fields
- •Transactions
- •Performing Update Operations
- •Updating Tables Manually
- •Building and Using Custom DataSets
- •Summary
- •An HTML Primer
- •HTML Code Elements
- •Server-Client Interaction
- •The Structure of HTML Documents
- •URLs and Hyperlinks
- •The Basic HTML Tags
- •Inserting Graphics
- •Tables
- •Forms and Controls
- •Processing Requests on the Server
- •Building a Web Application
- •Interacting with a Web Application
- •Maintaining State
- •The Web Controls
- •The ASP.NET Objects
- •The Page Object
- •The Response Object
- •The Request Object
- •The Server Object
- •Using Cookies
- •Handling Multiple Forms in Web Applications
- •Summary
- •The Data-Bound Web Controls
- •Simple Data Binding
- •Binding to DataSets
- •Is It a Grid, or a Table?
- •Getting Orders on the Web
- •The Forms of the ProductSearch Application
- •Paging Large DataSets
- •Customizing the Appearance of the DataGrid Control
- •Programming the Select Button
- •Summary
- •How to Serve the Web
- •Building a Web Service
- •Consuming the Web Service
- •Maintaining State in Web Services
- •A Data-Driven Web Service
- •Consuming the Products Web Service in VB
- •Summary
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 |