
- •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

THE IENUMERATOR AND ICOMPARER INTERFACES 513
Listing 11.15: Enumerating an Array
Dim Names(4) As String
Names(0) = “Name 0” : Names(1) = “Name 1” Names(2) = “Name 2” : Names(3) = “Name 3” Dim arrayEnum As IEnumerator
arrayEnum = Names.GetEnumerator While arrayEnum.MoveNext
Console.WriteLine(arrayEnum.Current) End While
Custom Sorting
The Sort method allows you to sort collections, as long as the items are of the same base data type. If the items are objects, however, the collection doesn’t know how to sort them. If you want to sort objects, you must help the collection a little by telling it how to compare two objects. A sorting operation is nothing more than a series of comparisons. Sorting algorithms compare items and swap them if necessary. They don’t even swap the items; they simply rearrange a list of pointers to the items. The first pointer points to the first item in the sorted collection, the second pointer points to the second item in the sorted collection, and so on. The items themselves remain in their original positions.
All the information needed by a sorting algorithm to operate on any type of item is a function that compares two objects. Let’s say you have a list of persons, and each person is a Structure that contains names, addresses, e-addresses, and so on. The System.Collections class can’t make any assumptions as to how you want your list sorted. This collection can be sorted by any field in the structure (names, e-addresses, postal codes, and so on). Even if the collection contains a built-in object, like a Rectangle or Color object, the collection doesn’t know how to sort them.
The comparer is implemented as a separate class, outside all other classes, and is specific to a custom data type. Let’s say you have created a custom structure for storing contact information. The Person object is declared as a structure with the following fields:
Structure Person
Dim Name As String
Dim BDate As Date
Dim EMail As String
End Structure
To add an instance of the Person object to an ArrayList or HashTable, create a variable of Person type and initialize its fields as follows:
Dim p As New Person Dim aList As ArrayList
p.Name = “Adams, George” p.Bdate = #4/17/1957#
p.EMail = “gadams@example.com” aList.Add(p)
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

514 Chapter 11 STORING DATA IN COLLECTIONS
To add another element, you can either create a new Person object (variable p1, for example) or set the p variable to Nothing and then initialize it again.
p = Nothing
p.Name = “New Name” p.BDate = #1/1/1950#
‘ The EMail field is empty aList.Add(p)
This collection can’t be sorted with the simple form of the Sort method, because the compiler doesn’t know how to compare two Person objects. You must provide your own function for comparing two variables of the Person type. Once this function has been written, the compiler will be able to compare items and therefore sort the collection. This custom function, however, can’t be passed to the Sort and BinarySearch methods by name. You must create a new class that implements the IComparer interface and pass an IComparer object to the two methods. Here’s the outline of a class that implements the IComparer interface (then we’ll look at the implementation details of the function that actually compares two objects).
Class customComparer : Implements IComparer
Public Function Compare(ByVal o1 As Object, ByVal o2 As Object) _ As Integer Implements IComparer.Compare
{ function’s code } End Function
End Class
The name of the class can be anything. It should be a name that indicates the type of comparison it performs, or the type of objects it compares. The name of the custom function must be Compare, and it must implement the IComparer.Compare interface. What exactly do we mean by “implement an interface”? As you have seen, all classes expose some standard members. The method ToString, for example, is a standard method, and the Framework knows how to implement it. It simply returns the name of the class. There are situations, however, where we know that we’re going to need a member, we know the name of the member, but we just can’t implement it.
The collection classes, for example, expose a Sort method. In order to sort their items, they must be able to compare two elements and figure out which one is first. The comparison can be carried out for the base types, but not for objects. So, they provide a placeholder, where you must place a function that knows how to compare two instances of the object. This ability to write classes that provide placeholders for the actual implementation of a method is known as interface. If you write a function that compares two objects of the specific type and you pass it to the Sort method, this function will be called in the place of the Compare method. The Sort method calls the Compare method of the class that represents the objects you’re comparing. If it finds one, it uses it. If not, the Sort method won’t work. The IComparer interface is the class’s way of incorporating your custom comparer function into its Sort method.
Let’s get back to our example. To use the custom function, you must create an object of customComparer type (or whatever you have named the class) and then pass it to the Sort and BinarySearch methods as argument:
Dim CompareThem As New customComparer aList.Sort(CompareThem)
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

THE IENUMERATOR AND ICOMPARER INTERFACES 515
You can combine the two statements in one by initializing the customComparer variable in the line that calls the Sort method:
aList.Sort(New customComparer())
You can also use the equivalent syntax of the BinarySearch method to locate a custom object that implements its own IComparer interface:
BinarySearch(object, New customComparer())
This is how you use a custom function to compare two objects. Everything is the same, except for the name of the class, which is different every time. The last step is to write the code that compares the two objects and returns an integer value, indicating the order of the elements. This value should be –1 if the first object is smaller than the second object, 0 if the two objects are equal, and 1 if the first object is larger than the second object. “Smaller” here means that the element appears before the larger one when sorted in ascending order. Listing 11.16 is the function that sorts Person objects according to the Age field.
Listing 11.16: A Custom Comparer
Class PersonAgeComparer : Implements IComparer
Public Function Compare(ByVal o1 As Object, ByVal o2 As Object) As Integer _ Implements IComparer.Compare
Dim person1, person2 As Person Try
person1 = CType(o1, Person) person2 = CType(o2, Person)
Catch compareException As system.Exception Throw (compareException)
Exit Function End Try
If person1.BDate < person2.BDate Then Return -1
Else
If person1.BDate > person2.BDate Then Return 1
Else Return 0
End If End If
End Function End Class
The code could have been considerably simpler, but I’ll explain momentarily why the Try statement is necessary. The comparison takes place in the If statement. If the first person’s birth date is numerically smaller than the second person’s, the function returns the value –1. If the first person’s birth date is numerically smaller than the second person’s, the function returns 1. Finally, if the two values are equal, the function returns 0.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

516 Chapter 11 STORING DATA IN COLLECTIONS
The code is straightforward, so why the error-trapping code? Before we perform any of the necessary operations, we convert the two objects into Person objects. It’s not unthinkable that the collection with the objects you want to sort contains objects of different types. If that’s the case, the CType() function won’t be able to convert the corresponding argument to the Person type, and the comparison will fail. The same exception that would be thrown in the function’s code is raised again from within the error handler, and it’s passed back to the calling code.
The Person objects can be sorted in many different ways. You may wish to sort them by ID, name, and so on. To accommodate multiple sorts, you must implement several classes, each one with a different Compare function. Listing 11.17 shows two classes that implement two different Compare functions for the Person class. The PersonNameComparer class compares the names, while the PersonAgeComparer class compares the ages.
Listing 11.17: A Class with Two Custom Comparers
Class PersonNameComparer : Implements IComparer
Public Function Compare(ByVal o1 As Object, ByVal o2 As Object) As Integer _ Implements IComparer.Compare
Dim person1, person2 As Person Try
person1 = CType(o1, Person) person2 = CType(o2, Person)
Catch compareException As system.Exception Throw (compareException)
Exit Function End Try
If person1.Name < person2.Name Then Return -1
Else
If person1.Name > person2.Name Then Return 1
Else Return 0
End If End If
End Function End Class
Class PersonAgeComparer : Implements IComparer
Public Function Compare(ByVal o1 As Object, ByVal o2 As Object) As Integer _ Implements IComparer.Compare
Dim person1, person2 As Person Try
person1 = CType(o1, Person) person2 = CType(o2, Person)
Catch compareException As system.Exception Throw (compareException)
Exit Function End Try
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

THE IENUMERATOR AND ICOMPARER INTERFACES 517
If person1.BDate > person2.BDate Then
Return -1
Else
If person1.BDate < person2.BDate Then
Return 1
Else
Return 0
End If
End If
End Function
End Class
To test the custom comparers, create a new application and enter the code of Listing 11.17 (the two classes) in a separate Class module. Don’t forget to include the declaration of the Person Structure. Then place a button on the form and enter the code of Listing 11.18 in its Click event handler. This code adds three persons with different names and birth dates to an ArrayList.
Listing 11.18:Testing the Custom Comparers
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim AList As New ArrayList()
Dim p As Person
‘Populate collection p.Name = “C Person”
p.EMail = “PersonC@sybex.com” p.BDate = #1/1/1961#
If Not AList.Contains(p) Then AList.Add(p) p.Name = “A Person”
p.EMail = “PersonA@sybex.com” p.BDate = #3/3/1961#
If Not AList.Contains(p) Then AList.Add(p) p.Name = “B Person”
p.EMail = “PersonB@sybex.com” p.BDate = #2/2/1961#
If Not AList.Contains(p) Then AList.Add(p)
‘Print collection as is
Dim PEnum As IEnumerator PEnum = AList.GetEnumerator
ListBox1.Items.Add(“Original Collection”) While PEnum.MoveNext
ListBox1.Items.Add(CType(PEnum.Current, Person).Name & vbTab & _
CType(PEnum.Current, Person).BDate)
End While
‘Sort by name, then print collection
ListBox1.Items.Add(“ “)
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

518 Chapter 11 STORING DATA IN COLLECTIONS
ListBox1.Items.Add(“Collection Sorted by Name”)
AList.Sort(New PersonNameComparer())
PEnum = AList.GetEnumerator
While PEnum.MoveNext
ListBox1.Items.Add(CType(PEnum.Current, Person).Name & vbTab & _
CType(PEnum.Current, Person).BDate)
End While
‘Sort by age, then print collection
ListBox1.Items.Add(“ “) ListBox1.Items.Add(“Collection Sorted by Age”) AList.Sort(New PersonAgeComparer())
PEnum = AList.GetEnumerator While PEnum.MoveNext
ListBox1.Items.Add(CType(PEnum.Current, Person).Name & vbTab & _
CType(PEnum.Current, Person).BDate)
End While
End Sub
The four sections of the code are delimited by comments, which appear in bold in the listing. The first section populates the collection with three variables of the Person type. The second section prints the items in the order in which they were added to the collection:
C Person 1/1/1961 A Person 3/3/1961 B Person 2/2/1961
The third section of the code calls the Sort method passing the PersonNameComparer custom comparer as argument, and it again prints the contents of the ArrayList. The names are listed now in alphabetical order:
A Person 3/3/1961 B Person 2/2/1961 C Person 1/1/1961
In the last section, it calls the Sort method again, this time to sort the items by age, and prints them:
C Person 1/1/1961 B Person 2/2/1961 A Person 3/3/1961
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |