- •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
508 Chapter 11 STORING DATA IN COLLECTIONS
custom comparers in discussed in detail shortly in the section “The IEnumerator and IComparer Interfaces.” In the last example of that section, you will build a custom comparer for sorting the SortedList based on a function of its keys, instead of the actual values of the keys.
Remember the WordFrequencies project we built earlier to demonstrate the use of the HashTable class? Change the declaration of the WordFrequencies variable from HashTable to SortedList, and the project will work as before. The only difference is that the words will appear on the TextBox control sorted alphabetically when you click the Show Word Count button.
Other Collections
The System.Collections class exposes a few more collections, including the Queue and the Stack collections. The main characteristic of these two collections is how you add and remove items to them. When you add items to a Queue, the items are appended to the collection. When you remove items, they’re removed from the top of the collection. You’d use this collection to emulate the customer line in a bank or a production line.
The Stack collection inserts new items at the top, and you can only remove the top item. The Stack collection is a FIFO (first in first out) structure, while the Queue class is a LIFO structure (last in first out). You’d use this collection to emulate the stack maintained by the CPU, one of the most crucial structures for the operating system and applications alike. Stacks and Queues are used heavily in computer science, but they aren’t as common in business applications. I’m not going to discuss any more collections in this book, but you can look them up in the documentation. There are quite a few more interesting topics to cover in this chapter—and most important is how to save a collection to a disk file and read it back.
The IEnumerator and IComparer Interfaces
Judging by its title, you probably thought this is a section for C++ programmers adapted for VB programmers. IEnumerator and IComparer are two objects that unlock some of the most powerful features of collections. The proper term for IEnumerator and IComparer is interface, a term I will describe shortly. If you don’t want to get too technical about interfaces, think of them as objects. The IEnumerator object retrieves a list of pointers for all the items in a collection, and you can use it to iterate through the items in a collection. Every collection has a built-in enumerator, and you can retrieve it by calling its GetEnumerator method. The IComparer object exposes the Compare and CompareTo methods, which tells the compiler how to compare two objects of the same type. Once the compiler knows how to compare the objects, it can sort a collection of objects with the same type.
The IComparer interface consists of a function that compares two items and returns a value indicating their order (which one is the smaller item, or whether they’re equal). The Framework can’t compare objects of all types. It only knows how to compare the base types—integers, strings, and so on. It doesn’t know how to compare two rectangles, or two color objects. If you have a collection of colors, you may want to sort them according to their luminance, saturation, brightness, and so on. The compiler can’t make any assumptions as to how you may wish to sort your collection, and, of course, it doesn’t expose members to sort a collection in all possible ways. Instead, it gives you the option to specify a function that compares two colors (or two objects of any other type, for that matter) and uses this function to sort the collection. The same function is used by the BinarySearch method, to
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
THE IENUMERATOR AND ICOMPARER INTERFACES 509
locate an item in a sorted collection. In effect, the IComparer interface is a function that knows how to compare two Color objects, for our example. If the collection contains items of a custom Structure, the IComparer interface is a function that knows how to compare two instances of the custom Structure.
So, what is an interface? An interface is another term in object-oriented programming and describes a very simple technique. When we write the code for a class, we may not know how to implement a few operations, but we do know that they’ll have to be implemented later. We insert a placeholder for these operations (a function declaration) and expect that the application that uses the class will provide the actual implementation of these functions. All collections expose a Sort method, which sorts the items in the collection by comparing them to one another. To do so, the Sort method calls a function that compares two items and returns a value indicating their relative order. Any class that exposes a function that can compare its objects can be sorted. The Integer data type, which is implemented by the System.Integer class, exposes such a function, and so do all the base types. Custom objects must provide their own comparison function—or more than a single function, if you want to sort them in multiple ways. Since you can’t edit the collection’s Sort method’s code, you must supply your comparison function through a mechanism that the class can understand. This is what the IComparer interface is all about. The code that compares two objects of the same type is actually trivial. You must follow the steps outlined here to make this function part of the class, so that the collection can see and use it.
Enumerating Collections
All collections expose the IEnumerator interface, which is a fancy term for a very simple operation. IEnumerator returns an object that allows you to iterate through the collection without having to know anything about its items, not even the count of the items. To retrieve the enumerator for a collection, call its GetEnumerator method, with a statement like the following:
Dim ALEnum As IEnumerator
ALEnum = AList.GetEnumerator
The IEnumerator object exposes two methods, the MoveNext and Reset methods. The MoveNext method moves to the next item in the collection and makes it the current item. When you initialize the IEnumerator object, it’s positioned in front of the very first item, so you must call the MoveNext method to move to the first item. The Reset method does exactly the same: it repositions the IEnumerator in front of the first element.
The MoveNext method doesn’t return an item, as you might expect. It returns a True/False value indicating whether it has successfully moved to the next item. Once you have reached the end of the collection, the MoveNext method will return False. Here’s how you can enumerate through an ArrayList collection with an enumerator:
Dim aItems As IEnumerator aItems = aList.GetEnumerator While aItems.MoveNext
{ process item aItems.Current } End While
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
510 Chapter 11 STORING DATA IN COLLECTIONS
At each iteration, the current item is given by the Current property of the enumerator, which represents the current object in the collection. Once you have reached the last item, the MoveNext method will return False and the loop will terminate. To rescan the items, you must reset the enumerator by calling its Reset method.
To process the current item, you can directly call its methods through the aItems.Current object. If the collection holds Rectangles, for example, you can access their sizes with these expressions:
CType(aItems.Current, Rectangle).Width
CType(aItems.Current, Rectangle).Height
The Strict option necessitates the explicit conversion of the Current item to a Rectangle object. In other words, you can’t use an expression like aItems.Current.Width with the Strict option on.
The event handler in Listing 11.12 populates an ArrayList with Rectangle objects and then iterates through the collection and prints the area of each Rectangle.
Listing 11.12: Iterating an ArrayList with an Enumerator
Protected Sub Button2_Click(ByVal sender As Object, _ ByVal e As System.EventArgs)
Dim aList As New ArrayList()
Dim R1 As New Rectangle(1, 1, 10, 10) aList.Add(R1)
R1 = New Rectangle(2, 2, 20, 20) aList.Add(R1)
aList.add(New Rectangle(3, 3, 2, 2)) Dim REnum As IEnumerator
REnum = aList.GetEnumerator Dim R As New Rectangle() While REnum.MoveNext
R = CType(REnum.Current, Rectangle)
Console.WriteLine(R.Width * R.Height) End While
End Sub
The third Rectangle variable is added to the collection directly, without using an intermediate variable, as I did with the first two objects. The Rectangle object is initialized in the same line that adds the object to the collection. Then the REnum variable is set up and used to iterate through the items of the collection. At each iteration, the code saves the current Rectangle to the R variable, and it uses this variable to access the properties of the Rectangle object (its width and height).
Of course, you can iterate a collection without the enumerator, but with a For Each…Next loop. To iterate through a HashTable, you can use either the Keys or the Values collections. The code of Listing 11.13 populates a HashTable with Rectangle objects. Then it scans the items and prints their keys, which are strings, and the area of each rectangle.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
THE IENUMERATOR AND ICOMPARER INTERFACES 511
Listing 11.13: Iterating a HashTable with Its Keys
Protected Sub Button3_Click(ByVal sender As Object, _ ByVal e As System.EventArgs)
Dim hTable As New HashTable()
Dim r1 As New Rectangle(1, 1, 10, 10) hTable.Add(“R1”, r1)
r1 = New Rectangle(2, 2, 20, 20) hTable.Add(“R2”, r1)
hTable.add(“R3”, New Rectangle(3, 3, 2, 2)) Dim key As Object
Dim R As Rectangle
For Each key In hTable.keys
R = CType(hTable(key), Rectangle)
Console.WriteLine(“The area of Rectangle {0} is {1}”, Key.ToString, _ R.Width * R.Height)
Next End Sub
The code adds three Rectangle objects to the HashTable and then iterates through the collection using the Keys properties. Each item’s key is a string (“R1”, “R2”, and “R3”). The Keys property is itself a collection and can be scanned with a For Each…Next loop. At each iteration, we access a different item through its key, with the expression hTable(key). The output produced by this code is shown here:
The area of Rectangle R1 is 100
The area of Rectangle R2 is 400
The area of Rectangle R3 is 4
(I have used a format string with the WriteLine method to avoid a very long statement by embedding the values into the string.)
Alternatively, you can iterate a HashTable with an enumerator, but be aware that the GetEnumerator method of the HashTable collection returns an object of the IDictionaryEnumerator type, not an IEnumerator object. The IDictionaryEnumerator is quite similar to the IEnumerator, but it exposes additional properties. They are the Key and Value properties, and they return the current item’s key and value. The IDictionaryEnumerator object also exposes the Entry property, which returns both the key and the value. You can access the current item’s key and value either as
DEnum.Key and DEnum.Value, or as DEnum.Entry.Key and DEnum.Entry.Value. DEnum is a properly
declared enumerator for the HashTable:
Dim DEnum As IDictionaryEnumerator
Assuming that you have populated the hTable collection with the same three Rectangle objects, you can use the statements in Listing 11.14 to iterate through the collection’s items.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
512 Chapter 11 STORING DATA IN COLLECTIONS
Listing 11.14: Iterating a HashTable with an Enumerator
Dim hEnum As IDictionaryEnumerator hEnum = hTable.GetEnumerator While hEnum.MoveNext
Console.WriteLine(“The value of “ & hEnum.Key & “{0} is “ & hEnum.Value) Console.WriteLine(CType(hEnum.Value, Rectangle).Width * _
CType(hEnum.Value, Rectangle).Height)
End While
If you execute these statements after populating the HashTable collection with three Rectangles, they will produce the following output:
The value of R1 is {X=1,Y=1,Width=10,Height=10} 100
The value of R2 is {X=2,Y=2,Width=20,Height=20} 400
The value of R3 is {X=3,Y=3,Width=2,Height=2} 4
The Value property of the enumerator returns an object, which must be cast to the appropriate type, before you can call its members—unless the Strict option has been set to Off, of course.
VB.NET at Work: The Enumerations Project
The project Enumerations (Figure 11.4) on the CD shows how to iterate through an ArrayList and a HashTable with and without an enumerator. The code should be quite familiar to you by now, so I will not list it list here. You can open the project and examine its code and routines.
Figure 11.4
How to scan ArrayLists and HashTables with and without an enumerator
You can also enumerate arrays with an IEnumerator object. You must declare the enumerator variable as IEnumerator and then call the MoveNext method to iterate the array from within a loop. Listing 11.15 iterates through the elements of a string array with an enumerator.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |