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

964 Chapter 22 PROGRAMMING THE ADO.NET OBJECTS
The Structure of a DataSet
The main object of any ADO.NET application is the DataSet object, which is s a miniature database that lives on the client. The main purpose of the Connection and DataAdapter objects is to populate the DataSet object, as well as move information from the DataSet to the database and update the underlying table(s). The basic concept behind ADO.NET is to move the required data to the client, process them there, and then, optionally, update the database with the changes made by the client application to the local data. The data on the client is a copy of the data on the server the moment the DataSet was generated, and the DataSet is totally disconnected from the underlying tables in the database.
The structure of the DataSet object is quite simple. VB6 programmers will have to get used to living with client-side data, but those of you new to VB.NET will find the DataSet a convenient method of working with subsets of tables. It’s made up of tables, which may or may not correspond to tables of the database. You can bring in an entire table, like the Categories table. Or you can select a few columns and/or a few rows from a table in the database and store them to a table in the DataSet. Finally, you can create a table by combining rows from multiple tables. For example, you can execute a query that retrieves all product names from the Products table along with the name of the category they belong to from the Categories tables and stores the returned rows to a new table in the DataSet. It is also possible to add and drop tables from a DataSet at any time during the course of the application.
Finally, you can create new tables from within your code. To do so, we create a DataTable object to represent the new table and then a series of DataRow objects to represent the table’s rows. Each row must have a data type, an optional default value, a length, and so on. You can create the same table structures from within your code as you would do with the visual tools of Enterprise Manager. After specifying the structure of the tables, you can add relations between them. Everything you can do visually with the XSD Designer, you can also do from within your code. The process of establishing relations between tables using the Designer was described in the last chapter. The product documentation discusses extensively how to create DataSets programmatically. You will find this information useful in situations where the data comes from someone else’s database. As you will see later in this chapter, it is possible to create a DataSet programmatically, populate it, and then store its contents to a XML file. This chapter shows you how to use DataSets to manipulate databases, which is what most of us will be doing anyway.
If the tables were totally independent of one another, the DataSet would be nothing more than a collection of arrays. Yet it’s not. In addition to the tables, the DataSet maintains relations between tables, as you have seen. If you need to see the product names along with the names of the categories they belong to, you can bring both tables into the DataSet and establish a relationship between them.
DataSets are not aware of the data sources. The exchange of information between DataSets and data sources (which, in most cases, are databases) takes place through the Data Adapter object, described in Chapter 21. Each table in a DataSet has an associated Data Adapter object, whose function is to retrieve data from the underlying table and populate the DataSet, as well as update the underlying table in the database with data from the DataSet.
The DataSet’s structure and its data are described with XML keywords. You don’t have to learn much about XML, but if you open the XSD file, you’ll see the XML code that describes the structure of the DataSet. The data aren’t available at design time, but once you populate the tables of the
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

THE STRUCTURE OF A DATASET 965
DataSet, the data will also be described in XML. The Data Adapter formats the data as XML documents and passes them to the DataSet. It also receives data in XML format from the DataSet and updates the underlying tables in the database.
Navigating the Tables of a DataSet
In this section, we’re going to take a closer look at the structure of the DataSet and how it organizes data in tables. You’ll learn how to enumerate the tables in the DataSet and how to iterate through their rows and access the values of individual columns. The information in this section will enable you to write applications for displaying data. Later in the chapter, we’ll take a closer look at techniques for updating the tables in the data source.
The DataSet object exposes members for accessing its contents. The tables in a DataSet are exposed through the Tables collection, which is made up of DataTable objects. If the tables are related, the relations are exposed by the Relations collection, which is made up of DataRelation objects. The following two loops print the names of the tables and relations in a DataSet:
Dim tbl As System.Data.DataTable
For Each tbl In AllOrders1.Tables
Console.WriteLine(tbl.TableName)
Next
Dim rel As System.Data.DataRelation
For Each rel In AllOrders1.Relations
Console.WriteLine(rel.RelationName)
Next
The DataTable object’s most important property is the Rows property, which is a collection of DataRow objects. The DataRow object, in turn, exposes the Item property, which is the value of a specific column (field) in a row. If the DSCustomers DataSet contains the Customers table, the following statement returns the CustomerID field of the third row in the Customers table:
DSCustomers.Tables(“Customers”).Rows(2).Item(“CustomerID”)
There’s a simpler expression for retrieving the same value, which is the following:
DSCustomers.Customers(2).CustomerID
Obviously, the second expression is easier to write and makes your code easier to read and maintain. However, this notation applies only to a typed DataSet—one with a schema available at design time. The DataSet is based on existing tables, so that their structures are known at design time and the compiler can create typed DataSets. If you create a DataSet at runtime from within your code, then the compiler has no way of knowing the structure of the DataSet and therefore can’t create a typed DataSet. This chapter deals with typed datasets only.
If you’re wondering how the compiler knows about the structure of the DataSet and how it can expose the names of the tables as members of the DataSet object, or the names of the columns as members of the DataTable object, the answer is quite simple. It creates a class for every DataSet and for every table you add to the DataSet. This is done on the fly, and all the information is hidden from you. To view the class that implements the DataSet, click the Show All Files button on the Solution Explorer. Under the XDS file with the DataSet’s schema, there’s another file with the same name and extension VB. If you open this for a DataSet that contains the Customers table, you will
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

966 Chapter 22 PROGRAMMING THE ADO.NET OBJECTS
see that it contains the definition of the class DSCustomers, which in turn contains the classes CustomersDataTable and CustomersRow. All these classes expose their own members, which you can access through the DSCustomers1 object. DSCustomers1 is an instance of the DSCustomers class.
Using the DataTable, DataRelation, and DataRow objects, you can navigate through all the tables of a DataSet, going from each row of any given table to the related rows in any other table. Let’s say you have created a DataSet with the Customers, Orders, and Order Details tables. Obviously, data binding is not the only way to build a user interface for browsing or editing a DataSet. You can write code to display the current row’s fields to a TextBox control and store the edited values back to the same row. You can also write code to navigate through the rows of a table or through the tables of a DataSet. Let’s look at an example of navigating through the tables of a DataSet and reading their fields with code.
VB.NET at Work: The Datasets Project
The Datasets project, whose main form is shown in Figure 22.1, can display the names of all tables in a DataSet, the names of all relations in the DataSet, and finally all the data in the DataSet. The Show Tables button displays the names of all the tables in the DataSet to the top-left ListBox control. The Show Relations button displays the names of all relations in the second ListBox control. The Show All Data button adds the customers, their orders, and each order’s details to a TreeView control. As you can see, the structure of the tree reflects the structure of the data in the DataSet.
Figure 22.1
The Datasets project demonstrates how to navigate through a DataSet and its tables.
The root nodes are customers (the parent table of the DataSet). Under each root node, you can find the customer’s orders (the IDs of the orders), and under each order you can find the order’s details. The nodes with the details contain the product ID, quantity, and price. You can modify the SELECT statement of the Order Details table so that it displays product names instead of product IDs, or include each order’s total. Or you can map the DataSet’s structure to a ListView control, which gives you more control over the appearance of the data. Listing 22.1 shows the code behind the Show Tables button, which iterates through the Tables collection of the AllOrders1 DataSet object. The DataSet was created by dropping the Customers, Orders, and Order Details tables of the Northwind database on the design surface, as described in the previous chapter. All three tables are stored in the same DataSet, and the relations between tables were established on the XSD file, as described in the previous chapter.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

THE STRUCTURE OF A DATASET 967
Listing 22.1: Navigating through the Tables of a DataSet
Private Sub Tables(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles bttnTables.Click
ListBox1.Items.Clear()
Dim tbl As System.Data.DataTable
For Each tbl In AllOrders1.Tables
ListBox1.Items.Add(tbl.TableName)
Next
End Sub
The Show Relations button is quite similar; it iterates through the Relations collection of the DataSet and prints the names of the relations on the second ListBox control. Listing 22.2 shows the code behind this button.
Listing 22.2: Navigating through the Relations of a DataSet
Private Sub Relations(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles bttnRelations.Click ListBox2.Items.Clear()
Dim rel As System.Data.DataRelation For Each rel In AllOrders1.Relations
ListBox2.Items.Add(rel.RelationName) Next
End Sub
When you select a relation by clicking its name, the program displays the properties of the selected relation in a message box. If you click the CustomerOrders relation, the following will appear on a message box:
PARENT Customers
CHILD Orders
ON CustomerID = CustomerID
The last button on the form displays the entire hierarchy of the DataSet on a TreeView control with the statements shown in Listing 22.3.
Listing 22.3: Navigating through the Rows of Related Tables
Private Sub Data(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles bttnData.Click
DAcustomers.Fill(AllOrders1, “Customers”)
DAOrders.Fill(AllOrders1, “Orders”)
DAOrderDetails.Fill(AllOrders1, “Order Details”)
Dim CORelation As String
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |

968 Chapter 22 PROGRAMMING THE ADO.NET OBJECTS
CORelation = AllOrders1.Customers.ChildRelations(0).RelationName()
Dim ODRelation As String
ODRelation = AllOrders1.Orders.ChildRelations(0).RelationName
Dim row As AllOrders.CustomersRow
For Each row In AllOrders1.Customers
Dim Cnode As TreeNode
Cnode = TreeView1.Nodes.Add(row.CompanyName)
Dim OrderRows() As AllOrders.OrdersRow
OrderRows = row.GetChildRows(CORelation)
Dim Orow As AllOrders.OrdersRow
For Each Orow In OrderRows
Dim Onode As TreeNode
Onode = Cnode.Nodes.Add(Orow.OrderID)
Dim DetailRows() As AllOrders.Order_DetailsRow
Dim Drow As AllOrders.Order_DetailsRow |
|
DetailRows = Orow.GetChildRows(ODRelation) |
|
For Each Drow In DetailRows |
|
Onode.Nodes.Add(Drow.ProductID & “ |
“ & Drow.Quantity & “ X “ & _ |
Drow.UnitPrice) |
|
Next |
|
Next |
|
Next |
|
End Sub |
|
|
|
The code starts by populating all three tables of the DataSet. Then it retrieves the name of the first child relation of the Customer table (the CORelation string, which is the name of the relation between the Customers and Orders tables) and the first child relation of the Orders table (the ODRelation string, which is the relation between the Orders and Order Details tables). We’ll use the names of these relations to navigate from each row to its child rows.
Then the code starts scanning the rows of the Customers table with a For Each loop. In the loop’s body, it adds a new root node to the TreeView control with the customer’s name. Then it retrieves the child rows of the current row. The child rows are stored in an array of OrdersRow objects with the following statement:
Dim OrderRows() As AllOrders.OrdersRow
OrderRows = row.GetChildRows(CORelation)
The GetChildRows method of the DataRow object accepts as argument the name of a relation, and it retrieves the child rows related to the current row with the specified relation. The rows are stored in properly declared array or OrdersRow items.
Then the code goes through the child rows and, for each row, adds a child node under the current customer’s node in the TreeView control. As you can guess, it retrieves the current order’s child rows, which are the order’s details. The innermost loop adds these rows under the current order’s node. All three nested loops have the same structure, and you should examine the code to understand how it navigates through all the rows of all tables following the relations between tables.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |