0% found this document useful (0 votes)
1K views751 pages

Database Programming With Visual Basic Net and Ado Net - Tips, Tutorials, and Code

Database Programming With Visual Basic Net and Ado Net - Tips, Tutorials, And Code

Uploaded by

oseuka
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
0% found this document useful (0 votes)
1K views751 pages

Database Programming With Visual Basic Net and Ado Net - Tips, Tutorials, and Code

Database Programming With Visual Basic Net and Ado Net - Tips, Tutorials, And Code

Uploaded by

oseuka
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
Download as pdf or txt
You are on page 1/ 751

[ Team LiB ]

Table of Contents

Index

Database Programming with Visual Basic .NET and


ADO.NET: Tips, Tutorials, and Code
By F. Scott Barker
Publisher

: Sams Publishing

Pub Date

: September 12, 2002

ISBN

: 0-672-32247-1

Pages

: 544

The topic combination of VB .NET and ADO.NET is unbeatable. VB .NET is the most popular language in
which to code. And, every developer needs to understand ADO.NET to allow data to be accessed from a Web
site. In this book Developers will be shown numerouse code examples that will illustr4ate how to program
database driven applications within the .NET Framework. The book is aimed at both established and new VB
Developers. Important topics covered include: Visual Studio development environment, ASP.NET
applications, Windows Forms application, using VB .NET with ADO.NET, complex queries, security, COM
interop., and application deployment.

[ Team LiB ]

[ Team LiB ]

Table of Contents

Index

Database Programming with Visual Basic .NET and


ADO.NET: Tips, Tutorials, and Code
By F. Scott Barker
Publisher

: Sams Publishing

Pub Date

: September 12, 2002

ISBN

: 0-672-32247-1

Pages

: 544

Copyright
About the Author
Acknowledgments
Tell Us What You Think!
Introduction
Who Is This Book For?
What's Covered in Database Programming with Visual Basic .NET and ADO.NET: Tips, Tutorials, and
Code?
Chapter 1. Developing Windows Forms Using Bound Controls
Section 1.1. Create a Bound List Box
Section 1.2. Limit the Data Displayed in a Bound List Box
Section 1.3. Bind and View Individual Text Boxes Based Off a Selected List Box Item
Section 1.4. Edit and Update Data Using Bound Controls
Section 1.5. Add and Delete Records Using Bound Controls
Section 1.6. Take Care of Error Handling with Bound Controls
Section 1.7. Put the Finishing Touches on a Data Bound Form
Section 1.8. Bind Data to ComboBox and DataGrid Controls
Section 1.9. Drill Down to Data in the DataGrid Control
Chapter 2. Creating SQL Server Database Objects From Visual Studio .NET
Working with Tables, Columns, and Rows
Utilizing Properties for Tables and Columns
Section 2.1. Create a New SQL Server Database from Within Visual Studio .NET
Section 2.2. Define Tables and Fields

Section 2.3. Define a Primary Key and Other Indexes


Section 2.4. Define Relations Between Tables
Section 2.5. Define Defaults and Constraints
Section 2.6. Create Views
Section 2.7. Create Stored Procedures
Chapter 3. Viewing Data With ADO.NET
Differences Between ADO and ADO.NET
Objects That Are Found in ADO.NET
Section 3.1. Retrieve Data by Using the DataReader Object
Section 3.2. Retrieve Results from SQL Server by Using the DataTable Object
Section 3.3. Locate Records with the DataTable Object
Section 3.4. Filter and Sort Records Using the DataView Object
Chapter 4. Manipulating Data With ADO.NET
Section 4.1. Edit Data and Update Changes That Are Made to an ADO.NET DataSet Object
Section 4.2. Add and Delete Rows in a Dataset with ADO.NET
Section 4.3. Execute Parameterized Stored Procedures in ADO.NET
Section 4.4. Create and Execute On-the-Fly Batch Updates by Using ADO.NET
Chapter 5. Working With Data In Web Forms
Dealing with Stateless Programming
Section 5.1. Use Bound Controls with Web Forms
Section 5.2. Validate Data Using Validation Controls
Section 5.3. Populate DropDown and ListBox Controls
Section 5.4. Display Data Using the Table Control
Section 5.5. Display Data Using the Repeater Control
Comments
Section 5.6. Display, Sort, and Page Data in the DataGrid Control
Section 5.7. Add, Edit, and Delete Data Using the DataGrid Control
Section 5.8. Hyperlink from a Row in the Data Grid to a Detail Page
Chapter 6. Creating Transact-SQL Commands
Section 6.1. Retrieve Unique Records Using Only a Select Query
Section 6.2. Use Variables and Functions in T-SQL
Section 6.3. Use Wildcards and Ranges of Values in a SQL Query
Section 6.4. Find Records in a Table Without Corresponding Entries in a Related Table
Section 6.5. Take Advantage of Using Subqueries
Section 6.6. Create, Modify, and Delete Tables
Section 6.7. Create a New Table with Data from Existing Tables
Section 6.8. Create and Call SQL Server 2000 User-Defined Functions
Chapter 7. Performing Common Database Tasks Using SQL-DMO
Looking at the SQL Server DMF APIs
Setting References in .NET for the SQL APIs
Section 7.1. Create a Dialog Box to Connect to a New Database, Including Listing Available SQL
Servers and Databases
Section 7.2. Back Up and Verify a SQL Server Database
Section 7.3. Restore a SQL Server Database
Section 7.4. Transfer Tables Between SQL Server Databases
Section 7.5. Create a Detach/Attach SQL Server Database Dialog Box

Chapter 8. Taking Advantage of Data-Driven Techniques


Section 8.1. Work with Data-Bound Multi-Select List Boxes Using Windows Forms
Section 8.2. Use a Single Windows Form to Update Multiple Lookup Tables
Section 8.3. Create a Point-and-Click SQL Server Query Tool for Users Using a Windows Form
Section 8.4. Make a Generic Search Form in a Visual Basic .NET Desktop Application
Section 8.5. Work with Data-Bound Multi-Select List Boxes Using Web Forms
Section 8.6. Use a Single Web Form to Update Multiple Lookup Tables
Section 8.7. Create a Point-and-Click Query Tool for Users Using a Web Form
Section 8.8. Make a Generic Search Form in an ASP.NET Web Application
Chapter 9. Using Classes With Databases to Make Life Easier
Section 9.1. Define a Class in Visual Basic .NET
Section 9.2. Create a Class That Implements the Interface You Defined
Section 9.3. Use Visual Studio .NET Tools to Speed Up Writing ADO.NET Code
Section 9.4. Control the Creation and Behavior of Classes
Section 9.5. Implement the Methods That Update the Database
Section 9.6. Validate Data Passed to Properties and Communicate Errors to Developers
Section 9.7. Write Data Validation Code That Can Be Reused in Other Classes
Chapter 10. Creating Reports Using Crystal Reports
Section 10.1. Create a Report Using Crystal Reports Report Expert
Section 10.2. Display a Report That Was Created
Section 10.3. Add Calculated Fields to the Crystal Reports Report
Section 10.4. Select Whether the Report Will Be Displayed, Printed, or Exported Using Visual Basic
.NET Code
Section 10.5. Determine Which Records Will Be Printed at Runtime
Section 10.6. Print Labels and Control the Order in Which Records Will Be Printed
Section 10.7. Create an Onscreen Report That Contains Hyperlinks
Chapter 11. Managing SQL Server Security
Section 11.1. Create Windows NT/2000 Users
Section 11.2. Create Windows NT/2000 Groups
Section 11.3. Establish a Windows NT/2000 Authentication Mode
Section 11.4. Establish Mixed-Mode Authentication
Section 11.5. Create a Standard Login
Section 11.6. Create a Windows NT/2000 Login
Section 11.7. Use a Fixed Server Role
Section 11.8. Create a Database User Account
Section 11.9. Use Statement Permissions
Section 11.10. Use Object Permissions
Section 11.11. Use Fixed Database Roles
Section 11.12. Create Custom Database Roles
Section 11.13. Create Application Roles
Chapter 12. Utilizing XML Data In Your Visual Basic .NET Applications
Ways of Utilizing XML in .NET
XML Namespaces in .NET
Section 12.1. Use XMLWriter to Create an XML Document
Section 12.2. Use XMLReader to Read an XML Document
Section 12.3. Work with the XML Document Object Model

Section 12.4. Retrieve XML from SQL Server 2000


Section 12.5. Work with Datasets and XML
Chapter 13. Creating XML Web Services
Overview of the XML Web Services Infrastructure
Section 13.1. Get Started with XML Web Services
Section 13.2. Create a Simple XML Web Service Using Parameters
Section 13.3. Consume XML Web Services
Section 13.4. Pass a Dataset Back from an XML Web Service
Appendix A. Desktop Development With ADO
When to Use ADO (Local Database/Single Tier Applications)
Looking At the ADO Object Models
Referencing the Type Libraries
Using the Connection Object
Working with the ADO Recordset Object
Executing a SQL Server Stored Procedure By Using ActiveX Data Objects
Executing Batch Updates with ADO and SQL Server
Creating SQL Server Objects with ActiveX Data Objects
Conclusion
Index

[ Team LiB ]

[ Team LiB ]

Copyright
Copyright 2003 by Sams Publishing
All rights reserved. No part of this book shall be reproduced, stored in a retrieval system, or transmitted by
any means, electronic, mechanical, photocopying, recording, or otherwise, without written permission from
the publisher. No patent liability is assumed with respect to the use of the information contained herein.
Although every precaution has been taken in the preparation of this book, the publisher and author assume
no responsibility for errors or omissions. Nor is any liability assumed for damages resulting from the use of
the information contained herein.
Library of Congress Catalog Card Number: 2001094459
Printed in the United States of America
First Printing: September 2002
05 04 03 02 4 3 2 1

Trademarks
All terms mentioned in this book that are known to be trademarks or service marks have been appropriately
capitalized. Sams Publishing cannot attest to the accuracy of this information. Use of a term in this book
should not be regarded as affecting the validity of any trademark or service mark.

Warning and Disclaimer


Every effort has been made to make this book as complete and as accurate as possible, but no warranty or
fitness is implied. The information provided is on an "as is" basis. The author and the publisher shall have
neither liability nor responsibility to any person or entity with respect to any loss or damages arising from the
information contained in this book.

Credits
ASSOCIATE PUBLISHER
Michael Stephens
ACQUISITIONS EDITOR
Neil Rowe
DEVELOPMENT EDITOR
Kevin Howard
MANAGING EDITOR
Charlotte Clapp
PROJECT EDITORS

Matt Purcell
Tony Reitz
COPY EDITOR
Karen A. Gill
INDEXER
Sandy Henselmeier
PROOFREADER
Linda Seifert
TECHNICAL EDITOR
Shawn Nanto
TEAM COORDINATOR
Lynne Williams
INTERIOR DESIGNER
Gary Adair
COVER DESIGNER
Alan Clements
PAGE LAYOUT
Stacey Richwine-DeRome

Dedication
I would like to dedicate this book to the innocent men, women, and children who died in the September 11,
2001 attack in New York, at the Pentagon, and those in the airplanes as well. God bless you, and God bless
America!

[ Team LiB ]

[ Team LiB ]

About the Author


F. Scott Barker holds a bachelor of science in computer science and has worked as a developer in the
database field for more than 16 years, first with Clipper, and then with Visual Basic, SQL Server, and
Microsoft Access for the past 10 years. Scott has already created and deployed a number of ASP.NET Web
sites for his clients.
Working at Microsoft for two years, Scott was a member of the Microsoft Access and FoxPro teams. Since
leaving, he has been a contractor with Microsoft developing in-house tools used throughout Microsoft. With
his company, Applications Plus, Scott has also been doing contract development for many Fortune 500
companies, developing in ASP.NET, Visual Basic .NET, C#, Visual Basic/Access/Office, and SQL Server.
Scott has trained for Application Developers Training Company and others all around the U.S. and is a
frequent speaker at Microsoft conferences throughout the U.S., Canada, South Asia, and Europe. Through
his classes and conferences, Scott has trained thousands of developers.
Scott is a writer for a number of development magazines and is the author of F. Scott Barker's Microsoft
Access 2002 Power Programming, published by Sams, and Access 97 Power Programming, published by
Que.
Scott can be reached at his Web site at www.appsplus.com, or via e-mail at FSBarker@AppsPlus.com.

[ Team LiB ]

[ Team LiB ]

Acknowledgments
To start off, I want to acknowledge Neil Rowe, my acquisitions editor, for putting up with me for another
book. Thanks also to Kevin Howard, my development editor. Bud, you had me scared at first.
A big thanks goes to my longtime friends Paul Sheriff, Ken Getz, Mike Groh, and Tom Howe, for putting up
with me that week at Paul's class, helping me hammer out the outline, and being around for me to bounce
ideas off of.
A really special thanks goes to Michael O'Neil for his work on the Classes chapter. He is a man who has a lot
of class. Good luck with NYU!
Another thanks to Mike Groh for his work on the Security chapter, you are a great friend Mike, and know
your stuff!
Thanks to the students who attended my .NET classes and helped to shape the contents of this book as I
was writing it. No, you don't get any of my royalties, but you have my thanks.
As usual, I want to thank some people from Microsoft for putting up with my questions, listening to my
beefs, and not telling me where to go when I got on their nerves. Ari Bixhorn, you are one of the most
enthusiastic guys I know. My thanks to you and Ed Robinson for being so patient with me and letting your
excitement for .NET show through so honestly.
My thanks to Woodinville #2 Starbucks for putting up with me rearranging their furniture so I could work by
both the plug-in and the fireplace while I cranked on this book and for keeping those lattes and cappuccinos
coming.
Finally, I need to acknowledge those people who put up with me the most: my wife Diana, and my kids
Christopher, Kari Anne, Nichole, David, and Joseph. Let's go skiing!

[ Team LiB ]

[ Team LiB ]

Tell Us What You Think!


As the reader of this book, you are our most important critic and commentator. We value your opinion and
want to know what we're doing right, what we could do better, what areas you'd like to see us publish in,
and any other words of wisdom you're willing to pass our way.
As an associate publisher for Sams, I welcome your comments. You can fax, e-mail, or write me directly to
let me know what you did or didn't like about this bookas well as what we can do to make our books
stronger.
Please note that I cannot help you with technical problems related to the topic of this book, and that due to
the high volume of mail I receive, I might not be able to reply to every message.
When you write, please be sure to include this book's title and author as well as your name and phone or fax
number. I will carefully review your comments and share them with the author and editors who worked on
the book.
Fax:

317-581-4770

E-mail:

feedback@samspublishing.com

Mail:

Michael Stephens
Sams Publishing
201 West 103rd Street
Indianapolis, IN 46290 USA

[ Team LiB ]

[ Team LiB ]

Introduction
Dear Reader,
Here we are on the brink of what feels like another revolution of computing: .NET. Now I know what some
people are saying: .NET is merely evolutionary, not revolutionary. I disagree. Here I sit at the San Francisco
airport, waiting for my return flight home to Seattle, sipping on my ice quad venti vanilla latte (yes, that is a
coffee drink) pondering all wonderful technology I witnessed this week.
You see, I spent the past five days sitting in on Paul Sheriff's "Jumpstart Introduction to VB.NET Week."
Along with around 20 regular students, Paul had asked a few of his friends who were also interested in
presenting the training down for what is called "Train the Trainer."
By the time you read this, I will have presented the training myself a few times. I have to admit I have not
been as excited about a product like this since my early days of working on the Access team. (No snide
remarks about Access, please.) I have been working primarily in Access/Visual Basic/SQL Server for the past
10 yearsand making a good living at it. The Web daunted me with all that there was to develop for it using
products such as ASP and HTML. That has all changed.
Now the line between developing for the Web and desktop has become almost invisible. Developing with
Visual Basic .NET and databases is more powerful than ever, and you can easily blend creating Windows
Forms (Visual Basic .NET) and Web Forms (ASP.NET) into the same solution. One of the most exciting things
is that when you know how to work with ADO.NET using Windows Forms, you can use similar techniques and
the same objects in Web Forms.
I hope that, with this book, you will become as excited as I am about creating database applications using
Visual Basic .NET!
Sincerely,
F. Scott Barker
Author, Database Programming with Visual Basic .NET and ADO.NET: Tips, Tutorials, and Code

[ Team LiB ]

[ Team LiB ]

Who Is This Book For?


This book is for the serious developer, whether corporate or independent, who wants to create database
applications using Visual Basic .NET. If you are looking for a primer on .NET or even a beginner book for
Visual Basic .NET, then you would be wise to pick up another book to give you the basics on those topics.
This book focuses on the topic I love best: database development.
Current Visual Basic developers will get quite a bit out of this book. Although you will find that much of Visual
Basic .NET looks familiar, the language has grown up quite a bit and even gets more of the respect it
deserves since we can now use the Visual Basic we know and love in Web Forms as well as Windows Forms.
Those who are familiar with ADO will appreciate the samples that show how to use ADO.NET. Hopefully, even
hard-core Visual Basic developers will appreciate the chapter on wrapping up some of the ADO.NET data
access into classes for simplified coding.
Those Access developers who are attempting to take the plunge and expand their horizons hopefully will be
placed at ease with how similar developing databases between Access and Visual Basic is. If you have
already worked with SQL Server and ADO, then you are ahead of the game. Even if you haven't developed in
Visual Basic previously, this book will give you some great sample code to get started.
Corporate management will find this book useful for getting an idea of what their developers can do with
Visual Basic .NET, both on the desktop and Web sides.

[ Team LiB ]

[ Team LiB ]

What's Covered in Database Programming with Visual Basic .NET and ADO.NET: Tips, Tutorials, and
Code?
I have laid out this book to logically follow the course of learning how to use the database features of Visual
Basic .NET and be as productive as possible, as quickly as possible. Hopefully, you can see this even with the
way I have laid out and labeled the chapters:

Chapter 1, "Developing Windows Forms Using Bound Controls," gets you started being productive from
the get-go. You will get a great start on creating Windows Forms in Visual Basic .NET that you can use
not only to view data, but also to add, edit, and delete data. You will accomplish this quickly by using
bound controls that are more robust than what is found in previous versions of Visual Basic.
Chapter 2, "Creating SQL Server Database Objects from Visual Studio .NET," shows you how to use
some of the design tools found inside Visual Studio .NET to create and maintain SQL Server databases.
Chapter 3, "Viewing Data with ADO.NET," takes you into using the objects found in ADO.NET, such as
the OleDbDataReader and OleDbDataSet. These objects and more are used to view data and load it
into various controls such as list boxes and combo boxes.
Chapter 4, "Manipulating Data with ADO.NET," takes you further into using the objects found in
ADO.NET. You will see how to use the various objects in ADO.NET to add, edit, and delete records and
manipulate data.
Chapter 5, "Working with Data in Web Forms," takes what you learned in the previous chapters and
shows you how to create useful Web Forms using ASP.NET, including taking advantage of the DataGrid
control.
Chapter 6, "Creating Transact-SQL Commands," brings you deeper into the language that makes SQL
Server such a powerful database. It teaches you how to take advantage of the Transact-SQL
commands to update and manipulate your SQL Server database from Visual Basic .NET.
Chapter 7, "Performing Common Database Tasks Using SQL-DMO," explains how to perform day-to-day
tasks in your Visual Basic .NET application that an administrator would normally have to do. This
includes common tasks such as importing/exporting data and backing up/restoring databases.
Chapter 8, "Taking Advantage of Data-Driven Techniques," takes what you have learned in the book so
far and puts it to even more practical use. This chapter shows you how to create utilities that perform
more tasks with less code. This chapter demonstrates how to create a point-and-click interface for a
query tool for your users.
Chapter 9, "Using Classes with Databases to Make Life Easier," shows that even though it can take
quite a bit of code to perform data access tasks, you can use classes to wrap that code for accessing
data. By taking table definitions up into properties and methods, you can make accessing data in
tables more intuitive and simpler to access throughout your applications.
Chapter 10, "Creating Reports Using Crystal Reports," gets into using crystal reports with Visual Basic
.NET. Besides learning how to generate standard reports, you will see how to create some of the more
advanced features that really take advantage of the engine.
Chapter 11, "Managing SQL Server Security," discusses the SQL Server security scheme and explains
the options available to you as a Visual Basic developer. Authentication modes are discussed, as are
the various permissions you might set on SQL Server objects.

Chapter 12, "Utilizing XML Data in Your Visual Basic .NET Applications," shows you just how much you
can use XML from within your applications. You are shown how to load and manipulate XML document
objects, as well as retrieve XML from SQL Server 2000.
Chapter 13, "Creating XML Web Services," show you how to take advantage of the one of the coolest
new features in .NET. You can create a XML Web Service that both Internet and Desktop applications
can take advantage of, and even pass ADO datasets.
Appendix A, "Desktop Development with ADO," discusses the use of ADO in your .NET applications.
Although .NET uses ADO.NET, you might have a lot invested in ADO development. This Appendix shows
how you can create a reference in your .NET application and what objects there are.

[ Team LiB ]

[ Team LiB ]

Chapter 1. Developing Windows Forms Using Bound Controls


In this chapter you will

Create a bound list box


Limit the data displayed in a bound list box
Bind and view individual text boxes based off a selected list box item
Edit and update data using bound controls
Add records using bound controls
Take care of error handling with bound controls
Put the finishing touches on an add/edit/delete form
Bind data to ComboBox and DataGrid controls
Drill down to data in the DataGrid control
Visual Basic .NET, one of the languages that ships with Visual Studio .NET, is a far more robust language
than what was found in Visual Basic 6.0. Now, before you VB 6 developers go off and start cussing me for
saying this, read on.
As in previous versions of Visual Basic, you can use code to bind controls manually, which takes more work
than using bound controls. The other method is to use data bound controls, which in the past were avoided
because of not having total control over the way data is handled. In VB .NET, Microsoft has done a better job
of not only creating the data bound controls, but because everything is class-based in the .NET languages,
you also can see the code that is generated when you place data bound controls onto a form.
In the prior versions of Visual Basic, the Microsoft Jet Database Engine was the database flavor of choice for
small- to medium-sized databases, as well as books such as this one. Now, Visual Basic ships with the
Microsoft SQL Server 2000 Desktop Edition (MSDE). This is a scaled-down version of SQL Server 2000,
allowing you to create databases on your local machine and then distribute them for use on a user's
individual local version of the MSDE or a larger SQL Server 2000 installation. If this sounds intimidating,
don't worry. You will learn how to use SQL Server from within Visual Studio and your application. This
includes creating utilities for connecting to new databases, as well as backing up and restoring databases.
Although this book uses the MSDE for the database, you only have to adjust the ConnectionString
property of the OleDbConnection object on forms and the ConnectionString property of the
OleDbConnection object discussed in Chapter 4, "Manipulating Data with ADO.NET." In the code in Chapter
4, you will see a commented line of code that provides an example of using a Jet database version of
Northwind.

[ Team LiB ]

[ Team LiB ]

1.1 Create a Bound List Box


It used to be that when you wanted to create a data entry form, you just assigned a recordset to the data
control and allowed the users to scroll through the data, making changes as needed. When you're dealing
with Client Server or Web applications, this just doesn't cut it.
One of the first things you need to do is provide a method to limit the amount of data so that users can pick
which record they want to edit/view, without pulling all the fields of a table over the Neteither LAN or
Internet. List boxes and combo boxes help with that. In this How-To, you will learn how to set up two data
controls: OleDbDataAdapter and DataSet . These controls enable you to populate a list box with one line
of code.
You want to see a list of customers on your Windows form in a ListBox control. You don't want to write
code at this point. You only want to prototype a form, so you just want to use bound data controls. How do
you create a list box and bind it using the data controls?

Technique
To get started with learning about any of the objects used for data in .NET, it's important to talk about what
.NET Namespaces are and how to use them.

Using .NET Namespaces


The .NET Framework contains a big class library. This class library consists of a number of Namespaces.
These Namespaces are made up of various classes that allow us to create our objects. All of the objects and
classes that make up the .NET objects, such as forms, controls, and the various data objects, can be found
in Namespaces.
Namespaces also can be made up of other Namespaces. For example, there is a Namespace called
System.Data. Although this Namespace has classes in it, such as DataSet and DataTable, it also has
Namespaces within it called System.Data.OleDb and System.Data.SQLClient, as well as others. To
check out the .NET Namespaces, choose Object Browser from the View menu. You can then expand the
System.Data Namespace to see the other Namespaces contained within.
If you are positive that the database you are going to be working with is SQL Server, then you would be far
better served performance-wise to use the classes found in the System.Data.SQLClient Namespace.
However, if you are not sure what database you will be working against, you should use the classes found in
System.Data.OleDb.
For this book, I am using objects created from the classes in the System.Data.OleDb Namespace. That
way, you can use the routines against other databases with less modifications.

Tip

If you know that the back end that you will be accessing is SQL
Server, then use the SQL Server type data controls because they are
optimized for it.

Eight data controls are available for Windows forms. Table 1.1 lists these controls and their uses. You can
find these controls by clicking on the Data group in the toolbox.

Table 1.1. Data Controls Used in Windows Forms


Control Name

Purpose

DataSet

This control is used in conjunction with the other data controls, storing the results that
are returned by commands and the DataAdapters. Unlike the recordset from ADO
and DAO, the DataSet actually brings back a hierarchical view of the data. Using
properties and collections in the DataSet object, you can get all the way down to
individual tables, rows, and columns.

OleDbDataAdapter This control stores and manages the commands you want to use against an OleDb
provider such as Jet, Oracle, or SQL Server. The commands for selecting, updating,
inserting, and deleting records can be used. The Connection against which to use the
commands is also tracked.

OleDbConnection This control maintains connection information for an OleDb provider. This control is
used with the OleDbDataAdapter.
OleDbCommand

Similar to the ADO command object, this control allows you to execute SQL
statements or stored procedures to either run bulk operations or return data.

SqlDataAdapter

This control is the same as the OleDbDataAdapter except that it is for use only
against SQL Server stores.

SqlConnection

This control is the same as the OleDbConnection except that it is for use only
against SQL Server stores.

SqlCommand

This control is the same as the OleDbCommand except that it is for use only against
SQL Server stores.

DataView

This control creates multiple views of the same table. This includes looking at data in
various states such as deleted, changed, or sorted differently.

Creating two types of the data controls just mentioned, OleDbDataAdapter and DataSet , bind them to a
list box to display a list of customers. Note that an OleDbConnection control is also created, but Visual
Studio .NET creates it. You then add a line of code to fill the dataset.

Steps
To preview this How-To, open the solution called VB.NetChapter1 found in the chapter folder. When you
run the project, the first form that comes up is the main switchboard with each of the How-Tos listed for this
chapter. Click on How-To 1.1 to open the form for How-To 1.1 (see Figure 1.1).
Figure 1.1. Main form and How-To form 1.1 from the first chapter's solution.

Note
You can find the source code for all the chapters in the book at
www.samspublishing.com. After you are there, just type the book
ISBN (0672322471).

1. Create a new Visual Studio .NET project using the Windows Application project template. This creates
the initial form called Form1 that you will use.
2. Drag the OleDbDataAdapter control from the Data Controls group located in the toolbox and drop it
onto the form. The Data Adapter Configuration Wizard appears. Read the introductory screen, and then
click Next to choose your data connection. At this point, if you don't see a data connection to
Northwind database in the drop-down list of data connections to use, click the New Connection button.
You then see the Data Link Properties dialog box with which you are familiar if you have used other
Microsoft products such as Visual Studio 6.0. Type (local) for the server name, select Use Windows NT
Integrated Security, and select Northwind for the database (see Figure 1.2.) Click OK.
Figure 1.2. From now on, this data connection will show up in Visual Studio .NET's Server
Explorer on this machine.

3. Now you will be back on the Choose Your Data Connection page of the Data Adapter Configuration
Wizard, with the Northwind database in the Data Connection drop-down list. Click Next. This brings you
to the page to select the query type on which the data adapter will be based. Leave the default of Use
SQL Statements, and click Next. In the text box that asks What Data Should the Data Adapter Load
into the Dataset?, type the following:

Select CustomerID, CompanyName From Customers

Note
By default, the Data Adapter Configuration Wizard creates select
statements not only for selecting (viewing) data, but also for
inserting, updating, and deleting. If you don't need these other
options, click the Advanced Options button at the bottom-left corner
of the dialog box. Deselect the check box that reads Generate
Insert, Update, and Delete statements. You don't need this because
we are just using the data to fill a ListBox control. Click OK to
close the Advanced Options dialog box.

4. Click Next to see results of your select statement, as shown in Figure 1.3. If you see something

4.
different from Figure 1.3, you either have entered your select statement incorrectly, or you have
forgotten to deselect the advanced options.
Figure 1.3. Success in creating the data adapter.

5. Click Finished to create a data adapter and connection object. You then see a new data adapter control
called OleDbDataAdapter1 and a Connection control called OleDbConnection1. Both controls are in
the Components tray below the Form designer.
6. After you have created the first two controls, it is time to create the Dataset object. Right-click on the
Data Adapter and choose Generate Dataset from the pop-up menu. This opens the Generate Dataset
dialog box. You can keep all the defaults and simply click OK to create a dataset control called
DataSet<number>. (The sequential number might vary if you have generated other dataset controls.)
7. Now you are ready to create the ListBox control. Drag the ListBox control from the Windows Forms
group in the toolbox and drop it on your form. Stretch the control to the size of your form, and then set
the following properties in Table 1.2 on the ListBox control.

Table 1.2. ListBox Control Property Settings Needed to Bind a DataSet Control

Property

Setting

DataSource

DataSet<Number>

DisplayMember

Customers.CompanyName

ValueMember

Customers.CustomerID

Although you have bound the dataset to the correct properties in the ListBox control, if you run the
form at this point, you will still see a blank ListBox control on the form.
8. Now it is time for the one line of code in this sample, in the Load event of the form. Click on the View
Code button in the Solution Explorer, or choose Code from the View menu. In the Code Editor, select
(Base Class Objects) from the Class Name drop-down list, and then select Load from the Methods
drop-down list. Next, type the line of code as displayed here, which tells the data adapter to fill the
dataset with data:

OleDbDataAdapter1.Fill(DataSet1)

Be sure to use the names of the controls you created. Listing 1.1 presents the Load event code
for the form called frmHowTo1_1 in the samples.
Listing 1.1 frmHowTo1_1.vb: Filling the Data Set on Which ListBox1 Is Based

Private Sub frmHowTo1_1_Load(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
Me.OleDbDataAdapter1.Fill(Me.DataSet1)
End Sub

How It Works
When the form called frmHowTo1_1 loads, the Fill method of the OleDbDataAdapter1 is called, with the
DataSet1 being passed. Because the DataSource property of ListBox1 was specified as being DataSet1
and the ValueMember and DisplayMember properties are both set appropriately, ListBox1 is populated
with the CustomerID and CompanyName from the Customers table in Northwind. Figure 1.4 shows what the
final form looks like in Design view, and Figure 1.5 shows what it looks like when running.
Figure 1.4. The final design view for your first database how-to.

Figure 1.5. This list is based on the Customers table in the Northwind SQL Server database.

Comments
In the .NET version of Visual Basic, Microsoft went to considerable effort to make the data controls more
robust than ever. One cool thing is that most of the tasks that are done for you in Visual Basic .NET are
discoverable. Even though you are using the data controls on your form, Visual Studio creates the code
under the covers. You can see this code by clicking on the #Region statement that reads like this:

#Region " Windows Form Designer generated code "

Beware: There is much code here, and you don't want to change it. You can learn a lot from reading it
though.
As you continue to use the Data Controls shown here, you will become comfortable with changing various
properties and getting more power and use out of them.

[ Team LiB ]

[ Team LiB ]

1.2 Limit the Data Displayed in a Bound List Box


Even populating a list box with a couple of columns from a table full of data can be a big performance hit.
This How-To shows you how to create a parameterized SQL statement to limit the items that are displayed in
the list box, thus giving you better performance on your forms.
You have hundreds of thousands of customers in your database, and you don't want the list box loaded up
with the whole customer table. How can you limit the data that is displayed in your list box?

Technique
You are going to make a copy of the form that you created in How-To 1.1. You will then add a Label and
TextBox control that the Select statement contained within the OleDbDataAdapter control will query
against to limit the data displayed in the list box. A command button will be added to allow you to call the Fill
method of the OleDbDataAdapter control whenever you update the text box, and then you can click the
command button (see Figure 1.6 ).
Figure 1.6. You can now limit the amount of data loaded into the list box.

Steps
To get started with this How-To, right-click the form you created in How-To 1.1, which should be listed in the
Solutions Explorer. Choose Copy from the pop-up menu. Next, right-click the project in the Solution
Explorer, and choose Paste from the pop-up menu. You will now have a new Class object in the Solutions
Explorer called Copy Of whatever the previous name of the form was . Rename the new form that you have
created to the name you desire. Then, with that form highlighted, click on the Code button above the
Solutions Explorer. Change the first line of code to say this:

Public Class <Name of the new form>

You see, VS does not change the line of code automatically for you. It thinks you have a duplicate Class
definition.
Now you can see that the icon of the form is correct. You can continue with the steps of the How-To.

1. Select the Data Adapter that you created. In the Properties pane, you will see the CommandText property when
click on the SelectCommand property plus sign. Replace the CommandText property with the following comman

SELECT CustomerID, CompanyName FROM Customers WHERE (CompanyName LIKE ? + '%')

You will learn more about the Select statement in Chapter 3 . However, the WHERE clause used here comp
CompanyName to a parameter that will be supplied, as indicated by the ?. This will be performed using co
the final step of this How-To. The % is a wildcard that tells the server to make it a fuzzy search.

2. Resize the ListBox control, and leave room at the top of the form for the Label, TextBox, and Command button
Create these three controls, setting the properties described in Table 1.3 .

Label
Text
Customer
TextBox
Name
txtCustLimit

Text
A
Command Button
Name
btnLoadList

Text
Load List

Table 1.3. Label, TextBox, and Command Button Control Property Settings
Object

Property

Setting

3. Double-click the new command button you just created called btnLoadList . Enter the code in Listing 1.2 in th

3.

Click event of the btnLoadList button. This code loads the data entered from txtCustLimit into the parame
of the OleDBDataAdapter1 , which was created by using the ? in the Select statement of the data adapter. T
Dataset1 is cleared of its data with the Clear method. Finally, DataSet1 is refilled with data based off the valu
txtCustLimit , using the data adapter.
Listing 1.2 frmHowTo1_2.vb : Submitting a Parameter to a DataAdapter and Filling the Dataset

Private Sub btnLoadList_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnLoadList.Click
Me.OleDbDataAdapter1.SelectCommand.Parameters(0).Value = _
Me.txtCustLimit.Text
Me.DataSet1.Clear()
Me.OleDbDataAdapter1.Fill(Me.DataSet1)
End Sub

Note

There is one big difference here between an OleDbDataAdapter and a SqlDataAdapter


. Whereas the OleDbDataAdapter takes a ? to specify a parameter within the Select
statement, the SqlDataAdapter requires a named parameter such as @parCustLimit .
Therefore, instead of the select statement in step 1 being this:

SELECT CustomerID, CompanyName FROM Customers WHERE (CompanyName LIKE ? + '%')


It would be this:

SELECT CustomerID, CompanyName FROM Customers WHERE (CompanyName LIKE @parCustLimit + '%')

The naming of the actual parameter is up to you.

4. Highlight and delete the Load event for the form because you don't want to just fill the event when the form loa
You want the user to click btnLoadList .

How It Works
When the form you created for this How-To loads, or when you're using the form called frmHowTo1_2 , you
will see a blank ListBox control with a text box on top with the letter A in it. If you click the command button
called btnLoadList , the list box becomes filled with values based on the letter (or letters) in
txtCustLimit and on the code described in step 3.

Comments

Try entering a few other letters, and then try entering no letters. What happens? You limit the list more by
the number of letters you enter, and you get all entries when you don't enter any letters.
The method displayed here, although simple, is powerful, and it can be used in a variety of ways.
You can continue building on this form for the next few How-Tos. If you want to copy your form and start a
new one as described at the beginning of the steps for this one, you have the instructions there. Otherwise,
by the time you reach How-To 1.8, you will have a data entry form that you can use. Each step, however, is
available in the sample solution for this chapter.

[ Team LiB ]

[ Team LiB ]

1.3 Bind and View Individual Text Boxes Based Off a Selected List Box Item
Using a list box similar to the one in the previous How-To, in this How-To, you will learn how to create
additional OleDbDataAdapter s and DataSets and bind them to individual text boxes for viewing data.
Although the list box is nice for displaying a couple of fields in my form and limiting the rows displayed, how
do you list the detail in individual text boxes by clicking on an item in the list box?

Technique
You are going to enhance the form that you created in How-To 1.2 to use additional data controls,
specifically another data adapter and dataset. You will set up the select statement in the new data adapter
to take the selected list box item as a parameter. The dataset will then be filled with data, and some text
boxes with the dataset set as the data source will display the current record. You can see an example of this
in Figure 1.7 .
Figure 1.7. You can bind text boxes to datasets as well as list boxes.

Steps
To go further with the form with which you have been working, you will want to rename the data adapter,
dataset, and list box currently on the form to something more meaningful. You can see this in Table 1.4 .

ListBox
Name
ListBox1
lstCustomers

OleDbDataAdapter
Name
OleDbDataAdapter1
odaCustomerList
DataSet
Name
DataSet1
dsCustomerList

Table 1.4. Changes to Current Objects on the Form


Object

Property

Old Setting

New Setting

Tip

Name your objects at the time you first create them. This is true of your
solutions, projects, and forms, as well as controls used on forms. With the .NET
languages being so class and code driven, some items take multiple steps to
rename. Visual Studio doesn't seem to catch all the places in code that need to
be changed. Renaming a form is a good example. Remember that you had to
change the Public Class statement in the code to have it match the new name of
the form.

1. Drop another Data Adapter control on the form from the Data group of the toolbox. Use the existing data
connection, specify that you will use SQL statements, and assign the following select statement that will
use a parameter supplied at runtime by using the selected list box item.

SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address, City, Region,


PostalCode, Country, Phone, Fax FROM Customers WHERE (CustomerID = ?)

You can click the Advanced Options button on the select statement page and deselect creating the
Insert, Update, and Delete commands. Finish the Data Adapter Wizard and rename the control
odaCustomerIndividual .

2. Right-click odaCustomerIndividual and choose Generate Dataset from the pop-up menu. Create a
new dataset called dsCustomerIndividual . Click OK. As of this writing, VS places a 1 at the end of
name of the dataset you specified. Clean up the name by removing the 1. Now you should have two data
adapters: odaCustomerList and odaCustomerIndividual , and two datasets: dsCustomerList

3.

and dsCustomerIndividual .
3. Now it's time to add the text boxes. Resize the form so that it allows you to add a column of text boxes
with labels beside them. You will then add the labels and text boxes, setting the Text property of the text
boxes to the column names in the Customers table as supplied by dsCustomerIndividual. The Text
property of text boxes falls under the Data Binding category in the property sheet. Table 1.5 describes
the controls and their property settings. Refer to Figure 1.8 for an example of where to place them.
Figure 1.8. Letting users know when they can edit data is helpful.

Label
Text
Customer ID

Label
Text
Company Name
Label
Text
Contact
Label
Text
Contact Title
Label
Text

Address
Label
Text
City
Label
Text
Region
Label
Text
Country
Label
Text
Phone
Label
Text
Fax
TextBox
Name
txtCustomerID

Text
dsCustomerIndividual - Customers.CustomerID
TextBox
Name
txtCompanyName

Text
dsCustomerIndividual - Customers.CompanyName
TextBox

Name
txtContact

Text
dsCustomerIndividual - Customers.Contact
TextBox
Name
txtContactTitle

Text
dsCustomerIndividual - Customers.ContactTitle
TextBox
Name
txtAddress

Text
dsCustomerIndividual - Customers.Address
TextBox
Name
txtCity

Text
dsCustomerIndividual - Customers.City
TextBox
Name
txtRegion

Text
dsCustomerIndividual - Customers.Region

TextBox
Name
txtPostalCode

Text
dsCustomerIndividual - Customers.PostalCode
TextBox
Name
txtCountry

Text
dsCustomerIndividual - Customers.Country
TextBox
Name
txtPhone

Text
dsCustomerIndividual - Customers.Phone
TextBox
Name
txtFax

Text
dsCustomerIndividual - Customers.Fax

Table 1.5. New Label and TextBox Control Property Settings for the Form

frmHowTo1_3
Object

Property

Setting

4. Now it's time for the code. To start off, you will change the Click event code for the button called
btnLoadList. The first three lines of code are basically the same as in the last How-To, except that the
name was changed as needed, and some lines were added to comment the code.

4.

Listing 1.3 frmHowTo1_3.vb : Adding the Call to the RefreshIndividual Subroutine

Private Sub btnLoadList_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnLoadList.Click
' Store the text entered to limit the list box to the
' data adapter's parameter
Me.odaCustomerList.SelectCommand.Parameters(0).Value = _
Me.txtCustLimit.Text
' Clear the current data in the dataset
Me.dsCustomerList.Clear()
' Fill the customer list dataset
Me.odaCustomerList.Fill(Me.dsCustomerList)
' Fill the initial entry's individual dataset
RefreshIndividual()
End Sub

The only new line of code is the one just before the End Sub , which calls the subroutine
RefreshIndividual for the first item in the newly populated list box. This subroutine first
handles clearing the dataset called dsCustomerIndividual . It ensures that a customer is
chosen in the list box and then places the selected item into the parameter value for
odaCustomerIndividual . The dataset called dsCustomerIndividual is then filled, displaying
the data in the text boxes bound to the dataset.
Listing 1.4 frmHowTo1_3.vb : Refreshing the Dataset on Which the Text Boxes Are Based

Private Sub RefreshIndividual()


' Clear individual customer dataset
Me.dsCustomerIndividual.Clear()
' Check to see if an item was selected
If lstCustomers.SelectedIndex <> -1 Then
' Store the selected customer ID into the
parameter of the SQL data adapter
Me.odaCustomerIndividual.SelectCommand.Parameters(0).Value = _
Me.lstCustomers.SelectedItem(0)
'

' Fill the dataset


Me.odaCustomerIndividual.Fill(Me.dsCustomerIndividual)
End If
End Sub

The only other code is required when a new item is selected in the list box. You want to refresh the
text boxes with the new data. This code is on the SelectedIndexChanged event for the

lstCustomers list box.


Listing 1.5 frmHowTo1_3.vb : Call the RefreshIndividual Routine

Private Sub lstCustomers_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) _
Handles lstCustomers.SelectedIndexChanged
' Fill the current list item's individual dataset
RefreshIndividual()
End Sub

How It Works
After the user types a letter into the txtCustLimit text box and clicks btnLoadList , the list box is
populated with the items that match the text in the text box. After lstCustomers is populated, the user
sees the details for the first item in the list box that is displayed in the text boxes. This occurs within the
routine called RefreshIndividual . The selected item is used for the parameter for the data adapter
called odaCustomerIndividual , then filling the dataset called dsCustomerIndividual . As the user
selects new items in the list box, the data that corresponds to the selected item is displayed.

Tip

Always try to break your code into specific routines such as the subroutine
RefreshIndividual . That way when someone tries to read the code, the code
is easy to read, you don't make mistakes by typing the same code two or more
times, and you only have to change the code in one place if necessary.

Comments
Binding text boxes to datasets saves work when switching from one record to another because you don't
have to specifically load each text box with data each time. It also gets easier every time you use a data
adapter and dataset in combination when manipulating data on your forms.
Bound datasets are particularly useful as you edit, add, and delete data on your forms, as you will see in the
following How-Tos.

[ Team LiB ]

[ Team LiB ]

1.4 Edit and Update Data Using Bound Controls


Although viewing data is fine in some situations, the real power comes in being able to edit the data in the
text box and update the data in the server. In this How-To, you learn just that, using some new methods
and properties from the various data controls, including autogenerated commands that create update,
insert, and delete SQL statements for you. This How-To also introduces you to the BinderContext class ,
which is used to perform updates with bound controls.
The form as created in How-To 1.4 is great if you just want to view data for various records. But what if you
want to let users edit and update the data?

Technique
Continuing with the form you used in the previous How-Tos, you are going to add a few command buttons
with code that will perform the following:

Edit. Toggle the look of the text boxes to let the user know that he can edit the data in the controls by
giving the controls a sunken look.
Save. Save the changes made to the data back to the server. Then toggle the look of the text boxes
back to flat so that the user knows he can't edit them.
Cancel. Toggle the text boxes back to flat so that the user knows he can't edit them anymore, but
don't save the data.
It is important to let users know when they can edit and when they can't. By toggling the style of the text
boxes between flat and sunken and the color between gray and white, users have a definite clue that they
can edit the data at certain times (see Figure 1.8 ).
Another useful item to note about this How-To is the introduction of the BindingContext class. This class
helps you work with data bound controls. Each control on a Windows form has a BindingContext object as
well as BindingContext objects for any controls that are contained within that control. Each form also has
a BindingContext object. BindingContext objects manage BindingManagerBase class object(s) for
each control. Through the BindingContext object, you can manage editing and updating data back to the
server. BindingManagerBase objects help with synchronization of datasets with controls. You are going to
use the BindingContext class throughout the rest of this chapter, but only a couple of methods.

Steps
Open the solution for the chapter called "VB .NET How-To Chapter 1 ," and run the application. From the
main form, click the command button with the caption "How-To 1.4." Click the Load List button . Along with
the list being filled with customers whose names start with A , you will see the detail How-To fill in on the
right side of the form with the first customer in the list. Click Edit. You will now be able to change the data in
the text boxes. After you have changed a couple of fields, click Save. If you select another customer from
the list box, select the one you changed. You will see that your changes have in fact been saved. If you click
Cancel instead of Save, your changes will not be saved. Note that if you change the name of the customer,
you must load the list again to see the changes in the list box.

1. Add the three command buttons to your form as described in Table 1.6 and as displayed in Figure 1.8 .

1.

Command Button
Name
btnEdit

Caption
&Edit
Command Button
Name
btnSave

Caption
&Save
Command Button
Name
btnCancel

Caption
&Cancel

Table 1.6. Command Buttons to Edit, Save, and Cancel Changes to Data
Object

Property

Setting

2. Add the code shown in Listing 1.6 to the btnEdit Click event.

Listing 1.6 frmHowTo1_4.vb : Calling the ActiveEditing Subroutine from the btnEdit Command Button

Private Sub btnEdit_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnEdit.Click
' Enable the editing of the form
ActivateEditing(True)
End Sub

This calls the routine called ActivateEditing , listed here. This code iterates through all the controls on
the form and uses the TypeOf condition to check whether the current control is a text box. It also ensures
that the control is not the txtCustLimit text box. If the control is a text box and the parameter called
bEnable has been passed in as True , then the sunken look is given to the text box using the
BorderStyle and the BackColor property is set to white. Otherwise, the BorderStyle is set to
FixedSingle , which is flat, and the BackColor is set to the backcolor of the form. The Enabled
property of the text box is set to whatever bEnabled is: True or False .
Listing 1.7 frmHowTo1_4.vb : Toggling the Enabled Property and Look of Text Boxes

Private Sub ActivateEditing(ByVal bEnable As Boolean)


Dim oCurr As Object
' Loop through each of the controls on the form
For Each oCurr In Me.Controls()
' Check to see if the control is a text box
If TypeOf oCurr Is TextBox And oCurr.Name <> "txtCustLimit" Then
' If so, toggle the properties
If bEnable Then
oCurr.BorderStyle() = _
System.Windows.Forms.BorderStyle.Fixed3D
oCurr.BackColor() = System.Drawing.Color.White
Else
oCurr.BorderStyle() = _
System.Windows.Forms.BorderStyle.FixedSingle
oCurr.BackColor() = Me.BackColor

End If
oCurr.Enabled = bEnable
End If
Next
End Sub

3. Add the code to the Click event of the btnSave command button. This code calls a new routine called
SaveRecord , which performs the actual save. The ActivateEditing is then called, passing False to disable
the controls because you are finished editing.

Listing 1.8 frmHowTo1_4.vb : Calling the SaveRecord Routine and Disabling the Text Boxes by Calling

ActivateEditing

Private Sub btnSave_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnSave.Click
' Save the information
SaveRecord()
' Disable the text boxes
ActivateEditing(False)
End Sub

Following is the code for the subroutine called SaveRecord . This routine calls the EndCurrentEdit
method of the BindingContext class, passing in the dataset called dsCustomerIndividual , and
specifying the Customers table. Then the Update method is called off the data adapter called
odaCustomerIndividual , passing the same parameters. This updates the changes back to the dataset
Finally, the dataset changes are sent back to the server.
Listing 1.9 frmHowTo1_4.vb : Saving the Data Back to the Server

Private Sub SaveRecord()


' Use the BindingContext class to end current editing
' so that we can update the server.
Me.BindingContext(Me.dsCustomerIndividual, "Customers").EndCurrentEdit()
' Perform the requested task at the dataset
'
level using the data adapter
Me.odaCustomerIndividual.Update(Me.dsCustomerIndividual, "Customers")
' By accepting the changes, the data gets sent back to the server
Me.dsCustomerIndividual.AcceptChanges()
End Sub

Note

If you haven't read about ADO.NET yet, then you might be confused about doing an
update to the dataset, accepting changes, and sending the changes back to the server.
ADO.NET works using disconnected data. When you create a dataset by using a data
adapter, the data is actually created using XML. To see proof of this, take a look at
dsCustomerIndividual.xsd, found in the Solutions Explorer.

<xsd:schemaid="dsCustomerIndividual"targetNamespace="http://www.tempuri.org
/
dsCustomerIndividual.xsd"xmlns="http://www.tempuri.org/
dsCustomerIndividual.xsd"xmlns:
xsd="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:
xml-msdata"attributeFormDefault="qualified"
elementFormDefault="qualified">
<xsd:element name="dsCustomerIndividual" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">

<xsd:element name="Customers">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="CustomerID" type="xsd:string" />
<xsd:element name="CompanyName" type="xsd:string" />
<xsd:element name="ContactName" type="xsd:string" minOccurs="0" />
<xsd:element name="ContactTitle" type="xsd:string" minOccurs="0" />
<xsd:element name="Address" type="xsd:string" minOccurs="0" />
<xsd:element name="City" type="xsd:string" minOccurs="0" />
<xsd:element name="Region" type="xsd:string" minOccurs="0" />
<xsd:element name="PostalCode" type="xsd:string" minOccurs="0" />
<xsd:element name="Country" type="xsd:string" minOccurs="0" />
<xsd:element name="Phone" type="xsd:string" minOccurs="0" />
<xsd:element name="Fax" type="xsd:string" minOccurs="0" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
<xsd:unique name="Constraint1" msdata:PrimaryKey="true">
<xsd:selector xpath=".//Customers" />
<xsd:field xpath="CustomerID" />
</xsd:unique>
</xsd:element>
</xsd:schema>

This is all performed under the covers, so the good news is that you don't have to know XML to work with
datasets and ADO. More information on this can be found in Chapter 3 .

4. The last task is to give you the capibility to cancel the edits. You do this by placing the following code in the
Click event on btnCancel . Calling the CancelCurrentEdit method of the BindingContext object for the
form, the current edits are cancelled. Next, the text boxes are disabled using the routine ActivateEditing.
Listing 1.10 frmHowTo1_4.vb : Canceling Changes to Data and Disabling the Text Boxes Again

Private Sub btnCancel_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnCancel.Click
' Use the BindingContext class to cancel the current editing.
Me.BindingContext(Me.dsCustomerIndividual, _
"Customers").CancelCurrentEdit()
ActivateEditing(False)
End Sub

How It Works
When the user clicks the btnEdit button, the ActiveEditing routine alerts the user that he can edit the
data in the text boxes. The text boxes are enabled.

If the user then clicks the btnSave button, the data is updated to the dataset and then back to the server. If
the user clicks the btnCancel button, the edits are canceled. In both cases, the ActivateEditing routine
is called to disable the text boxes and let the user know that the text boxes cannot be edited.

Comments
This How-To showed the bare bones for editing and updating data. It presented a basic technique to get
started. As you continue, you will learn methods to add and delete records, as well as handle errors.
Using ADO.NET, which you use for bound controls, takes some getting used to if you have worked with other
data accessing methods such as ADO and DAO. The BindingContext and BindingManagerBase objects
make working with bound objects in code much more manageable.

[ Team LiB ]

[ Team LiB ]

1.5 Add and Delete Records Using Bound Controls


Besides editing data, adding new records and deleting records in a table are obviously important features.
This How-To shows you just that, taking advantage of the BinderContext class.
Besides being able to edit current records that already exist, users need to be able to add and delete records
as well. How do you add and delete records using bound controls?

Technique
To create the Add New Record feature, you will add a button called btnAddNew that has code behind it. You
will use the BindingContext object again to add a new record by using the AddNew method . A module
variable will be added to the form to keep track of whether a record is being added or edited. If a new record
is being added, then when the update is performed, the application needs to reload the data in the list box in
case the new record's CompanyName data falls into the chosen list limit that is specified in the
txtCustLimit text box.
To create the Delete Record feature, you will add a button called btnDelete . This button will have code
behind it that will use the RemoveAt method of the ContextBinding object to remove the record from the
dataset. The data will then be posted back to the server.

Steps
Open the solution for the chapter called "VB .NET How-To Chapter 1 ," and run the application. From the
main form, click on the command button labeled How-To 1.5. Click the Load List button . Click the button
labeled New. Enter the text ALF for Customer ID and Alfred's Fried Foods in the Company Name.
Then click the button labeled Save, and see the record you just entered added to the list. After the record
has been entered, you can test the delete feature by clicking on the button named Delete. The record goes
away, and the list is rebuilt.

1. Add a new command button under btnEdit , and then set the Name property to btnNew and the
Caption property to &Save .
2. Add a new module-level variable called mbAddNew , which is a Boolean variable to keep track of whether
you are adding a new record. This variable will be placed outside of your routines, just under the forms
code. You can see this in Figure 1.9 .
Figure 1.9. By placing this variable just underneath Windows Form Designer Generated Code
and declaring it Private, routines inside the Class can see it, but those outside cannot.

Tip

You can collapse and expand routines that you are working on within modules.
This makes it handy when you are working on new routines (which can be
expanded) and don't want to mess with old routines (which can be collapsed).

3. Add the following code to the Click event of the new command button btnNew . This code first sets the
Boolean variable called mbAddNew to True . It then uses the AddNew method of the form's
BindingContext object to add a new record to the dsCustomerIndividual dataset. Finally, the code
calls the ActiveEditing routine to enable the text boxes.
Listing 1.11 frmHowTo1_5.vb : Adding a New Record to the dsCustomerIndividual Dataset
and Toggling Text Boxes

Private Sub btnNew_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnNew.Click
mbAddNew = True
' Using the BindingContext class add a new record
Me.BindingContext(Me.dsCustomerIndividual, "Customers").AddNew()
ActivateEditing(True)

End Sub

4. Modify the Click event of the btnSave to test whether the mbAddNew flag has been set to True ,
meaning that a new record is being added. If True , then after saving the record by calling the
SaveRecord and ActivateEditing routines, the LoadList and RefreshIndividual are called to
load the new record's data. Note that the SaveRecord, ActiveEditing , and RefreshIndividual
routines have not changed from How-To 1.4. The mbAddNew variable is then set to False .
Listing 1.12 frmHowTo1_5.vb : Saving the Changed Data, Toggling Text Boxes, and Reloading
the List Box and First Record in the List

Private Sub btnSave_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnSave.Click
' Save the information
SaveRecord()
' Disable the text boxes
ActivateEditing(False)
If mbAddNew Then
LoadList()
RefreshIndividual()
mbAddNew = False
End If
End Sub

5. Modify the Click event for the btnCancel button to reset the mbAddNew variable to False , as seen in
Listing 1.13 .
Listing 1.13 frmHowTo1_5.vb : Canceling the Edit and Resetting the mbAddNew Variable

Private Sub btnCancel_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnCancel.Click
' Use the BindingContext class to cancel the current editing.
Me.BindingContext(Me.dsCustomerIndividual, "Customers").CancelCurrentEdit()
ActivateEditing(False)
mbAddNew = False
End Sub

6. Now it is time to add the delete functionality. To do this, add the following code to the Click event of
the new button called btnDelete . The first line of the code you added begins by introducing a new
method called RemoveAt and a new property called Position, both used with the BindingContext

6.

object. You will work from the inside out. The Position property tracks the current position in the
dataset, in this case dsCustomerIndividual. The RemoveAt method marks the record that is at the
position passed to it for deletion.
Next, the Update method of the odaCustomerIndividual adapter is used to call the DELETE SQL
statement command against the dataset, deleting the actual rows in the data set. The AcceptChanges
method is called to send the changes to the dataset, a delete in this case, back to the server. Finally, the
LoadList , RefreshIndividual , and ActivateEditing subroutines are called to refresh the list,
refresh the first record in the list for the text boxes, and toggle the text boxes so that the user knows he
can't edit them.
Listing 1.14 frmHowTo1_5.vb : Deleting the Selected Record and Reloading the List Box

Private Sub btnDelete_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnDelete.Click
' Mark the row for deletion using the RemoveAt method of the BindingContext
Me.BindingContext(Me.dsCustomerIndividual, _
"Customers").RemoveAt(Me.BindingContext(Me.dsCustomerIndividual,
"Customers").Position)
' Perform the requested task at the dataset
'
level using the data adapter
odaCustomerIndividual.Update(dsCustomerIndividual, "Customers")
' By accepting the changes, the data gets sent back to the server
dsCustomerIndividual.AcceptChanges()
' Reload the list
LoadList()
' Display the first record
RefreshIndividual()
' Disable the text boxes
ActivateEditing(False)
End Sub

How It Works
When the user clicks the btnNew button, a flag (mbAddNew ) is set to True , the dataset is set for adding a
record with the AddNew method of the BindingContext , and the text boxes are enabled for editing.
If the user then clicks the btnSave button, the data is updated to the dataset and then back to the server.
If the user clicks the btnCancel button, the edits are canceled. In both cases, the mbAddNew variable is
set to False and the ActivateEditing routine is called to disable the text boxes and let the user know
that the text boxes are not available for editing. Finally, if the btnSave button was clicked and the mbAddNew
was set to True , the LoadList and RefreshIndividual routines are called.
When the user clicks the btnDelete button, the record is deleted from the recordset and then from the
server. The list box is reloaded and the first record in the list is displayed in the text boxes. The text boxes
are then disabled.

Comments
Here you have the basic commands needed to create a full-fledged data entry form. You can add, edit, and
delete information from the form. This actually takes less code than if you use Visual Basic 6.0.
The BindingContext object really goes a long way to helping you work with bound data.

[ Team LiB ]

[ Team LiB ]

1.6 Take Care of Error Handling with Bound Controls


When dealing with database tasks, you are going to get runtime errors. In .NET, we call the various types of
errors you can get exceptions. This How-To shows examples of what exceptions could occur and how to
handle them using the Try, Catch, and Finally statements.
Adding and deleting records is fine, but what happens if an error occurs? You tried to delete an existing
company, and got the screen that appears in Figure 1.10.
Figure 1.10. This is an unhandled error, called an exception in .NET.

How do you make it so errors are handled gracefully within the application using bound controls?

Technique
Error handling is one of the most important aspects of working with data, between when a user is entering
data and updating the server. If you don't have proper error handling, your system will, in the best situation,
give an ugly error message, and in the worst, blow up and put bad data in your system. In Visual Studio
.NET, errors are classes called exceptions. Many different (inherited) types of exceptions exist.
Run-time exceptions can occur in just about any part of your application. They can be handled by using
Try...Catch...Finally or they can be unhandled, where you will see the error in Figure 1.11.
Figure 1.11. This error was thrown up from one subroutine to the one that called it.

Take a look at a common way to trap exceptions shown in Listing 1.15.


Listing 1.15 Standard Code for Handling Exceptions

Private Sub MySub()


Try
'Code to be handled here
Catch dataException as Exception
MessageBox.Show(dataException.Message)
Finally
'Code that will occur regardless of if an error occurs.
End Try
End Sub

Following are some basic points to help you when working with exceptions and Try...Catch...Finally
blocks:

The name of the exception object can be whatever you want it to be. dataException was just used as
an example.
Specific types of exceptions inherit from the base class of System.Exception. OleDbException is
one of those classes, and you will see an example of using this class in the following steps.
You can use the Throw statement within a Catch statement to throw the exception back up to a
calling subroutine.
You can use the When clause on the Catch statement to trap for specific exceptions.
When multiple Catch statements are used, each one is executed unless an End Try or End Sub is
placed within a Catch block, as in this How-To.
You can use multiple Catch statements to handle various possible exceptions that can occur.
You will see an example of these bullets in the following steps.

Steps
Taking the form with which you have been working, you are going to modify it to trap exceptions when the
record is being saved (either for additions or editing) and when the record is deleted. You will also add some
code in the event when closing the form.

1. Modify the code in the Click event of the command button called btnSave. You will surround the
SaveRecord routine call with a Try...Catch...End Try block. If an exception occurs, the Show
method of the MessageBox class is called, passing the Message property of the Exception object
called saveException. Next, the subroutine is exited. If no errors occur, then life goes on, and so
does the subroutine.

Listing 1.16 frmHowTo1_6.vb: Code Added to btnSave Click Event to Trap Save Exceptions

Private Sub btnSave_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnSave.Click
Try
' Save the information
SaveRecord()
Catch saveException As Exception
MessageBox.Show("The following error has occurred: " & _
saveException.Message, "Error Saving Record")
Exit Sub
End Try
' Disable the text boxes
ActivateEditing(False)
If mbAddNew Then
LoadList()
RefreshIndividual()
mbAddNew = False
End If
End Sub

2. Modify the SaveRecord routine to "throw" the exception up to the btnSave_Click subroutine, or
whatever happens to have called the SaveRecord routine. Following is the SaveRecord subroutine
with the new Try...Catch...End Try block added, along with the Throw statement.
Listing 1.17 frmHowTo1_6.vb: Pass the Exception Back Up to the Calling Routine

Private Sub SaveRecord()


Try

'

' Use the BindingContext class to end the current editing so


that we can update the server.
Me.BindingContext(Me.dsCustomerIndividual, _
"Customers").EndCurrentEdit()
' Perform the requested task at the dataset
'
level using the data adapter
odaCustomerIndividual.Update(dsCustomerIndividual, "Customers")
' By accepting the changes, the data gets sent back to the server
dsCustomerIndividual.AcceptChanges()

Catch saveException As Exception


Throw saveException
End Try
End Sub

3. Now it's time to deal with adding exception handling to the btnDelete Click event, as seen next.
The Try statement is used to wrap the code that performs deletion of the data. The code then uses a
Catch statement to check whether the exception that occurs is a specific OleDbException, 3621,
which is an error having to do with trying to delete a record when related records exist in another
table. Note that you couldand in fact shouldassign this to a constant value. Because
delException has been caught as an OleDbException, you can look at the NativeError property
of the first error in the Errors collection to get the actual OleDb error number. If the error is not 3621,
then the exception is trapped with the next Catch statement, and a messagebox is displayed. If
errors occur, then the subroutine is exited before the final lines of code are executed.
Listing 1.18 frmHowTo1_6.vb: Catching Exceptions When You're Deleting a Record

Private Sub btnDelete_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnDelete.Click
Try
' Mark the row for deletion using the RemoveAt
' method of the BindingContext
Me.BindingContext(Me.dsCustomerIndividual,
"Customers").RemoveAt(Me.BindingContext(Me.dsCustomerIndividual,
"Customers").Position)
' Perform the requested task at the dataset
'
level using the data adapter
odaCustomerIndividual.Update(dsCustomerIndividual, "Customers")
' By accepting the changes, the data gets sent back to the server
dsCustomerIndividual.AcceptChanges()
Catch delException As System.Data.OleDb.OleDbException _
When delException.Errors(0).NativeError = 3621
MessageBox.Show("An error occurred because of related order records.",
"Error Deleting Customer", _
MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
Exit Sub
Catch delException As Exception
MessageBox.Show(delException.Message, "Error Deleting Customer",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
Exit Sub
End Try

' Reload the list


LoadList()
' Display the first record
RefreshIndividual()
' Disable the text boxes
ActivateEditing(False)
End Sub

How It Works
When an exception occurs within the Try...Catch...End Try block, the Catch statements are compared
when a When clause is used or when the code is simply executed. If a Throw statement is used, then an
execution is thrown back up a level to the Try...Catch...End Try block containing the call to the routine
where the Throw statement occurred. You can see an example of this in Figure 1.11.

Comments
Microsoft has gone to great length to let you control how you handle exceptions in .NET with the various
languages. You can be as creative as you need to by using the Try...Catch...End Try block with all the
clauses available.
Exceptions bring back plenty of information so that you can create pretty slick error handling. You can use
exceptions to create a centralized routine that logs errors, or even one that e-mails exception information to
you from your applications.

[ Team LiB ]

[ Team LiB ]

1.7 Put the Finishing Touches on a Data Bound Form


Besides handling the basics, you need to add some finishing touches to a data entry form, such as
enabling/disabling controls based on whether you're on a record. This How-To shows you how to use the
Enabled properties on controls to give the user more direction when using a Windows form (see Figure 1.12
).
Figure 1.12. You will create this complete form by How-To 1.7, with all the bells and whistles that
users expect from a good data entry form.

Although the majority of the major issues are taken care of for the form you created so far in the chapter,
your users become confused about when to push some of the buttons. How do you get the buttons to reflect
when the users can click them, and what other finishing touches might help the form?

Technique
An important part of user interfaces is letting the user know when he has access to certain features on the
form. In this case, you will see how to do the following:

Toggle the enabled property of the btnSave , btnCancel , btnNew , and btnDelete at the
appropriate moments, such as when a record is highlighted in the list box.
Add a command button to close the form.
Add code to the Closing event of the form that tests whether you have made changes to the current
record, and if so, whether you want to save the changes.
You can see an example of particular command buttons being enabled based on the current action being
performed in the form in Figure 1.13 .
Figure 1.13. This form allows users to access command buttons only when the functionality is

available.

Steps
Continuing on with the form that you have been using, you are going to make the changes just mentioned in
the previous bulleted list.

1. Start by modifying two routines already created: RefreshIndividual and ActivateEditing


subroutines. Then check whether a customer has been selected in lstCustomers . If not, then the two
buttons, btnEdit and btnDelete, are disabled. If a customer hasn't been selected, the two buttons are
enabled, and the dsCustomerIndividual dataset control is refreshed.
Listing 1.19 frmHowTo1_7.vb : Toggling the Enabled Property of the btnEdit and btnDelete
Buttons

Private Sub RefreshIndividual()


' Clear individual customer dataset
Me.dsCustomerIndividual.Clear()
If lstCustomers.SelectedIndex = -1 Then
btnEdit.Enabled = False
btnDelete.Enabled = False
Else
btnEdit.Enabled = True
btnDelete.Enabled = True
Me.odaCustomerIndividual.SelectCommand.Parameters(0).Value =
lstCustomers.SelectedItem(0)
' Fill the dataset
Me.odaCustomerIndividual.Fill(Me.dsCustomerIndividual, "Customers")
End If

End Sub

Similarly, you will add code to the ActivateEditing subroutine to toggle the Enable property of
the various command buttons, depending on their purpose. Listing 1.20 shows the entire routine.
Listing 1.20 frmHowTo1_7.vb : Toggling the Enabled Property of btnEdit and btnDelete
Buttons

Private Sub ActivateEditing(ByVal bEnable As Boolean)


Dim oCurr As Object
' Loop through each of the controls on the form
For Each oCurr In Me.Controls()
' Check to see if the control is a text box
If TypeOf oCurr Is TextBox And oCurr.Name <> "txtCustLimit" Then
' If so, toggle the properties
If bEnable Then
oCurr.BorderStyle() = _
System.Windows.Forms.BorderStyle.Fixed3D
oCurr.BackColor() = System.Drawing.Color.White
Else
oCurr.BorderStyle() = _
System.Windows.Forms.BorderStyle.FixedSingle
oCurr.BackColor() = Me.BackColor

End If
oCurr.Enabled = bEnable
End If
Next
' Enable/Disable the appropriate buttons
btnEdit.Enabled = Not bEnable
btnNew.Enabled = Not bEnable
btnDelete.Enabled = Not bEnable
btnCancel.Enabled = bEnable
btnSave.Enabled = bEnable
' Set the focus to the CustomerID text box
If bEnable Then
Me.txtCustomerID.Focus()
End If
End Sub

The specific lines of code added are shown here:

' Enable/Disable the appropriate buttons


btnEdit.Enabled = Not bEnable
btnNew.Enabled = Not bEnable
btnDelete.Enabled = Not bEnable
btnCancel.Enabled = bEnable
btnSave.Enabled = bEnable

These buttons are handled as the other buttons areby taking the opposite value to which bEnable
is currently set, and using it to toggle the Enabled property.
Finally, if the bEnable flag is True , then focus is moved to the txtCustomerID text box using the
following lines of code:

' Set the focus to the CustomerID text box


If bEnable Then
Me.txtCustomerID.Focus()
End If
End Sub

2. Add a new command button, with the properties Name and Text set to btnClose and &Close ,
respectively. Place the code in Listing 1.21 for the Click event.
Listing 1.21 frmHowTo1_7.vb : Close the Form

Private Sub btnClose_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnClose.Click
Me.Close()
End Sub

3. Add some code to the Closing event of the form. Listing 1.22 tests whether the btnSave button is
enabled. If it is, the MessageBox method is evoked, asking the user if he wants to save changes that
were made. If so, then the SaveRecord is called within a Try...Catch...End Try block.
Listing 1.22 frmHowTo1_7.vb : Checking Whether the User Wants to Save a Record Before
Closing

Private Sub frmHowTo1_7_Closing(ByVal sender As Object, _


ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
' If an edit or add has been requested, enabling the Save button,
'
then prompt to save the record

If btnSave.Enabled Then
If MessageBox.Show("Would you like to save the current record?", _
"Save Record?", MessageBoxButtons.YesNo) = _
DialogResult.Yes Then
Try
' Save the information
SaveRecord()
Catch saveException As Exception
If MessageBox.Show("The following error has occurred: " &
saveException.Message & vbCrLf & vbCrLf & _
"Continue with closing the form?", "Error Saving Record",
MessageBoxButtons.YesNo) = DialogResult.No Then
e.Cancel = True
End If

End Try
End If
End If
End Sub

How It Works
In the modifications made to the form in this How-To, many things happen depending on what is occurring.
When the user clicks the btnEdi t button, btnEdit , btnNew , and btnDelete are disabled, and
btnCancel and btnSave are enabled. The opposite is true when btnCancel and btnSave are pressed. If
bEnable is True , then the focus is moved to the txtCustomerID text box.
When the txtClosed button is clicked, the application then checks whether the btnSave command button
has been enabled, meaning data is being edited. If so, then the user is asked whether he wants to save the
current record. If the user does, the system then saves the current information back to the server.

Comments
The tasks displayed in this How-To are just a few of the tasks you can do to make your forms look and feel
more professional. They are also what users come to expect from database applications.
Play with the form a bit more. You're sure to come up with a few more ideas.

[ Team LiB ]

[ Team LiB ]

1.8 Bind Data to ComboBox and DataGrid Controls


Sometimes you might want to use a ComboBox control instead of a ListBox control to display a list of
choices. You also might want to display information in a grid style based on the item chosen in that combo
box. This How-To describes how to bind data to both ComboBox and DataGrid controls.
Instead of using a ListBox control to display customers, you would like to use a ComboBox control to
display them. After you choose a customer, you would like to display information about the orders that
belong to that customer. How do you bind data to the ComboBox and then bind the DataGrid control as
well?

Technique
To bind both the ComboBox and the DataGrid controls, you will use the same properties and coding
techniques that you have used for the ListBox control. You will first create DataAdapter and DataSet
controls to which to set the DataSource properties. The DataAdapter for the DataGrid will include a
parameter that will use the value selected in the ComboBox control.
You will set the DataSource properties of the controls to the DataSet created. That is all you really have to
set for the DataGrid control. For the ComboBox control, you will also set the ValueMember and
DisplayMember properties.
You will then add code to fill the DataSet control to which the DataSource property is set for the
ComboBox control. Finally, you will create a subroutine to store the selected value in the ComboBox in the
DataAdapter that was created for filling the DataSet on which the DataGrid control is based.
After the user has selected a value in the ComboBox that contains the customers, the DataGrid control
displays the orders for that customer, as can be seen in Figure 1.14.
Figure 1.14. Users can display the header information for customer orders using these two
controls.

Steps
You will be starting with a fresh form.

1. Add a new Windows Form called frmHowTo1_8.


2. Add the OleDbDataAdapter and DataSet controls with the properties set forth in Table 1.7.

Table 1.7. OleDataAdapter and DataSet Controls


Object

Property

OleDataAdapter Name

Setting

odaCustomerList

SelectCommand OleDbSelectCommand1

DataSet

CommandText

SELECT CustomerID , CompanyName FROM


Customers

Name

dsCustomerList

DataSetName

dsCustomerList

OleDataAdapter Name

odaOrdersForCustomer

SelectCommand OleDbSelectCommand2

DataSet

CommandText

SELECT OrderID, OrderDate, RequiredDate ,


ShippedDate FROM Orders WHERE (CustomerID = ?
) ORDER BY OrderDate

Name

dsOrdersForCustomer

DataSetName

dsOrdersForCustomer

Tip
You will want to create the OleDbDataAdapter controls using the
Data Adapter Configuration Wizard, and then set the name after
the DataAdapter has been created. You will want to create the
DataSet controls by right-clicking on the appropriate
OleDbDataAdapter and choosing Generate Dataset. If you have
questions about creating these controls, reread How-To 1.1.
Also, remember that when generating the DataSet, VS always
changes the name by adding a 1 on the end and capitalizing the
first letter. You might want to change it to what you really want in
the properties directly.

3. Add the ComboBox and DataGrid controls, as well as the Label for the ComboBox, setting the
properties as shown in Table 1.8.

Table 1.8. Label, ComboBox, and DataGrid Controls Property Settings


Object

Property

Setting

Label

Name

Label1

Text

"Customers to List Orders For:"

Name

cboCustomers

DataSource

dsCustomerList.Customers

DisplayMember

CompanyName

ValueMember

CustomerID

Name

dgOrders

DataSource

dgOrdersForCustomer.Orders

ComboBox

DataGrid

4. Add code shown in Listing 1.23 to the Load event of the form. This code fills the dsCustomerList
DataSet control based off the select statement used in the odaCustomerList OleDbDataAdapter
control. After this occurs, the RefreshOrders subroutine is called, displayed in Listing 1.24.
Listing 1.23 frmHowTo1_8.vb: Filling the Dataset Used by the ComboBox Control

Private Sub frmHowTo1_8_Load(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
Me.odaCustomerList.Fill(Me.dsCustomerList)
RefreshOrders()
End Sub

Listing 1.24 frmHowTo1_8.vb: Filling the Dataset Used by the DataGrid Control, Based
on the Item Selected in the ComboBox Control

Private Sub RefreshOrders()


' Clear Orders for customer dataset
Me.dsOrdersForCustomer.Clear()
' Check to see if an item was selected
If Me.cboCustomers.SelectedIndex <> -1 Then

'

' Store the selected customer ID into the parameter


the SQL data adapter
Me.odaOrdersForCustomer.SelectCommand.Parameters(0).Value = _
Me.cboCustomers.SelectedItem(0)
' Fill the dataset
Me.odaOrdersForCustomer.Fill(Me.dsOrdersForCustomer)

End If
End Sub

5.

5. Add the code in Listing 1.25 to the SelectedIndexChanged event of the cboCustomers ComboBox
controls.
Listing 1.25 frmHowTo1_8.vb: Refreshing the DataGrid Control Based on Selected Item

Private Sub cboCustomers_SelectedIndexChanged(ByVal sender


As System.Object, ByVal e As System.EventArgs)
Handles cboCustomers.SelectedIndexChanged
RefreshOrders()
End Sub

How It Works
When the form is first loaded or a new item is selected in the ComboBox control, the DataGrid control is
filled with order information for the selected customer.

Comments
As you can see from this How-To, using the ComboBox control and DataGrid controls are not much tougher
than using the ListBox control. The DataGrid control does offer quite a bit more power to let you create
Main/Subform type forms such as invoices. You will see an example of this in the next How-To. The
DataGrid control also lets you edit data.

[ Team LiB ]

[ Team LiB ]

1.9 Drill Down to Data in the DataGrid Control


The DataGrid control in Visual Basic .NET is an effective way to display multiple records related to a base
table, such as Customer and Orders tables. You can then drill down into individual orders to see the details.
This How-To explains how to open an additional form with another DataGrid control and drill down to the
order details.
Although it is nice to see what orders a customer has, you would like to display more information about the
orders. How do you drill down into the details of the orders displayed in the DataGrid control?

Technique
Using the form you started in How-To 1.8, you are going to allow the user to highlight a specific order. You
are going to drill down to it by opening a new form and having that form's text boxes display order header
information. Then you will use another DataGrid control to display order details. You can see this example in
Figure 1.15 .
Figure 1.15. Drilling down to an order's detail.

Besides adding a new form, you will also create a new module to be used to declare a public variable for
tracking the calling form. In the previous examples, the sample form was opened by declaring a variable in
the Click event of the individual Button controls on frmMain . For this How-To, the declaration of the
variable pointing to that specific form has been moved to a public module. You can see the code for the
Click event for btnHowTo1 _8 and btnHowTo_9 in Listing 1.26 .

Listing 1.26 frmMain.vb : Opening a New Form to Display Order Information

Private Sub btnHowTo1_8_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles btnHowTo1_8.Click
Dim frm1_8 As frmHowTo1_8
frm1_8 = New frmHowTo1_8()
frm1_8.Show()
End Sub
Private Sub btnHowTo1_9_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles btnHowTo1_9.Click
frm1_9a = New frmHowTo1_9a()
frm1_9a.Show()
End Sub

Listing 1.27 presents the code for the module created, called modPublicVariables .
Listing 1.27 modPublicVariables.vb : Declaring a Public Variable for a Form

Module modPublicVariables
Public frm1_9a As frmHowTo1_9a
End Module

Steps
You will want to create a module as described in the "Technique " section of this How-To to open the form
that you created in How-To 1.8. After you have created that module and added the Public variable
declaration as shown in Listing 1.27 , open the form from How-To 1.8 in Design.

1. Add a button control to the bottom-right side of the form, under the DataGrid control. Set the Name
property to btnOrderDetail and the Text property to &Order Detail .
2. In the Click event of the btnOrderDetail , add the code shown in Listing 1.28 .
Listing 1.28 frmHowTo1_9.vb : Opening a New Form to Display Order Information

Private Sub btnOrderDetail_Click(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles btnOrderDetail.Click


Dim frm As New frmHowTo1_9b()
frm.Show()
End Sub

3. Create a new form and name it frmHowTo1_9b (or whatever you called it when following Listing 1.28
).
4. Add some OleDataAdapters and DataSets to the form, as laid out in Table 1.9 .

OleDataAdapter
Name
odaIndividualOrder

SelectCommand
OleDbSelectCommand1

CommandText
SELECT OrderID , CustomerID , EmployeeID , OrderDate , RequiredDate , ShippedDate ,
ShipVia , Freight , ShipName , ShipAddress , ShipCity , ShipRegion , ShipPostalCode ,
ShipCountry FROM Orders WHERE (OrderID = ?)
DataSet
Name
dsIndividualOrder

DataSetName
dsIndividualOrder
OleDataAdapter
Name
odaEmployees

SelectCommand
OleDbSelectCommand2

CommandText
SELECT EmployeeID , RTRIM(LastName) + ' , ' + RTRIM(FirstName) AS FullName FROM
Employees
DataSet
Name
dsEmployees

DataSetName
dsEmployees
OleDataAdapter
Name
odaOrderDetails

SelectCommand
OleDbSelectCommand3

CommandText
SELECT OrderID , ProductID , UnitPrice , Quantity , Discount FROM [Order Details]
WHERE (OrderID = ?)
DataSet
Name
dsOrderDetails

DataSetName
dsOrderDetails

Table 1.9. OleDataAdapter and DataSet Controls for the frmHowTo1_9b Form
Object

Property

Setting

5. Add the controls that will use these data controls for data sources, found in Table 1.10 .

5.

Label
Name
Label1

Text
"Order ID"
Label
Name
Label2

Text
"Order Date "
Label
Name
Label3

Text
"Employee"
TextBox
Name
txtOrderID

BorderStyle
FixedSingle

Text
dsIndividualOrder - Orders .OrderID
DateTimePicker
Name

DateTimePicker1

DataBindings - Value
dsIndividualOrder - Orders . OrderDate

Format
Short
ComboBox
Name
cboEmployees

DataSource
dsEmployees . Employees

DisplayMember
FullName

ValueMember
EmployeeID

SelectedValue
dsIndividualOrder - Orders . EmployeeID
DataGrid
Name
dgOrderDetails

DataSource
DsOrderDetails . Order _Details

Table 1.10. Controls to Display Order Information, Including Order Details

Object

Property

Setting

6. Add the code in Listing 1.29 to the Load event of the new form you have created. The first thing that
this code does is fill the DataSet control use by cboEmployees . Next, using the
CurrentCell.RowNumber property of the DataGrid control used on the frmHowTo1_9a form , the
OrderID column is read in the specified row of the DataSet control called dsOrdersForCustomer .
The OrderID, which is stored in lngOrderID , is supplied to odaIndividualOrder and
odaOrderDetails . Then the OleDbDataAdapters are used to fill the datasets.
Listing 1.29 frmHowTo1_9b.vb : Loading the Appropriate Datasets

Private Sub frmHowTo1_9b_Load(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
Dim lngOrderID As Long
' Fill the dataset used for the Employee combo box.
Me.odaEmployees.Fill(Me.dsEmployees)
' Get the OrderID from the currently select order
' in the DataGrid control on the calling form.
lngOrderID = frm1_9a.dsOrdersForCustomer. _
Orders.Rows(frm1_9a.dgOrders.CurrentCell. _
RowNumber).Item("OrderID")
' Update the caption of the form with the order ID and customer
Me.Text = "Order #" & CStr(lngOrderID) & _
" For " & frm1_9a.cboCustomers.Text
' Load the parameter for the order header with the InvoiceID
Me.odaIndividualOrder.SelectCommand.Parameters(0).Value = lngOrderID
' Fill the order header dataset
Me.odaIndividualOrder.Fill(Me.dsIndividualOrder)
' Load the parameter for the order detail with the InvoiceID
Me.odaOrderDetails.SelectCommand.Parameters(0).Value = lngOrderID
' Fill the order detail dataset
Me.odaOrderDetails.Fill(Me.DsOrderDetails)
End Sub

How It Works
The user highlights one of the Orders in the dgOrders on the first form and then clicks the
btnOrderDetail button to open the second form. The second form is used to display the details of the
order. When the second form is opened, the cboEmployee ComboBox control is loaded, and the OrderID is
retrieved from dsOrdersForCustomer via dgOrders , located on the first form. The DataSets controls
dsIndividualOrder and dsOrderDetails are then filled.

Comments
This is just a start of what can be done using the DataGrid control. You will see this control used in quite a
few chapters, including Chapter 3 and 4 . As you can see from the way the OrderID was retrieved from the
first form, it can be a little confusing when you need to get to the data programmatically.

[ Team LiB ]

[ Team LiB ]

Chapter 2. Creating SQL Server Database Objects From Visual Studio .NET
You will learn about the following in this chapter:

Create a new SQL Server database from within Visual Studio .NET
Define tables and fields
Define a primary key and other indexes
Define relations between tables
Define defaults and constraints
Create views
Create stored procedures
When starting out with working with databases, it's a good thing to know how to create them. This example
shows how to create a SQL Server database using Visual Studio .NET and also talks about servers and
security.

[ Team LiB ]

[ Team LiB ]

Working with Tables, Columns, and Rows


The basic entity that holds data in your database is a table, which is analogous to the manila folder of
customers. A table is made up of columns (also referred to as fields.) Picture a page of information that you
keep on a customer. The information would have Last Name, First Name, Address, and so on. These would
be columns in a table. The filled out page of information would be a row (or record) in your table, and it
would be made up of columns.
The database would be the filing cabinet, which would be made up of multiple tables (manila folders) with
different subjects besides customers, such as invoices, parts information, and so on. Double-click on the
Northwind node in the server explorer, you will then see a list of tables that are part of the database. If you
open the Customers table in Design mode, you will see the various columns in the table.
Then when you open your table to see the data, you will see the columns for each row or record in your
table.

[ Team LiB ]

[ Team LiB ]

Utilizing Properties for Tables and Columns


Tables and columns, like other objects in your database, have properties that allow you to control the data
that is going into your tables. For example, in the Customers table, you can see the properties for the first
column, called CustomerID. The extent to which you use the properties depends on what your needs are.
At the table level, you also have properties that you can utilize that help you create and enforce business
rules, which are discussed later in this chapter.
You will create your tables by breaking down your data into logical entities. When you do so, you need to
keep in mind how you break them down, and you need to break them down so that they are created in what
is called normalization.
SQL Server has come a long way over the years. For every version, Microsoft works hard not only to make
SQL Server more powerful, but also easier to work with. This includes tools that come with the product and
from other applications. Visual Studio .NET is a good example of tools for working with SQL Server from
another product.
If you are not familiar with databases, here's a quick overview. Databases allow you to work with data in a
manner that reflects the real world on the computer. You can take a real subject, such as Customers, and
store that information in tables. A file cabinet is analogous to a database. Within the file cabinet you may
have your client folders. Other folders might contain information on Orders or Invoices. One of these folders
could be compared to a table of customers. Within the Customers folder, you might have individual pages of
information on a customer. Each page that you have on an individual customer would be a row, or a record
within a table. On each page, you would have pieces of information such as Customer Name, Address,
Phone, and so on. These would be fields, or columns, within each row.
In a database, you will also have objects that allow you to query information within tables and update
information. In SQL Server, you will use Views, Stored Procedures, and Functions to view and update data
within the database. To use these objects, you need to be able to create them. To create a database along
with its tables in SQL Server, you can use code or tools that came with SQL, such as the Enterprise Manager,
if you have installed one of the versions that include these tools.
Fortunately, you can use tools that are built within Visual Studio .NET to create and modify your databases.
The primary tool you will use is called the Server Explorer, as shown in Figure 2.1.
Figure 2.1. From the Server Explorer within Visual Studio .NET, you can perform most of the tasks
that are necessary to maintain a database.

[ Team LiB ]

[ Team LiB ]

2.1 Create a New SQL Server Database from Within Visual Studio .NET
Before working with a database, you have to be able to create it. Although you could use code to do this,
you would rather do it right from Visual Studio. How do you create a new SQL Server database from within
VS .NET?

Technique
As mentioned in the Introduction of this chapter, databases are basically file cabinets in computers. That
sounds pretty simplistic, but if you take the information that you store in your file cabinets and transfer it
over to the computer, you will end up with the same elements.
You can physically create a new SQL Server database in several ways:

Use SQL Server's Enterprise Manager to create the database.


Programmatically create the database. You can do this by using T-SQL and SQL-DMO, which are
discussed in Chapters 6, "Creating Transact-SQL Commands," and 7, "Performing Common Database
Tasks Using SQL-DMO," respectively.
Use a menu in the Server Explorer of Visual Studio .NET to perform this task.
You will use the last option to complete the task presented in this How-To.

Steps
To get started with this How-To, leave Visual Studio at the Start Page when you open it. Then place the
cursor on the Server Explorer icon on the left side of the screen.

Note
The way that the Server Explorer and toolbox icons are laid out on the
side of the screen varies according to how you set the Profile option in
the My Profile settings on the Start Pageeither Visual Studio
Developer or Visual Basic Developer.

After the Server Explorer has expanded, click on the plus symbol next to the Servers node. Then you can
follow down the tree by clicking on your computer's name (SHADRACH2 in Figure 2.1) and then SQL Servers.
Clicking on the name of the computer again, you will see the list of current databases that are set up by
default in the Microsoft SQL Server Desktop Edition (MSDE). Although doing this was not necessary to create
a new database, it does give you an idea of where you can see various databases in your system. Now you

will learn how to create a database in VS .NET.

1. You can open the Create Database dialog box from within the Server Explorer in two ways. The first
way is to right-click on the Data Connections node and choose Create New SQL Server Database. The
second way is to right-click on the SQL Server instance to which you want to add the databasein this
case, SHADRACH2and then choose New Database. Although both methods open the Create Database
dialog box, the second method fills in the server name for you.
2. Fill in the name of the database you want to createin this case, Chapter2.
3. Choose the type of security that you want to use with this database. If your network is strictly a
Windows 2000 network, you can leave this as the defaultUser Windows NT Integrated Security;
otherwise, choose the option. Your Create Database dialog box should look like Figure 2.2.
Figure 2.2. This is all the information you need to create a new database.

4. Click OK to complete the dialog box and create the new database.

How It Works
Now you when you click on the plus sign for the new database, you will see branches in the tree view of the
Server Explorer for the different database objects, described in this chapter's introduction.

Comments
The Microsoft Visual Studio and SQL Server teams have gone to a lot of effort to make VS .NET the only
design tool you need to use to create databases and their objects.
Sometimes you will need to perform tasks that are beyond what you can accomplish in the Server Explorer,
but this chapter will stick to VS .NET. Now it's time to see how to create some of the objects that actually
make a database useful.

[ Team LiB ]

[ Team LiB ]

2.2 Define Tables and Fields


A SQL Server database doesn't do much good unless you have tables in it. This tutorial not only gives you
information on how to create tables and fields, but also teaches you how to name them and what to look out
for when creating them. Databases by themselves don't do much for you, but you need to be able to store
data in them at the very least. To do this, you need to be able to create tables, which are made up of fields.
How do you go about defining tables and fields in VS .NET?

Technique
Using the Server Explorer and accessing the Table designer, you are going to create a table called
tblCustomers in the database that you created in the previous How-To. You will then add columns to the
database that will store various pieces of information having to do with individual customers.
Last, you will create another table called tblPhones and also create the necessary columns that will contain
information about phone information for your customers.

Steps
After Visual Studio .NET is opened, expand the Server Explorer and locate your new database called
Chapter2, created in How-To 2.1. Click on the plus sign by the database names so that the database object
categories are listed.

1. Right-click on the Tables node under the Chapter2 database and choose New Table. You will then be
presented with the Table Designer. Now you are ready to add the columns.
2. To add a column, you need to set four immediate properties:

Column Name. This is the name of the column. Don't use spaces or special characters, but do use
proper case, and make sure that the name you give the column makes sense for what it
contains. For instance, if the column is for the last name of a customer, put LastName for the
Name property of the column.
Data Type. Depending on the type of data that will be entered into the column, this will be one of
the many valid data types for SQL Server, shown here in Table 2.1.

Table 2.1. SQL Server Data Types


Data Type

Description

bigint

Integer (whole number) data from 26 3


(9,223,372,036,854,775,808) through 26 3\up6 1\up6
(9,223,372,036,854,775,807).

int

Integer (whole number) data from 2^31 (2,147,483,648)


through 23 1 1 (2,147,483,647).

smallint

Integer data from 2 1 5 (32,768) through 21 5 1 (32,767).

tinyint

Integer data from 0 through 255.

bit

Integer data with either a 1 or 0 value.

decimal

Fixed precision and scale numeric data from 103 8 + 1


through 103 8 1.

numeric

Functionally equivalent to decimal.

money

Monetary data values from 26 3


(922,337,203,685,477.5808) through 26 3 1
(+922,337,203,685,477.5807), with accuracy to a tenthousandth of a monetary unit.

smallmoney

Monetary data values from 214,748.3648 through


+214,748.3647, with accuracy to a ten-thousandth of a
monetary unit.

float

Floating precision number data from 1.79E + 308 through


1.79E + 308.

real

Floating precision number data from 3.40E + 38 through


3.40E + 38.

datetime

Date and time data from January 1, 1753, through


December 31, 9999, with an accuracy of three-hundredths
of a second, or 3.33 milliseconds.

smalldatetime

Date and time data from January 1, 1900, through June 6,


2079, with an accuracy of one minute.

char

Fixed-length non-Unicode character data with a maximum


length of 8,000 characters.

varchar

Variable-length non-Unicode data with a maximum length of


8,000 characters.

text

Variable-length non-Unicode data with a maximum length of


23 1 1 (2,147,483,647) characters.

nchar

Fixed-length Unicode data with a maximum length of 4,000


characters.

nvarchar

Variable-length Unicode data with a maximum length of


4,000 characters.

ntext

Variable-length Unicode data with a maximum length of 23 0


1 (1,073,741,823) characters.

binary

Fixed-length binary data with a maximum length of 8,000


bytes.

Varbinary

Variable-length binary data with a maximum length of 8,000


bytes.

image

Variable-length binary data with a maximum length of 23 1


1 (2,147,483,647) bytes.

cursor

A reference to a cursor.

sql_variant

A data type that stores values of various SQL Serversupported data types, except text, ntext, timestamp, and
sql_variant.

table

A special data type that stores a result set for later


processing.

timestamp

A database-wide unique number that is updated every time


a row is updated.

uniqueidentifier A globally unique identifier (GUID).

Tip
Unicode is a character-encoding standard that uses 16-bit code
values. This standard is used worldwide to represent all the
characters that are used in modern computing. Traditional
character sets are the previous character-encoding
standardssuch as the Windows ANSI character setthat use
8-bit code values or combinations of 8-bit values to represent
the characters used in a specific language or geographical
region.
It is recommended that you use Unicode data typesnchar,
nvarchar, and ntextrather than their non-Unicode
counterparts.
Also, use the variant length type data types whenever possible.
Doing so will save disk space and save you from having to trim
your values when you want to display the data in the fields.

Length. This varies depending on which data type you choose. For text data types, this will be the
maximum length you expect to be entered into the column.
Allow Nulls. This specifies whether you will allow null values to be saved in the record for the
column. This means that the user doesn't have to enter a value at all. This is sometimes a bad
idea, such as when you have specific data that has to be entered, like Social Security Numbers.
For the first few columns in the table, enter the following data. You can see how the table will look in
Figure 2.3.
Figure 2.3. Fields for your first table.

Column Name

Data Type

Length

Allow Nulls

LastName

nvarchar

50

Unchecked

FirstName

nvarchar

50

Unchecked

Address

nvarchar

50

Checked

City

nvarchar

50

Checked

State

nvarchar

Checked

ZipCode

nvarchar

Checked

BirthDate

datetime

Checked

MailingList

bit

Checked

EstimatedSales

money

Checked

3. Save the table by clicking on the X in the top-right corner of the Table Designer, and name the table
tblCustomers when prompted. After you click the X, you are asked if you want to save the table. Then
you are prompted for the name to save the table as.

How It Works
By entering the information in the various properties for each column, you are specifying how you want the
data in your database to be treated. Generally, your users won't create tables; you will create the tables for
the users, and they will fill the data into the tables using your applications.
When the user fills in the data, the application and SQL Server control what type of data goes into the table,
starting with what the data type of the data is and what the allowed length is. The Allow Nulls property
determines whether the user even has to enter data.

Comments

Creating the tables, made up of columns and rows, is the basis for the database's purpose: storing
information. Making sure that you use logical, descriptive names for columns, along with data types that help
control how the data gets entered into the database, is key to a successful database. Plan out your tables
ahead of time, examining each real-world object, and transfer those properties to the columns that make up
the tables that represent your objects.

[ Team LiB ]

[ Team LiB ]

2.3 Define a Primary Key and Other Indexes


Indexes are used to improve performance when querying data, such as searching on fields and sorting
information. The primary key is an index that ensures that you have at least one unique value in each
record, such as a Customer ID. This How-To discusses what the best candidates are for primary keys, as well
as how to create them and other indexes.
You have created a base table and have even entered data into it. How do you make sure that you have a
way of finding specific records? LastName and FirstName fields won't work because you can have duplicates
of those. You also want to make sure that when you search on fields, you get the best possible performance
from SQL Server. Indexes can be created to help performance. How do you define a primary key field to
make sure that you have unique records and can create other indexes as well?

Technique
Within the Server Explorer, you can use a field, such as a Social Security Number, and make that into a
primary key field. By making a field the primary key field, you will need to make that field unique and
prohibit null values there. The alternative to a current unique field is to create a field that is automatically
incremented, called an identity field. This identity field can also be set as the primary key field, again so that
the record is made unique. The primary key field will also be indexed.
Besides creating the special primary key field, you can create indexes that perform two main functions:

Performance. Used to increase performance when the column is used for search criteria and sorting
and when you're loading ranges of records.
Constraints. This is one of the jobs that the primary key field performsconstraining the data that
users can put into the table.

Note
When creating indexes for performance purposes only, take care that
you don't go overboard. Although indexes help performance when
searching and sorting, they can hurt performance when adding and
updating data. This is mainly true when importing or adding large
amounts of data.
The other point is that when the table is small, you can over-index as
well.

While you're in the table designer, you can get to Indexes, located on the Property Pages dialog box, by
choosing Indexes/Keys from the View menu. You can see the Property Pages dialog box in Figure 2.4.
Figure 2.4. You can create all the necessary indexes using this dialog box.

Within Indexes, you have some options to specify, such as whether you would like to have the values in the
index be unique You will want to have tables where you have a lot of data and you will be deleting or
updating large amounts of records at a time. You also will use a field in a range situation, such as using
BETWEEN , >, >= , <, and <= for operators.

Steps
Using the Server Explorer, open the Chapter2 database and right-click on the tblCustomers that you created
in the last How-To. Then select Design Table from the right-click menu.

1. Place the cursor in the first row, if it isn't already there. Click the right mouse button, and choose
Insert Column from the menu. Next, fill in the properties of the field as follows:
Property

Value

Column Name

CustomerID

DataType

int

Length

Allow Nulls

Unchecked

Identity

Yes

When you specify that you want to make this an identity field, the next two properties will be used. The
first, Identity Seed, is what the column's values will be started at. The next property, Identity
Increment, will be how records that are added will be incremented.
2. Right-click in the Column Name property of the CustomerID column, and then choose Set Primary Key
from the menu. You have now set the CustomerID to be the primary key, and the screen should look
similar to Figure 2.5. The index that you just created when you specified CustomerID to be the primary
key falls into the constraint category of indexes.
Figure 2.5. You need to have one primary key field per table.

3. To create other indexes that will help performance, choose Indexes/Keys from the View menu to open
the Property Pages dialog box.
4. On the Indexes page, click the New button, and then fill in the following properties to add an index for
the LastName field: Index Name with "IX_LastName" and Column Name with "LastName."

Tip
If you sort on multiple columns such as LastName and FirstName
together, which is quite common, you could add the FirstName column
underneath the LastName column in the last step. This is called a
compound index.

How It Works
When you create tables, you create your primary key columns and indexes at the same time. You can come
up with indexes at a later date as well.

Comments
Be sure to create a primary key column for each of your tables, and use identity columns for primary
columns whenever possible.
SQL Server will take advantage of indexes you have created, but just be careful not to over-index.
Remember to examine what columns you will be sorting and querying on. With particularly large tables,
indexes become more important than ever.

[ Team LiB ]

[ Team LiB ]

2.4 Define Relations Between Tables


Relationships define how your information needs to "relate" between tables in your database. For example,
perhaps you have tables in your database called Customers, Orders, and Order Details as the sample
Northwind database does. You would want to create relationships between these tables to make sure that
data integrity is maintained as users enter data into the database. Taking this further, you need to make
sure that each invoice has an existing customer for every invoice. This is called referential integrity. This
How-To shows you how to create relationships as well how to view the relationships by using database
diagrams.
You have created a couple of tables where one relies on another to have data in it. In database terms, they
are related. How do you create relationships between tables?

Technique
Referential integrity tells the database how tables are to relate to one another. You can use referential
integrity to help control what data goes in each of tables, based on data in other tables.
To maintain referential integrity, you need to create relationships between tables. An example of referential
integrity from Northwind is that to have a record entered into the Orders table, you have to have a record
entered into the Customers table. The two tables are related by the CustomerID field, which is found in both
two tables. In the Customer table, the CustomerID field is the primary key, which means that it will be
unique. In the Orders table, the CustomerID field is a foreign key, which means it points to a primary key in
another table. You can see this displayed in the database diagram in Figure 2.6.
Figure 2.6. The column CustomerID is the primary key in Customers and a foreign key in Orders.

Note
Database diagrams are the main graphical way to view, edit, and print
relationships. You will find them listed under the database you are
working on in the Server Explorer.
You can also get the Property Pages for tables from this dialog box and
perform the majority of work with regard to interaction between
tables, other than structure changes on tables.

Another purpose of referential integrity and relations is that you also can specify to do the following:

Prohibit a parent record (from table Customers, for example) from being deleted or the key field from
being edited if a record exists in the child records (Orders).
If the column relating the tables is updateable, have the system allow the key column to be updated in
the parent record, and have the child records reflect the change in the key columns. This is called
cascade updates.
Have the child records be deleted if the parent record is deleted. This is called cascade deletes.
Three different types of relationships exist when you're creating the relations between tables:

One-to-many. Used when you have one record in the parent table, with many records in the child
table. The Customers/Orders relationship that is displayed in Figure 2.6 is an example of this type of
relationship. This is the most common type of relationship. If you look at the join line between the
tables in Figure 2.6, you will see a key on the Customers end. This represents the one side, and the
infinity symbol by the Orders table represents the many side.
One-to-one. One record in the parent table will have one related record in the child table. An example
of this could be if you had private information for customers that you wanted to keep in a separate
table. These two tables would have the same primary key column. This relation type is not used often
because developers can use views to limit the data to which users have access.
Many-to-many. Records in the parent table can have many related records in the child table, and
records in the child table can belong to many records in the parent table. An example of this can also
be found in the Northwind database. Customers can have demographics tracked for them. Customers
can have many demographic types, and demographic types can be assigned to many customers. This
relationship is displayed in Figure 2.7.
Figure 2.7. The Customers, CustomerCustomerDemo, and CustomerDemographics table
make up this many-to-many relationship.

Steps
Using the Server Explorer, expand the Chapter2 database to see how to create a relationship. It helps to
have more than one table. Because you have already created the table called tblCustomers, create the new
table called tblCustomerPhones, with the following columns.
Column Name

Data Type

Length

Allow Nulls

Identity

PhoneID

int

Unchecked

Yes

CustomerID

int

Unchecked

No

PhoneType

nvarchar

50

Unchecked

No

PhoneNumber

nvarchar

10

Unchecked

No

Set the PhoneID as the primary key and save the table as tblCustomerPhones. This table will contain the
various phone numbers and types that an individual customer can have. This is an easy way to check out
how to create a one-to-many relationship.

1. Right-click on Database Diagrams in the Chapter2 database tree in the Server Explorer. Choose New
Diagram from the menu. The Add Table dialog box will appear.
2. Hold down the Shift Key and select both tblCustomers and tblCustomerPhones. Click the Add button to
add the tables to the diagram, and then click the Close button. You will now see the two tables in the
new database diagram.
3. Place the cursor on the primary key symbol in the CustomerID column of tblCustomers. Hold down the
left mouse button and drag and drop the cursor over to the CustomerID column in tblCustomerPhones.
The Create Relationships dialog box will be displayed, with the pertinent information filled in for you.
4.

3.

4. Select the Cascade Delete Related Records; that way, when a customer is deleted, the phone records
that have been assigned will also be removed. You can see the final relationship in Figure 2.8. Click OK
to accept this relationship.
Figure 2.8. You have complete control over relationships from this dialog box.

How It Works
Creating proper relationships in your database controls how good your data is going to be. The old term
"Garbage In, Garbage Out" has meaning in this case. When a user adds information to tables that have
relationships set up, he has to enter the data in a certain order. In this case, a user needs to add customers
before adding phone information.
If customers are deleted, then the phones that have been assigned to them will be deleted as well.

Comments
You will set up your forms in a logical manner based on the way that relationships are set up. For instance, a
good way to set up adding your phone numbers to the form would be to use the grid control to display your
many phones for one customer.
Another way to enhance this example would be to add another table to hold the phone types and then to link
this new table to tblCustomerPhones via a link called PhoneTypeID.
For more examples of relationships, check out the tables and their relationships in Northwind. Create a
database diagram, add a table in which you are interested, and then view the related tables.

[ Team LiB ]

[ Team LiB ]

2.5 Define Defaults and Constraints


One of the main concepts to keep in mind when you're creating databases is that you need to provide a
means to keep garbage (bad data) out of your tables. Default values make sure that each new record begins
with valid data. An example of this is if you want to make sure that a date is entered for the order date of an
invoice. You could set up a default value of today's date for new records.
Constraints allow you to specify what business rules you want to use with your tables, such as if you want to
allow dates that are entered into the Order Date field to come after today's date. This How-To explains how
to use defaults and constraints to their full potential and how to create them in Visual Studio .NET.
You have started down the path of handling your business rules by creating tables and setting the data types
of the columns in the tables. You have even set up relationships between tables. Now you want to make life
easier for your users and suggest what values they could use, and more importantly, what values are
acceptable. How do you do this by defining defaults and constraints?

Technique
By using default values, you can guide your users and save them time. For example, perhaps you have a
mailing list application, and the user is entering addresses. If the majority of the entries are from
Washington (or WA), then you could set the default property of the State field to be WA.
Check constraints, not to be confused with the constraint type of index, allow you to create validation clauses
that can control data that goes into the tables. An example of this would be if you didn't want someone from
California (CA) entered into the addresses table from the last paragraph. You could then enter a constraint
check that would read something like State <> 'CA '.

Note
The rest of this chapter will use the Northwind database because it has
a great structure that makes it easy to show how to use necessary
techniques.

For this How-To, in Northwind's Orders table, you are going to add a default value to the OrderDate column
and a check constraint that validates the ShipDate to make sure it occurs on or after the OrderDate.

Steps
Using the Server Explorer, expand the tables branch in the Northwind database, right-click on the Orders
table, and choose Design Table.

1.

1. Place the cursor in the OrderDate column.


2. Type GetDate() in the Default Value property. The GetDate() function returns the current system
date and time. This squares away the default value.
3. To enter the check constraint, open the Property Pages dialog box by right-clicking on the column and
choosing Check Contraints from the menu.
4. Click New on the Property Page dialog box.
5. Enter ([ShippedDate] >= [OrderDate]) into the Constraint Expression. You can see the
completed constraint in Figure 2.9. Notice that you can also specify when the constraint will be checked
by selecting the options located at the bottom of the Check Constraints page.
Figure 2.9. Constraints can be used to verify that data falls within business rules that you
specify.

6. Click OK to accept the new constraint.

How It Works

When a new record is saved to the Orders table, if no OrderDate is added, then the GetDate() will add the
system date. After that, the constraint that was added will check to see that the ShippedDate entered falls
on or after the OrderDate. If this is the case, then an error message will be displayed.

Comments
Default values make users' lives easier when they can be used. You can also use default values so that users
will be less tempted to leave values blank, or NULL.
Check constraints allow you to further control how data is entered into your database. They can be used
both when modifying data and when adding new records. Be sure to set up what you need for constraints
when you are creating the database.

[ Team LiB ]

[ Team LiB ]

2.6 Create Views


After you figure out you want to use a view, the trick is to create it by using the designer. This How-To
explains how to do this from within Visual Studio .NET, including specifying sort order and criteria.
Now that you have your tables created, with relationships in place, you can add data by double-clicking the
table name in the Server Explorer. I also know how to bind forms and controls to data using data binding
from the last chapter. How do you create views, using VS .NET?

Technique
Within SQL Server, you can view and manipulate data using one of three ways:

Views. These allow you to display different views of your data, including joining tables, sorting (SQL
Server 200x), and using criteria. Views are limited to using the SELECT statement, and they can be
used as the base for updating as well displaying data. Views are great when you need to filter your
data but want to be able to update like you would a single table.
Stored procedures. Perform bulk operations such as updating, inserting, and deleting records. You can
also create select queries that can be sorted. Another difference from Views is that you can use
multiple SQL and control-of-flow statements within a stored procedure. You can also use parameters
with stored procedures.
User-defined functions. User-defined functions are one of three types: Scalar, Table, and In-Line.
These types combine the best features of views and stored procedures into a single query that you can
nest, pass parameters to, sort, and return values.
You can find more on stored procedures in How-To 2.7.
When you want to have various views of your data that you will want to use throughout your application(s),
you can create views to do so. You can then use the view to populate forms, controls, and reports.

Note
In versions of SQL Server prior to 2000, use of views was frowned
upon because of performance and sorting limitations, among other
reasons. This has changed with 2000, where views are more flexible
and offer better performance.

Within Visual Studio .NET, you can create, update, and delete SQL Server views all from within the Server
Explorer, within the desired database. To work with views, you will use the Views Designer. For new views,
you will choose New View while right-clicking on the Views node in the database. If you're editing, choose
Design View while right-clicking on the desired view. You will then be taken into the View Designer, as shown

in Figure 2.10, with the view called Current Product List.


Figure 2.10. Using the View Designer, you can see the tables you want to include, the fields you
are using, the SQL Statement created, and even the data that will be returned.

As you can see from Figure 2.9, the View Designer has the four main areas mentioned:

Diagram pane. This area allows you to display the table or tables(s) that you will be using in your view.
This could also contain the following: other views, user-defined functions, sub queries (in FROM clause,
and linked views. You will also see check boxes (or other objects) flowing down the left side of the
tables. These allow you to choose fields that you want to include. You can also see some other icons
along the right side of the tables, when grouping and sorting fields or when using criteria. You will see
examples of these if you look through the existing views in Northwind. You can also join tables so that
you can view data using multiple record sources.
Grid Pane. This is where you will specify how you want individual columns to be handled within the
view. The following table describes each property:

Column Description
Column

Display of either the name of a data column used for the query or the expression
for a computed column.

Alias

The name you want to use in the result set. This allows you to either rename an
existing column or name a new computed column. You might want to rename an
existing column if you have the same column used in two different tables.

Table

The name of the table where the column is from.

Output

Whether to display the column in the results set.

Sort
Type

Whether you want to sort the column in ascending or descending order. If you
don't want to sort on the column, leave it blank.

Sort
Order

Here you will specify where in the sorted columns you want to include this column.
You can place a number that corresponds with the column order that you want
this column included in.

Group
By

This is where you specify that you want to use the current column for aggregating
information. To get this column to show, you need to choose Group By from the
Query menu. Besides Grouping data on this column, you can also specify functions
such as Min, Max, Count, and more.

Criteria

This column allows you to specify to which value you would like to compare the
column to narrow down and return specific records. If you specify values in
multiple columns, you will create a Boolean (AND) expression.

Or

Placing values in these columns will cause Boolean ( OR ) expressions to be created.

SQL Pane. You will see the SQL Select statement that is created by filling in the two previous panes
mentioned.
Results Pane. As the title suggests, this pane will display the results of the view created when you click
the Run Query toolbar button, which is the exclamation.

Steps
To learn how to create a view in Visual Studio .NET, you will create a view that displays all the orders for a
given date, including the owners of each of the orders. You will also have the view sorted by CompanyName,
then OrderID.
Open the Server Explorer and expand the Northwind database.

1. Right-click on the icon on the Views node and select New View from the menu. The Add Table dialog
box will then be opened. From the menu, you will select the tables, views, or functions that you want
to include in the view.
2. Select Customers. Then, holding down the Ctrl key, select the Orders table. Click Add and then click
Close. The tables will then be dropped into the Diagram pane, and you will be presented with the View
Designer.
One thing to notice is the symbol shown between the two tables, displayed in Figure 2.11. The symbol
chosen by default in this case represents an inner join. An inner join is when values in the first table
must match values from the second table, using the join column which, in this case, is CustomerID.
Figure 2.11. Using this menu, you can specify how you want the tables to be joined.

You can see the symbols for the other two types of joins displayed in the right-click menu. These types
of joins include Left Outer Join (Select All Rows From Customers) and Right Outer Join (Select All Rows
From Orders.) Using the various types of joins, you can alter the data results that are returned.
3. Place checkmarks in the CompanyName column, from the Customers table, and then the OrderID and
OrderDate columns, located in the Orders table. When you place the checkmark in the columns, you
will see both the Grid pane and the SQL pane fill out, provided you have them showing.
4. Next, type the expression = '7/19/1996' into the Criteria column of OrderDate, located in the Grid
pane. If you just type the date, VS will place the other values around it. You have now added criteria.
Only those orders with this date will be returned.
5. Pick Ascending for the Sort Type of the CompanyName and OrderID columns. Notice that Sort Order
will be filled in automatically, depending on the order in which you pick the Sort Type.

How It Works
When you click on the Run Query command, which is an icon on the toolbar, you will see two records
displayed in the Results pane.

Comments
After a view has been created, you can use it in various ways throughout your applications. In the next
chapter, you will see examples of using views with ADO.NET to populate various controls. You can also use
view within views, store procedures, and user-defined functions. This is handy when you have a set of
results that you want to use consistently in your applications and they rely on more than one table.

[ Team LiB ]

[ Team LiB ]

2.7 Create Stored Procedures


Stored procedures allow you to perform bulk operations on records, such as updating, inserting, and deleting
data as needed. This tutorial shows you how to create stored procedures as well as how to test them from
within the Server Explorer.
Viewing data is great, but you need to perform bulk operations such as adding, updating, and deleting
records. You know that you will be using a stored procedure to perform these tasks, and you will be using
them more than once in your application. How do you create stored procedures from VS?

Technique
Stored procedures are the way to go when you want add, delete, or update records or when you want to use
a parameter. When you have a task that requires multiple actions, including temporary tables, stored
procedures give you this ability. Stored procedures are powerful to use and easy to create.

Note
To create stored procedures, you will use what is called Transact-SQL
(T-SQL). Although this chapter will present some simple commands to
show the interface used to create stored procedures from VS .NET,
Chapter 6, "Creating Transact-SQL Commands," goes into more detail
on the commands of the language.

As with views, you will use a designer within Visual Studio. Unlike the Views designer, the stored procedure
designer is not visual initially, but more text oriented. However, you can pull up a visual designer after you
are in the text designer.
When you're creating a new stored procedure, you will right-click on the Stored Procedures node in the
database to which you want to add the stored procedure, and then you will choose New Stored Procedure.
To edit existing stored procedures, you will highlight the stored procedure, right-click, and choose Edit
Stored Procedure.
After the stored procedure is open, you will see a select statement or a number of T-SQL statements. If it is
a new stored procedure, you can right-click and choose Insert SQL. You will be taken to the Query Builder,
which happens to look like the View designer. If it is an existing stored procedure, you can place the cursor
within a block of SQL code, which is outlined with a blue line, and choose Design SQL Block from the rightclick menu, as shown in Figure 2.12.
Figure 2.12. You can also set break points in your stored procedures using this menu.

You will then see the SQL block displayed once again in the Query Builder.
When specifying parameters that can be used as criteria in stored procedures, you will use the @ symbol in
front of the parameter name, and declare them at the top of your stored procedure. Again, you can see this
in Figure 2.12.

Steps
For this How-To, you are going to create a simple Select statement with a parameter, listing customers for
a given city. If you're not already there, open the Server Explorer and expand the Northwind database.

1. Right-click on the Stored Procedures node, and then choose New Stored Procedure. You will be taken
into a new page that is a template for stored procedure text. You will see the following listed:

CREATE PROCEDURE dbo.StoredProcedure1


/*
(
@parameter1 datatype = default value,
@parameter2 datatype OUTPUT
)
*/
AS
/* SET NOCOUNT ON */
RETURN

2. Replace all the text displayed with the following:

2.

CREATE PROCEDURE dbo.spListCustomersForCountry


@parCountry char
AS
Select * From Customers where Country = @parCountry
RETURN

With this, you can see the use of the parameter.

3. Save the stored procedure.

How It Works
To test the stored procedure that you just created, you can right-click on the block of code and choose
Design SQL Block from the menu. You can then click on the Run Query toolbar button and fill in the parCity
parameter with USA when the dialog box is presented. You will then see the information displayed in the
Results pane.
You will see examples of using stored procedures in the next chapter, which discusses using ADO.NET with
SQL Server objects.

Comments
Although you can create stored procedures on-the-fly and not save them in the database, it is sometimes
necessary and desirable to save them permanently so that you can use the same code in different places in
your application.

[ Team LiB ]

[ Team LiB ]

Chapter 3. Viewing Data With ADO.NET


In this chapter you will

Retrieve data using the DataReader object


Retrieve results from SQL Server by using the DataTable object
Locate records with the DataTable object
Filter and sort records by using the DataView object
ActiveX Data Objects (ADO) was introduced a few years ago as a solution to accessing data that can be
found in various forms, not only over a local area network (LAN), but over the Internet as well. ADO was the
new generation of data access that replaced Remote Data Objects (RDO) and Data Access Objects (DAO),
originally created for the JET database engine. JET was originally created for Microsoft Access, and was later
used as a choice of databases for small- and medium-sized single- and two-tier database solutions. Then
along came ADO.NET.

[ Team LiB ]

[ Team LiB ]

Differences Between ADO and ADO.NET


ADO and ADO.NET are different in several ways:

ADO works with connected data. This means that when you access data, such as viewing and updating
data, it is real-time, with a connection being used all the time. This is barring, of course, you
programming special routines to pull all your data into temporary tables.
ADO.NET uses data in a disconnected fashion. When you access data, ADO.NET makes a copy of the
data using XML. ADO.NET only holds the connection open long enough to either pull down the data or
to make any requested updates. This makes ADO.NET efficient to use for Web applications. It's also
decent for desktop applications.
ADO has one main object that is used to reference data, called the Recordset object. This object
basically gives you a single table view of your data, although you can join tables to create a new set of
records. With ADO.NET, you have various objects that allow you to access data in various ways. The
DataSet object will actually allow you to store the relational model of your database. This allows you
to pull up customers and their orders, accessing/updating the data in each related table individually.
ADO allows you to create client-side cursors only, whereas ADO.NET gives you the choice of either
using client-side or server-side cursors. In ADO.NET, classes actually handle the work of cursors. This
allows the developer to decide which is best. For Internet development, this is crucial in creating
efficient applications.
Whereas ADO allows you to persist records in XML format, ADO.NET allows you to manipulate your
data using XML as the primary means. This is nice when you are working with other business
applications and also helps when you are working with firewalls because data is passed as HTML and
XML.

[ Team LiB ]

[ Team LiB ]

Objects That Are Found in ADO.NET


As mentioned previously, the main object that is used with ADO.NET is the DataSet object. You can see the
DataSet object and its properties, methods, and additional objects in Figure 3.1.
Figure 3.1. ADO.NET has several more objects than ADO does.

Take a look at Table 3.1 to see a brief description of some of the objects that you will be using during this
How-To.

Table 3.1. ADO.NET Data Objects That Are Used to Manipulate Data

Object

Purpose

DataSet

This object is used in conjunction with the other data controls, storing the results that are
returned by commands and the data adapters. Unlike the recordset from ADO and DAO, the
data set actually brings back a hierarchical view of the data. Using properties and collections
in the DataSet object, you can get overall relations, individual tables, rows, and columns.

DataTable One of the objects off of the data set, the DataTable object enables you to manipulate an
individual table's worth of data. The data table is similar to the recordset object that is found
in ADO.

DataView

Using this object, you can filter and sort your data, keeping various views of the data. Each
data table has a default view, which is the starting data view that can be modified and stored
in a separate data view.

DataRow

This object enables you to manipulate the rows of data in your data tables. This can be
thought of as a cache of data that you can manipulate by adding, deleting, and modifying
records. You can then accept the changes back to the recordset, where you will then run SQL
statements to update data back at the server.

DataColumn As the name suggests, you can get information at the column level by using the DataColumn
object. You can get schema information as well as data using this object. For example, if you
want to create a list box of names of fields, you could iterate through the DataColumn
collection off a data row and retrieve all the names of the fields.

PrimaryKey This object allows you to specify a primary key for a data table. That way, when you use the
Find method of the data table, it knows which column to use.
.NET also provides classes, called data providers, to work with ADO.NET objects to provide access to data.
You can see some of those objects in Figure 3.2.
Figure 3.2. You can use either OleDb classes or SQLClient classes for better performance.

Note

Your Visual Studio .NET applications are made up of one or more


assemblies. Each assembly contains one or more Namespaces.
Namespaces are then made up of one or more classes (objects).
Therefore, the Namespace for your OleDb objects is
System.Data.OleDb . You can find these objects using the Object
browser.

In Table 3.2, you can see a brief description of some of the objects that you will be using during this How-To.

Table 3.2. .NET Data Provider Classes That Are Used to Manipulate Data
Object

Purpose

Command

Similar to the ADO Command object, this allows you to execute stored procedures in code.
Unlike the ADO version, however, you can create a DataReader object using the
ExecuteReader method.

Connection This object opens a connection to the server and database with which you want to work.
Unlike the ADO Connection object, the way that the connection remains open depends on
the object with which you are working, such as a DataReader or DataSet object.
DataAdapter A real workhorse, the DataAdapter object allows you to create SQL statements and fill
datasets with the data. It also creates other necessary action queries, such as Insert,
Update, and Delete ADO.NET command objects.
DataReader This object creates a read-only, forward-only stream of data that allows you to quickly
populate controls, such as ListBox and ComboBox controls.
Parameter

This object allows you to specify the parameter (or parameters if you use more than one)
that DataAdapter objects can specify and use.

Tip
Chapter 1, "Developing Windows Forms Using Bound Controls,"
mentioned that the OleDb data controls are the ones that you will
want to use for various types of backends, whereas the SQLClient data
controls work strictly with SQL Server. The same is true of using these
objects as well. If you know that you will just be using a SQL Server
backend, you will get better performance by using the SQLClient
objects because a layer is cut out.

You will get a chance to see all of the items listed in the previous two tables throughout the following HowTos.

Tip

If you have to stick with ADO or just want to be stubborn, check out
using ADO with .NET by reading Appendix A, "Desktop Development
with ADO."
Although ADO.NET does take more work sometimes to accomplish a
task that you could do using ADO, the power and flexibility of ADO.NET
is well worth the learning curve.

Note
Although this chapter was written using Windows Forms, the majority
of the objects can also be used in Web Forms as well as by using
ADO.NET with ASP.NET. You will see this in Chapter 5, "Working with
Date in Web Forms."
You will learn various methods for achieving the same goal. When and
how you use these methods will depend on the scenario.

All of the examples in this chapter can be found in the Solution called VB.NETChapter 3 on the Web site.

[ Team LiB ]

[ Team LiB ]

3.1 Retrieve Data by Using the DataReader Object


In Chapter 1 , you learned how to use bound controls to OleDb controls that could be included on the forms.
Some developers prefer to use unbound controls to perform the same task. The DataReader object allows
you to add items to a list box in a more efficient manner because it is only a read-and-forward-only type
object. This How-To tells you how to generate a limited ListBox control by using a DataReader object.
You want to create a limited list of customers. You don't want to use bound controls because you are a cool
VB developer who knows better than that. You heard that the DataReader object is a fast way to get data.
How do you retrieve data using the DataReader object to perform this task?

Technique
For this How-To, you will be using the ListBox control and loading items into it by using the DataReader
object. To get to the DataReader object, you need to look at the Command object.
The Command object in .NET works similarly to the ADO Command in that you will assign the stored
procedure name or SQL statement to the CommandText property as well as the connection to use. One
difference is that you will use the Open method of the Command object, and then the ExecuteReader
method, to create the DataReader object.
You will then use the Read method off of the DataReader object to perform two tasks. The first task is that
when used in a loop, you can test for datareader .Read() to check whether to terminate the loop and
also to iterate through the rows that the DataReader object returns.
After you have the DataReader populated and you are iterating through the rows, you will load the data
into the ListBox control. You will be using the Clear and Add methods, which were used in VB 6. In
addition, you will use the BeginEdit and EndEdit methods, which speed up loading ListBox controls when
you are loading a large amount of data.
To view this example in design view, open the form called frmHowTo3_1.vb in the chapter's solution.

Steps
Open and run the VB.NETChapter 3 solution. From the main form, click on the command button with the
caption How-To 3.1. When the form loads, click on the Load List command button. You will see the list below
fill with all the company names that start with A .
You will be creating a form similar to one that was created in Chapter 1 . Instead of using bound controls,
however, you will use code along with ADO.NET to populate the ListBox control.

1. Create a Windows Form. Then place a Label, TextBox, ListBox, and Command button on the form with
the properties that are listed in Table 3.3 set.

Label
Name
Label1

Caption
Customer
TextBox
Name
txtCustLimit

Text
A
ListBox
Name
lstCustomers
Command Button
Name
btnLoadList

Caption
Load List

Table 3.3. Label, TextBox, ListBox, and Command Button Control Property Settings
Object

Property

Setting

Notice that the list box does not have the DataBindings properties set. That is because you will be using
the ListBox control unbound. Look at Figure 3.3 to see what the form should look like.
Figure 3.3. Arrange the controls on the form that you created to look like this form.

2. Before creating the code that will be attached to the Click event of the btnLoadList command button, you
need to devise a support routine to create the connection string. Called BuildCnnStr , the function can
been seen in Listing 3.1 . This function takes a server and database names that are passed to it and
creates a connection string.
Listing 3.1 modGeneralRoutines.vb : Creating a Connection String

Function BuildCnnStr(ByVal strServer As String, _


ByVal strDatabase As String) As String
Dim strTemp As String
strTemp = "Provider=SQLOleDB; Data Source=" & strServer & ";"
strTemp &= "Initial Catalog=" & strDatabase & ";"
strTemp &= "Integrated Security=SSPI"
Return strTemp
End Function

Although you could create a routine that would pass back a Connection object, a more versatile
method is to pass back a string. The reason for this is that for some objects, you are asked for a
Connection object, whereas in other objects, you just need a string. You will see BuildCnnStr
called in the next step.

3. On the btnLoadList Command button, add the following code from Listing 3.2 to the Click event. In this
routine, a SQL string is created and stored in the strSQL string, taking Text property of the
txtCustLimit text box and adding it to a literal. Then, within a Try-Catch-End-Try block, a new instance of
an OleDbCommand object called ocmdCust is created. The routine then follows the steps that are
discussed in the Technique section.

Listing 3.2 frmHowTo3_1.vb : Loading a List Box By Using the DataReader Object

Private Sub btnLoadList_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnLoadList.Click
Dim ocmdCust As OleDb.OleDbCommand
Dim odrCust As OleDb.OleDbDataReader
Dim strSQL As String
'-- Create the SQL String
strSQL = "Select CompanyName From Customers Where CustomerID Like '" &
Me.txtCustLimit.Text & "%'"
'-- Set up the exception catch
Try
'-- Create an instance of the command
ocmdCust = New OleDb.OleDbCommand()
With ocmdCust
'-- Set up the connection of the command and the command text
.Connection = _
New OleDb.OleDbConnection(BuildCnnStr("(local)", "Northwind"))
.Connection.Open()
.CommandText = strSQL
'-- Set up the data reader instance
odrCust = .ExecuteReader(CommandBehavior.SequentialAccess)
End With
'-- Add the items to the list box.
With lstCustomers
.Items.Clear()
.BeginUpdate()
Do While odrCust.Read
.Items.Add(odrCust.Item("CompanyName"))
Loop
.EndUpdate()
End With
Catch oexpData As OleDb.OleDbException
MsgBox(oexpData.Message)
End Try
End Sub

Note

Something of interest to those VB developers is the fact that the lines of code
that read as follows:

.Connection = _
New OleDb.OleDbConnection(BuildCnnStr("(local)", "Northwind"))

actually declare, initialize, and use an OleDBConnection object in the single statement. This is
new to .NET and is extremely useful.

How It Works
When the user clicks the btnLoadList button, the Command object is assigned the necessary properties, the
connection is opened, and the ExecuteReader method is called.
After the list has been cleared, the DataReader is iterated through, and the ListBox control is loaded.

Comments
The DataReader object is one of the most efficient ways to get data from the server and load lists into your
application. Other options besides CommandBehavior.SequentialAccess are available that make the
DataReader convenient to use. Most notable is CommandBehavior.SchemaOnly , which returns
information only about the columns, and no data.
You can use the Command object in a number of ways besides what was mentioned in this How-To. You will
see additional examples of using the Command object with stored procedures to perform batch actions later
in the chapter.
You have seen how to use the ListBox control in a total unbound technique. In the next How-To, you will
see a blend of using the ListBox control in a semibound technique, where you will bind the data at runtime.

[ Team LiB ]

[ Team LiB ]

3.2 Retrieve Results from SQL Server by Using the DataTable Object
The data reader is great when you just want to load data into a ListBox or ComboBox control manually, but
you can save some coding by binding the ListBox control to a data table at runtime, as well as providing the
ability to get not only the displayed value, but the key column as well. This How-To demonstrates how to
bind a limited ListBox control to a data table.
Although getting the quick information is great, you need to be able to refer back to the table of information,
and you don't want to have to open another connection to get there. You know that the DataTable object
should allow you to perform this task. How do you get results from SQL Server by using the DataTable
object?

Technique
Using the Windows Forms controls that were introduced in How-To 3.1, you will use a familiar object from
Chapter 1 called the DataAdapter object. This time, instead of using the OleDbDataAdapter data control,
you will use the OleDbDataAdapter class from the System.Data.OleDb Namespace .
Using a similar technique that was used when filling a DataSet, you will instantiate the data adapter by
assigning the SQL string and connection object. Then, instead of filling a DataSet object, you will fill a
DataTable object. Because you will only be dealing with a table's worth of data, you just need to use a data
table. That way, you will be able to perform lookups more conveniently, as shown in the next How-To.
For now, the next step will be to assign the following properties of the list box:

DataSource. This will be set to the DataTable objectin this case, dtCust.
DisplayMember. This specifies which column from the data table to use for display in the list box.
ValueMember. Here, you will specify which column you want to use for the value that is retrieved
when an item is selected from the list box.
By programming the ListBox control using this technique, you can access the ValueMember column in the
SelectItem property of the list box.

Steps
Open and run the VB.NETChapter 3 solution. From the main form, click on the command button with the
caption How-To 3.2. When the form loads, click on the Load List command button. You will see the list below
fill with all the company names that start with A.

1. To save time, you can make a copy of the form that was created in the first How-To in this chapter.
2. Replace the btnLoadList Click event with the following code listed here in Listing 3.3. That's it. After
creating the SQL string that will be used and storing it in strSQL, the data adapter called odaCust is
created. The odtCust data table is then filled using odaCust. Last, the DataSource, DisplayMember, and
ValueMember properties are set for the lstCustomers list box. This was all accomplished with a TryCatch-End-Try block of code.

Listing 3.3 frmHowTo3_2.vb: Loading a List Box By Using the DataTable Object

Private Sub btnLoadList_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnLoadList.Click
Dim odaCust As OleDb.OleDbDataAdapter
Dim dtCust As DataTable = New DataTable()
Dim strSQL As String
'-- Create the SQL String
strSQL = "Select CustomerID, CompanyName From Customers " & _
"Where CustomerID Like '" &
Me.txtCustLimit.Text & "%'"

'-- Set up the exception catch


Try
'-- Create an instance of the data adapter,
' and then fill the data table
odaCust = New OleDb.OleDbDataAdapter(strSQL, _
BuildCnnStr("(local)", "Northwind"))
odaCust.Fill(dtCust)
'-- Bind the data to the list box
lstCustomers.DataSource = dtCust
lstCustomers.DisplayMember = "CompanyName"
lstCustomers.ValueMember = "CustomerID"
Catch oexpData As OleDb.OleDbException
MsgBox(oexpData.Message)
End Try
End Sub

How It Works
When the user clicks on the btnLoadList button, the data adapter called odaCust is instantiated. The data
adapter is passed strSQL and the connection string that is created by the function called BuildCnnStr,
which was introduced in the first How-To in this chapter. The data table is then filled, and then the
DataSource, DisplayMember, and ValueMember properties of the ListBox control are assigned.

Comments
Using the data table sets up the scene for using the list box in retrieving data in the next How-To.
Remember: By using the DataTable object, you can assign both the display value and the data item to be
tracked.

[ Team LiB ]

[ Team LiB ]

3.3 Locate Records with the DataTable Object


Using the DataTable object, you can use another object called the DataRow object that allows you to
locate a specific row in the data table. This is useful when you want to present your users with a search
mechanism for your form. This How-To shows you how to locate a specific row within your data table and
how to use the same data table for two different purposes.
After you have your DataTable object loaded in memory, you want to be able to locate specific records
within the DataTable object. How do you locate records in the DataTable object?

Technique
For this How-To, you are going to use a ComboBox control instead of a ListBox control. You will use the same
technique for loading the combo box as you would the list box. The change comes when you select an item
from the combo box.
When an item is selected in the combo box, the SelectedIndexChanged event is fired off. Within this
event, you will take the combo box's SelectedItem , which gives the ValueMember that is located in the
selected row, and use that with the Find method off the DataTables Rows collection.
With the data row located, the corresponding columns are loaded into text boxes on the form, as shown in
Figure 3.4.
Figure 3.4. This combo box will point the user to a specific customer.

Steps
Open and run the VB.NETChapter 3 solution. From the main form, click on the command button with the
caption How-To 3.3. When the form loads, pick a new customer from the list that is presented in the
customer ComboBox control. You will see the text boxes below the ComboBox control display new data that
corresponds to the chosen customer.

1. Create a new Windows Form.


2. Add some labels, combo boxes, and text boxes, as listed in Table 3.4.

Table 3.4. Label, TextBox, and ComboBox Control Property Settings


Object

Property

Setting

Label

Name

Label1

Caption

Customer

ComboBox

Name

cboCustomers

Label

Name

Label2

Caption

Customer ID

Name

Label3

Caption

Company Name

Name

Label4

Caption

Address

Name

Label5

Caption

City

TextBox

Name

txtCustomerID

TextBox

Name

txtCompanyName

TextBox

Name

txtAddress

TextBox

Name

txtCity

Label
Label
Label

You will also want to make sure that the Text properties in the TextBox controls are blank.
3. In the class module for the form, add the following two Private declarations just below the line of code
that reads Windows Form Designer generated code.

Private modaCust As OleDb.OleDbDataAdapter


Private mdtCust As DataTable = New DataTable()

These lines of code declare a data adapter and a data table that will be used throughout the
form.

Note

Adding the m on the front tells you that it is a module- or memberlevel variable.
Also, remember that although you are declaring this at the form
level, the connection that is used for the data adapter is not going
to be left open the whole time the form is. When the data table is
filled, the connection is opened. Then the data is accessed locally
using XML under the covers. It is disconnected from the server.

4. Add the code shown in Listing 3.4 to the Load event of the form. Almost identical to the code in the last
How-To to load a ListBox control, this code sets modaCust to a SQL String and the connection string
to be used. mdtCust is then filled using the Fill method of modaCust. Next, the first element in the
DataColumn array called dc is set to the CustomerID column. mdtCustPrimaryKey is then set to the
DataColumn array. Last, the DataSource, DisplayMember, and ValueMember properties are set.
Listing 3.4 frmHowTo3_3.vb: Loading a ComboBox by Using the DataTable Object

Private Sub frmHowTo3_3_Load(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
Dim strSQL As String
Dim dc(1) As DataColumn
'-- Set up the exception catch
Try
'-- Create the data adapter and fill the data table
modaCust = New _
OleDb.OleDbDataAdapter("Select * From Customers", _
(BuildCnnStr("(local)", "Northwind")))
modaCust.Fill(mdtCust)
'-- Set up the primary key for the data table
dc(0) = mdtCust.Columns("CustomerID")
mdtCust.PrimaryKey = dc
'-- Bind the data to the combo box
cboCustomers.DataSource = mdtCust
cboCustomers.DisplayMember = "CompanyName"
cboCustomers.ValueMember = "CustomerID"

Catch oexpData As OleDb.OleDbException


MsgBox(oexpData.Message)
End Try
End Sub

The PrimaryKey property that was set will be used in the code for the next step by the Find
method of mdtCust's Rows collection.

5.

5. This last bit of code needs to be added to the SelectedIndexChanged event of the cboCustomers
ComboBox control. As with the last step, when a data column was set up for the PrimaryKey property,
in this step an array is specified to pass the SelectedItem value to find the Find method. The text
boxes' Text properties are then set to the column values by using the ToString method.
Listing 3.5 frmHowTo3_3.vb: Locating a Record in the Data Table, and Then Assigning Values
to Text Boxes

Private Sub cboCustomers_SelectedIndexChanged(ByVal sender As System.Object, _


ByVal e As System.EventArgs) _
Handles cboCustomers.SelectedIndexChanged
Dim drCurr As DataRow
Dim aFindValue(0) As Object
'-- Load the item to look up, and use the find method
aFindValue(0) = cboCustomers.SelectedItem(0)
drCurr = mdtCust.Rows.Find(aFindValue)
'-- Load up the fields on the form
txtCustomerID.Text = drCurr("CustomerID").ToString
txtCompanyName.Text = drCurr("CompanyName").ToString
txtAddress.Text = drCurr("Address").ToString
txtCity.Text = drCurr("City").ToString
End Sub

How It Works
When a user picks a customer from the cboCustomer ComboBox control, the code then locates the desired
value within the mdtCust data Table using the Find method off the rows collection. Text boxes are then
loaded from the row that is retrieved.

Comments
Locating records within a data table and data row is pretty easy when you're using the methods that are
supplied. ADO.NET provides the control you need, not only at the overall hierarchical level, but also at the
row and column levels.

[ Team LiB ]

[ Team LiB ]

3.4 Filter and Sort Records Using the DataView Object


After your data is loaded into the data table, you will probably want to be able to view your data using
different filters and sort orders. To do this, you can use the DataView object. This How-To goes into detail
and shows you how to take advantage of the DataView control to manipulate your data.
Although you can put data into the DataGrid control and let the users sort data using the columns, you
want to display a ComboBox control and let users pick a field from the drop-drown list. How can you filter
and sort records using the DataView object to present your data in different ways?

Technique
This How-To displays a set of command buttons that display a letter and an extra command button that
displays all records. A data adapter, data table, and data view are declared at the form level. The data
adapter is created and the DataTable is filled when the form is loaded with all customers. Using a
DataColumn object, a combo box is filled by getting the names of each column that is in the data table. You
can see this form in action in Figure 3.5.
Figure 3.5. Selecting a letter here limits the data displayed in the DataGrid control.

Using the command buttons, a routine is called that creates a DataView object, sets the RowFilter
property, and then assigns the data view to the DataSource property of a DataGrid control.

Tip
Although the RowFilter allows you to filter data based on a criteria
such as CompanyName Like 'A%', you can set another property to
display data based on the state of the row in which data occurs. The
property is called RowStateFilter.
You can set the RowStateFilter to one of the following
DataViewRowState values in Table 3.5.

Table 3.5. Label, TextBox, and ComboBox Control Property Settings


Setting

Description

Added

New rows

CurrentRows

Current rows including unchanged, new, and modified rows

Deleted

Deleted rows

ModifiedCurrent A current version, which is a modified version of original data (see


ModifiedOriginal)
ModifiedOriginal The original version (although it has since been modified and is available as
ModifiedCurrent)
None

None

OriginalRows

Original rows including unchanged and deleted rows

Unchanged

Unchanged row

The Sort property of the DataView object is used when a column name is chosen from the ComboBox. The
current setting of the Sort property is compared to the column name that is chosen. If the Name matches,
then the expression DESC is added to the value that is assigned to the Sort property.

Steps
Open and run the VB.NETChapter 3 solution. From the main form, click on the command button with the
caption How-To 3.4. When the form loads, click on different letters that are displayed. You will see the data
grid display different customers based on their first letter.
If you choose a column name from the Column to Sort On ComboBox control, the data grid will then be
sorted based on the column chosen.

1. Create a new Windows Form.


2. Add a GroupBox control with the text property set to Click on a Letter.
3.

1.
2.
3. Now you will be creating buttons that you will place within the GroupBox control you just created. The
buttons will have their property set as listed in Table 3.6.

Table 3.6. Buttons Property Settings


Object

Property

Setting

Button

Name

btnA

Caption

Name

btnB

Caption

Name

btnC

Caption

Name

btnZ

Caption

Name

btnAll

Caption

All

Button
Button
...
Button
Button

4. Add the DataGrid, the Label, and the ComboBox controls shown in Listing 3.6.

Table 3.7. DataGrid, Label, and ComboBox Controls Property Settings


Object

Property

Setting

DataGrid

Name

dgCustomers

Label

Name

Label1

Label

Caption

Column to Sort On :

ComboBox

Name

cboSortColumns

5. In the class module for the form, add the following three Private declarations just below the line of
code that reads Windows Form Designer generated code. These three objects will be used
throughout the form.

Private modaCust As OleDb.OleDbDataAdapter


Private mdtCust As DataTable = New DataTable()
Private mdvCust As DataView = New DataView()

6. Add the following code to the Load event of the form as shown in Listing 3.6. This code starts out by
setting up the modaCust data adapter to grab all the customers to fill the data table called mdtCust.
Note that at this point, the data grid has not been filled.
The next task is to load cboSortColumns with the column headings by iterating through each of the
data columns in mdtCust and adding them to the Items collection in cboSortColumns. Last, the
SetDataViewFilter routine is called. This routine is discussed in step 8.

Listing 3.6 frmHowTo3_4.vb: Loading the Data Table to Be Used in the Form, and Adding
Column Names to a ComboBox Control

Private Sub frmHowTo3_4_Load(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
Dim strSQL As String
Dim dcCurr As DataColumn
'-- Set up the exception catch
Try
'-- Create the data adapter and fill the data table
modaCust = New _
OleDb.OleDbDataAdapter("Select * From Customers", _
(BuildCnnStr("(local)", "Northwind")))
modaCust.Fill(mdtCust)
'-- Load the column names into the sort ComboBox control
For Each dcCurr In mdtCust.Columns
Me.cboSortColumns.Items.Add(dcCurr.ColumnName)
Next
SetDataViewFilter("B")
Catch oexpData As OleDb.OleDbException
MsgBox(oexpData.Message)
End Try
End Sub

7. For each of the command buttons that has a single letter, add the first subroutine displayed here in
Listing 3.7 to each of its Click events. For the btnAll Button control, add the second subroutine to the
Click event. Each Button control will pass the letter that it represents to the subroutine called
SetDataViewFilter , discussed in the next step. The btnAll code simply passes the empty string.
Listing 3.7 frmHowTo3_4.vb: Click Events for Each of the Button Controls

Private Sub btnA_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnA.Click
SetDataViewFilter("A")
End Sub
Private Sub btnAll_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnAll.Click
SetDataViewFilter("")
End Sub

8. Add the subroutine that is found in Listing 3.8 to the class module of the form. This routine takes the
letter value that is passed in strFilterLetter as a parameter. The first task to perform is assigning

8.
the DefaultView of the mdtCust DataTable object to the mdvCust data view. Next, the RowFilter
property of mdvCust is set to compare the CompanyName column with the Like expression using the
strFilterLetter and the % (wildcard). Note that if "" is passed to strFilterLetter, all the
records will be listed. Finally, mdvCust is set as the DataSource for dgCustomers, which is the
DataGrid control.
Listing 3.8 frmHowTo3_4.vb: Setting the RowFilter Property for a DataView Object

Sub SetDataViewFilter(ByVal strFilterLetter As String)


mdvCust = mdtCust.DefaultView
mdvCust.RowFilter = "CompanyName Like '" & strFilterLetter & "%'"
dgCustomers.DataSource = mdvCust
End Sub

9. Add the piece of code that is shown in Listing 3.9 to the SelectdIndexChanged event of the
cboSortColumns ComboBox control. This routine compares the current setting of mdvCust's Sort
property to the current column name chosen in cboSortColumns. If the two are the same, then the
column name is assigned to the Sort property with the DESC keyword added on. If not, then the
name of the column is assigned to the Sort property.
Listing 3.9 frmHowTo3_4.vb: Specifying a Column on Which to Sort

Private Sub cboSortColumns_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) _
Handles cboSortColumns.SelectedIndexChanged
'-- Check to see if the column is currently the sorted field.
'
If it is, sort on the column in descending order.
'
Otherwise, set the sort to the name of column.
If mdvCust.Sort = Me.cboSortColumns.Text Then
mdvCust.Sort = Me.cboSortColumns.Text & " DESC"
Else
mdvCust.Sort = Me.cboSortColumns.Text
End If
End Sub

How It Works
When the user clicks on a letter, the data view is created, and the data grid reflects the new data. When a
field is selected from the ComboBox control, the Sort property of the data view is set and the data grid
automatically reflects the new sort order, also showing an arrow in the column heading. If the user chooses
the field again, the column will sort in descending order.

Comments
Using the DataView object, you can keep track of multiple views of your data and display them for the
users' use. You can also access all of the default views of the data tables in your data set using the
DefaultViewManager.

Note
Some people might think that the sorting combo box that was added
to this example is unnecessary. It was added for two reasons. First, it
shows how to use the Sort property of a DataView object. Second,
it's convenient for the user. The user might not want to have to scroll
over to a column that is not displayed in the data grid. By using the
combo box, he can sort on fields that are not currently displayed.

[ Team LiB ]

[ Team LiB ]

Chapter 4. Manipulating Data With ADO.NET


In this chapter you will

Edit data and update changes made to an ADO.NET DataSet object


Add and delete rows in a dataset with ADO.NET
Execute parameterized stored procedures in ADO.NET
Create and execute on-the-fly batch updates by using ADO.NET
In Chapter 3, "Viewing Data with ADO.NET," you saw how to use ADO.NET to display data in various forms.
However, there is so much more to ADO.NET, such as manipulating your data by modifying current data, as
well as inserting and deleting data. ADO.NET gives you the tools you need to accomplish these tasks with
some of the objects you were introduced to in Chapter 3.
A few more steps are required to manipulate data using ADO.NET then there were in ADO. The main reason
for this is that ADO.NET is disconnected; therefore, after you have saved your changes to the ADO.NET
objects, such as a dataset, you need to commit those changes back to the server. You will see how to do this
in this chapter. The good news is that after you learn how to have the objects update and commit the data
back to the server for one task, such as editing of data, you will use the same basic commands for the other
tasks as well. Let's get started.

[ Team LiB ]

[ Team LiB ]

4.1 Edit Data and Update Changes That Are Made to an ADO.NET DataSet Object
Listing and viewing data is easy. What you really need to do is to be able to edit and update data. You know
you can use the DataSet object and some of its objects and methods to perform this task. How do you edit
and update data using the DataSet object?

Technique
In this How-To, you will use the DataAdapter , DataSet , DataTable , and DataRow objects. You have
experienced some of the properties and methods of each of these objects before. In this chapter, you are
going to be using the following properties and methods that are shown in Table 4.1 .

DataAdapter
Fill
Fills DataSet and DataTable objects.

CommandBuilder
GetUpdateCommand
Creates an Update command and places it into the data adapter's UpdateCommand property.

DataAdapter
UpdateCommand
Holds the SQL statement for the update.

DataAdapter
Close
Closes the connection off the UpdateCommand . The syntax is dataadapter
.UpdateCommand.Connect.Close() .

DataAdapter
Update
Performs the update command against the dataset.

DataSet
Tables
Represents a collection of tables found within a dataset.

DataSet

Rows
Contains a collection of rows within a specified table in a dataset.

DataSet
AcceptChanges
Sends the changes back to the server.

DataRow
ToString
Retrieves the data from the column that is specified in the DataRow and returns it as a string value.

DataRow
BeginEdit
Begins the editing of a DataRow , allowing you to replace values in the columns.

DataRow
EndEdit
Completes the editing of a DataRow .

Table 4.1. DataAdapter , DataSet , DataTable , and DataRow Properties and Methods
Object /Method

Property

Description

You will see these objects with their properties and methods used in the following steps.

Steps
Open and run the VB.NETChapter 4 solution. From the main form, click on the command button with the
caption How-To 4.1. When the form loads, click on the Load List button to display the customers that begin
with the letter A . Click the Edit button. You will notice that the fields have now taken on a sunken look. Place
the cursor into the City field and change the value to Dunkirk . Now click Save. If you move off the record
and move back on, you will notice that the value has been saved.
This form looks similar to the form created in Chapter 1 . The difference is that this time you will not be using
controls that are bound at design time. You can see the form in Figure 4.1 .

1. Create a new Windows Form.


2. Add the following controls, setting the properties as listed in Table 4.2 .

Label
Name
Caption

Label1
Customer
TextBox
Name
Text
txtCustLimit
A
Button
Name
Caption
btnLoadList
Load List
ListBox
Name
lstCustomers
Label
Caption
Customer ID
Label
Caption
Company Name
Label
Caption
Contact
Label
Caption
Contact Title
Label
Caption

Address
Label
Caption
City
Label
Caption
Region
Label
Caption
Country
Label
Caption
Phone
Label
Caption
Fax
TextBox
Name
txtCustomerID
TextBox
Name
txtCompanyName
TextBox
Name
txtContact
TextBox
Name
txtContactTitle
TextBox

Name
txtAddress
TextBox
Name
txtCity
TextBox
Name
txtRegion
TextBox
Name
txtPostalCode
TextBox
Name
txtCountry
TextBox
Name
txtPhone
TextBox
Name
txtFax
Button
Name
Caption
btnEdit
&Edit
Button
Name
Caption
btnSave

&Save
Button
Name
Caption
btnCancel
&Cancel

Table 4.2. Controls Property Settings


Object

Property

Setting

Note

Notice that the Text property of the text boxes is not being set at design time. In
Chapter 1 , "Developing Windows Forms Using Bound Controls," they were set to
columns of a dataset that was included on the form. In this How-To, they will be set at
run-time.

3. In the class module for the form, add the following three Private declarations just below the line of code that
reads Windows Form Designer generated code . These three objects will be used throughout the form.

Dim mdsCustIndiv As New DataSet()


Dim modaCustIndiv As OleDb.OleDbDataAdapter
Dim mdrCustIndiv As DataRow

4. Enter the following code as the Click event for btnLoadList :

Private Sub btnLoadList_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnLoadList.Click
'-- Move the loading of the list to a subroutine for
'
additional(calls)
LoadList()
End Sub

5. Create the LoadList routine by entering the following code into the form you created for this How-To. This code
creates and fills a data table using a data adapter. The string that the data adapter uses creates a Select
statement by using the txtCustLimit text box. The DataSource , DisplayMember , and ValueMember

5.

properties of the list box are then bound. Last, the LoadIndividual routine is called, which is described in the
next step.

Private Sub LoadList()


Dim odaCustList As OleDb.OleDbDataAdapter
Dim dtCustList As DataTable = New DataTable()
Dim strSQL As String
'-- Create the SQL String
strSQL = "Select CustomerID, CompanyName " & _
From Customers Where CustomerID Like '" & _
Me.txtCustLimit.Text & "%'"

'-- Set up the exception catch


Try
'-- Create an instance of the data adapter; then fill the data
table
odaCustList = New OleDb.OleDbDataAdapter(strSQL, _
BuildCnnStr("(local)", "Northwind"))
odaCustList.Fill(dtCustList)
'-- Bind the data to the list box
lstCustomers.DataSource = dtCustList
lstCustomers.DisplayMember = "CompanyName"
lstCustomers.ValueMember = "CustomerID"
LoadIndividual()
Catch oexpData As OleDb.OleDbException
MsgBox(oexpData.Message)
End Try
End Sub

6. Create the LoadIndividual routine by entering the following code in the form you created for this How-To.
Taking the SelectedItem from the list box, a data adapter is created, and a dataset is filled. Next, the
individual DataRow is created. Last, each of the TextBox controls is loaded with the value from the column with
the corresponding name. Notice the use of the Try-Catch-End-Try to ignore controls that don't have a like
column in the DataRow .

Private Sub LoadIndividual()


Dim strSQL As String
Dim strName As String
Dim oCtl As Object
mdsCustIndiv.Clear()
If Me.lstCustomers.SelectedIndex <> -1 Then

Try
'-- Load the individual record into the dataset
strSQL = "Select * from Customers Where CustomerID = '" &
Me.lstCustomers.SelectedItem(0) & "'"
modaCustIndiv = New OleDb.OleDbDataAdapter(strSQL, _
BuildCnnStr("(local)", "Northwind"))
'-- Fill the dataset
modaCustIndiv.Fill(mdsCustIndiv, "Customers")
'-- Grab the individual data row
mdrCustIndiv = mdsCustIndiv.Tables("Customers").Rows(0)
Catch oexpData As OleDb.OleDbException
MessageBox.Show("Error loading individual data: " _
& oexpData.Message)
Exit Sub
End Try
'-- Run through the text boxes on the form, and
'-- if they match up with a field from the record,
' load them.
For Each oCtl In Me.Controls
If TypeOf oCtl Is TextBox Then
strName = Mid(oCtl.Name, 4)
'-- By trapping the exception this way, errors are ignored.
Try
oCtl.text = mdrCustIndiv(strName).ToString
Catch oexp As Exception
End Try
End If
Next
End If
End Sub

7. Enter the following code to the Click event for lstCustomers:

Private Sub lstCustomers_Click(ByVal sender As Object,


ByVal e As System.EventArgs) Handles lstCustomers.Click
'-- Fill the current list item's individual dataset
LoadIndividual()
End Sub

8.

8. Enter the following code to the Click event for btnEdit:

Private Sub btnEdit_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnEdit.Click
'-- Enable the editing of the form
ActivateEditing(True)
End Sub

9. Create the ActivateEditing routine by entering the following code in the form you created for this How-To.
Introduced in Chapter 1 , this code goes through each of controls on the form, looking for text boxes, then
setting the BorderStyle and BackColor properties based on whether the controls are to be enabled or
disabled. The Enabled property of each control is then set as well.

Private Sub ActivateEditing(ByVal bEnable As Boolean)

Dim oCurr As Object


'-- Loop through each of the controls on the form
For Each oCurr In Me.Controls()
'-- Check to see if the control is a text box
If TypeOf oCurr Is TextBox And oCurr.Name <> "txtCustLimit" Then
'-- If so, toggle the properties
If bEnable Then
oCurr.BorderStyle() = _
System.Windows.Forms.BorderStyle.Fixed3D
oCurr.BackColor() = System.Drawing.Color.White
Else
oCurr.BorderStyle() = _
System.Windows.Forms.BorderStyle.FixedSingle
oCurr.BackColor() = Me.BackColor

End If
oCurr.Enabled = bEnable
End If
Next
End Sub

10.

10. Enter the following code to the Click event btnSave:

Private Sub btnSave_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnSave.Click
'-- Save the information
SaveRecord()
'-- Disable the text boxes
ActivateEditing(False)
End Sub

11. Create the SaveRecord routine by entering the following code in the form that you created for this How-To.
Using a DataRow object, the BeginEdit method is called, and then each of the controls is stored back into the
columns of the same names, if they exist. The EndEdit method is then called to complete the editing of the
DataRow . A CommandBuilder object is created to create the Update command for the DataAdapter object.
The DataAdapter Update method is called to update the dataset with the data changed and then the
AcceptChanges of the DataSet object. This accepts all the changes for all the objects and posts the data
back to the server. Finally, the connection is closed for the UpdateCommand of the DataAdapter object.

Private Sub SaveRecord()


Dim oCtl As Object
Dim strName As String
'-- Start the editing in the datarow.
mdrCustIndiv.BeginEdit()
'-'-'-For

Run through the text boxes on the form, and


if they match up with a field from the record,
place the value back in the record.
Each oCtl In Me.Controls
If TypeOf oCtl Is TextBox Then
strName = Mid(oCtl.Name, 4)
'-- By trapping the exception this way, errors are ignored.
Try
mdrCustIndiv(strName) = oCtl.text
Catch oexp As Exception
End Try
End If

Next
'-- Finish the editing of the data row
mdrCustIndiv.EndEdit()
Try
'-- Create an instance of the command builder

Dim ocbCustIndiv As OleDb.OleDbCommandBuilder


ocbCustIndiv = New OleDb.OleDbCommandBuilder(modaCustIndiv)
'-- Have the command builder create an update SQL command
modaCustIndiv.UpdateCommand = ocbCustIndiv.GetUpdateCommand
'-- Perform the update SQL command; then close the connection
modaCustIndiv.Update(mdsCustIndiv, "Customers")
mdsCustIndiv.Tables("Customers").AcceptChanges()
modaCustIndiv.UpdateCommand.Connection.Close()

Catch excData As Exception


End Try
End Sub

12. Enter the following code to the Click event btnCancel:

Private Sub btnCancel_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnCancel.Click
'-- Use the BindingContext class to cancel the current editing.
LoadIndividual()
ActivateEditing(False)
End Sub

Figure 4.1. Although this looks like the form created in Chapter 1 , you have more control over
this version with unbound controls.

How It Works

When the user clicks on the btnLoadList Button, the lstCustomers list box is loaded via the odaCustList
data adapter and dtCustList data table. The first customer's information is then loaded in the text boxes
on the right side of the form. When the btnEdit button is clicked, the look of the text boxes is changed to
sunken, and they are enabled for editing of the text. After changing the data, when the user clicks on the
btnSave button, the data is then stored back into the server, and the text boxes are changed to disabled. If
the btnCancel is clicked, the text boxes are changed to disabled.

Comments
Although it takes a bit more code to handle the editing and updating of data with unbound controls versus
bound controls, you might like it better because you can control the code. With bound controls, the code is
written for you. The code that is displayed here can be modified to be more generic so that you don't have to
write individual routines for each form.

[ Team LiB ]

[ Team LiB ]

4.2 Add and Delete Rows in a Dataset with ADO.NET


Again using the OleDbDataAdapter and OleDbCommandBuilder objects, this How-To shows you how to
use unbound controls with the dataset to add and delete rows from SQL Server.
As with editing and updating data, you need to be able to add and delete rows using the dataset. How do
you perform this task?

Technique
The main difference between this technique and the previous one will be which action command you will use
with the DataAdapter object. You will also be presented with the Add method on the Rows collection.

Steps
Open and run the VB.NETChapter 4 solution. From the main form, click on the command button with the
caption How-To 4.2. When the form loads, click on the Load List button to display the customers that begin
with the letter A. Click the Add button. You will notice that the fields have now taken on a sunken look and
that they are cleared. Fill in the Customer ID and Company Name text boxes with AAA1 and Another
Example . Now click Save. If you move off the record and move back on, you will notice that the value has
been saved. Now select the new record you added, and click the Delete button. The record disappears, and
the list box is updated to reflect the deletion.

1. Make a copy of the form you created in the last How-To.


2. Add two buttons for adding and deleting records, setting the properties as listed here in Table 4.3.

Table 4.3. Add and Delete Buttons Property Settings


Object

Property

Setting

Button

Name

btnAdd

Caption

&Add

Name

btnDelete

Text

&Delete

Button

3. Add the following line of code where you placed the other module-level variable declarations. This
variable will be set to True in the btnAdd click event, and it can be used when saving the record.

Dim mblnAdd As Boolean

4. Enter the following code to the Click event btnAdd. The first task is to set the mblnAdd variable to

4.

True. Then the routine clears the current text in the text boxes. It also enables the text boxes by
calling ActiveEditing.

Private Sub btnAdd_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnAdd.Click
Dim oCtl As Object
Dim strName
mblnAdd = True
'-- Clear the fields
For Each oCtl In Me.Controls
If TypeOf oCtl Is TextBox And oCtl.name <> "txtCustLimit" Then
strName = Mid(oCtl.Name, 4)
'-- By trapping the exception this way, errors are ignored.
Try
oCtl.text = ""
Catch oexp As Exception
End Try
End If
Next
ActivateEditing(True)
End Sub

5. Replace the SaveRecord routine with the following code in the form that you created for this How-To.
The first change is to add the lines of code that test mblnAdd ; if they're True, call the NewRow method
off the dataset's Tables collection, specifying the Customers table. The DataRow returned is assigned
to mdrCustIndiv . You can then enter the other changes wherever the blnAdd variable is queried.

Private Sub SaveRecord()


Dim oCtl As Object
Dim strName As String
If mblnAdd Then
mdrCustIndiv = mdsCustIndiv.Tables("Customers").NewRow
End If
'-- Start the editing in the datarow.
mdrCustIndiv.BeginEdit()
'-'-'-For

Run through the text boxes on the form, and


if they match up with a field from the record,
place the value back in the record.
Each oCtl In Me.Controls

If TypeOf oCtl Is TextBox Then


strName = Mid(oCtl.Name, 4)
'-- By trapping the exception this way, errors are ignored.
Try
mdrCustIndiv(strName) = oCtl.text
Catch oexp As Exception
End Try
End If
Next
'-- Finish the editing of the datarow
mdrCustIndiv.EndEdit()
Try
If mblnAdd Then
mdsCustIndiv.Tables("Customers").Rows.Add(mdrCustIndiv)
End If
'-- Create an instance of the command builder
Dim ocbCustIndiv As OleDb.OleDbCommandBuilder
ocbCustIndiv = New OleDb.OleDbCommandBuilder(modaCustIndiv)
If mblnAdd Then
'-- Have the command builder create an Insert SQL command
modaCustIndiv.InsertCommand = ocbCustIndiv.GetInsertCommand
Else
'-- Have the command builder create an update SQL command
modaCustIndiv.UpdateCommand = ocbCustIndiv.GetUpdateCommand
End If
'-- Perform the specified SQL command; then close the connection
modaCustIndiv.Update(mdsCustIndiv, "Customers")
mdsCustIndiv.Tables("Customers").AcceptChanges()
'-- Close the connection
If mblnAdd Then
modaCustIndiv.InsertCommand.Connection.Close()
LoadList()
Else
modaCustIndiv.UpdateCommand.Connection.Close()
End If
Catch excData As Exception
MessageBox.Show("Error Occurred: " & excData.Message)
End Try
End Sub

6. Enter the following code to the Click event btnCancel.

6.

Private Sub btnCancel_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnCancel.Click
'-- Cancel the current editing.
If mblnAdd Then
mblnAdd = False
End If
LoadIndividual()
ActivateEditing(False)
End Sub

7. Enter the following code to the Click event btnDelete. Follow the comments to see what is
happening.

Private Sub btnDelete_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnDelete.Click
Dim ocbCustIndiv As OleDb.OleDbCommandBuilder
Try
'-- Delete the record from the datarow object
mdrCustIndiv.Delete()
'-- Instantiate the command builder
ocbCustIndiv = New OleDb.OleDbCommandBuilder(modaCustIndiv)
'-- Have the command builder create a Delete SQL command
modaCustIndiv.DeleteCommand = ocbCustIndiv.GetDeleteCommand
'-- Perform the specified SQL command; then close the connection
modaCustIndiv.Update(mdsCustIndiv, "Customers")
mdsCustIndiv.Tables("Customers").AcceptChanges()
'-- Close the connection
modaCustIndiv.DeleteCommand.Connection.Close()
Catch excData As Exception
MessageBox.Show("Error Occurred: " & excData.Message)
End Try
LoadList()
ActivateEditing(False)
End Sub

How It Works
When a user clicks the Add button, the text boxes are all blanked out, and the mblnAdd flag is set as True.

Then, after the user adds his information and clicks the Save button, the new record is added back to the
server. If the Cancel button is clicked, the individual customer to whom the list is currently pointed is loaded
into the text boxes. When the Delete key is pressed, the current record is deleted from the server. Then the
customer list is refreshed, and the first customer in the list is displayed in the text boxes.

Comments
As you can see, adding and deleting a record does not take much more than editing and updating a record
using ADO.NET. Using the commands in this How-To and the prior one, you can set it up to handle updating
and canceling of multiple records as well.

[ Team LiB ]

[ Team LiB ]

4.3 Execute Parameterized Stored Procedures in ADO.NET


To take advantage of stored procedures to their full power, you need to be able to pass parameters so that
specific criteria can be used. This How-To describes how to create parameters off the OleDbCommand objects
to pass parameters to SQL Server.
You need to execute a parameterized stored procedure in your application. How do you do this using Visual
Basic .NET and ADO.NET?

Technique
In ADO, you have a Command object to execute stored procedures, among other tasks. In ADO.NET, you also
have a Command object that performs basically the same task. In fact, many of the properties and methods
that you use are the same. You can see a list of those in Table 4.4.

Table 4.4. Objects That Are Used for This Technique, with Properties and Methods
Object

Property

Description/Method

Connection ConnectionString Contains the connection string that is used.


Connection Open

Opens the connection that the Command object uses.

Command

cmdText

Specifies the SQL statement to use. Can be SQL statement or names of


objects such as tables or stored procedures.

Command

Connection

Uses the Connection object.

Command

CommandType

Specifies the type of command you want to execute. Can be one of the
following command types: StoredProcedure, DirectTable, or Text.

Command

Parameters

Parameters to pass to the stored procedure.

Command

ExecuteReader

Creates a DataReader object with the data that the command object
specifies.

DataReader Read

Reads the next record in the DataReader, and also tests the end of the
data returned.

DataReader GetString

Returns the current record, getting the column specified, and returns it
as string.

DataReader GetInt32

Returns the current record, getting the column specified, and returns it
as 32-bit integer.

You will use these objects and their properties and methods for the following steps.

Steps
Open and run the VB.NETChapter 4 solution. From the main form, click on the command button with the
caption How-To 4.3. When the form loads, click on the View button to display the orders for the customer ID
that is specified. By default, this is ALKI. A TextBox control is then displayed on the bottom of the form. You
can see the form in Figure 4.2.

1.

1. Create a new Windows Form.


2. Add the following controls, setting the properties as listed in Table 4.5.

Table 4.5. Controls Property Settings


Object

Property

Setting

Label

Name

Label1

Caption

Products and Quantities Ordered By:

TextBox Name

txtCustID

Text

ALFKI

Button Name

btnView

Text

&View

TextBox Name

txtResults

MultiLine True
3. Enter the following code to the Click event btnView . This code takes the connection and creates the
command object. The name of the stored procedure is passed, and the command type is specified,
which is CommandType.StoredProcedure. Next, parameters and the DataReader are created. The
last task is to iterate through the data and add it to the display text box.

Private Sub btnView_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnView.Click
Dim ocnn As New OleDb.OleDbConnection(BuildCnnStr("(local)",
"Northwind"))
Dim ocmdCustHist As New OleDb.OleDbCommand("CustOrderHist", ocnn)
Dim odrCustHist As OleDb.OleDbDataReader
Try
'-- Specify the name of the stored procedure
ocmdCustHist.CommandType = CommandType.StoredProcedure
'-- Specify the parameters.
ocmdCustHist.Parameters.Add("@CustomerID", Me.txtCustID.Text)
'-- Open the connection object.
ocnn.Open()
'-- Establish the DataRead object.
odrCustHist = _
ocmdCustHist.ExecuteReader(CommandBehavior.SequentialAccess)
'-- Iterate through the data loaded, building the results string.
Do While odrCustHist.Read
Me.txtResults.Text &= odrCustHist.GetString(0) & _
", " & odrCustHist.GetInt32(1) & vbCrLf
Loop

Catch excpData As Exception


MessageBox.Show("Error Occurred: " & excpData.Message)
End Try
End Sub

Figure 4.2. This form uses the Command object with a stored procedure to populate the TextBox
control.

How It Works
When the user clicks on the View button with Customer ID filled in, the text box below is filled in, displaying
order information for that customer.

Comments
Using the technique presented here, you can pretty well perform the majority of the tasks you need to by
using Command objects and stored procedures.

[ Team LiB ]

[ Team LiB ]

4.4 Create and Execute On-the-Fly Batch Updates by Using ADO.NET


Sometimes in database applications, you want to create and execute stored procedures that don't currently
exist. When you have a situation in which you need to use highly dynamic stored procedures that might use
criteria that is entirely created at runtime, you might need to create those stored procedures on-the-fly. This
How-To shows you how to create and execute these stored procedures.
It's great that you can execute stored procedures that are already created, but what if you need to generate
one at runtime? How do you do this?

Technique
To perform this How-To, you will be utilizing the OleDBCommand object, and feeding in the CommandText
property from a text box. The text box is set to "Update Employees Set City = 'Redmond' Where
City = 'Seattle'" to give you something to start with.

Steps
Open and run the VB.NETChapter 4 solution. From the main form, click on the command button with the
caption How-To 4.4. When the form loads, you will see an example update statement in a text box. Click on
the Execute button to execute the update statement. A TextBox control is then displayed on the bottom of
the form showing the number of records that are affected. You can the form in Figure 4.3 .
Figure 4.3. This form uses the Command object with a SQL statement passed to execute the
specified action.

Note

The number of records affected might be different on your system depending on


what you have been doing with the Northwind data.

1.

1. Create a new Windows Form.


2. Add the following controls, setting the properties as listed in Table 4.6 .

Label
Name
Caption
Label1
Update Statement to Execute:
Button
Name
btnExecute
TextBox
Name
Text
txtSQL
Update Employees Set City = 'Redmond' Where City = 'Seattle'

MultiLine
True
Label
Name
Caption
Label2
Records Affected:
TextBox
Name
txtRecsAffected

Table 4.6. Controls Property Settings

3.

Object

Property

Setting

3. Enter the following code to the Click event btnExecute . When the command is instantiated in this
case, the string in the txtSQL text box is passed as the CommandText . The CommandType is set as
CommandType.Text . The connection is then open. Finally, the command is executed with the
ExecuteNonQuery method, with the ToString passing back the number of records that were affected
to the Text property of the txtRecsAffected text box.

Private Sub btnExecute_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnExecute.Click
Dim ocnn As New OleDb.OleDbConnection(BuildCnnStr("(local)", "Northwind"))
Dim ocmdPhoneUp As New OleDb.OleDbCommand(Me.txtSQL.Text, ocnn)
Try
'-- Specify the name of the stored procedure
ocmdPhoneUp.CommandType = CommandType.Text
'-- Open the connection object.
ocnn.Open()
Me.txtRecsAffected.Text = ocmdPhoneUp.ExecuteNonQuery.ToString
Catch excpData As Exception
MessageBox.Show("Error Occurred: " & excpData.Message)
End Try
End Sub

Tip

Use a TryCatchEnd Try block to trap any exceptions that might occur
when working with ADO.NET. In this case, the error is trapped and a message
box displays the error. Remember that exceptions that are not trapped will cause
the application to fail.

How It Works
When a valid SQL statement is entered into the text box with the label Update Statement to Execute:
and the Execute button is clicked, the command entered is executed, and the number of records that were
affected is returned.

Comments
The Command object is a real workhorse when it comes to performing bulk operations, whether working with
store procedures already created or when using statements that have been created on-the-fly.

[ Team LiB ]

[ Team LiB ]

Chapter 5. Working With Data In Web Forms


In this chapter you will:

Dealing with stateless Programming


Use bound controls with Web forms
Validate data using validation controls
Populate DropDown and ListBox controls
Display data using the Table control
Display data using the Repeater control
Display, sort, and page data in the DataGrid control
Add, edit, and delete data using the DataGrid control
Hyperlink from a row in the data grid to a detail page
With .NET, developing for the Web becomes easier than ever. ASP.NET is actually even fun to work with. In
the past, ASP proved quite a task to develop applications in. Now, however, you can develop your Web
applications in much the same way you do Windows desktop applications, with few major differences.

[ Team LiB ]

[ Team LiB ]

Dealing with Stateless Programming


One of the big differences lies in the fact that Web pages are still inherently stateless. This means that when
you move from page to page, you tend to lose the data that the pages use. Although this is not a big
problem when you are creating simple Web pages that perform easy tasks, when you begin creating real
applications, especially dealing with databases or data that users input, this factor can be a big hassle.

State Management on the Client


In the past, the statelessness of Web pages was dealt with to an extent in a number of ways on the client
side:

Cookies. Small files that a Web application creates. Cookies store data on the local machines of those
who are accessing the Web application. A couple of problems exist with just using cookies. First,
cookies are stored on the local machine, so the machine (or browser) must allow them. Second, the
type of data that can be stored in cookies is limited.
Query strings. Information that is appended to the end of a page's URL. You will see examples of this
when you're creating hyperlinks and calling new Web pages.
Hidden fields. An HTML field that is hidden to the user. These are not visible to the class module either.
Control.ViewState property. Provides a dictionary object for retaining values between multiple requests
for the same page. Note that this is the method that the page uses to preserve page and control
property values between round trips.
Although some of these management techniques have been used in ASP, they are still valid in .NET, and
they are useful given the correct circumstances. When you're developing in ASP.NET, some additional
solutions are available for state management. Those solutions are handled on the server side.

Server-Side Solution to State Management


Depending on the information that you want to retain, .NET has a number of ways to handle state
management. Although you could use the methods just mentioned, a few other options are available as well:

Application state. You can save values to this object Application("ItemName") = value by using
an instance of the HttpApplicationState class, but all those people who are in the application at that
time will be able to see it.
Session state. This option allows you to maintain state for the individual session. This is discussed
further in the next section.
Database support. SQL Server helps you to maintain state by adding values to the Temp database on
the server.

Using the Session Object

The Session object is pretty straightforward to work with. When you assign the variable, you will use the
following syntax:

Session("SessionVariableName") = Value

SessionVariableName is in fact used inside the quotes. It can be whatever you want to call it.
Value can be any type of variable, including a DataTable object, as you will see in the steps that follow.
Sometimes, you can stash the data table and a Boolean variable to the Session object. Following is one of
the lines of code that stores the data table:

Session("MyLookupData") = mdtLookupData

MyLookupData is the name of the entry that is created in the Session object. mdtLookupData is the
variable name of the DataTable object, declared at the module level.
When you're reading a variable, you will turn the statement around and add a CType() function to convert
the value to the desired type.

Variable = CType(Session("SessionVariableName",Type)

In the instance of a data table, such as the one used in this How-To, you would see something like this:

mdtLookupData = CType(Session("MyLookupData"), DataTable)

Note that you can use other conversion functions besides CType() . If you don't convert the value, then the
Object type will be returned.
Another task that is necessary to perform is testing whether the entry in the Session object has been
made. You do this by testing the Session entry against the Nothing keyword, as seen here:

'Put user code to initialize the page here


If Not (Session("MyLookupData") Is Nothing) Then
mdtLookupData = CType(Session("MyLookupData"), DataTable)
End If

This code is used in the Load event of the page. If the entry has been created in the Session object, then
the data is loaded into the variable. You can also use the opposite as well. If the entry in the Session object
is Nothing , then create the entry.
This is just a quick and simple way of taking advantage of the Session object. There are probably hundreds
of other ways to take advantage of the Session object. For more information on this, check out Session State
in the .NET Framework Developer's Guide, which is part of the help provided by Visual Studio. Type in "state
management in ASP.NET" for the index to locate the Session object topic.

You can find all of the examples in this chapter in the Solution called Visual Basic .NETChapter 5 on the
Web site.

[ Team LiB ]

[ Team LiB ]

5.1 Use Bound Controls with Web Forms


I want to create a Web Form that allows my users to view data much like my Windows Forms and be able to
use data bound controls on it. How do I use data bound controls on a Web Form?

Technique
The data objects that you used in Chapter 1 , "Developing Windows Forms Using Bound Controls," including
OleDbDataAdapter, datasets, and so on, will be used with Web Forms as they would with Windows Forms.
The main difference between Windows Forms and Web Forms in this case is the extra steps needed to
handle round trips to the server from the client machines. For discussion on OleDBDataAdapters and
datasets, see Chapter 1 .
The IsPostBack Property
One property that you will be using when you're developing Web forms is the IsPostBack property, which
is used in the Load event of the Web Form. That's rightWeb Forms now have an event model much like
Windows Forms. You can now work with the Web Form properties and Web server control properties from
the Web page's class module.
The IsPostBack property is set to True if the load event is fired on a round trip from the server. Therefore,
the first time a Web Form is loaded, IsPostBack is False .
Web Server Controls Versus HTML Controls
Within ASP.NET Web Forms, you now have the ability to use either your classic HTML controls, which are
available for compatibility purposes, or the new Web server controls, which, because they run on the server,
have the following advantages:

You can access Web server control properties and methods from the Web Forms class module, but not
with HTML controls.
Because Web server controls are rendered from the server side, on the client side they come through
as pure HTML, and they are compatible with more browsers and earlier versions.
Web server controls generally have more features than their HTML counterparts, and in some cases,
they can be bound to data.

Note

You can change some of the HTML controls to Web server controls by placing the
control on the Web Form, right-clicking on the control, and choosing Run as Web
Server Control from the pop-up menu.
After choosing this menu option, you can see the object in your code behind your
page.

Note

Although you will be dealing with a ListBox Web server control, it has different
properties and methods than the Windows Form ListBox control. These are
discussed in the following steps.

The AutoPostBack Property


Web server controls have a property called AutoPostBack . This property tells .NET to post back to the
server, which is something you need to do if you want to have an event fire off, such as the
SelectedIndexChanged event of the lstCustomers list box. This is also true for Button controls that are
used.
The DataBind Method
When you're binding Web server controlsin this case, a ListBox controlto data such as DataSet or
DataTable objects, you need to invoke the DataBind method of the control that is being bound. You need
to do this when data changes, as you will see in the following steps.

Steps
Open and run the Visual Basic .NETChapter 5 solution. From the main page, click on the hyperlink with the
caption How-To 5.1: Using Data Bound Controls with Web Forms. When the Web Form loads, you will see a
list box filled with customers, and the details for the first customer will be displayed in the list (see Figure 5.1
).
Figure 5.1. Arrange the controls on the form you created to look like this form.

To start off, you will be creating a Web Form that is similar to a Windows Form that was created in Chapter 1
. You will actually be creating the form exactly the way you did in the first chapter with the Windows Form,
with the exception of a few commands in the code.

1. Create a Web Form. Then place the controls listed in Table 5.1 with the following properties set. You will be usin
Northwind for the database to connect to.

OleDbDataAdapter

ID

odaCustomerList

SelectCommand
Select CustomerID , CompanyName From Customers
DataSet

ID

dsCustomerList
OleDbDataAdapter

ID

odaCustomerIndividual

SelectCommand
Select * From Customers Where Customer ID = ?
DataSet

ID

dsCustomerIndividual
ListBox

ID

lstCustomers

DataSource
dsCustomerList

DataTextField
CompanyName

DataValueField
CustomerID

AutoPostBack
True
Label
Caption
Customer ID
Label
Caption
Company Name

Label
Caption
Contact
Label
Caption
Contact Title
Label
Caption
Address
Label
Caption
City
Label
Caption
Region
Label
Caption
Country
Label
Caption
Phone
Label
Caption
Fax
TextBox

ID

txtCustomerID

Text

dsCustomerIndividual - Customers.CustomerID
TextBox

ID

txtCompanyName

Text
dsCustomerIndividual - Customers.CompanyName
TextBox

ID

txtContactName

Text
dsCustomerIndividual - Customers.Contact
TextBox

ID

txtContactTitle

Text
dsCustomerIndividual - Customers.ContactTitle
TextBox

ID

txtAddress

Text
dsCustomerIndividual - Customers.Address
TextBox

ID

txtCity

Text
dsCustomerIndividual - Customers.City
TextBox

ID

txtRegion

Text
dsCustomerIndividual - Customers.Region
TextBox

ID

txtPostalCode

Text
dsCustomerIndividual - Customers.PostalCode
TextBox

ID

txtCountry

Text
dsCustomerIndividual - Customers.Country
TextBox

ID

txtPhone

Text

dsCustomerIndividual - Customers.Phone
TextBox

ID

txtFax

Text
dsCustomerIndividual - Customers.Fax
HyperLink

ID

hplReturnToMain

NavigateURL
wfrmMain.aspx

Table 5.1. Label, TextBox, ListBox, and Command Button Control Property Settings
Object

Property

Setting

2. Add the code in Listing 5.1 to the Load event of the page. (Double-click on the page to bring up the code.) The
first command you see is the if statement, which tests to see whether the page is being loaded for the first time
it is, then the dsCustomerList dataset is filled using the odaCustomerList OleDbDataAdapter . Next, the
DataBind method is called. Last, the first item in lstCustomers is selected, and the RefreshIndividual routi
is called, which is described in the next step.
Listing 5.1 wfrmHowTo5_1.aspx.vb : Loading the Page

Private Sub Page_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
'-- Perform only the first time the page is loaded
If Not Page.IsPostBack Then
'-- Fill the customer list box dataset
Me.odaCustomerList.Fill(Me.dsCustomerList)
'-- Bind the list box to the dataset
Me.lstCustomers.DataBind()
'-- Pick the first customer in the list and
'
display the individual's data

Me.lstCustomers.SelectedIndex = 0
RefreshIndividual()
End If
End Sub

3. Add the code in Listing 5.2 to the class module of the page, creating the RefreshIndividual routine . The
routine starts off by clearing the dataset and then tests to make sure a customer is selected. Next, the data
adapter parameter is supplied with the item that is selected in lstCustomers, which will be the CustomerID.
dsCustomerIndividual is filled, and the DataBind method of each of the text boxes is called.
Listing 5.2 wfrmHowTo5_1.aspx.vb : Listing Detail Information About the Customers

Private Sub RefreshIndividual()


'-- Clear individual customer dataset
Me.dsCustomerIndividual.Clear()
If lstCustomers.SelectedIndex <> -1 Then
Me.odaCustomerIndividual.SelectCommand.Parameters(0).Value = _
lstCustomers.SelectedItem.Value
'-- Fill the dataset
Me.odaCustomerIndividual.Fill(Me.dsCustomerIndividual, "Customers")
'-- Call the DataBind method for each of the text boxes
Me.txtCustomerID.DataBind()
Me.txtCompanyName.DataBind()
Me.txtContactName.DataBind()
Me.txtContactTitle.DataBind()
Me.txtAddress.DataBind()
Me.txtCity.DataBind()
Me.txtRegion.DataBind()
Me.txtCountry.DataBind()
Me.txtPostalCode.DataBind()
Me.txtPhone.DataBind()
Me.txtFax.DataBind()
End If
End Sub

4. Add the code in Listing 5.3 to the SelectedIndexChanged event of lstCustomers.


Listing 5.3 wfrmHowTo5_1.aspx.vb : Calling the RefreshIndividual Routine for Each New Customer
Who Is Selected

Private Sub lstCustomers_SelectedIndexChanged(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles lstCustomers.SelectedIndexChange


RefreshIndividual()
End Sub

Comments
Using bound controls on Web Formsor unbound controls, for that matter, such as list boxesdoes not take
much more work than it does in Windows Forms. Except for maintaining the data, binding can take more
work with the round trips to the server to deal with, as you will see in How-To 5.7.

[ Team LiB ]

[ Team LiB ]

5.2 Validate Data Using Validation Controls


I want to be able to validate various types of data entry without having to wait for an error to come back
from the server. Can I validate my data on my Web Form?

Note
A note of caution: Through the writing and tech editing of this chapter,
there have been some inconsistencies noted in the reaction of some of
the validator controls. The tech editor and I both contend that this
area might have some .NET bugs. This is also the reason for the lack
of coverage in the CustomValidator.

Technique
One new feature found in ASP.NET is the inclusion of Validation Web server controls. These validation
controls allow you to specify other controls of which you want to validate based on data entered into the
controls. You can then do the following:

Have the Validation control display an error message.


Test at a page level to see if all controls that are being validated are valid.
Display a list of error messages for all the controls that are being validated.

Available Validation Web Server Controls


You will find the controls for validation in the toolbox, and can see them listed in Table 5.2, with a description
of what they validate.

Table 5.2. Validation Web Server Controls

Control

Description

RequiredFieldValidator

Validates whether the specified control has been left blank.

CompareValidator

Compares the values in two controls and validates whether they are
equal.

RangeValidator

Checks to see if the value entered into a control falls within a range that
is specified.

RegularExpressionValidator Compares a value entered into a control to see if it matches an entered


mask, such as 999-99-9999, which is the U.S. Social Security Number.

CustomValidator

Allows you to create custom functions on both the client and server side
for validation.

ValidationSummary

Used to consolidate the messages that all validation controls return on a


page into a list-like format.

The main properties you will set on a validation control are FieldToValidate and ErrorMessage .

Note
The CustomValidator control is different in that you will create
functions to perform the validation. Other than these, the validation
controls require no coding unless you want to validate the whole page.

Testing Page Validation in Code


Depending on what is required, you can test for validation at the page level using the Page.Validate
method. When you call this method, all the Validation controls are retested, and the Page.IsValid
property is set. You can use the IsValid property in a condition statement to perform tasks based on the
value of the IsValid property, as shown in the last step of this How-To.

Steps
Open and run the Visual Basic .NETChapter 5 solution. From the main page, click on the hyperlink with the
caption How-To 5.2: Validate Data Using Validation Controls. When the Web Form loads, you will see a
number of text boxes with instructions on how to see the various validation checks. Here is the Web Form in
Design view (see Figure 5.2).

1. Create a Web Form. Then place the controls listed in Table 5.3 and shown in Figure 5.2 with the
following properties set.

Table 5.3. Control Property Settings for Validation Controls Web Form

Object

Property

Setting

Label

Text

Required Field Validator


Example (Leave Blank to Test)

TextBox

txtRequiredExample
ID

Label

Text

TextBox

Compare Validator Example


(Enter Two Different Values in
These Text Boxes)

txtCompareFirst
ID

TextBox

txtCompareSecond
ID

Label

Text

TextBox

Range Validator Example


(Enter a Number Outside the
Range 1 and 10)

txtRangeExample
ID

Label

Text

TextBox

Regular Expression Validator


Example (Enter a SSN not
matching 999-99-9999)

txtRegularExprExample
ID

Label

Text

TextBox

Custom Validator Example


(Enter a State Other Than WA
or CA)

txtCustomExample
ID

Button

btnTestValidators
ID

Text

Test Validators

Label

Text

Page Validation Errors

RequiredFieldValidator
Control

ToValidate

txtRequiredExample

ErrorMessage

Here is the message for the


Required Field Validator

ControlToCompare

txtCompareFirst

ControlToValidate

txtCompareSecond

ErrorMessage

Here is the message for the


Compare Validator

ControlToValidate

txtRangeExample

CompareValidator

RangeValidator

MaximumValue
10

MinimumValue

ErrorMessage

Here is the message for the


Range Validator

RegularExpressionValidator ControlToValidate

txtRegularExprExample

ValidationExpression \d{3}-\d{2}-\d{4}
ErrorMessage

Here is the message for the


Regular Expression Validator

ValidationSummary

HeaderText

HyperLink

Error Summary List


hplReturnToMain

ID

NavigateURL

wfrmMain.aspx

Note
For the ValidationExpression property of the
RegularExpressionValidator, you will want to click the builder
button beside the property and choose the SSN from the list of
expressions.

2. Add the code in Listing 5.4 to the Click event of btnTestValidator. You can see an example here of
how you might use the Validate method for the page, and act based on when the IsValid property is
set.
Listing 5.4 wfrmHowTo5_2.aspx.vbs Testing to See If All Controls That the Validation
Controls Handle Are Valid

Private Sub btnTestValidators_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnTestValidators.Click
'-- Forces the Validation controls to validate
Page.Validate()
If Page.IsValid Then
'-- Code if all controls are valid
Else
'-- Code if even one of the controls isn't valid
End If
End Sub

Figure 5.2. These controls aren't seen on your Web forms unless a validation error occurs.

Comments
You normally wouldn't have the ValidationSummary control on the same page if you were listing the
individual error messages using the Validation controls. It is nice to be able to have all the errors show up in
a list sometimes.

Tip
You can affect the way the list looks in the ValidationSummary control
by setting the DisplayMode property. You have the choice of using a
list, a bulleted list, or a paragraph.
You can also display a message box instead of a list by setting the
ShowMessageBox property to True and the ShowSummary property
to False.

Note

You can display one message in the individual validation controls and
another in the ValidationSummary control. The ErrorMessage
property is reflected in the ValidationSummary list, whereas the Text
property of the individual validation controls displays something
different if it's set.

[ Team LiB ]

[ Team LiB ]

5.3 Populate DropDown and ListBox Controls


I saw in this chapter's first How-To how to bind a ListBox control to a dataset that was created in the design.
How do I populate DropDown and ListBox controls on a Web Form using code at runtime?

Technique
ListBoxes and DropDowns, which are equivalent to ComboBoxes on Windows Forms, have different
properties that are used for data binding than their Windows counterparts. Besides these properties,
displayed in Table 5.4 , you also need to use the Databind method and session object to track the data table
that is created for products.

DataTextField
Column in data source to use for displaying in the DropDown or ListBox control.

DataValueField
Column in data source that is the lookup value.

Table 5.4. Properties Used to Bind Columns to Controls


Property

Description

Note

You can use a property called DataFormatString to format the


DataTextField data for display in the ListBox or DropDown controls.

Steps
Open and run the Visual Basic .NETChapter 5 solution. From the main page, click on the hyperlink with the
caption How-To 5.3: Populate DropDown and ListBox controls. When the Web Form loads, you will see a
Categories dropdown with the Beverages category selected and the products for that category in the list box
with the label Products. If you click on a product, the three text boxes are loaded on the right of the page
(see Figure 5.3 ).

1. Create a Web Form. Then place the controls listed in Table 5.5 and seen in Figure 5.3 with the following
properties set.

1.

Label
Text
Categories:
DropDown

ID

ddCategories

AutoPostBack
True
Label
Text
Products :
ListBox

ID

lstProducts

AutoPostBack
True
Label
Text
Product ID
Label
Text
Product Name
Label
Text
Unit Price
TextBox

ID

txtProductID

BackColor
Transparent
TextBox

ID

txtProductName

BackColor
Transparent
TextBox

ID

txtUnitPrice

BackColor
Transparent
HyperLink

ID

hplReturnToMain

NavigateURL
wfrmMain.aspx

Table 5.5. Control Property Settings for Validation Controls Web Form
Object

Property

Setting

2. As with some of the other chapters' projects, a support routine needs to be built to create the Connection
string. Called BuildCnnStr , the function can been seen in Listing 5.5 . This function takes a server and
database name passed to it and creates a connection string.

Listing 5.5 modGeneralRoutines.vb: Creating a Connection String

Function BuildCnnStr(ByVal strServer As String, ByVal strDatabase As String) As String


Dim strTemp As String
strTemp = "Provider=SQLOleDB; Data Source=" & strServer & ";"
strTemp &= "Initial Catalog=" & strDatabase & ";"
strTemp &= "Integrated Security=SSPI"
Return strTemp
End Function

Although you could create a routine that would pass back a Connection object, a more versatile
method would be to pass back a string. The reason for this is that for some objects, you are asked for a
Connection object, but for others, you are asked for just a string.

3. In the class module for the Web Form, add the following Private declaration just below the line of code that
reads Web Form Designer Generated Code.

Private mdtProducts As New DataTable()

This line of code declares a DataTable object that you will use throughout the Web Form. However, in
addition to using this variable, you will use the Session object to retain the data between round trips to
the server.

4. Add the code in Listing 5.6 to the Load event of the page. This code creates a DataAdapter object and then
fills the dtCategories DataTable object. The ddCategories DropDown control is bound to
dtCategories . The LoadProducts routine is called to load the products into the lstProducts ListBox
control, which is described in the next step. Finally, the Session object is checked to see if the item
MyProductsTable has been saved to it, and if so, it is loaded back into the mdtProducts variable.
Listing 5.6 wfrmHowTo5_3.aspx.vb: Initializing the Page

Private Sub Page_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
If Not Me.IsPostBack Then
Dim dtCategories As New DataTable()
Dim odaCategories As _
New OleDb.OleDbDataAdapter( _
"Select CategoryID, CategoryName From Categories",
BuildCnnStr("(local)", "Northwind"))
'-- Fill the data table
odaCategories.Fill(dtCategories)

'-- Assign the properties and bind the dropdown.


ddCategories.DataValueField = "CategoryID"
ddCategories.DataTextField = "CategoryName"
ddCategories.DataSource = dtCategories
ddCategories.DataBind()
LoadProducts()
End If
'-- Load the products data table back from the session variable
If Not (Session("MyProductsTable") Is Nothing) Then
mdtProducts = CType(Session("MyProductsTable"), DataTable)
End If
End Sub

5. In the class module for the page, create the LoadProducts routine that is displayed in Listing 5.7 . This code
looks similar to other routines that generate a DataTable object and then assign the properties to bind
mdtProducts to the lstProducts ListBox control. mdtProducts is then added to the Session object for round
trips to the server.
Listing 5.7 wfrmHowTo5_3.aspx.vb: Creating the LoadProducts Routine

Private Sub LoadProducts()


Dim odaProducts As New OleDb.OleDbDataAdapter( _
"Select * From Products Where CategoryID = "
& ddCategories.SelectedItem.Value, _
BuildCnnStr("(local)", "Northwind"))
mdtProducts.Clear()
odaProducts.Fill(mdtProducts)
'-- Assign the properties and bind the list box.
lstProducts.DataValueField = "ProductID"
lstProducts.DataTextField = "ProductName"
lstProducts.DataSource = mdtProducts
lstProducts.DataBind()
'-- Save the data table out to a session variable for round trips
Session.Item("MyProductsTable") = mdtProducts
End Sub

6. Add the code in Listing 5.8 to the SelectedIndexChanged event off the ddCategories DropDown control.
Listing 5.8 wfrmHowTo5_3.aspx.vb: Calling the LoadProducts Routine When a New Category Is
Chosen

Private Sub ddCategories_SelectedIndexChanged(ByVal sender As Object,


ByVal e As System.EventArgs) _
Handles ddCategories.SelectedIndexChanged
LoadProducts()
End Sub

7. Add the code in Listing 5.7 to the SelectedIndexChanged event off of lstProducts. This code takes the
SelectedIndex property of the lstProducts ListBox control and helps retrieve the row in the DataTable
object. The individual columns are then loaded into the corresponding text boxes on the page.
Listing 5.9 wfrmHowTo5_3.aspx.vb: Locating the Row in the mdtProducts DataTable Object

Private Sub lstProducts_SelectedIndexChanged(ByVal sender As Object,


ByVal e As System.EventArgs) _
Handles lstProducts.SelectedIndexChanged
With mdtProducts.Rows(lstProducts.SelectedIndex)
txtProductID.Text = .Item("ProductID")
txtProductName.Text = .Item("ProductName")
txtUnitPrice.Text = .Item("UnitPrice")
End With
End Sub

Figure 5.3. DropDown and ListBox controls used to display data on this Web Form.

Comments
One of the main items to note, besides the use of the Session variables, is the use of the
ddCategorie.SelectedItem.Value and lstProducts.SelectedIndex. These are two ways to use items that
are selected in the DropDown and ListBox objects, respectively.
After you have used the Session object to keep variables during round trips to the server, it becomes more
intuitive as you use it.

[ Team LiB ]

[ Team LiB ]

5.4 Display Data Using the Table Control


.NET has a number of controls available for displaying data. How do I know which control to use to display
data, and how do I use the Table control to display data?

Technique
When you're first deciding to list data on your Web forms, you have a few Web server controls to choose
from:

Table. This Web server control allows you to create a read-only table type display of data. This control
is not data bound, and it uses the TableColumn and TableRow objects for creation.
Repeater. This control is used to display read-only lists. You can use hyperlinks and program the
ItemCommand event so that you can perform actions when items are selected. You must use
templates to format the display. Templates are discussed in How-To 5.5, as is the Repeater control.
This is a great control for quick lists.
DataList. Using this control, you will use templates not only to display, but also to select and edit data
in the list.
DataGrid. By far, this is the most powerful of the controls. In addition, it gives you the most control
over manipulating data. You display, sort, edit, and use various types of controls in each column. The
last three How-Tos in this chapter thoroughly cover the DataGrid control.

Anatomy of the Table Web Server Control


You can create the Table Web server control at design time, or as shown in this How-To, at runtime. The
control is created by adding the TableRows and TableCells to the Table control. The TableRows and
TableCells are controls in their own right. You can see the objects, properties, and methods that you will use
to create the Table Web server control in Table 5.6.

Table 5.6. Using a Standard Method of Creating Objects Within Objects to Construct a Table Web
Server Control
Object

Property Description

TableCell Controls Controls are added to an individual TableCell object using the Add method of the
Controls collection. In this example, a LiteralControl object is used to display
information.

TableRow Cells

The Add method of the TableRow.Cells collection adds a new cell to the
TableRow.

Table

The Add method of the Table.Rows adds the TableRow object to the collection of
rows for the Table control.

Rows

You will see these objects and methods used in step 4.

Note
One of the issues with using the Table control is that it does not
persist in trips to the server and back. Therefore, you need to
reconstruct the control in the page Load event, checking with the
IsPostBack property. If you are tracking a lot of changes with the
Table control, this is another good reason to use one of the other
controls listed at the beginning of this technique.

Steps
Open and run the Visual Basic .NETChapter 5 solution. From the main page, click on the hyperlink with the
caption How-To 5.4: Display Data Using the Table Control. When the Web Form loads, you will see a
DropDown control displaying the list of categories. Below the DropDown control, you will see a Table control
with the products for the selected category (see Figure 5.4).

1. Create a Web Form. Then place the controls listed in Table 5.7 and seen in Figure 5.4 with the
following properties set.

Table 5.7. Property Settings for Label, DropDown, and Table Controls
Object

Property

Setting

Label

Text

Categories:

DropDown

ddCategories
ID

AutoPostBack
Table

True
tblProducts

ID

GridLines
HyperLink

Both
hplReturnToMain

ID

NavigateURL

wfrmMain.aspx

2. Add the code in Listing 5.10 to the Load event of the page. If the page is first being loaded, then the
dtCategories is filled and bound to the ddCategories dropdown. Last, the LoadProducts routine is
called, which is described in the next step.
Listing 5.10 wfrmHowTo5_4.aspx.vb: Loading the Categories DropDown and Product Table
Controls

Private Sub Page_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load

'Put user code to initialize the page here


If Not Me.IsPostBack Then
Dim dtCategories As New DataTable()
Dim odaCategories As _
New OleDb.OleDbDataAdapter( _
"Select CategoryID, CategoryName From Categories",
BuildCnnStr("(local)", "Northwind"))
'-- Fill the data table, and bind it to the dropdown.
odaCategories.Fill(dtCategories)
ddCategories.DataValueField = "CategoryID"
ddCategories.DataTextField = "CategoryName"
ddCategories.DataSource = dtCategories
ddCategories.DataBind()
LoadProducts()
End If
End Sub

3. In the page's class module, create the LoadProducts routine shown in Listing 5.11. After creating a
DataTable object called dtProducts and filling it with a DataAdapter object, the number of columns in
the dtProducts is stored in intNumCols.
A TableRow object is then created, which will be used for the heading row of the table. It displays the
column heads. Then, for each of the columns, a TableCell object called tcHead is created. A
LiteralControl is added to it, which is derived from the ColumnName property of the data table for each
column. Each TableCell object is then added to the TableRow object. After all the columns have
been added, the TableRow object called trHead is added to the tblProducts Table control.
After the table headings have been created, the same commands are created for each row in the
DataTable object.
Listing 5.11 wfrmHowTo5_4.aspx.vb: Loading the Categories DropDown and Product Table
Controls

Private Sub LoadProducts()


Dim
Dim
Dim
Dim
Dim

dtProducts As New DataTable()


drCurr As DataRow
intCurrRow As Integer
intCurrCell As Integer
intNumCols As Integer

Dim odaProducts As New OleDb.OleDbDataAdapter( _


"Select * From Products Where CategoryID = "
& ddCategories.SelectedItem.Value, _
BuildCnnStr("(local)", "Northwind"))
odaProducts.Fill(dtProducts)

intNumCols = dtProducts.Columns.Count
'-- Create the headings for the displayed table
Dim trHead As New TableRow()
For intCurrCell = 0 To intNumCols - 1
Dim tcHead As New TableCell()
tcHead.Controls.Add(New LiteralControl(dtProducts. _
Columns(intCurrCell).ColumnName))
trHead.Cells.Add(tcHead)
Next
tblProducts.Rows.Add(trHead)
'-- Add the rows and cells
For intCurrRow = 0 To dtProducts.Rows.Count - 1
drCurr = dtProducts.Rows(intCurrRow)
Dim trNew As New TableRow()
For intCurrCell = 0 To intNumCols - 1
Dim tcNew As New TableCell()
tcNew.Controls.Add(New LiteralControl(drCurr.Item(intCurrCell)))
trNew.Cells.Add(tcNew)
Next intCurrCell
tblProducts.Rows.Add(trNew)
Next intCurrRow
End Sub

4. Add the code in Listing 5.12 to the SelectedIndexChanged event of ddCategories.


Listing 5.12 wfrmHowTo5_4.aspx.vb: Loading the Categories DropDown and Product Table
Control

Private Sub ddCategories_SelectedIndexChanged(ByVal sender As Object,


ByVal e As System.EventArgs) _
Handles ddCategories.SelectedIndexChanged
LoadProducts()
End Sub

Figure 5.4. Using the Table control to display tables takes a lot of work.

Comments
You can see from this example that using the Table Web server control takes a bit of work, and if you have a
lot of data to display, it could take quite a while to build. Also, remember that you can't edit the data that is
built into the control.
If you have just a small amount of data to display, the Table Web server control could work out nicely.

[ Team LiB ]

[ Team LiB ]

5.5 Display Data Using the Repeater Control


I have heard that the Repeater control is a great control for displaying a read-only list of values, including
hyperlinks. How do I populate a Repeater control and take advantage of templates to display the data the
way I want it to look?

Technique
The Repeater control allows you to list data in various formats, such as bulleted or numbered. It relies on the
use of templates to display information in a list format.
This How-To shows you how to use the Repeater control not only to display a list, but also to use a couple of
different controlsa Button and HyperLinkto display another list using the Repeater control. The second
list will be displayed using the button on the same page as the first list. The hyperlink will take you to
another page to display the second list.
A main tool in the creation of the Repeater control is the use of templates.
Use of Templates
Templates are used within HTML and allow you to include controls in your ASP.NET Web server controls.
Within a template, you can specify various details about the area for which you are creating a template.
Following is a list of the templates:

HeaderTemplate
ItemTemplate
FooterTemplate
AlternatingItemTemplate
SeparatorTemplate
You can get an idea of what each of the templates is used for by its name. Here is an example of the
HeaderTemplate , used in this How-To:

<HeaderTemplate>
<font face="Arial Black" size="2">List of Regions </font>
<br>
</HeaderTemplate>

These lines are literally used as a template for how you want the section to be laid out, as well as what data
to display. For the ItemTemplate in this How-To, two controls are displayed: a button and a hyperlink,
shown by this snippet of the HTML:

<asp:Button runat="server"

Text= '<%# DataBinder.Eval(Container.DataItem, "RegionID") %> ' />


<asp:HyperLink Runat="server"
Text='<%# DataBinder.Eval(Container, "DataItem.RegionDescription") %>'
NavigateURL='<%# DataBinder.Eval(Container, "DataItem.RegionURL") %>'
ID="HyperLink1"/>

As the name implies, the DataBinder supplies data from the data source that is specified for the Repeater
object. You will see how to bind the Repeater object in step 3.

Creating URLs On-the-Fly


In the NavigateURL of the hyperlink that was created, you can see the column DataItem.RegionURL being
used. This is not a column that is found in the Regions table in Northwind. This column is created within the
SQL statement that is supplied to a data adapter, by the following line of code:

odaRegions = New OleDb.OleDbDataAdapter("Select RegionID, _


'wfrmHowto5_5b.aspx?RegID=' +
Cast(RegionID as Char(2)) As RegionURL, RegionDescription From Region",
BuildCnnStr("(local)", "Northwind"))

The RegionURL consists of a Web Form name and the statement ?RegID=Cast(RegionID as Char(2)) ,
which ASP.NET loads into the new page and passes to the RegionID of the page, letting the code within the
page read the RegionID using the Request object, as displayed here:

odaTer = New _
OleDb.OleDbDataAdapter(_
"Select TerritoryDescription From Territories Where RegionID = " &
Request.Item("RegID"), _
BuildCnnStr("(local)", "Northwind"))

You will see both the creation and utilization of the URL in the steps that follow.

Programming Repeater Events by Using ItemCommand


When you're using buttons, you can program the response for when the buttons are pressed by using the
ItemCommand event. This event is raised for all the controls that are used in the Repeater. You will see an
example of this in step 5.

Steps
Open and run the Visual Basic .NETChapter 5 solution. From the main page, click on the hyperlink with the
caption How-To 5.4: Display Data Using the Repeater Control. You will then see a page open displaying a list
of the regions (see Figure 5.5 ).
Figure 5.5. This list includes both a button, displaying the RegionID, and a hyperlink, displaying
the region description.

If you click one of the buttons displaying the Region ID, then another list is displayed using the Repeater
control. This list is displayed below the regions and contains the territories for the region clicked (see Figure
5.6 ).
Figure 5.6. Another Repeater control is utilized for this list of territories.

When you click on the hyperlinks in the list, another page displays, with yet another Repeater control used to
display the territories for the region chosen (see Figure 5.7 ).

1. Create a Web Form. Then place the controls listed in Table 5.8 with the following properties set.

Repeater

ID

repRegions
Repeater

ID

repTeritories
HyperLink

ID

hplReturnToMain

NavigateURL
wfrmMain.aspx

Table 5.8. Property Settings Repeater and HyperLink Controls


Object

Property

Setting

2. Switch to the HTML tab in the designer. By adding the repeaters and naming the repeaters in step 1, you
will see a line of code that looks like this:

<asp:Repeater

id="repRegions"runat="server"></asp:Repeater>

Replace this line of code with the code displayed in Listing 5.13 . This code lays out the templates
that were described in the "Technique " section, as well as the button and hyperlink described. Note
that the FooterTemplate , listed here, is not really used for anything. It was included so that you
could see how to use it. It can be excluded.
Listing 5.13 wfrmHowTo5_5a.aspx: HTML for the repRegions Repeater

<asp:Repeater id="repRegions" runat="server">


<HeaderTemplate>
<font face="Arial Black" size="2">List of Regions </font>

<br>
</HeaderTemplate>
<ItemTemplate>
<asp:Button runat="server"
Text= '<%# DataBinder.Eval(Container.DataItem, "RegionID") %> ' />
<asp:HyperLink Runat="server"
Text='<%# DataBinder.Eval(Container, "DataItem.RegionDescription") %>'
NavigateURL='<%# DataBinder.Eval(Container, "DataItem.RegionURL") %>'
ID="HyperLink1"/>
<br>
</ItemTemplate>
<FooterTemplate>
</FooterTemplate>
</asp:Repeater>

3. Next, replace the HTML code inserted for the repTerritories repeater with the code in Listing 5.14 . After
the HeaderTemplate is specified, a Label control displays the TerritoryDescription column.
Listing 5.14 wfrmHowTo5_5a.aspx: HTML for the repTerritories Repeater

<asp:Repeater id="repTerritories" runat="server">


<HeaderTemplate>
<br>
<font face="Arial Black" size="2">List of Territories </font>
<br>
</HeaderTemplate>
<ItemTemplate>
<asp:Label runat="server"
Text= '<%# DataBinder.Eval(Container.DataItem, _
"TerritoryDescription") %> ' />
<br>
</ItemTemplate>
</asp:Repeater>

4. Add the code in Listing 5.15 to the Load event of the page. If the page is first being loaded, then the
odaRegions DataAdapter fills the data table called dtRegions . dtRegions is set as the data source for
repRegions , and DataBind method is called, binding the Repeater to the data table.
Listing 5.15 wfrmHowTo5_5a.aspx.vb: Loading the repRegions Repeater Control

Private Sub Page_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
If Not Me.IsPostBack Then
Dim odaRegions As OleDb.OleDbDataAdapter
Dim dtRegions As DataTable

odaRegions = New OleDb.OleDbDataAdapter("Select RegionID, _


'wfrmHowto5_5b.aspx?RegID=' +
Cast(RegionID as Char(2)) As RegionURL, _
RegionDescription From Region",
BuildCnnStr("(local)", "Northwind"))
dtRegions = New DataTable()
odaRegions.Fill(dtRegions)
repRegions.DataSource = dtRegions
repRegions.DataBind()
End If
End Sub

5. Add the code in Listing 5.16 to the ItemCommand event of repRegions . This code takes the Text
property of the button, which is the RegionID , and uses it in the SQL string to supply the odaTer data
adapter. Next, odaTer fills dtTer , which is then used as the data source for repTerritories .
Listing 5.16 wfrmHowTo5_5a.aspx.vb: Loading the Categories DropDown and Product Table
Control

Private Sub repRegions_ItemCommand(ByVal source As Object, _


ByVal e As System.Web.UI.WebControls.RepeaterCommandEventArgs)
Handles repRegions.ItemCommand
Dim odaTer As OleDb.OleDbDataAdapter
Dim dtTer As DataTable
odaTer = New _
OleDb.OleDbDataAdapter(_
"Select TerritoryDescription From Territories Where RegionID = " &
CType(e.CommandSource, Button).Text(), _
BuildCnnStr("(local)", "Northwind"))
dtTer = New DataTable()
odaTer.Fill(dtTer)
repTerritories.DataSource = dtTer
repTerritories.DataBind()
End Sub

6. Create another Web Form. Then place a Repeater control on it and set the ID of the Repeater to be
repTerritories.

Note

When you are saving and naming the Web Form created in this step, make sure
you name it the same as that used in the code in step 4. Remember that it was
the Web Form used in the RegionURL.

7. Switch to the HTML tab in the designer. Replace the line of code that has been created for the
repTerritories Repeater control with the code shown in Listing 5.17 .
Listing 5.17 wfrmHowTo5_5b.aspx : HTML for the repTerritories Repeater on the Second Web
Form

<asp:Repeater id="repTerritories" runat="server">


<HeaderTemplate>
<br>
<font face="Arial Black" size="2">List of Terriories </font>
<br>
</HeaderTemplate>
<ItemTemplate>
<asp:Label runat="server"
Text= '<%# DataBinder.Eval(Container.DataItem, _
"TerritoryDescription") %> '
ID="Label1" NAME="Label1"/>
<br>
</ItemTemplate>
</asp:Repeater>

8. Add the code in Listing 5.18 to the Load event of the page. This code reads the RegionID first from the
first Web Form using the Request object. Next, it is used to fill the dtTer data table, which is then
assigned as the data source for repTerritories.
Listing 5.18 wfrmHowTo5_5b.aspx.vb: Loading the repTerritories Repeater Control on the
Second Web Form

Private Sub Page_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
Dim odaTer As OleDb.OleDbDataAdapter
Dim dtTer As DataTable
odaTer = New
OleDb.OleDbDataAdapter(_
"Select TerritoryDescription From Territories Where RegionID = " &
Request.Item("RegID"), _
BuildCnnStr("(local)", "Northwind"))
dtTer = New DataTable()
odaTer.Fill(dtTer)

repTerritories.DataSource = dtTer
repTerritories.DataBind()
End Sub

Figure 5.7. A third Repeater control is utilized for this list of territories on a new page.

[ Team LiB ]

[ Team LiB ]

Comments
After you have used DataRepeater a couple of times, it is really quite simple to use. By playing with the
templates, you can display fairly attractive lists that are nice and dynamic.

[ Team LiB ]

[ Team LiB ]

5.6 Display, Sort, and Page Data in the DataGrid Control


The Table controls and DataRepeater are fine when I have small sets of data, but the display just keeps
repeating and I have to write a bunch of code to change the sort order of the data. How do I create a tablelike display that will show a set number of rows at a time and let me sort the data?

Technique
The DataGrid control is by far the most flexible and powerful of the controls used in ASP.NET for displaying
data on your Web page. It gives you the ability not only to list data, but also to page through it and sort it
nicely with headers, and even to edit data. This last feature will be saved for the next How-To.
To perform the other tasks, you will use a combination of setting properties at design time and programming
some events. In particular for the DataGrid control, you will add code to events raised by sorting and
paging.
This How-To will use the DataGrid bound at design time. You will also see a simple example of using the
DataView object for sorting your data.

Steps
Open and run the Visual Basic .NET Chapter 5 solution. From the main page, click on the hyperlink with the
caption How-To 5.6: Display, Sort, and Page Data in the DataGrid Control. You will then see all the territories
loaded into a data grid. You can click the column headings, displayed in blue, to sort the columns, and you
can click the arrows at the bottom to page through the data. You can see the Web Form in Design mode in
Figure 5.8.

1. Create a Web Form. Then place the controls in Table 5.9 and Figure 5.8 with the following properties
set.

Table 5.9. Property Settings for the Controls Used in This How-To

Object

Property

OleDbDataAdapter

Setting

odaTerritory
ID

SelectCommand SELECT Territories.TerritoryID,


Territories.TerritoryDescription,
Region.RegionDescription,
Region.RegionID FROM Territories INNER
JOIN Region ON Territories.RegionID =
Region.RegionID
DataSet

dsTerritory
ID

DataView

dvTerritory
ID

Table
DataGrid

dsTerritory.Territories
dgTerritory

ID

HyperLink

hplReturnToMain
ID

NavigateURL

wfrmMain.aspx

2. Right-click on the DataGrid control and choose Property Builder. You will then see the General tab of
the DataGrid Property Builder. Set the properties as displayed in Figure 5.9.
Figure 5.9. Setting the General properties of the DataGrid control.

3.

3. Click on the Columns tab. You can now add the columns TerritoryID, TerritoryDescription, and
RegionDescription by clicking on the field in the available columns and then clicking the Add button for
each one. You can then set the Header text for each to be TerritoryID, Territory, and Region
respectively, by clicking on the column in the Selected Column list and changing the Header text
property. When you are finished and you have the Region highlighted, it should look like Figure 5.10.
Figure 5.10. Setting the columns for the DataGrid control is easy at design time.

Note
Be sure to uncheck the Create Columns Automatically at Run Time
check box; otherwise, the columns will end up showing up
twiceonce from you specifying them here at design time, and
once when the code generates them.

4. Click on the Paging tab. Click on the Allow Paging check box, and type 10 for the Page Size (see Figure
5.11.) You can then close the Property Builder dialog box by clicking OK.
Figure 5.11. Setting the Paging properties at design time for the DataGrid control.

5. Create the BindTheGrid routine in Listing 5.19. Note that you are now binding to the DataView,
rather than to a dataset.
Listing 5.19 wfrmHowTo5_6.aspx.vb: Binding the Data Grid to the DataView Control

Sub BindTheGrid()
odaTerritory.Fill(dsTerritory)
dgTerritory.DataSource = dvTerritory
dgTerritory.DataBind()
End Sub

6. Add the code in Listing 5.20 to the Load event of the page.
Listing 5.20 wfrmHowTo5_6.aspx.vb: Loading the Page

Private Sub Page_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
If Not Me.IsPostBack Then

BindTheGrid()
End If
End Sub

7. Add the code in Listing 5.21 to the PageIndexChanged event of dgTerritory. This routine takes the
NewPageIndex and assigns it to the data grid's CurrentPageIndex. Next, if MySort exists in the
ViewState object, it is assigned to the DataView control. MySort is stored in the next step.
Listing 5.21 wfrmHowTo5_6.aspx.vb: Paging the Data Grid

Private Sub dgTerritory_PageIndexChanged(ByVal source As Object,


ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs)
Handles dgTerritory.PageIndexChanged
'-- Set the current page in the data grid
Me.dgTerritory.CurrentPageIndex = e.NewPageIndex
If Not (ViewState("MySort") = Nothing) Then
dvTerritory.Sort = ViewState("MySort")
End If
BindTheGrid()
End Sub

8. Add the code in Listing 5.22 to the SortCommand event of dgTerritory. When a column heading is
clicked and sorting is turned on, this event is raised. This routine assigns the SortExpression to the
Sort property of the dgTerritory. The SortExpression property is then stored in the ViewState object
for round trips to the server, and then it is used in the code listed in step 6.
Listing 5.22 wfrmHowTo5_6.aspx.vb: Sorting the Data Grid

Private Sub dgTerritory_SortCommand(ByVal source As Object,


ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs)
Handles dgTerritory.SortCommand
dvTerritory.Sort = e.SortExpression.ToString
ViewState("MySort") = e.SortExpression.ToString
BindTheGrid()
End Sub

Figure 5.8. The DataGrid control is a great way to manipulate data.

Comments
The DataView is a great control for seeing different views of your data by using a single dataset.
As with the DataRepeater, working with some of the basic features in the data grid is pretty easy. It's when
you have to start really manipulating data that it gets more difficult, as you will see in the next How-To.

[ Team LiB ]

[ Team LiB ]

5.7 Add, Edit, and Delete Data Using the DataGrid Control
The Table controls and DataRepeater are fine when I have small sets of data, but the display just keeps
going on and on, and I have to write a bunch of code to change the sort order of the data. How do I create a
table-like display that will show a set number of rows at a time and let me sort the data?

Technique
One of the nice things about the DataGrid control that makes it so much more powerful than the other list
controls is its ability to add, edit, and delete directly within the control. This How-To shows you how to create
columns to manage your data using the DataGrid control.

Adding Buttons to the DataGrid Control


You will use the DataGrid control with more code this time so that you can work with data more. You will also
be adding a couple of buttons to the DataGrid display to allow you to edit, delete, and update data.
You will add buttons to the data grid by right-clicking on the control, choosing Property Builder, and then
choosing the Columns tab. You can then select from the list of button types (see Figure 5.12).
Figure 5.12. The buttons listed in Selected Columns are being used for this example.

Note
Be sure to leave the Create Columns Automatically at Run Time check
box checked. Unless you specify other columns or have the data
loaded at run-time, you will end up with just the buttons, which would
be pretty boring.

Using Events with Buttons on the Data Grid


After you have selected to include buttons in the data grid, you not only have to add the code to the events
for the specific buttons, but you also have to make sure that ASP.NET knows the events to use. You can do
this in a couple of ways.
One way is to set the AutoEventWireUp attribute of the page to True. You can see this attribute in the first
line of the Web page. By default, the attribute is set to False.
After you add the buttons to the data grid, you will see the buttons in the DataGrid control.

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="wfrmHowTo5_7.aspx.vb"


Inherits="VB.NET__Chapter_5.wfrmHowTo5_7"%>

However, a couple of disadvantages result from setting the AutoEventWire:

You have to use the required names for your events.


Events sometimes end up being called twice on the form.
Microsoft recommends not setting the AutoEventWireUp to True.
The other alternative is to add the events and HTML tags yourself. You will see the HTML code added in step
3.

Loading a Schema into a Data Table


Another option that is introduced in this How-To is the fill of a data table by using the FillSchema method
of the data adapter. This means that the data table will be smart and know what the constraints and
properties of the columns are before you try to save the data back to the server. The big benefit with using
the FillSchema method with the DataGrid control is that it will be intelligent, and it won't let a user try to
edit an auto increment column, such as an identity column. It will make such a column disabled.
When you update the columns from the data grid back into the data table, you can also check to see if the
column's AutoIncrement property is True. Unless you use the FillSchema method, the AutoIncrement
property will come back False, even if the actual column in the table on the server has it set to True.

Additional Objects, Properties, and Methods Used to Handle Data


In addition to the FillSchema method of the data adapter, you will also use the Update method, which will
call the Update, Delete, or Append statement, depending on the task that is being performed.
The CommandBuilder object will generate your SQL statements used to send modifications back to the
server.
In addition to the methods mentioned for the data adapter, you will use some properties and methods of the
data table to add, edit, and delete data. You can see a list of those objects, properties, and methods in Table
5.10.

Table 5.10. Objects, Properties, and Methods of the DataTable Object


Object

Property/Method Description

DataTable.Rows Delete

Deletes a row from the DataTable object (the data is not deleted
from the server at this point).

DataTable.Rows Count

Returns the number of rows that are currently in the DataTable


object.

DataTable

Creates a new DataRow object.

NewRow

DataTable.Rows Add

Adds the DataRow object to the DataTable object.

DataTable

BeginLoadData

Turns off the schema checking that occurs when you're adding the
new row to the DataTable object.

DataTable

AcceptChanges

Resets the DataTable status, including ending edits on DataRow


objects. The DataRowState also changes. All added and modified
rows become unchanged, and deleted rows are removed.

DataTable

RejectChanges

Rejects the changes made since the last AcceptChanges was


called. Lets you roll back changes if errors occur.

Steps
Open and run the Visual Basic .NETChapter 5 solution. From the main page, click on the hyperlink with the
caption How-To 5.7: Add, Edit, and Delete Data Using the DataGrid Control. You will then see all the regions
loaded into a data grid. You can click on the Edit button to edit data, and so on. You can see the form
created in Design view in Figure 5.13.

1. Create a Web Form. Then place the controls in Table 5.11 and Figure 5.13 with the following properties
set. Don't worry about the Edit and Delete buttons displayed until the next step.

Table 5.11. Property Settings for the Controls Used in This How-To

Object

Property

DataGrid

Setting

dgRegion
ID

Button

btnAdd
ID

Label

lblDispExcp
ID

ForeColor
HyperLink

#C00000
hplReturnToMain

ID

NavigateURL

wfrmMain.aspx

2. Right-click on dgRegion, and choose Property Builder from the pop-up menu. Click on the Columns tab.
You are going to add Edit, Update, Cancel (one button choice), and Delete buttons, as displayed in
Figure 5.12. Change the ButtonType of each button to PushButton. After you have selected the
buttons, click OK.
3. Now it's time to add the HTML code to connect the buttons to some code you will add in the following
steps. In Listing 5.23, you can see the final HTML for the DataGrid control. The lines of code that you
will need to add are the ones for OnUpdateCommand, OnCancelCommand, OnEditCommand, and
OnDeleteCommand. By entering these lines as they are listed and creating the events as named, your
code will work for the buttons in the DataGrid control. You will then add code behind in the following
steps that will match these commands.
Listing 5.23 wfrmHowTo5_7.aspx: Wiring Up Events for the DataGrid Control

<asp:datagrid id="dgRegion" runat="server" Width="254px" Height="111px"


OnUpdateCommand="dgRegion_UpdateCommand"
OnCancelCommand="dgRegion_CancelCommand"
OnEditCommand="dgRegion_EditCommand"
OnDeleteCommand="dgRegion_DeleteCommand"
>
<Columns>
<asp:EditCommandColumn ButtonType="PushButton"
UpdateText="Update" CancelText="Cancel" EditText="Edit">
</asp:EditCommandColumn>
<asp:ButtonColumn Text="Delete" ButtonType="PushButton"
CommandName="Delete">
</asp:ButtonColumn>
</Columns>
</asp:datagrid>

4. Now it's time to add some Visual Basic code. In the class module behind the Web Form, add the
following line of code just after the region that says Web Form Designer Generated Code. mdtRegion
will be used throughout the form for managing the data and synchronizing with the data grid.
mdtRegion will also be saved to the Session object (called RegionDT) for round trips to the server
and back. You will see this in the next step.

Dim mdtRegion As New DataTable()

5.

5. Add the code in Listing 5.24 to the Load event of the Page. The task that occurs is checking for the
existence of RegionDT in the Session object. If it exists, then this is a round trip, and you don't need
to reload the data from the server. If it doesn't exist, then the Region table Schema is supplied using
the FillSchema method, and then the DataTable object is filled. Next, the RegionDT and IsAdding
Session variables are saved. The IsAdding session variable is used to track whether you are adding a
record. Last, the data is bound to the DataGrid object using the BindTheGrid routine, which follows the
Page_Load event in this listing.
Listing 5.24 wfrmHowTo5_7.aspx.vb: Initially Loading the DataGrid Object and Tracking
Session Variables

Private Sub Page_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
If (Session("RegionDT") Is Nothing) Then
Dim odaRegion As OleDb.OleDbDataAdapter
odaRegion = New _
OleDb.OleDbDataAdapter("Select RegionID as [Region ID], " &
"RegionDescription as Region From Region", _
BuildCnnStr("(local)", "Northwind"))
odaRegion.FillSchema(mdtRegion, SchemaType.Source)
odaRegion.Fill(mdtRegion)
Session("RegionDT") = mdtRegion
Session("IsAdding") = False
BindTheGrid()
Else
mdtRegion = CType(Session("RegionDT"), DataTable)
End If
End Sub
Sub BindTheGrid()
dgRegion.DataSource = mdtRegion
dgRegion.DataBind()
End Sub

Tip

To figure out when the page is going back to the server, put a
break point in the Page_Load event code. Then you can see the
code break whenever you go back to the server.
If you note which routine you were in before you went back to the
server, you can then place code in it to save any data you need to.

6. Create the dgRegion_EditCommand routine as shown in Listing 5.25. This is one of the events
specified in step 3. This code sets the EditItemIndex of the DataGrid object to the selected item
and then binds the data.
Listing 5.25 wfrmHowTo5_7.aspx.vb: Setting the Data Grid to Edit Mode

Sub dgRegion_EditCommand(ByVal sender As Object, _


ByVal e As DataGridCommandEventArgs)
'-- Clear the error display
lblDispExcp.Text = ""
'-- Turn on the editing by pointing the EditItemIndex
'
to the ItemIndex
dgRegion.EditItemIndex = e.Item.ItemIndex
BindTheGrid()
End Sub

7. Add the code in Listing 5.26 to the Click event of btnAdd. The first task is to invoke the
BeginLoadData method for mdtLookupData. This turns off constraint checking while you're loading
data. You should turn this off so that it doesn't check for required fields until you actually edit the
record, which is caused by the line of code setting the EditItemIndex property of the DataGrid
object. The Session variables are then updated, with the IsAdding item set to True. Lastly, the
EditItemIndex of dgLookupData is then set, and the DataGrid bound to the data table using the
BindTheGrid routine.
Listing 5.26 wfrmHowTo5_7.aspx.vb: Adding a New Record to the Data Table

Private Sub btnAdd_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnAdd.Click
Dim intColCnt As Integer
Dim drCurr As DataRow
lblDispExcp.Text = ""
mdtRegion.BeginLoadData()
'-- Add the row to the data table via the data row

drCurr = mdtRegion.NewRow
mdtRegion.Rows.Add(drCurr)
'-- Set the Adding flag.
Session("MyLookupData") = mdtRegion
Session("IsAdding") = True
'-- Set the item index based on the rows on this page only.
dgRegion.EditItemIndex = mdtRegion.Rows.Count - 1
BindTheGrid()

End Sub

8. Create the dgRegion_DeleteCommand routine as shown in Listing 5.27. This is one of the events
specified in step 3. This code is a lot like the code in the previous step, when the record was being
added. The big difference in this step's code listing is that the deletion is posted back to the server,
and in step 7, it wasn't. The deletion wasn't posted in that step because the server never knew
anything about the record; it had only been added to the data table and not sent back to the server.
You can see the data being updated back to the server for the add and edit in the next step.
One other item to note is the RejectChanges method called in the Catch of the exception handling
code. This way, if an error occurs, then the change is undone, the message is noted, and life goes on.
The rest of this code follows pretty closely what was done in the previous step.
Listing 5.27 wfrmHowTo5_7.aspx.vb: Deleting a Row in the Data Grid

Sub dgRegion_DeleteCommand(ByVal sender As Object, _


ByVal e As DataGridCommandEventArgs)
Dim intColCnt As Integer
Dim cnn As New OleDb.OleDbConnection(BuildCnnStr("(local)",
"Northwind"))
'-- Create the command builder to update (post) the data
'
in the data grid back to the server.
Dim odaRegion As OleDb.OleDbDataAdapter
Try
'-- Take the txtSQLString text and create the data table,
'
and then set the data source of the data grid.
odaRegion = New _
OleDb.OleDbDataAdapter("Select RegionID as [Region ID], " &
"RegionDescription as Region From Region", cnn)
Dim ocbRegion As OleDb.OleDbCommandBuilder = _
New OleDb.OleDbCommandBuilder(odaRegion)
'-- Delete the row from the data table

mdtRegion.Rows(e.Item.ItemIndex).Delete()
'-- Commands necessary to actually post back to server.
cnn.Open()
odaRegion.Update(mdtRegion)
mdtRegion.AcceptChanges()
cnn.Close()
Session("MyLookupData") = mdtRegion
Session("IsAdding") = False
'-- Just in case they were editing and press Delete, Clear.
dgRegion.EditItemIndex = -1
Catch excp As Exception
lblDispExcp.Text = excp.Message
mdtRegion.RejectChanges()
End Try
BindTheGrid()
End Sub

9. Create the dgRegion_UpdateCommand routine as shown in Listing 5.28. This is one of the events
specified in step 3. This routine starts off by declaring DataAdapter and CommandBuilder objects to
update your data back to the server. Before the actual update, however, the current row that is being
edited in the data grid is assigned to a DataRow object. Then each of the items in the row is saved
from the data grid cells to the column in the data row.
Thanks to using the FillSchema method when you're filling the data table, the AutoIncrement
property will reflect whether a column is an Identity column. If the FillSchema method is not used,
you have to handle the exception that occurs when you try to write the value to the column.
When you're writing the cells into the columns of the data row, the Trim function is used. Because of
using the FillSchema method, the values are padded out as SQL Server columns generally are.
The rest of the code runs similarly to step 8 in that the changes are accepted, written back to the
server, and so forth.
Listing 5.28 wfrmHowTo5_7.aspx.vb: Updating Changes Back to the Server

Sub dgRegion_UpdateCommand(ByVal sender As Object,


ByVal e As DataGridCommandEventArgs)
Dim
Dim
Dim
Dim
Dim
Dim

intColCnt As Integer
intColCurr As Integer
drCurr As DataRow
cnn As New OleDb.OleDbConnection(BuildCnnStr("(local)", "Northwind"))
blnAdding As Boolean
strCurrValue As String

'-- Create the command builder to update (post)


'
the data in the data grid back to the server.
lblDispExcp.Text = ""
Dim odaTableData As OleDb.OleDbDataAdapter
Try
'-- Take the txtSQLString text and create data table; then set the
'
data source of the data grid.
Dim odaRegion As New _
OleDb.OleDbDataAdapter("Select RegionID as [Region ID], " &
"RegionDescription as Region From Region", cnn)
Dim ocbTableData As OleDb.OleDbCommandBuilder = _
New OleDb.OleDbCommandBuilder(odaRegion)
drCurr = mdtRegion.Rows(dgRegion.EditItemIndex)
'-- Update the fields in the rows
intColCnt = e.Item.Cells.Count
For intColCurr = 2 To intColCnt - 1
If mdtRegion.Columns(intColCurr - 2).AutoIncrement = False Then
drCurr.Item(intColCurr - 2) = _
Trim(CType(e.Item.Cells(intColCurr).Controls(0), _
TextBox).Text)
End If
Next
'-- Commands necessary to actually post back to server.
cnn.Open()
odaRegion.Update(mdtRegion)
mdtRegion.AcceptChanges()
cnn.Close()
Session("RegionDT") = mdtRegion
Session("IsAdding") = False
dgRegion.EditItemIndex = -1
BindTheGrid()
Catch excp As Exception
lblDispExcp.Text = excp.Message

End Try
End Sub

10.

10. Create the dgRegion_CancelCommand routine as shown in Listing 5.29. This is one of the events
specified in step 3. If you're in the middle of adding an entry, this code uses the EditItemIndex of the
DataGrid object to the selected item.
The value that EditItemIndex returns is used to position the pointer in mdtLookupData so that the
Delete method can be called.
After the code accepts the changes, it resaves the session variables and cleans up the page index for
the DataGrid object by comparing the current page number relative to the pointer of the DataTable
position to the CurrentPageIndex property. Regardless of whether the item is being added or edited,
the code clears EditItemIndex by setting it to 1 and it rebinds the data grid by calling
BindTheData().
Listing 5.29 wfrmHowTo5_7.aspx.vb: Cancelling Edits to the Data Grid

Sub dgRegion_CancelCommand(ByVal sender As Object, _


ByVal e As DataGridCommandEventArgs)
Dim blnAdding As Boolean
'-- If you cancel while you're adding a record, you need to back the
'
row out of the data table and data grid. You don't have to send it
'
to the server because it really was never added to it.
lblDispExcp.Text = ""
If CType(Session("IsAdding"), Boolean) Then
mdtRegion.Rows(dgRegion.EditItemIndex).Delete()
mdtRegion.AcceptChanges()
Session("IsAdding") = False
Session("RegionDT") = mdtRegion
End If
dgRegion.EditItemIndex = -1
BindTheGrid()
End Sub

Figure 5.13. The DataGrid control is a great way to manipulate data.

Comments
Whew! This seems like a lot of work! The good news is that after you have created the code, you can cut and
paste when you're creating new pages that use the same techniques.
Remember that this How-To is a starting part, and it's by no means bullet proof. It is up to you to take this
code to the next level.

[ Team LiB ]

[ Team LiB ]

5.8 Hyperlink from a Row in the Data Grid to a Detail Page


Often, I need to zero in and display data based on a record in the DataGrid control. How do I display detail
information in a separate page from a DataGrid control?

Technique
One of the types of columns that you can use in the data grid is the HyperLink column. This column makes it
fairly easy to link pages based on data. To see how the HyperLink type column is used in this How-To, take a
look at Figure 5.14.
Figure 5.14. No code is required for link pages based on data.

By your specifying the URL Field to be ProductID and URL Format String to be wfrmHowTo5_8b.aspx?
ID={0}, the data grid automatically calls the wfrmHowTo5_8b.aspx and passes the ProductID to the form
when you click on a product.

Steps
Open and run the Visual Basic .NETChapter 5 solution. From the main page, click on the hyperlink with the
caption How-To 5.8: Hyperlink From a Row in the Data Grid to a Detail Page. You then see all the products

loaded into a data grid. Notice that the products are actually hyperlinks (see Figure 5.15).
Figure 5.15. These hyperlinks require no code to call a detail page.

When you click on a product, another page is displayed, with detail information supplied (see Figure 5.16).

1. Create a Web Form. Then place the controls in Table 5.12 and Figure 5.15 with the following properties
set.

Table 5.12. Property Settings for the Controls Used on the First Page of This HowTo

Object

Property

OleDbDataAdapter

Setting

odaProducts
ID

SelectCommand SELECT ProductID, ProductName FROM


Products
DataSet

dsProducts
ID

DataGrid

dgProducts
ID

DataSource

dsProducts

DataKeyField ProductID
DataMember
HyperLink

Products
hplReturnToMain

ID

NavigateURL

wfrmMain.aspx

2. Right-click on the DataGrid control and choose Property Builder. Click on the Columns tab and set the
properties as displayed in Figure 5.14. Be sure to note the name of the form you are calling in the URL
Format String so that you can name it the same in step 4.
3. Add the code in Listing 5.30 to the Load event of the page.
Listing 5.30 wfrmHowTo5_8a.aspx.vb: Filling and Binding the Products to the DataGrid
Object

Private Sub Page_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
odaProducts.Fill(DsProducts)
dgProducts.DataBind()
End Sub

4. Create another Web Form. Then place the controls in Table 5.13 and Figure 5.16 with the following
properties set.

Table 5.13. Property Settings for the Controls Used on the Second Page of This
How-To
Object

Property

Setting

Label

Text

Product Name

Label

Text

Unit Price

TextBox

ID

txtProductName

TextBox

ID

txtUnitPrice

5. Add the code in Listing 5.31 to the Load event of the page. In the SQL select statement created in this

5.
listing, the Request.Item is used to grab the productID that was passed from the first form. The
dtProdIndiv data table is filled, and the individual column information is loaded into the text boxes.
Listing 5.31 wfrmHowTo5_8b.aspx.vb: Loading the Detail Information Based on the
ProductID

Private Sub Page_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
Dim odaProdIndiv As OleDb.OleDbDataAdapter
odaProdIndiv = New _
OleDb.OleDbDataAdapter(_
"Select * From Products Where ProductID = " &
Request.Item("ID"), BuildCnnStr("(local)", "Northwind"))
Dim dtProdIndiv As New DataTable()
odaProdIndiv.Fill(dtProdIndiv)
With dtProdIndiv.Rows(0)
Me.txtProductName.Text = .Item("ProductName")
Me.txtUnitPrice.Text = .Item("UnitPrice")
End With
End Sub

Figure 5.16. You will use the request object in code on this page to retrieve detail data.

Comments
That's it! A good way to expand this example is to add the coding technique learned in the previous How-To
for editing data.
Using data with your Web Forms is not much harder than using Windows Forms. Just remember to stash
some variables for round trips to the server and to bind your data.

[ Team LiB ]

[ Team LiB ]

Chapter 6. Creating Transact-SQL Commands


In this chapter you will

Retrieve unique records using only a select query


Use variables and functions in T-SQL
Use wildcards and ranges of values in a SQL query
Find records in a table without corresponding entries in a related table
Take advantage of using subqueries
Create, modify, and delete tables
Create a new table with data from existing tables
Create and call SQL Server 2000 user-defined functions
Many databases use the SQL database language as the primary way of communicating from an application to
the database and working with data. Transact-SQL (T-SQL) is an enhanced version developed to work with
Microsoft SQL Server. Besides being able to handle standard SQL commands, T-SQL provides additional
statements and commands that allow you to create database objects, maintain data, and use programmatic
control from within stored procedures and user-defined functions.
With T-SQL, you can perform pretty well any of the necessary tasks that you have from day to day. When
you combine that with SQL-DMO, discussed in the next chapter, you can create quite advanced applications
that take care of all your users' needs.

Note
This chapter takes you through creating SQL statements and more
extensive routines by setting the command text of command objects.
Normally, you would create stored procedures to perform these tasks.
This chapter uses the technique it does so that you don't need to add
stored procedures to your copy of the SQL Server Northwind
database.

You can find all of the examples in this chapter in the Solution called Visual Basic .NETChapter 6 on the
Web site.

[ Team LiB ]

[ Team LiB ]

6.1 Retrieve Unique Records Using Only a Select Query


I need to figure out which customers have invoices. The problem is that when I join the Customers with the
Orders tables, I get the customers listed for each order. I only want each customer listed once. How do I
return only those customers who have orders, but only once?

Technique
For this How-To, you will be using the DISTINCT clause on a SQL SELECT statement to limit the data to
unique values. When you include the DISTINCT clause, SQL Server uses the columns that are returned to
determine how to limit the data.
For the opposite affect, you can include the ALL clause, although it is not necessary because this is the
default. You will create two SELECT statements for this task. The first one is for all records:

SELECT Customers.CompanyName FROM Customers INNER JOIN Orders ON Customers .CustomerID =


Orders.CustomerID

To limit the records, use the DISTINCT clause:

SELECT DISTINCT Customers.CompanyName


FROM Customers
INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID

Steps
Open and run the Visual Basic .NETChapter 6 solution. From the main form, click on the button with the
caption How-To 6.1. When the form loads, you will see two option buttons, Show All and Distinct, with Show
All selected. The SELECT statement showing an inner join between customers and order is displayed in a
Label control. You will also see a DataGrid control filled with multiple entries of customers displayed (see
Figure 6.1 ).
Figure 6.1. A common problem with inner joins is retrieving multiple records when you want to
see just one per occurrence.

If you click on the option button labeled Use Distinct , then the DataGrid control will be refreshed, but only
one customer per set of orders will be displayed.

1. Create a Windows Form. Then place the controls listed in Table 6.1 with the following properties set, as
displayed in Figures 6.1 and 6.2 .
Figure 6.2. Using the DISTINCT clause gives you control over displaying unique records.

RadioButton
Name
rbShowAll

Checked
True
RadioButton
Name
rbDistinct
Label
Text
SQL Statement
Label
Name

lblSQLString
Label
Text
Results
DataGrid
Name
dgResults

Table 6.1. Control Property Settings for How-To 6.1


Object

Property

Setting

2. As with some of the other chapters' projects, you need to build a support routine to create the
Connection string. Called BuildCnnStr , the function can been seen in Listing 6.1 . This function takes
a server and database name passed to it and creates a connection string.
Listing 6.1 modGeneralRoutines.vb : Creating a Connection String

Function BuildCnnStr(ByVal strServer As String, _


ByVal strDatabase As String) As String
Dim strTemp As String
strTemp = "Provider=SQLOleDB; Data Source=" & strServer & ";"
strTemp &= "Initial Catalog=" & strDatabase & ";"
strTemp &= "Integrated Security=SSPI"
Return strTemp
End Function

Although you could create a routine that would pass back a Connection object, a more versatile
method would be to pass back a string. The reason for this is that for some objects, you are asked
only for a Connection object, but other objects want just a string.

3. Add the code in Listing 6.2 to the Load event of the form. (Double-click on the form to bring up the
code.)
Listing 6.2 frmHowTo6_1.vb : Loading the Form

Private Sub frmHowTo6_1_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
GenerateData(Me.rbDistinct.Checked)

End Sub

4. Add the code in Listing 6.3 to the class module of the page, creating the GenerateData routine . This
routine creates the necessary SQL SELECT statement based on whether blnUseDistinct is true or
false . If you look back at Listing 6.2 , you will see that this is the value of option button rbDistinct.
After the SQL string is created, it is assigned to lblSQLString to display the string, and then it is used
in a data adapter to fill a dataset. Last, the SQL string is assigned as the data source for the data grid
dgResults .
Listing 6.3 frmHowTo6_1.vb : Building the SQL String for Retrieving the Data

Sub GenerateData(ByVal blnUseDistinct As Boolean)


'-- Build the SQL String
Dim strSQL As String
strSQL = "SELECT "
If blnUseDistinct Then
strSQL += "DISTINCT "
End If
strSQL += "Customers.CompanyName FROM Customers "
strSQL += "INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID"
'-- Store the SQL String
Me.lblSQLString.Text = strSQL
'-- Use the SQL String to build the data adapter and fill the data table.
Dim odaResults As New OleDb.OleDbDataAdapter(Me.lblSQLString.Text, _
BuildCnnStr("(local)", "Northwind"))
Dim dtResults As New DataTable()
odaResults.Fill(dtResults)
'-- Assign the data table to the data grid's DataSource property
Me.dgResults.DataSource = dtResults
End Sub

5. Add the code in Listing 5.4 to the CheckChanged event of the rbDistinct Radio Button control .
Listing 6.4 frmHowTo6_1.vb : Regenerating the Data Based on the Radio Button That Is
Checked

Private Sub rbDistinct_CheckedChanged(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles rbDistinct.CheckedChanged

GenerateData(Me.rbDistinct.Checked)
End Sub

Tip

You might have noticed that besides the loading of the form, I only call the
GenerateData routine when the rbDistinct option button is changed, and not
when the rbShowAll option button is changed. Because only two buttons are
available, you only have to program one of the control's events. If you put it
on both, you will have the routine called twice, which is not a good thing in this
case.

Comments
It is hard to believe that just one word can affect the data that a SQL statement returns. For the most part,
you will want to see all of the records that a SELECT statement returns, but it is nice to have the DISTINCT
clause when you need to limit the data.

[ Team LiB ]

[ Team LiB ]

6.2 Use Variables and Functions in T-SQL


I understand that I can use parameters in my stored procedures, but how do I use variables and functions
within T-SQL?

Technique
It is time to expand the coding you do using T-SQL. In this book so far, you have pretty well been limited to
single-line SELECT statements. Now you will learn how to use variables and built-in functions. To achieve
this, look at the routine that will be created here:

DECLARE @Cust_Id nchar(5), @Order_Date datetime


SET @Cust_Id = 'ANTON'
SET @Order_Date = '11/27/1996'
SELECT OrderID, OrderDate, ShippedDate,
DateDiff(day, @Order_Date, ShippedDate) as Days_To_Ship
FROM Orders
WHERE CustomerID = @Cust_Id and OrderDate = @Order_Date

Declaring Local Variables in T-SQL


You can use variables in T-SQL much like you would in your other coding languages. First, you must declare
them. To declare variables in T-SQL, you will use the DECLARE statement , the ampersand with the variable
name, and the data type. You can see an example of the variable declaration here, where nchar , with the
length and datetime variables, is declared.

DECLARE @Cust_Id nchar(5), @Order_Date datetime

Note

Besides the standard SQL Server data types, such as nchar , int , and
datetime , you can also declare and create a Table datatype. You will see an
example of this in How-To 6.8, found later in this chapter.

After you have declared the variables, you need to initialize them before you can use them.
Initialing Local Variables in T-SQL

To initialize the variables, you will use the SET command, shown in these two lines of code:

SET @Cust_Id = 'ANTON'


SET @Order_Date = '11/27/1996'

By setting the initial values, you are then ready to use the variables within the rest of your procedure, any
way that you need them, again, by using the @varname syntax.
Utilizing Built-In Functions
Within T-SQL, you can also use functions to perform some of the tasks needed, just as you do within Visual
Basic. Not all of the functions are the same, nor are there necessarily as many. For instance, instead of a
Date() function, which is used in Visual Basic, in T-SQL, you use the GetDate() function. Functions also
will not necessarily return the same values or accept the same parameters.
This How-To calls the DateDiff() function . As with Visual Basic's DateDiff() function, this function takes
two dates, and based on the interval requested, it returns the difference between the two. To check out
other functions that are available, you can look up the word function in the Books On-Line for SQL-SQL
Server.

Steps
Open and run the Visual Basic .NETChapter 6 solution. From the main page, click on the button with the
caption How-To 6.2. When the form loads, you will see a SQL statement display in a label, and a DataGrid
displayed below (see Figure 6.3 ).

1. Create a Windows Form. Then place the controls listed in Table 6.2 and seen in Figure 6.3 with the
following properties set.

Label
Text
SQL Statement
Label
Name
lblSQLString
Label
Text
Results
DataGrid
Name
dgResult s

Table 6.2. Control Property Settings for This How-To

Object

Property

Setting

2. Add the code in Listing 6.2 to the Load event of the form. (Double-click on the form to bring up the
code.) Creating the T-SQL routine described in the "Technique " section, this code then assigns the
routine to the Text property of the Label called lblSQLString . It then creates a data adapter using
the string and fills the dtResults DataTable . Last, the code assigns the data adapter as the data
source for the dgResults data grid.
Listing 6.5 frmHowTo6_2.vb : Storing the SQL Statement and Then Executing

Private Sub frmHowTo6_2_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load

'-- Build the SQL String


Dim strSQL As String
strSQL = "DECLARE @Cust_Id nchar(5), @Order_Date datetime " & _
vbCrLf & vbCrLf
strSQL &= "SET @Cust_Id = 'ANTON'" & vbCrLf
strSQL &= "SET @Order_Date = '11/27/1996'" & vbCrLf & vbCrLf
strSQL
strSQL
strSQL
strSQL

&=
&=
&=
&=

"SELECT OrderID, OrderDate, ShippedDate, "


"DateDiff(day, @Order_Date, ShippedDate) as Days_To_Ship "
"FROM Orders" & vbCrLf
"WHERE CustomerID = @Cust_Id and OrderDate = @Order_Date"

'-- Store the SQL String


Me.lblSQLString.Text = strSQL
'-- Use the SQL String to build the data adapter and fill the data table.
Dim odaResults As New OleDb.OleDbDataAdapter(Me.lblSQLString.Text,
BuildCnnStr("(local)", "Northwind"))
Dim dtResults As New DataTable()
Try
odaResults.Fill(dtResults)
Catch excp As Exception
MessageBox.Show(excp.Message)
Exit Sub
End Try
'-- Assign the data table to the data grid's DataSource property
Me.dgResults.DataSource = dtResults
End Sub

Figure 6.3. The Days_To_Ship is derived from using the DateDiff function with the OrderDate and
ShippedDate.

Comments
For the most part, you will use parameters when you are creating your T-SQL routines. However, when
you're creating multiple steps in your routines that are getting more complex, you'll use variables more
often.

[ Team LiB ]

[ Team LiB ]

6.3 Use Wildcards and Ranges of Values in a SQL Query


I need to be able to either search for a range of values, or at least be able to use wild cards with my query.
How do I do this using T-SQL?

Technique
This is one of those fairly simple but necessary How-Tos. You will learn how to use a combination of both wild
cards and a range of values. Here is the T-SQL routine that you will use for this How-To:

SELECT Customers.CompanyName, Orders.OrderID, Orders.OrderDate FROM Orders


INNER JOIN Customers ON Orders.CustomerID = Customers.CustomerID
WHERE Customers.CustomerID LIKE 'A%'
AND Orders.OrderDate BETWEEN '11/01/1996' AND '12/01/1996'

Note

The literal values have been used here, rather than the text box values that will
be used in the How-To.

Using Wild Cards


Fairly similar to the wild cards of the old DOS days, wild cards in T-SQL are fairly straightforward to use. It is
just a matter of knowing which one to use for which task. When using wild cards, you will use the LIKE
operator, as seen in the SQL string for this How-To.
You can see where the LIKE operator is used with A% . The % is used to specify anything after the given
letter. In this case, it's used for anything starting with the letter A . (This operator will, of course, have to
have the OrderDate fall between the dates specified, but we'll talk about this in a moment. You can use
other wild cards as well, such as the following:

% (Percent sign). You use this to specify any given group of characters. If used before a letter or group
of letters, you are then specifying that you want values ending with those letters. For instance, if you
specify %ing, you get skiing, flying, and so on.

_ (Underscore). You use this to specify a single character. For instance, if you type _ake , then you
would get four-letters words, such as lake, bake, and sake.

[] (Square brackets) This is a range or group of characters to compare against. For example, if you
type [B-D]ake , you would get bake and cake. Another way to use it, with a group of letters, would be
to type [BF]ake . In this case, you would get bake and fake, but not the other letter that fall

inbetween.

[^] (Caret). This is not within the given range or group. Opposite of the last entry, if you typed [^BD]ake , you would get those words ending in ake, where the first letter doesn't fall within BD. The
same works for the group of letters as well.
Using BETWEEN
When you need to look at a range of values, whether it be numbers or dates, you use the BETWEEN
operator. The syntax for BETWEEN is as follows:

table.column BETWEEN startingvalue AND endingvalue

This returns all records where the given column falls between the two values, including the two values.
Because the BETWEEN statement mentioned a moment ago was Orders.OrderDate BETWEEN '11/01/1996'
AND '12/01/1996', then those records with the OrderDate falling between 11/1/1996 and 12/1/1996
inclusively will be displayed.

Steps
Open and run the Visual Basic .NETChapter 6 solution. From the main form, click on the button with the
caption How-To 6.3. When the form loads, you will see a form that allows you to specify letter(s) for the
company name to fill the data grid for, along with a range to specify for order dates (see Figure 6.4 ).

1. Create a Windows Form. Then place the controls listed in Table 6.3 with the following properties set, as
displayed in Figure 6.4 .

Label
Text
Customer ID
TextBox
Name
txtCustomerID

Text

A%

Label
Text
Order Date: From
Label

Text

To

TextBox
Name
txtFromDate

Text
11/01/1996
TextBox
Name
txtToDate

Text
12/01/1996
Label
Text
SQL String
Label
Name
lblSQLString
Label
Text
Results
DataGrid
Name
dgResults

Table 6.3. Control Property Settings for This How-To


Object
2.

Property

Setting

2. Add the following code in Listing 6.6 to the Load event of the form. (Double-click on the form to bring
up the code.)
Listing 6.6 frmHowTo6_3.vb : Calling GenerateData When Loading the Form

Private Sub frmHowTo6_3_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
GenerateData()
End Sub

3. In the class module for the form, add the code in Listing 6.7 to create the GenerateData routine. After
creating the SQL statement, this routine assigns it to the Text property of lblSQLString . Then the
string is used in a data adapter that was created to fill the dtResults data table . Last, the data
table is set as the data source for dgResults.
Listing 6.7 frmHowTo6_3.vb : Generating Data Using LIKE and BETWEEN Statements

Sub GenerateData()
'-- Build the SQL String
Dim strSQL As String
strSQL &= "SELECT Customers.CompanyName, " & _
"Orders.OrderID, Orders.OrderDate "
strSQL &= "FROM Orders INNER JOIN Customers "
strSQL &= "ON Orders.CustomerID = Customers.CustomerID" & vbCrLf
strSQL &= "WHERE Customers.CustomerID LIKE '" & _
Me.txtCustomerID.Text & "' AND "
strSQL &= "Orders.OrderDate BETWEEN '" & Me.txtFromDate.Text
strSQL &= "' AND '" & Me.txtToDate.Text & "'"
'-- Store the SQL String
Me.lblSQLString.Text = strSQL
'-- Use the SQL String to build the data adapter and fill the data table.
Dim odaResults As New OleDb.OleDbDataAdapter(Me.lblSQLString.Text,
BuildCnnStr("(local)", "Northwind"))
Dim dtResults As New DataTable()
Try
odaResults.Fill(dtResults)
Catch excp As Exception
MessageBox.Show(excp.Message)
Exit Sub
End Try
'-- Assign the data table to the data grid's DataSource property
Me.dgResults.DataSource = dtResults

End Sub

4. Add the code in Listing 6.8 to the TextChanged events of txtCustomerID, txtFromDate, and txtToDate,
respectively. These routines call GenerateDate when the values change.
Listing 6.8 frmHowTo6_3.vb : Calling the GenerateData Routine When Text Is Updated

Private Sub txtCustomerID_TextChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles txtCustomerID.TextChanged
GenerateData()
End Sub
Private Sub txtFromDate_TextChanged(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles txtFromDate.TextChanged
GenerateData()
End Sub
Private Sub txtToDate_TextChanged(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles txtToDate.TextChanged
GenerateData()
End Sub

Figure 6.4. A common problem with inner joins is retrieving multiple records when you just want
to see one per occurrence.

Comments

By placing your use of wild cards and allowing for ranges of values, you can make your applications and the
querying of data more versatile than ever!

[ Team LiB ]

[ Team LiB ]

6.4 Find Records in a Table Without Corresponding Entries in a Related Table


I have a situation in which I need to find which clients don't have invoices. How do I do this?

Technique
To find out which records (customers) don't have corresponding records (invoices) in a related table, you
have to have a better understanding of the types of joins that can be used between tables during queries.
Before looking at the joins, following is the T-SQL statement that will be used in this How-To:

SELECT Customers.CustomerID, Customers.CompanyName


FROM Customers LEFT OUTER JOIN Orders
ON Customers.CustomerID = Orders.CustomerID WHERE Orders.CustomerID IS NULL

Types of Joins
You can use three types of joins to bring back different information. These join types include inner joins, left
outer joins, and right outer joins.

Inner Join
This join displays records in which at least one record exists in each table for the joined field. This means
that customers are displayed only if they have at least one invoice. If you want to see the CompanyName
and OrderDate, the SELECT statement would be as follows:

SELECT Customers.CompanyName, Orders.OrderDate


FROM Customers INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID

Left Outer Join


You use this join when you want to see all of the records in the first table, but only those records in the
second table that have matching records in the first table. An example of this would be if some customers
didn't have invoices; all the customers would appear, but only those invoices that had customers assigned to
them would be displayed.

Note

Normally, if your database is set up with referential integrity correctly,


as Northwind is, you won't have any invoices without customers
assigned to them.

The example SELECT statement for this will be used in the How-To.
ON Customers.CustomerID = Orders.CustomerID WHERE Orders.CustomerID IS NULL

Remember, because you want only those customers without invoices, you need to check to see where
Orders.CustomerID is NULL.

Note
Although it is not strictly necessary to check whether the
Orders.CustomersID is NULL, make sure to check for a column that
would not be NULL if a record were there. OrderID would be another
good column to check.

Right Outer Join


As you might have guessed by now, the right outer join is the opposite of the left outer join. Instead of
listing all the customers and only those invoices that have customers, the right outer join shows you all
records in the second table (invoices) and only those records from the first table (customers) in which there
is a matching record on the joined column (CustomerID).
One of the ways that the right outer join is used is to find bad data, such as non-relational data that could
result from importing data from other systems. Use a SELECT statement similar to the one used for the left
outer join type:

SELECT Customers.CustomerID, Customers.CompanyName


FROM Customers RIGHT OUTER JOIN Orders
ON Customers.CustomerID = Orders.CustomerID WHERE Customers.CustomerID IS NULL

You will notice that instead of testing for Orders.CustomerID being NULL, you are checking to see if
Customers.CustomerID is NULL.

Steps

Open and run the Visual Basic .NETChapter 6 solution. From the main form, click on the button with the
caption How-To 6.4. When the form loads, you will see the SQL String displayed with the data grid displaying
customers who don't have orders (see Figure 6.5).

1. Create a Windows Form. Then place the controls listed in Table 6.4 with the following properties set, as
displayed in Figure 6.5.

Table 6.4. Control Property Settings for This How-To


Object

Property

Setting

Label

Text

SQL Statement

Label

Name

lblSQLString

Label

Text

Results

DataGrid

Name

dgResults

2. Add the following code in Listing 6.9 to the Load event of the form. (Double-click on the form to bring
up the code.) The only difference in this routine from the previous How-To is the SELECT statement,
which is described in the "Technique" section.
Listing 6.9 frmHowTo6_4.vb: Using the Left Outer Join

Private Sub frmHowTo6_4_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
'-- Build the SQL String
Dim strSQL As String
strSQL
strSQL
strSQL
strSQL

&=
&=
&=
&=

"SELECT Customers.CustomerID, Customers.CompanyName "


"FROM Customers LEFT OUTER JOIN Orders "
"ON Customers.CustomerID = Orders.CustomerID" & vbCrLf
"WHERE Orders.CustomerID IS NULL"

'-- Store the SQL String


Me.lblSQLString.Text = strSQL
'-- Use the SQL String to build the data adapter and fill the data table.
Dim odaResults As New OleDb.OleDbDataAdapter(Me.lblSQLString.Text, _
BuildCnnStr("(local)", "Northwind"))
Dim dtResults As New DataTable()
Try
odaResults.Fill(dtResults)
Catch excp As Exception
MessageBox.Show(excp.Message)
Exit Sub
End Try
'-- Assign the data table to the data grid's DataSource property
Me.dgResults.DataSource = dtResults

End Sub

Figure 6.5. Using an outer join to retrieve records without corresponding records.

Comments
The majority of the time, you will be using the inner join rather than the outer joins. However, sometimes
outer joins will be necessary, so you should experiment with them and get comfortable with them.

[ Team LiB ]

[ Team LiB ]

6.5 Take Advantage of Using Subqueries


I think there are some duplicate records in one of my tables. Also, I need to know which of the cities (and
regions) in a Northwind database has more than one customer in it. How can I use subqueries to perform
these tasks?

Technique
To get the answers to the situations just presented, you will use a subquery in your T-SQL statement. A
subquery is a complete SELECT statement. Following is the query that will be used for this How-To. It uses a
subquery in the WHERE clause of the outer, or main query:

SELECT Customers.City, Customers.Region, Customers.CustomerID,


Customers.CompanyName From Customers WHERE (((Customers.City)
IN (SELECT Tmp.City FROM Customers As Tmp GROUP BY Tmp.City,Tmp.Region
HAVING Count(*)>1 And Region = Customers.Region)))
ORDER BY Customers.City, Customers.Region

You can see in this query that in the WHERE clause, the City column in Customers is used with an IN
operator. The subquery in this case returns all the cities that are assigned to more than one customer.

Note

In this case, you are using a subquery much as you would a joined table or
against another view. The nice thing about using the subquery is that you can
see the entire query here, instead of opening another view that is joined.
Performancewise, there is no benefit or degradation in using subqueries.

In addition to using subqueries in WHERE clauses, you can use them anywhere you would use an expression.

Steps
Open and run the "Visual Basic .NETChapter 6 " solution. From the main form, click on the button with the
caption How-To 6.5. Much like a couple of the other How-Tos in this chapter, you will see the query displayed
in the label on the top of the form, with the results displayed in the data grid object below (see Figure 6.6 ).

1. Create a Windows Form. Then place the controls listed in Table 6.5 with the properties set displayed in
Figure 6.6 .

1.

Label
Text
SQL Statement
Label
Name
lblSQLString
Label
Text
Results
DataGrid
Name
dgResults

Table 6.5. Control Property Settings for This How-To


Object

Property

Setting

2. Add the code in Listing 6.10 to the Load event of the form. (Double-click on the form to bring up the
code.)
Listing 6.10 frmHowTo6_5.vb : Loading and Executing the SQL Statement by Using the
Subquery

Private Sub frmHowTo6_5_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
'-- Build the SQL String that returns cities that
'
have more than one customer in them.
Dim strSQL As String
strSQL = "SELECT Customers.City, Customers.Region, " &
"Customers.CustomerID, Customers.CompanyName "
strSQL &= "From Customers "
strSQL &= "WHERE (((Customers.City) In "
strSQL &= "(SELECT Tmp.City FROM Customers As Tmp "
strSQL &= "GROUP BY Tmp.City,Tmp.Region HAVING Count(*)>1 "
strSQL &= "And Region = Customers.Region))) " & _
"ORDER BY Customers.City, Customers.Region"
Me.lblSQLString.Text = strSQL
'-- Use the SQL String to build the data adapter and fill the data table.
Dim odaResults As New OleDb.OleDbDataAdapter(Me.lblSQLString.Text,

BuildCnnStr("(local)", "Northwind"))
Dim dtResults As New DataTable()
Try
odaResults.Fill(dtResults)
Catch excp As Exception
MessageBox.Show(excp.Message)
Exit Sub
End Try
'-- Assign the data table to the data grid's DataSource property
Me.dgResults.DataSource = dtResults
End Sub

Figure 6.6. Using subqueries in one step to retrieve information saves time and effort.

Comments
Subqueries are a powerful tool for getting at your data in different ways. Following are some rules you have
to remember when using subqueries:

You can only use an ORDER BY clause if you are using the TOP clause.
If a table is being used in the subquery but not in the main query, then you cannot use columns from
that table in the output of the main query.
You need to use parentheses around the subquery SELECT statement.
You can't include a FOR BROWSE or COMPUTE clause in the subquery.

[ Team LiB ]

[ Team LiB ]

6.6 Create, Modify, and Delete Tables


It is common in database applications to programmatically create, modify, and delete tables. How do I do
this using T-SQL?

Technique
To perform these tasks, you will use the CREATE TABLE , ALTER TABLE , and DROP TABLE T-SQL
statements. With these statements, you can handle any requirements that your application might have. Look
at these statements one at a time.
Creating a Table Using CREATE TABLE
With the CREATE TABLE statement, not only can you specify columns and their data types, but you also can
specify indexes, check constraints, and other table level properties. For this How-To, you will be use the
following T-SQL statement:

CREATE TABLE ListsExample


(
ListID smallint IDENTITY(1, 1) PRIMARY KEY CLUSTERED,
LastName varchar(50) NOT NULL,
FirstName varchar(50) NOT NULL,
Age smallint ,
DateEntered datetime NOT NULL DEFAULT GetDate()
CHECK (DateEntered <= GetDate())
)

This statement shows how to perform a number of different tasks:

1. Specify the name of the table:

CREATE TABLE ListsExample

2. Create an Identity column, which is the primary key:

ListID smallint IDENTITY(1, 1) PRIMARY KEY CLUSTERED,

3. Create fields that can't be NULL, and using different data types:

LastName varchar(50) NOT NULL,


FirstName varchar(50) NOT NULL,
Age smallint ,

4. Create a field with the Default Value set, and with a Check Constraint specified.

DateEntered datetime NOT NULL DEFAULT GetDate()


CHECK (DateEntered <= GetDate())

Modifying a Table Using ALTER TABLE


The example T-SQL statement used for modifying the table in this How-To is pretty simple in that it adds a
column and removes (drops) a column:

ALTER TABLE ListsExample ADD MyNewColumn varchar(30)


ALTER TABLE ListsExample DROP COLUMN Age

You can perform quite a few other tasks with this statement. You can even see by the syntax displayed here
that you can handle many tasks, including dropping constraints.

ALTER TABLE table


{ [ ALTER COLUMN column_name
{ new_data_type [ ( precision [ , scale ] ) ]
[ COLLATE < collation_name > ]
[ NULL | NOT NULL ]
| {ADD | DROP } ROWGUIDCOL }
]
| ADD
{ [ < column_definition > ]
| column_name AS computed_column_expression
} [ ,...n ]
| [ WITH CHECK | WITH NOCHECK ] ADD
{ < table_constraint > } [ ,...n ]
| DROP
{ [ CONSTRAINT ] constraint_name
| COLUMN column } [ ,...n ]
| { CHECK | NOCHECK } CONSTRAINT
{ ALL | constraint_name [ ,...n ] }
| { ENABLE | DISABLE } TRIGGER
{ ALL | trigger_name [ ,...n ] }
}

You can do even more. Look at the Books Online for SQL Server to see complete coverage of this statement.
Deleting a Table Using the DROP TABLE Statement
This statement is the easiest, and it's a one liner:

DROP TABLE ListsExample

However, you need to keep some things in mind when you are trying to drop a table:

You can't use the DROP TABLE statement when the table is used in a relationship and is referenced in
the FOREIGN KEY constraint. You will need to drop the other table or the constraint.
You will need to be the administrator or owner of the table to be able to use the DROP TABLE
statement.
You can't use the DROP TABLE statement on system tables.

Steps
Open and run the Visual Basic .NETChapter 6 solution. From the main form, click on the button with the
caption How-To 6.6 (see Figure 6.7 ).

1. Create a Windows Form. Then place the controls listed in Table 6.6 with the following properties set, as
displayed in Figures 6.7 .

Label
Text
SQL Statement to Create a Table
Label
Name
lblCreateTable
Button
Name
btnCreateTable

Text
Create Table
Label
Text
SQL Statement to Modify a Table
Label
Name
lblModifyTable

Button
Name
btnModifyTable

Text
Modify Table
Label
Text
SQL Statement to Delete a Table
Label
Name
lblDeleteTable
Button
Name
btnDeleteTable

Text
Delete Table

Table 6.6. Control Property Settings for This How-To


Object

Property

Setting

2. Add the code in Listing 6.11 to the Load event of the form. (Double-click on the form to bring up the
code.) This routine creates the SQL statements for all three tasks and assigns them to the appropriate
label for display.
Listing 6.11 frmHowTo6_6.vb : Loading the SQL Statements into the Appropriate Labels

Private Sub frmHowTo6_6_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
'-- Build the SQL String for Creating a Table
Dim strSQL As String
strSQL = "CREATE TABLE ListsExample" & vbCrLf
strSQL &= "(" & vbCrLf

strSQL &= "


"PRIMARY KEY
strSQL &= "
strSQL &= "
strSQL &= "
strSQL &= "
strSQL &= "
strSQL &= ")"

ListID smallint IDENTITY(1, 1) " & _


CLUSTERED," & vbCrLf
LastName varchar(50) NOT NULL, " & vbCrLf
FirstName varchar(50) NOT NULL, " & vbCrLf
Age smallint , " & vbCrLf
DateEntered datetime NOT NULL DEFAULT GetDate() " & vbCrLf
CHECK (DateEntered <= GetDate())" & vbCrLf

Me.lblCreateTable.Text = strSQL
strSQL = "ALTER TABLE ListsExample ADD MyNewColumn varchar(30) " & vbCrLf
strSQL &= "ALTER TABLE ListsExample DROP COLUMN Age"
Me.lblModifyTable.Text = strSQL
strSQL = "DROP TABLE ListsExample"
Me.lblDeleteTable.Text = strSQL
End Sub

3. Add the code in Listing 6.12 to the Click event of the btnCreateTable button. This routine calls the
function PerformTask(), passing the text in the lblCreateTable label. PerformTask() is described in the
next step. If you perform the task successfully, then a message box is displayed letting you know that
all went as it should have.
Listing 6.12 frmHowTo6_6.vb : Calling PerformTask() from the btnCreateTable Click Event

Private Sub btnCreateTable_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnCreateTable.Click
If PerformTask(Me.lblCreateTable.Text) Then
MessageBox.Show("Table Created Successfully, " & _
_Look for ListsExample " &
"Table in Northwind Database in Server Explorer", _
"Action Performed")
End If
End Sub

4. In the class module of the form created for this How-To, create the code displayed in Listing 6.13 for
the PerformTask() function. This code creates a Connection object. Next, create a Command object
that is based on the string passed in strSQL . Open the connection and execute the command. Notice
that the execution of the command has been wrapped in the Try..Catch..End Try code block to
make sure the command is executed correctly; if it's not, a message is displayed.
Listing 6.13 frmHowTo6_6.vb : Executing the SQL Statement Passed in Using strSQL

Function PerformTask(ByVal strSQL As String) As Boolean


Dim cnn As New OleDb.OleDbConnection(BuildCnnStr("(local)", "Northwind"))
Dim cmdAction As New OleDb.OleDbCommand(strSQL)
cmdAction.Connection = cnn
cnn.Open()
PerformTask = True
Try
cmdAction.ExecuteNonQuery()
Catch excp As Exception
MessageBox.Show(excp.Message, "Error with Action")
PerformTask = False
End Try
cnn.Close()
End Function

5. Add the code snippets in Listing 6.14 to the appropriate Click events for btnModifyTable and
btnDeleteTable to the Load event of the form.
Listing 6.14 frmHowTo6_6.vb: Calling PerformTask from btnModifyTable and
btnDeleteTable Click Events

Private Sub btnModifyTable_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnModifyTable.Click
If PerformTask(Me.lblModifyTable.Text) Then
MessageBox.Show("Table Modified Successfully, " & _
Look for ListsExample " &
"Table in Northwind Database in Server Explorer", _
"Action Performed")
End If
End Sub
Private Sub btnDeleteTable_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnDeleteTable.Click
If PerformTask(Me.lblDeleteTable.Text) Then
MessageBox.Show("Table Deleted Successfully, " & _

"Look for ListsExample " &


"Table in Northwind Database in Server Explorer", _
"Action Performed")
End If
End Sub

Figure 6.7. A common problem with inner joins is retrieving multiple records when you just want
to see one per occurrence.

Comments
You don't have to add, modify, and delete tables manually. Just make sure that you back up your data
before performing these tasks.

[ Team LiB ]

[ Team LiB ]

6.7 Create a New Table with Data from Existing Tables


I often need to create new tables from existing data. If the table already exists, I need to delete the old
table first. How do I do this using T-SQL?

Technique
To perform these tasks, you will create two T-SQL statements and use them in one Command object. Here
are the two statements that will be used:

IF EXISTS (SELECT * FROM sysobjects


WHERE id = object_id(N'[Northwind].[dbo].[MyProdAndCat]'))
DROP TABLE [Northwind].[dbo].[MyProdAndCat]
SELECT Categories.CategoryName, Products.ProductName INTO MyProdAndCat
FROM Categories INNER JOIN ProductsON Categories.CategoryID =
Products.CategoryID"

The first statement checks for the existence of the particular table you will be creating, in this case
MyProdAndCat. This statement demonstrates a couple of techniques that you can use in T-SQL:

Using the EXIST statement with a SELECT statement, querying the system table called sysobjects
Using an IF statement to conditionally execute another command, in this case the DROP TABLE
statement

Tip
Now that you have learned this technique, you will want to use it
repeatedly. Make sure you mark this page!

The last statement uses an inner join to join two tables displaying the CategoryName and ProductName. The
clause that creates the new table is this:

INTO MyProdAndCat

This tells SQL Server to create a new table called MyProdAndCat from the SELECT statement that is

specified.

Steps
Open and run the Visual Basic .NETChapter 6 solution. From the main form, click on the button with the
caption How-To 6.7. You will see the SQL string specified in the "Technique" section displayed in a label. If
you click the Execute button, the new table is generated a SELECT statement is executed, and the results
are displayed in the DataGrid object (see Figure 6.8).

1. Create a Windows Form. Then place the controls listed in Table 6.7 with the following properties set, as
displayed in Figure 6.8.

Table 6.7. Control Property Settings for This How-To


Object

Property

Setting

Label

Text

SQL Statement

Label

Name

lblSQLString

Label

Text

Results

Button

Name

btnExecute

Text

Execute

Name

dgResults

DataGrid

2. Add the code in Listing 6.16 to the Load event of the form. (Double-click on the form to bring up the
code.)
Listing 6.16 frmHowTo6_7.vb: Storing the SQL Statement in the lblSQLString Label to
Display and Use Later

Private Sub frmHowTo6_7_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
'-- Build the SQL String that returns cities that
have more than one customer in them.
Dim strSQL As String
strSQL = "IF EXISTS (SELECT * from sysobjects " & vbCrLf
strSQL &= " WHERE id = object_id(N'[Northwind].[dbo].[MyProdAndCat]'))" & _
vbCrLf
strSQL &= " DROP Table [Northwind].[dbo].[MyProdAndCat]" & _
vbCrLf & vbCrLf
strSQL &= "SELECT Categories.CategoryName, Products.ProductName" & vbCrLf
strSQL &= "INTO MyProdAndCat" & vbCrLf
strSQL &= "FROM Categories INNER JOIN Products" & vbCrLf
strSQL &= "ON Categories.CategoryID = Products.CategoryID"
Me.lblSQLString.Text = strSQL
'

End Sub

3.

3. Add the following code in Listing 6.17 to the Click event of btnExecute. This code creates
Connection and Command objects by using the T-SQL routine discussed in the "Technique" section.
Then the code executes the query. Next, a select query is run against the new table, and the
DataSource property is set to the data table that was filled.
Listing 6.17 frmHowTo6_7.vb: Loading the Form

Private Sub btnExecute_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnExecute.Click
Dim dtResults As New DataTable()
Try
Dim ocnn As New OleDb.OleDbConnection(BuildCnnStr("(local)", _
"Northwind"))
Dim ocmd As New OleDb.OleDbCommand(Me.lblSQLString.Text)
ocmd.Connection = ocnn
ocnn.Open()
ocmd.ExecuteNonQuery()
ocnn.Close()

'

'-- Use the SQL String to build the data adapter


and fill the data table.
Dim odaResults As _
New OleDb.OleDbDataAdapter("Select * From MyProdAndCat",
BuildCnnStr("(local)", "Northwind"))
odaResults.Fill(dtResults)
Catch excp As Exception
MessageBox.Show(excp.Message)
Exit Sub
End Try
'-- Assign the data table to the data grid's DataSource property
Me.dgResults.DataSource = dtResults

End Sub

Figure 6.8. These results are based on a new table created by the SQL string that is displayed.

Comments
You will probably want to go ahead and drop the new table after you are finished using it if you don't need to
keep it around for any other purposes.

[ Team LiB ]

[ Team LiB ]

6.8 Create and Call SQL Server 2000 User-Defined Functions


In SQL Server 2000, I have heard that you can create user-defined functions (UDFs). Where would you use
UDFs, and how do you create and call them from within T-SQL?

Technique
UDFs have been used for years in application development languages. You can now create them in SQL
Server 2000 as well.
Creating SQL Server 2000 UDFs
You can create UDFs in SQL Server 2000 by using the CREATE FUNCTION command . Normally, you would
do this using the Enterprise Manager or some tool, but here you will learn how to do it using VB.NET.
Following is the function that will be created:

CREATE FUNCTION udf_ShowProdAndCat (@UnitPriceParm money)


RETURNS @ProdAndCatTab TABLE
(
ProductID
int,
ProductName
nvarchar(80),
CategoryName nvarchar(80),
UnitPrice
int
)
AS
BEGIN
INSERT @ProdAndCatTab
SELECT P.ProductID, P.ProductName,
C.CategoryName, P.UnitPrice
FROM Products AS P INNER JOIN Categories AS C
ON P.CategoryID = C.CategoryID
WHERE P.UnitPrice > @UnitPriceParm
RETURN
END

This function definitely looks a lot different from functions you have created in other languages, but it doesn't
look as funky when you remember you are using T-SQL commands.
Passing Parameters to SQL Server UDFs
The parameter that is passed starts with the @ symbol, much like local variables that were discussed in
How-To 6.1.
You will also want to declare the data type.
Returning Scalar or Table Types from SQL Server UDFs
When returning values from a UDF, you will either pass back a scalar type, which is actually a single value of

one of the standard data types, or pass back a new Table data type .
The example for this How-To creates and returns a Table data type, specified with the following lines of
code:

RETURNS @ProdAndCatTab TABLE


(
ProductID
int,
ProductName
nvarchar(80),
CategoryName nvarchar(80),
UnitPrice
int
)

By including the opening and closing parentheses, you can specify and return an entire table's worth of data,
but be careful not to because performance would not be good. This is the same as it would be using SQL
Server data.
You can then use this table that is returned in another T-SQL statement.
After establishing the return value, in this case the table ProdAndCatTab, you need to create the body of
code for the UDF.

Note

Although this return value has been called ProdAndCatTab, there is not going to
be a table that you can access outside the function by that name. It will just be
strictly for use in the calling statement.

Formatting the Body of Code for SQL Server UDFs


You can see from the block of code that follows that you need to have a BEGIN and END statement:

AS
BEGIN
INSERT @ProdAndCatTab
SELECT P.ProductID, P.ProductName,
C.CategoryName, P.UnitPrice
FROM Products AS P INNER JOIN Categories AS C
ON P.CategoryID = C.CategoryID
WHERE P.UnitPrice > @UnitPriceParm
RETURN
END

Notice also that you will have INSERT @ProdAndCatTab and RETURN statements in there to create the Table
return value. The rest of the code is much the same as other T-SQL statements.

Calling SQL Server 2000 UDFs


You can call UDFs from other T-SQL Statements, as displayed here for this How-To:

Select * From udf_ShowProdAndCat(" & Me.txtUnitPrice.Text & ")"

There, the UDF is called in a SELECT statement, and the parameter is passed.

Steps
Open and run the VB.NET Chapter 6 solution. From the main form, click on the button with the caption
How-To 6.8 (see Figure 6.9 ).
Figure 6.9. A common problem with inner joins is retrieving multiple records when you want to
see only one per occurrence.

You will see the UDF described in the "Technique " section in a label. Click the button labeled Create UDF. If
the UDF already exists, then a message box will tell you so. Otherwise, the UDF is created. Next, click the
button labeled Use UDF . The data grid is then filled and the data is displayed. You can change the value in

the Products Greater Than text box. Then click Use UDF again to see the new data displayed.

1. Create a Windows Form. Then place the controls listed in Table 6.8 with the following properties set, as
displayed in Figure 6.9 .

Label
Text
Report Products Greater Than:
TextBox
Name
txtUnitPrice
Label
Text
SQL Statement Creating Temporary Table
Label
Name
lblCreateUDF
Button
Name
btnCreateUDF

Text
Create UDF
Label
Text
SQL Statement Using UDF
Label
Name
lblUseUDF
Button
Name

btnUseUDF

Text
Use UDF
Label
Text
Results
DataGrid
Name
dgResults

Table 6.8. Control Property Settings for This How-To


Object

Property

Setting

2. Add the code in Listing 6.18 to the Load event of the form. (Double-click on the form to bring up the
code.)
Listing 6.18 frmHowTo6_8.vb : Create the UDF Code, and Assign It to a Label for Display and
Use Later

Private Sub frmHowTo6_8_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
'-- Create the UDF string
Dim strSQL As String
strSQL = "CREATE FUNCTION udf_ShowProdAndCat ( @UnitPriceParm money)"
strSQL &= "RETURNS @ProdAndCatTab TABLE" & vbCrLf
strSQL &= "(" & vbCrLf
strSQL &= "
ProductID
int," & vbCrLf
strSQL &= "
ProductName
nvarchar(80)," & vbCrLf
strSQL &= "
CategoryName nvarchar(80)," & vbCrLf
strSQL &= "
UnitPrice
int" & vbCrLf
strSQL &= ")" & vbCrLf
strSQL &= "AS" & vbCrLf
strSQL &= "BEGIN" & vbCrLf
strSQL &= "
INSERT @ProdAndCatTab" & vbCrLf
strSQL &= "
SELECT P.ProductID, P.ProductName," & vbCrLf
strSQL &= "
C.CategoryName, P.UnitPrice" & vbCrLf
strSQL &= "
FROM Products AS P INNER JOIN Categories AS C" & _
vbCrLf
strSQL &= "
strSQL &= "
strSQL &= "

ON P.CategoryID = C.CategoryID" & vbCrLf


WHERE P.UnitPrice > @UnitPriceParm" & vbCrLf
RETURN" & vbCrLf

strSQL &= "END"

Me.lblCreateUDF.Text = strSQL
'-- Create the SQL string that calls the UDF
Me.lblUseUDF.Text = "Select * From udf_ShowProdAndCat(" & _
Me.txtUnitPrice.Text & ")"
End Sub

3. Add the code in Listing 6.19 to the Click event of btnCreateUDF . This code uses Connection and
Command objects to create the UDF based on the code that is provided.
Listing 6.19 frmHowTo6_8.vb : Creating the New UDF in SQL Server

Private Sub btnCreateUDF_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnCreateUDF.Click
Try
Dim ocnn As New OleDb.OleDbConnection(BuildCnnStr("(local)", _
"Northwind"))
Dim ocmd As New OleDb.OleDbCommand(Me.lblCreateUDF.Text)
ocmd.Connection = ocnn
ocnn.Open()
ocmd.ExecuteNonQuery()
ocnn.Close()
Catch excp As Exception
MessageBox.Show(excp.Message)
Exit Sub
End Try
MessageBox.Show("UDF Created")
End Sub

4. Add the code in Listing 6.20 to the TextChanged event of the button txtUnitPrice.
Listing 6.20 frmHowTo6_8.vb : Updating the SELECT Statement That Is Calling the UDF

Private Sub txtUnitPrice_TextChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles txtUnitPrice.TextChanged
Me.lblUseUDF.Text = "Select * From udf_ShowProdAndCat(" & _
Me.txtUnitPrice.Text & ")"

End Sub

5. Add the code in Listing 6.21 to the Click event of the button btnUseUDF . This code fills a dataset
based on the SELECT statement that calls the UDF. The code then assigns the dataset to the
DataSource property of dgResults.
Listing 6.21 Displaying Data Using the SELECT Statement in the Text Property of lblUseUDF

Private Sub btnUseUDF_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnUseUDF.Click

'

Dim dtResults As New DataTable()


Try
'-- Use the SQL String to build the data adapter
and fill the data table.
Dim odaResults As New OleDb.OleDbDataAdapter(Me.lblUseUDF.Text,
BuildCnnStr("(local)", "Northwind"))
odaResults.Fill(dtResults)
Catch excp As Exception
MessageBox.Show(excp.Message)
Exit Sub
End Try
'-- Assign the data table to the data grid's DataSource property.
Me.dgResults.DataSource = dtResults

End Sub

Comments
When you have to use the same T-SQL statements repeatedly, it is handy to be able to store that code
somewhere, just like you can do with UDFs you create in Visual Basic.
When using UDFs, you can use the value inline and save a number of steps when creating your T-SQL
routines.

[ Team LiB ]

[ Team LiB ]

Chapter 7. Performing Common Database Tasks Using SQL-DMO


In this chapter you will

Create a dialog box to connect to a new database


Back up and verify a SQL Server database
Restore a SQL Server database
Transfer tables between SQL Server databases
Create a Detach/Attach SQL Server database dialog box
Regardless of the language you are now using for creating your database applications, if you are using SQL
Server for your database, you still need to be able to perform basic database tasks on your back-end
database. In addition, you will probably want to give your users the ability to perform these tasks without
having to use Enterprise Manager.
The way to allow the users to perform some of the common database tasks such as backing up and restoring
the database is to take advantage of SQL-Distributed Management Objects (SQL-DMO) and Data
Transformation Services (DTS). When you use the Backup/Restore Wizard (see Figure 7.1) in the Enterprise
Manager, you are really using SQL-DMO, with the interface created for you.
Figure 7.1. Under the covers, the SQL-DMO objects are being used to perform the requested tasks
such as backup.

The same also can be said of the DTS object model, used to create transformation packages and other
services (see Figure 7.2.)
Figure 7.2. As with SQL-DMO and backing up databases, when exporting data, SQL-DTS objects
are used.

As this chapter describes each of the tasks that you will be able to accomplish, you will see more of the
various objects, properties, and methods that you can use to accomplish your tasks.

[ Team LiB ]

[ Team LiB ]

Looking at the SQL Server DMF APIs


The SQL Distributed Management Framework is a framework of objects, services, and components used to
manage Microsoft SQL Server 2000. It is made up of a few APIs that you can use to accomplish tasks in your
applications.
You can see how these APIs are used in Figure 7.3.
Figure 7.3. Under the covers, the SQL-DMO objects are being used to perform the requested
tasks.

Following is a brief description of each of the APIs. We will be using the last two directly in the rest of this
chapter.

SQL-NS. SQL Namespace provides a way to actually call the Enterprise Managers dialog boxes and
User Interface. SQL Namespace uses the other APIs listed.
SQL-DMO. SQL Distributed Management Objects give you access to the various objects within SQL
Server, as well as some of the tasks that can be performed using the Enterprise Manager, so that you
can perform them within your own application.
SQL-DTS. SQL Data Transformation Services allows you to create transformation packages and tasks,

much like you would by using the DTS user interface.

[ Team LiB ]

[ Team LiB ]

Setting References in .NET for the SQL APIs


Before you can use the APIs within your .NET application, you need to create a reference to the particular
library you are going to use. The libraries you will be using are COM libraries; therefore, you will not have
certain benefits of using the .NET Framework, such as some of the performance gains. However, you can still
use the libraries. Hopefully, if and when the libraries are updated to the framework, you will be able to
convert your code over to use them.
To access the necessary libraries, choose Add Reference from the Project menu. Click on the COM tab to
display possible COM libraries. Next, click Select after highlighting the following libraries: Microsoft
DTSPackage Object Library and Microsoft SQLDMO Object Library. Click OK to close the dialog box. At that
point, if you open the Object Browser, you can expand the Interop.SQLDMO library and even highlight the
SQL Server object to see the properties and methods available. This is shown in Figure 7.4.
Figure 7.4. The objects displayed here are now available for use in your applications.

As you work through the following How-Tos, the various collections, objects, properties, and methods that
you will be using will be listed as you need them. So many objects are available that it would take multiple
pages to display the whole trees of the SQL-DMO and SQL-DTS object models.

[ Team LiB ]

[ Team LiB ]

7.1 Create a Dialog Box to Connect to a New Database, Including Listing Available SQL Servers and
Databases
Users sometimes need to connect to various databases. An example of this is a large company that might
keep its site information in separate databases in the sameor even differentSQL Servers. Management
might need to point the application to different site databases, depending on which one it needs to work
with. This How-To shows you how to create a dialog box to let the user pick the SQL Server and database
and then create a new connection based on the selections.
Within a database application, it is necessary to allow users to select a SQL Server back end to which to
connect. If you have created my application with the necessary flexibility, you should not have hard-coded
SQL Server or database names within my application. How do you create a dialog box that lists available SQL
Servers and databases and that the user can utilize to connect to a new database?

Technique
For this How-To, you will be using the base object in SQL-DMO, which is the Application object, and then the
SQLServer object. You can use many objects off this object, the first layer of which is shown in Figure 7.5 .
Figure 7.5. These are just the tip of the iceberg as far as collections and objects used in SQLDMO.

Table 7.1 presents the objects, properties, and methods that will be used for this -To.

Application
NameList
Collection used to hold the list of available servers

ListAllAvailableServers
Method used to retrieve available servers on the network

SQLServer

Connect
Connection string that connects you to the SQL Server, allowing you access to the databases

LoginSecure
Flag that specifies that you want to connect to the SQL Server using a trusted connection

Databases
Collection of databases for the specified SQL Server

Table 7.1. SQL-DMO Objects Used for Listing SQL Servers, Databases, and Connecting to a
Database
Object

Property/Method

Description

You will also be using the OleDbConnection object.

Steps
Open and run the VB.NET Chapter 7 solution. From the main Windows form, click on the command button
with the caption How-To 7.1.
When the form loads, you will see the SQL Servers that are available on your network. If you are not on the
network, you will only see a SQL Server called "local."
If you click on a SQL Server and you are in fact using the Windows NT Integrated Security , then you will see
the list of databases located in the chosen SQL Server.
You can then select a database and then click the Connection button. If you are successful, you will see the
connection string displayed in the text box at the bottom of the form. The form will then look like the form
displayed in Figure 7.6 .

1. Create a Windows Form. Then place the controls shown in Figure 7.6 with the following properties set.

Label
Name
Label1

Text
SQL Servers
ListBox

Name
lstSQLServers
Label
Name
Label2

Text
Databases
ListBox
Name
lstDatabases
Label
Name
Label3

Text
Connection String
TextBox
Name
txtConnectionString

Text
Not Connected
Command Button
Name
btnConnect

Text
Connect

Table 7.2. Label, ComboBox, ListBox, and Command Button Control Property
Settings
Object

Property

Setting

2. As with some of the other chapters' projects, before creating the code that will be attached to the Load
event of the form, you need to create a support routine to create the Connection string. Called
BuildCnnStr , the function can be seen in Listing 7.1 . This function takes a server and database
names passed to it and returns a connection string. You will want to create a basic module in which to
keep the routine.
Listing 7.1 modSQLDMORoutines.vb : Creating a Connection String

Function BuildCnnStr(ByVal strServer As String, _


ByVal strDatabase As String) As String
Dim strTemp As String
strTemp = "Provider=SQLOleDB; Data Source=" & strServer & ";"
strTemp &= "Initial Catalog=" & strDatabase & ";"
strTemp &= "Integrated Security=SSPI"
Return strTemp
End Function

Although you could create a routine that would pass back a Connection object, a more versatile
method would be to pass back a string. The reason for this is that some objects ask for a
Connection object, but others just want a string.

3. On the form, add the code in Listing 7.2 to the Load event.
Listing 7.2 frmHowTo7_1.vb : Loading SQL Servers into a List Box

Private Sub frmHowTo7_1_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
'-- Load up the SQL Servers
LoadSQLServers(Me.lstSQLServers)
End Sub

4. In the same module as step 2, create the routine called LoadSQLServers . After establishing an
instance of the SQL-DMO application, the code calls the ListAvailableSQLServer method . If no
names are loaded into the oNames namelist object (meaning that they were not available or you
weren't on the network), then the (local) server is added to the list box. Otherwise, the list returned is
added to the list box. The first item in the list box is then selected, causing the event described in the
next step to be executed.

Listing 7.3 modSQLDMORoutines.vb : Loading SQL Servers into a List Box

Sub LoadSQLServers(ByRef lstSQLServers As ListBox)


Dim intCurrSQL As Integer
Dim oNames As SQLDMO.NameList
Dim oSQLApp As New SQLDMO.Application()
'-- Load available SQL Servers into a NameList
'
(those that are able to be seen by the network)
oNames = oSQLApp.ListAvailableSQLServers
'-- Load the local instance as first in the list, if not on the network.
If oNames.Count = 0 Then
lstSQLServers.Items.Add("(local)")
End If
'-- Load the namelist into the list box
For intCurrSQL = 1 To oNames.Count
lstSQLServers.Items.Add(oNames.Item(intCurrSQL))
Next intCurrSQL
'-- Choose the first instance
lstSQLServers.SelectedIndex = 0
End Sub

Note

At this point, if you try to run your sample application, you might get an error
that includes the text QueryInterface for interface
SQLDMO.NameList failed. on the ListAvailableSQLServer method . If
this is the case, then you need to install Service Release 2 for SQL Server
2000. This will take care of this problem.

5. Add the code listed in Listing 7.4 to the SelectedIndexChanged event of the list box called
lstSQLServers. This routine starts by testing to see whether a SQL Server and database have been
selected. If so, the routine enables the btnConnect button; otherwise, the button is disabled. The
routine called GetSQLDatabases is then called, passing selected SQL Server and the lstDatabases list
box. You can see the GetSQLDatabases routine listed in the next step.
Listing 7.4 frmHowTo7_1.vb : Enabling or Disabling the btnConnect Button and Calling the
Routine to Load the Database List Box

Private Sub lstSQLServers_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) _
Handles lstSQLServers.SelectedIndexChanged

'-- If both the SQL Server and database are chosen, enable
' the Connect button.
If lstSQLServers.SelectedItems.Count > 0 And _
lstDatabases.SelectedItems.Count > 0 Then
Me.btnConnect.Enabled = True
Else
Me.btnConnect.Enabled = False
End If
GetSQLDatabases(Me.lstSQLServers.SelectedItem, Me.lstDatabases)
End Sub

6. Create the GetSQLDatabases routine by entering the code in Listing 7.5 to the new module that you
created in step 2. The first task that this routine attempts is to connect to the server that is selected. If
the routine can't connect, then the function is exited; otherwise, the names of the database that are
within the SQL Server are loaded into the list box called lstDatabases.
Listing 7.5 modSQLDMORoutines.vb : Retrieving Database Names for a Given SQL Server

Function GetSQLDatabases(ByVal strSQLServer As String, _


ByRef lstTemp As ListBox)
Dim intDBs As Integer
Dim db As Object
Dim strDBs As String
'-- Establish a connection to the server. If not, then exit the function.
Dim osvr As SQLDMO.SQLServer
osvr = New SQLDMO.SQLServer()
osvr.LoginSecure = True
Try
osvr.Connect(strSQLServer)
Catch excp As Exception
MessageBox.Show("There is a problem retrieving " & _
"databases for this server. " &
"Please check the name and try again. ", vbInformation,
"Error with Server")
Exit Function
End Try
'-- Clear the current list
lstTemp.Items.Clear()
'-- Load up the database names for the selected server into the list box.
For Each db In osvr.Databases

lstTemp.Items.Add(db.Name)
Next
End Function

7. Add the code from Listing 7.6 to the SelectedIndexChanged event of the lstDatabases list box. This
code is similar to the listing in step 4 in that it enables the btnConnect button if an item is selected in
both the lstSQLServers and lstDatabases list boxes.
Listing 7.6 frmHowTo7_1.vb : Repopulating the List Boxes Based on the Current Category
Selected

Private Sub lstDatabases_SelectedIndexChanged(ByVal sender As System.Object, _


ByVal e As System.EventArgs) _
Handles lstDatabases.SelectedIndexChanged
'-- If both the SQL Server and database are chosen,
' enable the Connect button.
If lstSQLServers.SelectedItems.Count > 0 And _
lstDatabases.SelectedItems.Count > 0 Then
Me.btnConnect.Enabled = True
Else
Me.btnConnect.Enabled = False
End If
End Sub

8. Add the code in Listing 7.7 to the Click event of the btnConnect button . In this routine, a
Connection object is instantiated and its Connection string is set to the chosen SQL Server and
database. If the connection has a problem opening, then the Text property of txtConnectionString
is set to an error message. Otherwise, the Text property is set to the new connection string.
Listing 7.7 frmHowTo7_1.vb : Calling the Routine to Reload the lstUnSelected List Box If This
Check Box Is Changed

Private Sub btnConnect_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnConnect.Click
Dim ocnn As New OleDb.OleDbConnection()
Try
'-- Create the connection string and open the connection
ocnn.ConnectionString = BuildCnnStr(Me.lstSQLServers.SelectedItem, _
Me.lstDatabases.SelectedItem)
ocnn.Open()
Catch excp As Exception
Me.txtConnectString.Text = "Error with Connection"

Exit Sub
End Try
Me.txtConnectString.Text = ocnn.ConnectionString
End Sub

Figure 7.6. This form works great for logging users into a database using Windows NT Integrated
Security.

How It Works
When the form opens, the SQL Servers that are available on the network are loaded into the first list box on
the form. When the user clicks on a specific SQL Server, then the databases from that SQL Server are
loaded into the Databases list box. The user can then select a database from the list. When the user does
this, the Connect button is enabled. If the user then clicks on the Connect button, the connect string is
loaded into the text box on the bottom of the form.

Comments
Like the others, this How-To is set up to use the Windows NT Integrated Security. If you wanted to use the
SQL Server security, you could add text boxes for login name and password and then supply them when
calling certain methods such as the Connect method in the code listed in step 6. You would also then set
the LoginSecure property used in the same listing to False .
This How-To is great for exactly what it does: letting the user (or administrator) pick a new SQL Server and
database to connect to if necessary. You can then save this connection string to a Public variable and use it
with all your connection objects. You will also be using similar controls and code for the rest of the How-Tos
in this chapter.

[ Team LiB ]

[ Team LiB ]

7.2 Back Up and Verify a SQL Server Database


Backing up a database is probably one of the most important features to incorporate into your application. If
you leave it up to the user to use the Enterprise Manager to back up the database, then it won't happen. You
can arrange to have the backup scheduled, but sometimes you need to back it up at a moment's notice. This
How-To shows you how to create a custom dialog box that will allow the user not only to back up your SQL
Server database, but also to verify the backup.
Your users need to have a method for backing up and verifying their databases without using Enterprise
Manager. How do you create a dialog box that would let them back up and verify their databases?

Technique
To accomplish this task, you will once again use the SQL-DMO objects. A couple of the new objects you will
use are the Backup and BackupDevices objects. You can see some of the additional objects, along with
the properties and methods that will be used, in Table 7.3.

Table 7.3. Additional SQL-DMO Objects Used for Backing Up and Verifying a Database
Object

Property/Method

Description

Backup

Action

This property allows you to specify what type of backup that


you want to take place. The choices are
SQLDMOBackup_Database, SQLDMOBackup_Differential,
SQLDMOBackup_Files, and SQLDMOBackup_Log.

BackupSetDescription This property allows you to specify a description for the backup
set.

BackupSetName

This property is the Backup set name.

Database

This property allows you to specify the database to back up.

Devices

This property specifies the devices you will be backing up to.

Initialize

This method initializes the Backup process.

TruncateLog

This property allows you to specify how to handle the truncate


log when backing up.

SQLBackup

This method performs the actual backup.

BackupDevices

This is a collection of backup devices for a SQL Server.

BackupDevice Name

Thisis the name of a specific backup device.

ReadBackupHeader
QueryResults ColumnName
GetColumnString

This method reads in the header information for a backup,


returning a QueryResults object.
This is the individual property information about a backup.
This is the actual value for individual header information.

Using the objects listed in Table 7.3, you will create a form with options for the user to back up his database
and verify the information after it has been saved.

Note
Not all the possible options will be included in the form created for this
How-To. For example, you could allow the user to back up the
database to a separate file and give him additional options for the type
of backup to perform.
Odds are good that you will not want to give the users all the options
they could have; that is one of the reasons you want to create the
dialog box here instead of letting users use the Enterprise Manager.

Steps
Open and run the VB.NET Chapter 7 solution. From the main Windows form, click on the command button
with the caption How-To 7.2.
As with How-To 7.1, a user clicks on the SQL Server that he wants to display the databases of. He can then
choose the database and backup device. From there, the user can click the Backup button to perform the
backup. You can then click on the verify button to have information about the backup displayed in the text
box at the bottom of the form. The form will look similar to the one displayed in Figure 7.7.

1. Create a Windows Form. Then place the controls shown in Figure 7.7, with the following properties set
as in Table 7.4.

Table 7.4. Controls and Their Property Settings


Object

Property

Setting

Label

Name

Label1

Text

SQL Servers

ListBox

Name

lstSQLServers

Label

Name

Label2

Text

Databases

ListBox

Name

lstDatabases

Label

Name

Label3

Text

Backup Devices

ListBox

Name

lstBackupDevices

Command Button

Name

btnBackup

Text

&Backup

Name

Label4

Text

Backup Set Name

Name

txtBackupSetName

Text

MyTestBackup

Name

Label5

Label
TextBox
Label

Text

Options

Panel

Name

Panel1

Groupbox

Name

grpAction

Text

Action

Name

rbFull

Checked

True

Text

Full

Name

rbIncremental

Checked

False

Text

Incremental

Name

grpTruncateLog

Text

Truncate Log

Name

rbNoLog

Checked

True

Text

No Log

Name

rbNoTruncate

Checked

False

Text

No Truncate

Name

rbTruncate

Checked

False

Text

Truncate

Name

Label5

Text

Backup Set Description

Name

txtBackupSetDescription

Text

MyTestBackup

Name

chkInitialize

Text

Initialize?

Name

txtVerify

Multiline

True

Scrollbars

Both

Name

btnVerify

Text

&Verify

Name

btnClose

Text

&Close

Radio Button

Radio Button

Groupbox
Radio Button

Radio Button

Radio Button

Label
TextBox
Checkbox
TextBox

Command Button
Command Button

2. On the form, add the code in Listing 7.8 to the Load event. This will look familiar to How-To 7.1. For an
examination of the LoadSQLServers routine, check out step 4 in that How-To.
Listing 7.8 frmHowTo7_2.vb: Calling the Routine That Loads Available SQL Servers into a List
Box

Private Sub frmHowTo7_2_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load

'-- Load up the SQL Servers


LoadSQLServers(Me.lstSQLServers)
End Sub

3. On the lstSQLServers list box, add the code in Listing 7.9 to the SelectedIndexChanged event. This
routine calls both the GetSQLDatabases, described in step 6 of How-To 7.1, and
GetBackupDevices, described in the next step.
Listing 7.9 frmHowTo7_2.vb: Populating the lstDatabases and lstBackupDevices List Boxes

Private Sub lstSQLServers_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) _
Handles lstSQLServers.SelectedIndexChanged
GetSQLDatabases(Me.lstSQLServers.SelectedItem, Me.lstDatabases)
GetBackupDevices(Me.lstSQLServers.SelectedItem, Me.lstBackupDevices)
End Sub

4. Create the GetBackupDevices routine by entering the code in Listing 7.10 into the new module you
created in How-To 7.1. The first task attempted by this routine is to connect to the server that is
selected. Next, the names of the backup devices that are within the SQL Server are loaded into the list
box called lstBackupDevices.
Listing 7.10 modSQLDMORoutines.vb: Retrieving Backup Device Names for a Given SQL
Server

Public Sub GetBackupDevices(ByVal strSQLServer As String, _


ByRef lstBackupDevices As ListBox)
Dim oDevice As SQLDMO.BackupDevice
'-- Log on to the SQL Server.
Dim osvr As SQLDMO.SQLServer = New SQLDMO.SQLServer()
osvr.LoginSecure = True
osvr.Connect(strSQLServer)
lstBackupDevices.Items.Clear()
'-- Iterate through the backup devices.
For Each oDevice In osvr.BackupDevices
lstBackupDevices.Items.Add(oDevice.Name)
Next
End Sub

5.

5. On the btnBackup button, add the code in Listing 7.11 to the Click event. After connecting to the
chosen SQL Server, a Backup object is instantiated. Next, certain properties of the Backup object,
called oBackup , are set to values that are specified on the form. The SQLBackup method is called off
the Backup object, and the connection is closed. Last, the variables are cleared (set to Nothing ) and
a message box is displayed.
Listing 7.11 frmHowTo7_2.vb: Performing the Backup

Private Sub btnBackup_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnBackup.Click
'-- Create a connection to the server
Dim osvr As New SQLDMO.SQLServer()
osvr.LoginSecure = True
osvr.Connect(Me.lstSQLServers.SelectedItem)
'-- Create a Backup object, and set the necessary properties
'
based on options chosen on the form.
Dim oBackup As New SQLDMO.Backup()
With oBackup
If Me.rbFull.Checked Then
.Action = SQLDMO.SQLDMO_BACKUP_TYPE.SQLDMOBackup_Database
Else
.Action = SQLDMO.SQLDMO_BACKUP_TYPE.SQLDMOBackup_Differential
End If
.BackupSetDescription = Me.txtBUSetDescription.Text
.BackupSetName = Me.txtBUSetName.Text
.Database = Me.lstDatabases.SelectedItem
.Devices = "[" & Me.lstBackupDevices.SelectedItem & "]"
If Me.rbNoLog.Checked Then
.TruncateLog = _
SQLDMO.SQLDMO_BACKUP_LOG_TYPE.SQLDMOBackup_Log_NoLog
ElseIf Me.rbNoTruncate.Checked Then
.TruncateLog = _
SQLDMO.SQLDMO_BACKUP_LOG_TYPE.SQLDMOBackup_Log_NoTruncate
Else
.TruncateLog = _
SQLDMO.SQLDMO_BACKUP_LOG_TYPE.SQLDMOBackup_Log_Truncate
End If
.Initialize = Me.chkInitialize.Checked
'-- Execute the backup
.SQLBackup(osvr)
End With
'-- Disconnect from the server and clean up.
osvr.DisConnect()
osvr = Nothing

oBackup = Nothing
MessageBox.Show("Database Backed Up", "Task Completed", _
MessageBoxButtons.OK)
End Sub

6. Add the code in Listing 7.12 to the Click event of btnVerify. After connecting to the SQL Server, the
code iterates through each of the backup devices for the server and locates the one that the form
specifies.
After the specific backup device is located, the ReadBackupHeader method is called, with an
SQLDMO.QueryResults object returned. Each row of the QueryResults is read, and then the
information is displayed in a text box called txtVerifyDisplay. From there, the SQLServer object is
disconnected.
Listing 7.12 frmHowTo7_2.vb: Performing the Backup

Private Sub btnVerify_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnVerify.Click
Dim oDevice As SQLDMO.BackupDevice
Dim oResults As SQLDMO.QueryResults
Dim intRowCount As Integer
Dim intColCount As Integer
'-- Create a connection to the server
Dim osvr As New SQLDMO.SQLServer()
osvr.LoginSecure = True
osvr.Connect(Me.lstSQLServers.SelectedItem)
'-- Iterate through the devices
For Each oDevice In osvr.BackupDevices
If oDevice.Name = Me.lstBackupDevices.SelectedItem Then
'-- If found, display the results
oResults = oDevice.ReadBackupHeader
For intRowCount = 1 To oResults.Rows
For intColCount = 1 To oResults.Columns
Me.txtVerifyDisplay.Text &= _
oResults.ColumnName(intColCount) & " - "
Me.txtVerifyDisplay.Text &= _
oResults.GetColumnString(intRowCount, _
intColCount) & vbCrLf & vbCrLf
Next
Next
End If

Next
'-- Disconnect and clean up
osvr.DisConnect()
osvr = Nothing
oDevice = Nothing
oResults = Nothing
End Sub

7. Add the code in Listing 7.13 to the Click event of btnClose.


Listing 7.13 frmHowTo7_2.vb: Performing the Backup

Private Sub btnClose_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnClose.Click
Me.Close()
End Sub

Figure 7.7. Creating a form for backing up and verifying a SQL Database gives you control over
what options the user has when performing the task.

How It Works
After the form loads, the user can select the SQL Server, database, and backup device. When the user clicks
the button labeled Perform Backup, a SQLDMO Backup object is created. The Backup object gives you the
same capabilities as if you were using the Enterprise Managerexcept that you control them. Only put
options on the form that you want to have the user set, and then set the other options yourself, as you
deem necessary. After setting the options, the SQLBackup method performs the backup.
By using the QueryResults object off the BackupDevice , you have a means of looking at some of the
properties of the backup and verifying them to make sure the database did indeed get backed up.

Comments
You could enhance this utility in a number of ways, a couple of which include the following:

Add a text box for letting the user specify a filename for the backup, rather than using a backup
device.
Allow the user to add backup devices. Currently, only existing backup devices can be used.
Just be careful on what options you give the user so he doesn't shoot himself in the foot.

[ Team LiB ]

[ Team LiB ]

7.3 Restore a SQL Server Database


Sometimes the user needs to restore a database, and again, it is nice if he doesn't have to go to Enterprise
Manager to accomplish this task. This How-To explains how to use the Restore object from SQL-DMO to
accomplish this task.
It's all well and good to be able to back up and verify a SQL Server database, but what about being able to
restore the database if necessary? How do you create a dialog box to restore a SQL Server database?

Technique
For this How-To, you will use the Restore object of the SQL-DMO object model. You can see the properties
and methods that will be used in Table 7.5.

Table 7.5. Properties and Methods of the Restore Object


Property/Method Description

Action

This property allows you to specify what type of backup you want to take place. The
choices are found in the SQLDMO.SQLDMO_RESTORE_TYPE namespace and are
SQLDMORestore_Database , SQLDMORestore_Files, SQLDMORestore_Log .

Database

This property allows you to specify the database name to restore to.

Devices

This property shows which device(s) you are restoring from.

ReplaceDatabase This property tells whether to replace the database.


SQLRestore

This method causes the restore to be executed.

Using the objects listed in Table 7.5, you will create a form with options for the user to restore his database.

Steps
Open and run the VB.NET Chapter 7 solution. From the main Windows Form, click on the command button
with the caption How-To 7.3. You will then see the form displayed in Figure 7.8.
Figure 7.8. This form restores a SQL Server database.

As with How-To 7.2, a user clicks on the SQL Server for which he wants to display the databases. He can
then choose the database and backup device. From there, the user can click the Restore button to restore
the database.

1. Create a Windows Form. Then place the controls shown in Figure 7.8, with the following properties set
as in Table 7.6.

Table 7.6. Label, ListBox, DataGrid, and Command Button Controls Property
Settings
Object

Property

Setting

Label

Name

Label1

Text

SQL Servers

ListBox

Name

lstSQLServers

Label

Name

Label2

Text

Databases

ListBox

Name

lstDatabases

Label

Name

Label3

Text

Backup Devices

ListBox

Name

lstBackupDevices

Command Button

Name

btnBackup

Text

&Backup

Name

Label4

Text

Options

Panel

Name

Panel1

CheckBox

Name

chkReplaceDB

Text

Replace Database?

Label

Command Button

Checked

True

Name

btnClose

Text

&Close

2. On the form, add the code in Listing 7.14 to the Load event. This will look familiar from the first HowTo. For an examination of the LoadSQLServers routine, check out step 4 in How-To 7.1.
Listing 7.14 frmHowTo7_3.vb: Calling the Routine That Loads Available SQL Servers into a
List Box

Private Sub frmHowTo7_3_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
'-- Load up the SQL Servers
LoadSQLServers(Me.lstSQLServers)
End Sub

3. On the lstSQLServers list box, add the code in Listing 7.15 to the SelectedIndexChanged event. This
routine calls both GetSQLDatabases, described in step 6 of How-To 7.1, and GetBackupDevices,
described in step 4 of How-To 7.2.
Listing 7.15 frmHowTo7_3.vb: Populating the lstDatabases and lstBackupDevices List Boxes

Private Sub lstSQLServers_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) _
Handles lstSQLServers.SelectedIndexChanged
GetSQLDatabases(Me.lstSQLServers.SelectedItem, Me.lstDatabases)
GetBackupDevices(Me.lstSQLServers.SelectedItem, Me.lstBackupDevices)
End Sub

4. On the btnRestore button, add the code in Listing 7.16 to the Click event. After logging into the
server, the Restore object is created and its properties are set from the form. The SQLRestore
method is invoked and the connection is closed.
Listing 7.16 frmHowTo7_3.vb: Performing the Backup

Private Sub btnRestore_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnRestore.Click
'-- Create a connection to the server
Dim osvr As New SQLDMO.SQLServer()
osvr.LoginSecure = True

osvr.Connect(Me.lstSQLServers.SelectedItem)
'-- Create the restore object, set the properties from the form,
'
and execute the restore.
Dim oRestore As New SQLDMO.Restore()
With oRestore
.Action = SQLDMO.SQLDMO_RESTORE_TYPE.SQLDMORestore_Database
.Database = Me.lstDatabases.SelectedItem
.Devices = "[" & Me.lstBackupDevices.SelectedItem & "]"
.ReplaceDatabase = Me.chkReplaceDB.Checked
.SQLRestore(osvr)
End With
'-- Disconnect and clean up.
osvr.DisConnect()
osvr = Nothing
oRestore = Nothing
MessageBox.Show("Database Restored", "Task Completed", _
MessageBoxButtons.OK)
End Sub

5. Add the code in Listing 7.17 to the Click event of btnClose.


Listing 7.17 frmHowTo7_3.vb: Performing the Backup

Private Sub btnClose_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnClose.Click
Me.Close()
End Sub

How It Works
The Restore object is the counter object to the Backup object, allowing you to restore databases that have
been backed up. As with the Backup object, you can either specify the properties yourself or let the user
choose them on the form.

Comments
Again, you need to be careful which options you let the user specify and which you specify yourself. This is a
utility that you might want to secure; only let an administrator have access to it so that users don't
accidentally overwrite a good database with an old one.

As with the backup, you could enhance this utility by allowing the user to specify a file to restore from
instead of a backup device.

[ Team LiB ]

[ Team LiB ]

7.4 Transfer Tables Between SQL Server Databases


Users sometimes need to transfer (copy) tables between SQL Server databases. This How-To shows how to
allow the user to choose multiple tables and copy them from one database to another as well as tables from
two different databases on two different SQL servers.
One of the tasks your clients have you perform for them using the Enterprise Manager is to transfer objects,
such as tables, between SQL Server databases. How do you create a dialog box that would allow the user to
transfer databases between two SQL Server databases?

Technique
Unlike the earlier How-Tos in this chapter, you will be using the SQL-DTS object model in addition to SQLDMO. You can see the objects, properties, and methods that will be used from SQL-DTS in Table 7.7 .

Package
Steps.New

Tasks.New

Steps.Add

Tasks.Add

Execute
Step
TaskName

Name
Task
CustomTask
CustomTask
Name

SourceServer

SourceUseTrustedConnection

SourceDatabase
DestinationServer
DestinationUseTrustedConnection
DestinationDatabase

CopyAllObjects

IncludeDependencies

IncludeLogins

IncludeUsers

DropDestinationObjectsFirst

CopySchema

CopyData

AddObjectForTransfer

Table 7.7. SQL-DTS Objects That Are Used to Perform the Transfer of Tables from One SQL Server
Database to Another
Object

Property/Method

Using the items just listed, you will create a form with options to transfer tables between two SQL databases.

Steps
Open and run the VB.NET Chapter 7 solution. From the main Windows form, click on the command button
with the caption How-To 7.4. You will then see a form allowing you to pick SQL Servers on the network to
transfer from and to. After you have chosen these, you can then select which databases you want to transfer
from and to. After choosing from the database to transfer from, you are then presented with a list of tables
to transfer from. You can then highlight multiple tables, as shown in Figure 7.9 .
Figure 7.9. Transferring tables between SQL Server databases.

Tip

One of the options included as a property of the CustomTask object is


IncludeDependencies . This option specifies whether to have DTS transfer
related tables as well as the selected table(s). This could be put as an option on
the form as well. For this example, I have it set to True .

1. Create a Windows Form. Then place the controls shown in Figure 7.9 , with the following properties set
as in Table 7.8 .

Label
Name

Label1

Text
From SQL Servers
ListBox
Name
lstFromSQLServer
Label
Name
Label2

Text
To SQL Servers
ListBox
Name
lstToSQLServer
Label
Name
Label3

Text
Transfer from Database
ListBox
Name
lstFromDB
Label
Name
Label4

Text
Transfer to Database
ListBox
Name
lstToDB
Label
Name
Label5

Text
Table(s) to Transfer
ListBox
Name
lstTables

SelectionMode
MultiSimple
Command Button
Name
btnTransfer

Text
&Perform Transfer

Table 7.8. Label, ListBox, and Command Button Controls Property Settings
Object

Property

Setting

2. On the form, add the code in Listing 7.18 to the Load event. This will look familiar from How-To 7.1. For
an examination of the LoadSQLServers routine, check out step 4 in that How-To. Different from the
other How-Tos in this chapter thus far, however, is the fact that the LoadSQLServers routine is called
twice: once for the lstFromSQLServer , and a second time for the lstToSQLServer .
Listing 7.18 frmHowTo7_4.vb : Calling the Routine That Loads Available SQL Servers into a

List Box

Private Sub frmHowTo7_4_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
LoadSQLServers(Me.lstFromSQLServer)
LoadSQLServers(Me.lstToSQLServer)
End Sub

3. On the lstFromSQLServer and lstToSQLServer list boxes, add the code in Listing 7.19 to the
SelectedIndexChanged event of each, as appropriate. These routines call GetSQLDatabases ,
described in step 6 of How-To 7.1.
Listing 7.19 frmHowTo7_4.vb : Populating the lstDatabases and lstBackupDevices List Boxes

Private Sub lstFromSQLServer_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles lstFromSQLServer.SelectedIndexChanged
GetSQLDatabases(Me.lstFromSQLServer.SelectedItem, Me.lstFromDB)
End Sub
Private Sub lstToSQLServer_SelectedIndexChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles lstToSQLServer.SelectedIndexChanged
GetSQLDatabases(Me.lstToSQLServer.SelectedItem, Me.lstToDB)
End Sub

4. On the lstFromTables list box, add the code in Listing 7.20 to the SelectedIndexChanged event. This
routine starts off by logging onto the server that is selected in the lstFromSQLServer list box, and then
creates a reference to the database that is selected in the lstFromDB list box. After clearing the
lstTables list box, the routine iterates through each of the tables in the database and adds the names of
those that are user tables to the lstTables items.
Listing 7.20 frmHowTo7_4.vb : Populating the lstDatabases and lstBackupDevices List Boxes

Private Sub lstFromDB_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles lstFromDB.SelectedIndexChanged
'-Dim
Dim
Dim
Dim

Create the connection and specify the stored procedure to use.


odb As SQLDMO.Database
otbl As SQLDMO.Table
oapp As New SQLDMO.Application()
osvr As New SQLDMO.SQLServer()

Try
osvr.LoginSecure = True
osvr.Connect(Me.lstFromSQLServer.SelectedItem)
odb = osvr.Databases.Item(Me.lstFromDB.SelectedItem)
Me.lstTables.Items.Clear()
For Each otbl In odb.Tables
If otbl.TypeOf = _
SQLDMO.SQLDMO_OBJECT_TYPE.SQLDMOObj_UserTable Then
Me.lstTables.Items.Add(otbl.Name)
End If
Next
Catch excpData As Exception
MessageBox.Show("Error Occurred: " & excpData.Message)
End Try

End Sub

Tip

You could really make this a flexible and powerful utility by including different
objects to transfer other than just user tables. Some examples could be stored
procedures or views.

5. On the lstTables list box, add the code in Listing 7.21 to the SelectedIndexChanged event. This routine
enables the btnTransfer button.
Listing 7.21 frmHowTo7_4.vb : Performing the Transfer of Tables

Private Sub lstTables_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles lstTables.SelectedIndexChanged
Me.btnTransfer.Enabled = True
End Sub

6. Add the code in Listing 7.22 to the Click event of btnTransfer. This routine begins by declaring all the
objects to be used, and then creates new Step and Task objects, with the type of task being specified.
In this case, the task is of type DTSTransferObjectsTask . Next, the various necessary properties
are set on the Task object. For each of the tables to be transferred, the AddObjectForTransfer
method is executed, with the name of the table being passed to the method. After the name of the task
is added to the step, both objects are added to their collections in the Package object. The Execute

method of the Package object is then called.


Listing 7.22 frmHowTo7_4.vb : Performing the Transfer of Tables

Private Sub btnTransfer_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnTransfer.Click
Dim
Dim
Dim
Dim
Dim

oPackage As New DTS.Package()


oStep As DTS.Step
oTask As DTS.Task
oXferObj As DTS.TransferObjectsTask
intCurrTable As Integer

Try
'-- Create step and task
oStep = oPackage.Steps.New
oTask = oPackage.Tasks.New("DTSTransferObjectsTask")
oXferObj = oTask.CustomTask
'-- Configure transfer objects task
With oXferObj
.Name = "XferObjTask"
.SourceServer = Me.lstFromSQLServer.SelectedItem
.SourceUseTrustedConnection = True
.SourceDatabase = Me.lstFromDB.SelectedItem
.DestinationServer = Me.lstToSQLServer.SelectedItem
.DestinationUseTrustedConnection = True
.DestinationDatabase = Me.lstToDB.SelectedItem
.CopyAllObjects = False
.IncludeDependencies = True
.IncludeLogins = False
.IncludeUsers = False
.DropDestinationObjectsFirst = False
.CopySchema = True
.CopyData = DTS.DTSTransfer_CopyDataOption.DTSTransfer_AppendData
For intCurrTable = 0 To Me.lstTables.SelectedItems.Count - 1
.AddObjectForTransfer( _
Me.lstTables.SelectedItems.Item(intCurrTable), "dbo",
DTS.DTSSQLObjectType.DTSSQLObj_UserTable)
Next
End With
'-- Link step to task
oStep.TaskName = oXferObj.Name
oStep.Name = "XferObjStep"
oPackage.Steps.Add(oStep)
oPackage.Tasks.Add(oTask)

oPackage.Execute()
Catch excp As Exception
MessageBox.Show(excp.Message, "Error Occurred")
Exit Sub
End Try
MessageBox.Show("Tables Transferred")
End Sub

7. Add the code in Listing 7.23 to the Click event of btnClose.


Listing 7.23 frmHowTo7_4.vb : Closing the Form

Private Sub btnClose_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnClose.Click
Me.Close()
End Sub

How It Works
Using the Data Transformation Services API requires a bit more work than just using SQL-DMO. To use SQLDTS, you need to also have a concept of using workflow. Workflow allows you to specify steps in a package
and assign tasks to those steps. Task objects that are not assigned to steps can be included in the package,
but they will not be executed. You can see an example of multiple tasks being performed by the arrows in
the package in Enterprise Manager (see Figure 7.10 ).
Figure 7.10. This DTS package has multiple tasks that are being performed and shows workflow.

This example is simple in that it has only one step and task. For more information on using workflow and
DTS packages, check out SQL Server's Books On-Line, and look up "workflow."
As you create each of the tasks, you will have to set the various properties that are necessary to perform the
tasks. The source and destination servers and databases are examples of this.

Comments
As mentioned, using DTS takes a bit more work to understand than DMO, but after you understand what
needs to be done, there is little you can't perform using it.

[ Team LiB ]

[ Team LiB ]

7.5 Create a Detach/Attach SQL Server Database Dialog Box


Situations sometimes arise that require a database to be attached, detached, or both. Perhaps your client
needs to move the database from one SQL Server to another. This How-To shows you the methods you can
use to perform these tasks.
Before diving into solving some of the tasks that can be accomplished in the How-Tos just listed, it's
important to discuss the SQL-DMO object model, as well as create a reference to it.
Many times, you've used the Enterprise Manager to move databases for users by attaching and detaching
them. It would be nice to be able to perform this task without users calling you. At he very least, you'd like
to be able to walk users through the details over the phone, without having to use the Enterprise Manager.
How do you create a dialog box to perform this task?

Technique
This is probably the easiest of the How-Tos to create for this chapter, other than How-To 7.1. For this task,
you will be using two methods of the SQLServer object: DetachDB and AttachDBWithSingleFile .
Along with the two methods just mentioned, you will also be using a Tab control to choose which task to
perform, as well as an OpenFile Dialog control to allow the user to choose the file to attach.

Steps
Open and run the VB.NET Chapter 7 solution. From the main Windows form, click on the command button
with the caption How-To 7.5. You can select a database, such as the one displayed in Figure 7.11 , and click
the Detach button . You will see the database disappear from the list of databases to choose from.
Figure 7.11. Ready to detach the chosen database.

After you have chosen the database, you can reattach the database by clicking on the tab labeled Attach
Database. You can then type in the name you want to attach the database as, and click on the Locate File
button to locate the database file to attach (see Figure 7.12 .)

Figure 7.12. Choosing the database file to attach.

Select the file and click Open. To attach the file, click the Attach Database button . The database file will
then be attached, and you can see it in the list of databases. To check this, you can look at the list back on
the Detach Database tab; that list was refreshed when you clicked the Attach Database button.

1. Create a Windows Form.


2. Add a Tab control from the Windows Form Controls list. Click the builder button in the Tab Pages
property, and add two pages. Set the Text property for Page1 to Detach Database, and the Text
property for Page2 to Attach Database.
3. Add an OpenFileDialog control from the Windows Form Controls list. Go ahead and leave the default
name given to the control, but make a note of it.
4. Place the other controls shown in Figure 7.10 and 7.11 , with the following properties set as in Table
7.9 .

Label
Main Form
Name
Label1

Text
SQL Servers
ListBox

Main Form
Name
lstSQLServers
Label
Main Form
Name
Label2

Text
Databases
ListBox
Tab Page1
Name
lstDatabases
Button
Tab Page1
Name
btnDetach

Text
&Detach Database
Label
Tab Page2
Name
Label3

Text
File to Attach

TextBox
Tab Page2
Name
txtFileToAttach
Label
Tab Page2
Name
Label4

Text
Name of Attached Database
TextBox
Tab Page2
Name
txtNameOfAttach
Button
Tab Page2
Name
btnLocate

Text
&Locate File
Button
Tab Page2
Name
btnAttach

Text
&Attach Database

Table 7.9. Label, ListBox, and Command Button Controls Property Settings
Object

Location

Property

Setting

5. On the form, add the code in Listing 7.24 to the Load event. This will look familiar from How-To 7.1. For
an examination of the LoadSQLServers routine , check out step 4 in that How-To.
Listing 7.24 frmHowTo7_5.vb : Calling the Routine That Loads Available SQL Servers into a
List Box

Private Sub frmHowTo7_5_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
LoadSQLServers(Me.lstSQLServers)
End Sub

6. On the lstSQLServers list box, add the code in Listing 7.25 to the SelectedIndexChanged event. This
routine toggles the btnDetach button, depending on whether a SQL Server and database has been
selected. It then calls GetSQLDatabases , described in step 6 of How-To 7.1.
Listing 7.25 frmHowTo7_5.vb : Populating the lstDatabases List Boxes

Private Sub lstSQLServers_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) _
Handles lstSQLServers.SelectedIndexChanged
If lstSQLServers.SelectedItems.Count > 0 And _
lstDatabases.SelectedItems.Count > 0 Then
Me.btnDetach.Enabled = True
Else
Me.btnDetach.Enabled = False
End If
GetSQLDatabases(Me.lstSQLServers.SelectedItem, Me.lstDatabases)
End Sub

7. On the lstDatabases list box, add the code in Listing 7.26 to the SelectedIndexChanged event. This
routine toggles the btnDetach button, depending on whether a SQL Server and database have been
selected.
Listing 7.26 frmHowTo7_5.vb : Toggling the btnDetach Button Based on Whether a SQL

Server and Database Have Been Chosen

Private Sub lstDatabases_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) _
Handles lstDatabases.SelectedIndexChanged
If lstSQLServers.SelectedItems.Count > 0 And _
lstDatabases.SelectedItems.Count > 0 Then
Me.btnDetach.Enabled = True
Else
Me.btnDetach.Enabled = False
End If
End Sub

8. On the btnDetach button, add the code in Listing 7.27 to the Click event. After connecting to the
server, the DetachDB method is called. Then the GetSQLDatabases routine is called to refresh the
database list.
Listing 7.27 frmHowTo7_5.vb : Detaching a SQL Server Database

Private Sub btnDetach_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnDetach.Click
Dim oSQLSvr As New SQLDMO.SQLServer()
Dim strDetachMsg As String
Try
'-- Connect to the server
oSQLSvr.LoginSecure = True
oSQLSvr.Connect(Me.lstSQLServers.SelectedItem)
'-- Perform the detach
strDetachMsg = oSQLSvr.DetachDB(Me.lstDatabases.SelectedItem)
'-- Refresh the databases
GetSQLDatabases(Me.lstSQLServers.SelectedItem, Me.lstDatabases)
Catch excp As Exception
MessageBox.Show(excp.Message)
Exit Sub
End Try
MessageBox.Show("Database Detached")
End Sub

9.

9. Add the code in Listing 7.28 to the Click event of btnLocateFile. This routine uses the
OpenFileDialog control to retrieve the name of the file you want to attach.
Listing 7.28 frmHowTo7_5.vb : Locating the Database File to Attach

Private Sub btnLocateFile_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnLocateFile.Click
With OpenFileDialog1
.InitialDirectory = _
"E:\Program Files\Microsoft SQL Server\MSSQL\Data\"
.Filter = "SQL Server Database files (*.mdf)|*.mdf"
.ShowDialog()
Me.txtFileToAttach.Text = .FileName
End With
End Sub

10. Add the code in Listing 7.29 to the TextChanged event of txtFileToAttach and txtNameToAttach
as appropriate.
Listing 7.29 frmHowTo7_5.vb : Closing the Form

Private Sub txtFileToAttach_TextChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles txtFileToAttach.TextChanged
If Len(Me.txtFileToAttach.Text) > 0 And _
Len(Me.txtNameOfAttach.Text) > 0 Then
Me.btnAttach.Enabled = True
Else
Me.btnAttach.Enabled = False
End If
End Sub
Private Sub txtNameOfAttach_TextChanged(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles txtNameOfAttach.TextChanged
If Len(Me.txtFileToAttach.Text) > 0 And _
Len(Me.txtNameOfAttach.Text) > 0 Then
Me.btnAttach.Enabled = True
Else
Me.btnAttach.Enabled = False
End If
End Sub

11. Add the code in Listing 7.30 to the Click event of btnAttach. After connecting to the server, this code
calls the AttachDBWithSingleFile method . Then it refreshes the database list using the routine

11.

GetSQLDatabases .
Listing 7.30 frmHowTo7_5.vb : Attaching a SQL Server Database

Private Sub btnAttach_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnAttach.Click
Dim oSQLSvr As New SQLDMO.SQLServer()
Dim strAttachMsg As String
Try
oSQLSvr.LoginSecure = True
oSQLSvr.Connect(Me.lstSQLServers.SelectedItem)
'-- Attach the database
strAttachMsg = oSQLSvr.AttachDBWithSingleFile( _
Me.txtNameOfAttach.Text, Me.txtFileToAttach.Text)
'-- Refresh the databases
GetSQLDatabases(Me.lstSQLServers.SelectedItem, Me.lstDatabases)
Catch excp As Exception
MessageBox.Show(excp.Message)
Exit Sub
End Try
MessageBox.Show("Database Attached")
End Sub

How It Works
This How-To uses the DetachDB and AttachDBWithSingleFile methods to attach and detach a
database.

Comments
You can enhance this routine by allowing for databases that have multiple files to be attached and detached.
There is so much you can do with the APIs that SQL Server provides. Open the Enterprise Manager and look
at some of the various utilities, including Data Transformation Services. Then open the Object Browser to the
SQL-DMO and SQL-DTS libraries, and notice the correlation between tasks that are displayed using the
Enterprise Manager and the APIs.

[ Team LiB ]

[ Team LiB ]

Chapter 8. Taking Advantage of Data-Driven Techniques


In this chapter you will

Work with data-bound multi-select list boxes using Windows Forms


Use a single Windows Form to update multiple lookup tables
Create a point-and-click query tool for users using a Windows Form
Make a generic search form in a Visual Basic .NET desktop application
Work with data-bound multi-select list boxes using Web Forms
Use a single Web Form to update multiple lookup tables
Create a point-and-click query tool for users using a Web Form
Make a generic search form in an ASP.NET Web application
Whenever you are working with databases, whether with Visual Basic .NET or other languages, an efficient
method of development is to use data-driven techniques.
The term data driven can mean different things. Sometimes people have data-driven applications by having
all the options stored in tables and the interface driven from those tables. You can also make an application,
or portions of it, be data driven through other methods. Some of the ways are outlined here in this chapter,
along with this first one, which discusses using multi-select list boxes in Visual Basic .NET.
One of the benefits of using data-driven techniques is that you usually end up with the smaller code base,
especially if you use the technique for more than one task. An example of this is the search form example in
How-To 8.4 (for Windows Forms) and How-To 8.8 for the Web Form version. You can see the Windows Form
version of this technique used with the Customers table in Northwind in Figure 8.1.
Figure 8.1. This particular form can be called for searching different record sources.

As with other data-driven techniques, although this form is shown being used with the Customers table, with
just four lines of code-setting custom properties on this form, you can use it with just about any other
tables, such as Suppliers. The more you use a specific technique in your application, such as this search
form, the bigger benefit you receive in terms of number of lines of code and objects that are added to the
application.

Note
Make a note that this is a techniques chapter. By now, you should be
pretty familiar with all the objects and should be seeing new ways to
use those objects.

Note

The solution for this chapter has been broken up into two projects, a
Visual Basic .NET project called VB.NET Chapter 8, which covers
How-Tos 8.18.4. The rest of the How-Tos in this chapter are in an
ASP.NET project called VBNetHowToChap8Web.
As with the code, this chapter could be started in two different
chapters, depending on whether you wanted to use the techniques for
Visual Basic .NET or ASP.NET. If you want the desktop solutions, start
with How-To 8.1; for the Web, you can start with How-To 8.5.

Note
This chapter is split up to cover data-driven techniques using both
Windows Forms and Web Forms. These techniques are useful in both
areas. In addition, they are different enough in the way they are
coded that they warranted separate How-Tos for each technique and
environment.

[ Team LiB ]

[ Team LiB ]

8.1 Work with Data-Bound Multi-Select List Boxes Using Windows Forms
It is common to have to assign products to categories, which is a one-to-many relationship. Sometimes you
want to be able to do this in a somewhat bulk fashion. One of the methods that works well is using the
ListBox control. Using the ListBox control for single selections is no big deal, but when it comes to using
it in a multi-select method, it starts getting trickier. This How-To shows you how to create an intuitive
interface for assigning products to categories using a couple of multi-select list boxes on a Windows Form.
You can assign a category to each product, but you would like to have a method of maintaining all the
products for a category at one time. How do you take advantage of a multi-select list box to perform this
task?

Technique
Using the ListBox control in regular single selection mode is as straightforward in .NET as in prior versions
of Visual Basic. The same can be said in the case of using the multi-select mode of list boxes. It is as
confusing in .NET as it was in prior versions.
Using the ListBox control in single entry mode is pretty straightforward. You just need to use the
SelectedItem property with the index of 0. However, if you want to use the ListBox control in multiselect mode, then you must perform some more work and access some other properties (see Table 8.1).

Table 8.1. Properties Having to Do with Multi-Selection on ListBox Controls (In Order of Use in
This How-To's Steps)
Property/Object

Description

SelectionMode

Property of the ListBox control. The settings for this are None, One,
MultiSimple, or MultiExtended.

SelectedIndices
(index)

A collection of the ListBox control, this returns the indices (location in the
list) of all the selected items.

SelectedIndices.Count Property of the ListBox control. A count of the number of items selected in
the list box.

DataRowView

Object type that the data provider provides.

Items (index)

A collection of the ListBox control. Returns a DataRowView type object. If


you have multiple columns, they are also returned in the DataRowView
object.

Steps
Open and run the VB.NET Chapter 8 solution. From the main Windows Form, click on the command button
with the caption How-To 8.1. You will then see the form displayed in Figure 8.2.
Figure 8.2. This form uses controls bound at runtime and takes advantage of multi-select list
boxes.

When the form loads, you will see the Beverages category chosen in the top combo box. The Selected and
Unselected Products ListBox controls are filled in with the appropriate products. If you click on a product in
the Unselected Products list box and then click on the arrow button pointing to the right (>), then the item is
moved to the Selected Products list box. If you select items in the Selected Products list box and click on the
arrow button pointing to the left (<), those items are moved to the Unselected Products list box.
If you click on the Unassigned Products Only check box located at the bottom of the form, then the
Unselected Products list box is filled with products that are not assigned to any category.

Note
If you check the Unassigned Products Only check box when you're first
getting into Northwind and running this example, you probably won't
see unassigned items. You will need to unselect products from a
category.

1. Create a Windows Form. Then place the controls shown in Figure 8.2 with the following properties set
in Table 8.2.

Table 8.2. Label, ComboBox, ListBox, and Command Button Control Property
Settings

Object

Property

Setting

Label

Text

Category

ComboBox

Name

cboCategories

Label

Text

Unselected Products

ListBox

Name

lstUnSelected

SelectionMode MultiSimple
Label

Text

Selected Products

ListBox

Name

lstSelected

SelectionMode MultiSimple
Command Button
Command Button
CheckBox

Name

btnSelect

Text

>

Name

btnUnSelect

Text

<

Name

chkUnAssignedOnly

Text

UnAssigned Products Only

2. As with some of the other chapters' projects, before creating the code that will be attached to the Load
event of the form, you need to create a support routine to create the Connection string. Called
BuildCnnStr, the function can been seen in Listing 8.1. This function takes a server and database
names passed to it and creates a connection string.
Listing 8.1 modGeneralRoutines.vb: Creating a Connection String

Function BuildCnnStr(ByVal strServer As String,


ByVal strDatabase As String) As String
Dim strTemp As String
strTemp = "Provider=SQLOleDB; Data Source=" & strServer & ";"
strTemp &= "Initial Catalog=" & strDatabase & ";"
strTemp &= "Integrated Security=SSPI"
Return strTemp
End Function

Although you could create a routine that would pass back a Connection object, a more versatile
method would be to pass back a string. Some objects ask you for a Connection object, but
others just ask for a string. You will see BuildCnnStr called in the next step.

3. On the form, add the code in Listing 8.2 to the Load event. In this code, you will start off by creating a
data adapter called odaCategories and loading the category's SQL Statement into it. The
dtCategories data table is then filled and set as the DataSource property of cboCategories. The
DisplayMember and ValueMember of cboCategories are then set. Finally, two new subroutines called
LoadUnSelectedProducts and LoadSelectedProducts are called to populate the appropriate list
boxes. These routines are discussed in the next two steps.
Listing 8.2 frmHowTo8_1.vb: Loading Categories into the List Box

Private Sub frmHowTo8_1_Load(ByVal sender As Object,


ByVal e As System.EventArgs) Handles MyBase.Load
Dim odaCategories As OleDb.OleDbDataAdapter
Dim dtCategories As New data table()
'-- Load the Categories combo box up first
odaCategories = New _
OleDb.OleDbDataAdapter( _
"Select CategoryID, CategoryName From Categories",
(BuildCnnStr("(local)", "Northwind")))
odaCategories.Fill(dtCategories)
Me.cboCategories.DataSource = dtCategories
Me.cboCategories.DisplayMember = "CategoryName"
Me.cboCategories.ValueMember = "CategoryID"
'-- Load each of the product list boxes based on the selected category.
LoadUnSelectedProducts()
LoadSelectedProducts()
End Sub

4. Create the LoadUnSelectedProducts routine by entering the code shown in Listing 8.3 into the form
you created for this example. This routine starts off by testing the check box called
chkUnAssignedOnly . Based on that value, a SQL string is created that grabs the products that are
not assigned to any product, if chkUnAssignedOnly = True. All products that are not assigned to
the chosen category are retrieved. The SQL String is stored in the variable called strSQL. Next, the
DataAdapter object called odaUnselected is set to strSQL and the SQL Server connection string.
The DataTable object called dtUnSelected is then filled and assigned to the list box called
lstUnSelected. The DisplayMember and ValueMember properties are then set. Last, the
ClearSelected method is called to make sure no entries remain selected.
Listing 8.3 frmHowTo8_1.vb: Populating the List Box Displaying Unselected Products

Sub LoadUnSelectedProducts()
Dim odaUnSelected As OleDb.OleDbDataAdapter
Dim dtUnSelected As New DataTable()
Dim strSQL As String
'-- If the check box for Unassigned Only is checked, then
'
grab the product items where the category is null; otherwise, load
'
it up with those products not assigned to the current category.
If chkUnAssignedOnly.Checked Then
strSQL = "Select ProductID, ProductName From Products " & _ "
Where CategoryID IS NULL Order By ProductName"
Else
strSQL = "Select ProductID, ProductName From Products " & _
"Where CategoryID <> " & _

Me.cboCategories.SelectedItem(0) &
" Or CategoryID IS NULL Order By ProductName"
End If
'-- Pretty well same old, same old here. Create a data adapter
' and fill the dataset.
'
Next, bind it to the list box.
odaUnSelected = New OleDb.OleDbDataAdapter(strSQL,
(BuildCnnStr("(local)", "Northwind")))
odaUnSelected.Fill(dtUnSelected)
Me.lstUnSelected.DataSource = dtUnSelected
Me.lstUnSelected.DisplayMember = "ProductName"
Me.lstUnSelected.ValueMember = "ProductID"
Me.lstUnSelected.ClearSelected()
End Sub

5. Create the LoadSelectedProducts routine by entering the code in Listing 8.4 into the form you
created for this tutorial. This routine performs basically the same tasks that the routine listed in the
previous step does, except that it performs the tasks using the lstSelected ListBox control. It also
doesn't need to test the CheckBox control.
Listing 8.4 frmHowTo8_1.vb: Populating the List Box Displaying Selected Products

Sub LoadSelectedProducts()
Dim odaSelected As OleDb.OleDbDataAdapter
Dim dtSelected As New DataTable()
Dim strSQL As String
'-- Load the products assigned to this category.
strSQL = _
"Select ProductID, ProductName From Products Where CategoryID = " &
Me.cboCategories.SelectedItem(0) & " Order By
ProductName"
odaSelected = New _
OleDb.OleDbDataAdapter(strSQL, (BuildCnnStr("(local)",
"Northwind")))
odaSelected.Fill(dtSelected)
Me.lstSelected.DataSource = dtSelected
Me.lstSelected.DisplayMember = "ProductName"
Me.lstSelected.ValueMember = "ProductID"
Me.lstSelected.ClearSelected()
End Sub

6.

6. Add the code in Listing 8.5 to the SelectedIndexChanged event of the cboCategories combo box.
Listing 8.5 frmHowTo8_1.vb: Repopulating the List Boxes Based on the Current Category
That Is Selected

Private Sub cboCategories_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles cboCategories.SelectedIndexChanged
'-- Load each of the product list boxes based on the selected category.
LoadUnSelectedProducts()
LoadSelectedProducts()
End Sub

7. Add the code in Listing 8.6 to the CheckChanged event of the chkUnAssignedOnly check box.
Listing 8.6 frmHowTo8_1.vb: Call the Routine to Reload the lstUnSelected List Box If This
Check Box Is Changed

Private Sub chkUnAssignedOnly_CheckedChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles chkUnAssignedOnly.CheckedChanged
LoadUnSelectedProducts()
End Sub

8. Add the code in Listing 8.7 to the Click event of the btnSelect command button. This and the next
step contain the most code as well as some new objects and properties. The first thing that happens is
that the number of highlighted items (SelectedIndices.Count) is stored to an Integer variable
called intItemsNum. One is subtracted from the figure because the collections in .NET are zero based.
Next, the code iterates through the SelectedItems collection of the lstUnSelected list box, and using
the indices in that collection, the code accesses selected items. The type of object derived from the
Items collection is the DataRowView object, mentioned in the "Techniques" section of this example.
These items are then added to a string variable called strItems, which is then used to create the
criteria for an IN clause of a SQL Update statement. This statement is passed to the Command object
called ocmdSelect. This Command object is then executed, and the selected products are updated to
reflect the category chosen. Last, the list boxes are reloaded to reflect the changes.
Listing 8.7 frmHowTo8_1.vb: Updating the Server with Products Selected for the Given
Category

Private Sub btnSelect_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnSelect.Click
Dim intItemsNum As Integer

Dim intCurr As Integer


Dim strItems As String
Dim drv As DataRowView
'-- Grab the number of selected items for the products
' unselected list box.
intItemsNum = Me.lstUnSelected.SelectedIndices.Count - 1
'-- Iterate through each of the items and create a string.
For intCurr = 0 To intItemsNum
If Len(strItems) > 0 Then
strItems = strItems & ", "
End If
drv = Me.lstUnSelected.Items(Me.lstUnSelected. _
SelectedIndices(intCurr))
strItems = strItems & CType(drv(0), String)
Next
'-- Run an update query to assign the category to the desired products
'-- using an IN clause in the SQL statement
Try
Dim ocnn As New OleDb.OleDbConnection(BuildCnnStr("(local)",
"Northwind"))
Dim ocmdSelect As New _
OleDb.OleDbCommand("Update Products Set CategoryID = " &
Me.cboCategories.SelectedItem(0) & _
" Where ProductID IN (" & strItems & ")", ocnn)
ocmdSelect.CommandType = CommandType.Text
ocnn.Open()
ocmdSelect.ExecuteNonQuery()
Catch excpCommand As Exception
MessageBox.Show(excpCommand.Message)
End Try
LoadUnSelectedProducts()
LoadSelectedProducts()
End Sub

9. Add the code in Listing 8.8 to the Click event of the btnUnSelect command button. Again, this code is
similar to the previous step, but it is used to set the CategoryID column to null if the product was
highlighted in the lstSelected list box and btnUnSelect was clicked.
Listing 8.8 frmHowTo8_1.vb: Updating the Server with Products That Are Unselected for the
Given Category

Private Sub btnUnSelect_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnUnSelect.Click
Dim
Dim
Dim
Dim

intItemsNum As Integer
intCurr As Integer
strItems As String
drv As DataRowView

'-- Grab the number of selected items for the products


' selected list box.
intItemsNum = Me.lstSelected.SelectedIndices.Count - 1
'-- Iterate through each of the items and create a string.
For intCurr = 0 To intItemsNum
If Len(strItems) > 0 Then
strItems = strItems & ", "
End If
drv = Me.lstSelected.Items(Me.lstSelected.SelectedIndices(intCurr))
strItems = strItems & CType(drv(0), String)
Next
Try
Dim ocnn As New OleDb.OleDbConnection(BuildCnnStr("(local)", _
"Northwind"))
Dim ocmdUnSelect As New OleDb.OleDbCommand(
"Update Products Set CategoryID = Null Where ProductID IN ("
& strItems & ")", ocnn)
ocmdUnSelect.CommandType = CommandType.Text
ocnn.Open()
ocmdUnSelect.ExecuteNonQuery()
Catch excpCommand As Exception
MessageBox.Show(excpCommand.Message)
End Try
LoadUnSelectedProducts()
LoadSelectedProducts()
End Sub

How It Works
When the user chooses a category, the appropriate items are loaded into the two list boxes; unselected
items are placed in the list box on the left, and selected items are placed in the list box on the right.
If the check box is selected, then only those items that are not currently assigned to a category are
displayed in the list box on the left, which is titled Unselected Products.
When the btnSelect button is clicked, any items highlighted in the lstUnSelected list box are used in a query
that updates the server with the new category they now belong to. Similarly, when the btnUnSelect is

clicked, items in the lstSelected list box are used in a query that updates the CategoryID of the products to
null.

Comments
This example is not the smartest to create in real life because you want products to be assigned to a
category. However, this example does a good job of showing the properties and methods you can use to
work with the multi-select features of the ListBox control.
For examples of using this same basic technique in a Web Form, check out example 8.5.

[ Team LiB ]

[ Team LiB ]

8.2 Use a Single Windows Form to Update Multiple Lookup Tables


Just about every database application uses lookup tables of some sort or another, such as categories,
regions, and territories in the Northwind database. Normally, each of these lookup tables would get its own
form for viewing or updating the information in the tables. This example will provide the means for you to
create a single form to maintain any of your simple lookup tables, again using a Windows Form.
You have a number of simple lookup tables in your application, and it is a pain to have to create a form for
each table. How do you create a Windows Form that can be used to update most if not all of your lookup
tables that are contained in your database?

Technique
Using two main controls, the ListBox control and DataGrid control, you can create a form that will take care
of most of your simple lookup tables. When you get into those tables that contain lookups or graphics, you
will have to come up with a more complete method to modify the data.
For this example, you will be using some familiar friends: DataAdapter , CommandBuilder , and
DataTable objects.

Steps
Open and run the VB.NET Chapter 8 solution. From the main Windows Form, click on the command button
with the caption How-To 8.2. You will then see the form displayed in Figure 8.3 .
Figure 8.3. The DataGrid control is filled with different data every time a new item is chosen in
the ListBox control.

When you choose a new item from the list of tables on the left, the data grid on the right becomes filled with
the data from the chosen table. You can then modify the data in the data grid and click on the Update button
to update the data back to the server. This includes modifying existing records, as well as adding and
deleting records in the DataGrid control.

1. Create a Windows Form. Then place the controls shown in Figure 8.3 with the properties set forth in Table

1.
8.3 .

Label
Name
Label1

Text
Lookup Table to Edit
Label
Name
Label2

Text
Lookup Table Data
ListBox
Name
lstLookupTables
DataGrid
Name
dgTableData
Button
Name
btnUpdate

Table 8.3. Label, ListBox, DataGrid, and Command Button Controls Property Settings
Object

Property

Setting

2. Add data to the Items collection of lstLookupTables. To do this, click on the builder button beside the
Items property in the property sheet for lstLookupTables. You will then see the String Collection Editor as
shown in Figure 8.4 . The Items you will add include Categories, Region, and Territories.
Figure 8.4. Adding hard-coded items at design is pretty straightforward using this dialog box.

Note

To make this example truly "data driven," you would want to either keep these
table names in a table by themselves and set the data source of lstLookupTables
to that table, or generate the list at runtime by naming your lookup tables a
specific way.
These values are hard coded for the sake of expediency and so that no more tables are added to
Northwind.

3. In the class module for the form, add the following Private declarations just below the line of code that
reads Windows Form Designer generated code .

Dim mcnn As New OleDb.OleDbConnection()


Dim modaLookupData As OleDb.OleDbDataAdapter

These lines of code declare Connection and DataAdapter objects that will be used throughout the
form.

4. On the form, add the code in Listing 8.9 to the Load event.
Listing 8.9 frmHowTo8_2.vb : Establishing the Connection String and Pointing to the First Item
in lstLookupTables

Private Sub frmHowTo8_2_Load(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
'-- Initialize the connection string.
mcnn.ConnectionString = BuildCnnStr("(local)", "Northwind")
'-- Point to the first lookup table; this fires
'
the SelectedIndexChanged event off the list box.
Me.lstLookupTables.SelectedIndex = 0
End Sub

5. On the lstLookupTables list box, add the code in Listing 8.10 to the SelectedIndexChanged event. This
routine assigns the new table chosen in lstLookupTables as the Select command for the
modaLookupData data adapter. The data table called dtData is then filled and set as the data source for
dgTableData.
Listing 8.10 frmHowTo8_2.vb : Populating the DataGrid Control

Private Sub lstLookupTables_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles lstLookupTables.SelectedIndexChanged
Dim dtData As New DataTable()
Try
'-- Update the data adapter and data table to reflect the new data,
'
and reassign the data source of the data grid.
modaLookupData = New OleDb.OleDbDataAdapter("Select * From " &
Me.lstLookupTables.Text, mcnn)
modaLookupData.Fill(dtData)
Me.dgTableData.DataSource = dtData
Catch excData As Exception
MessageBox.Show(excData.Message)
End Try
End Sub

6. On the btnUpdate button, add the code in Listing 8.11 to the Click event. This routine performs a task
you have seen in one form or another throughout this book. A CommandBuilder object is created off the
modaLookupData data adapter, and it is used to update the data table. The Update method of
modaLookupData is executed, followed by the AcceptChanges method of the dtFromGrid data table.

Listing 8.11 frmHowTo8_2.vb : Populating the DataGrid Control

Private Sub btnUpdate_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnUpdate.Click
Dim dtFromGrid As DataTable
'-- Create the command builder to update (post) the data in the data grid
'
back to the server.
Dim custCB As OleDb.OleDbCommandBuilder = _
New OleDb.OleDbCommandBuilder(modaLookupData)
Try
'-- Have to open the connection.
mcnn.Open()

'

'-- Grabbing the data table from the DataSource


property of the data grid
'
saves a bunch of hassles trying to track the data table directly.
dtFromGrid = CType(dgTableData.DataSource, DataTable)
'-- Commands necessary to actually post back to server.
modaLookupData.Update(dtFromGrid)
dtFromGrid.AcceptChanges()
'-- Don't forget to close the connection.
mcnn.Close()
Catch excp As Exception
MessageBox.Show("Couldn't update server")
End Try

End Sub

Tip

One interesting thing of note is the way that the DataTable object is derived in
this routine. Instead of creating it from a DataSet or DataAdapter object, we
get it from the DataGrid object, with the line of code that reads like this:

dtFromGrid = CType(dgTableData.DataSource, DataTable)

How It Works
When the form opens, the initial table's data in the list box is loaded into the DataGrid control by setting
the SelectedIndex of the lstLookupTables to 0. When this occurs and when a user selects a new item in the
list, a Select statement is generated and loaded into a data adapter, which fills a data table. This, in turn, is
used for the data source of the data grid, and the data is displayed.
When the user clicks the button with the caption Update, a CommandBuilder object is generated off the
DataAdapter object. The Update command for the data adapter is then invoked, with the Update method
called. The data table is then referenced from the data source of the DataGrid control, and the
AcceptChanges method is called.

Comments
Creating this kind of utility will save hundreds of hours in some cases, especially if you use it across
applications. When starting with something this simple, you can add features to it every time you use it for a
new and expanded purpose.

[ Team LiB ]

[ Team LiB ]

8.3 Create a Point-and-Click SQL Server Query Tool for Users Using a Windows Form
Clients usually want a means of querying the tables, but they do not necessarily know how to create SQL
statements. This example describes how to create a point-and-click query interface using a Windows Form
and display fields from individual tables as they are chosen.
In just about every application you create, your clients need a means to view the data and want to be able
to create their own lists. However, most don't want to have to learn how to create SQL statements. In this
How-To, you will see a method for not only creating a point-and-click query tool that will allow the users to
examine all tables in the database, but also for using the Windows Form in an application without
modification.

Technique
To accomplish the task just presented, you will be using the OleDbCommand and DataReader object. Along
with these objects, you will be using some stored procedures that SQL Server supplies. These stored
procedures list the various objects within a SQL Server databasein this case, Northwind's tables and
columns.
You will take the elements returned in the DataReader object and load the Add method of the ListBox
object.

Steps
Open and run the VB.NET Chapter 8 solution. From the main Windows Form, click on the command button
with the caption How-To 8.3. The first list box you see to the left is populated with the tables from
Northwind. Click on the Customer table, and you will see the columns in the next list box labeled Columns.
Click on the CompanyName and ContactName, and you will see the SQL String text box filled in. After
clicking on the View button, the form will look like the one displayed in Figure 8.5.

1. Create a Windows Form. Then place the controls shown in Figure 8.5 with the properties set forth in
Table 8.4.

Table 8.4. Labels, ListBoxes, DataGrid, TextBox, and Command Button Controls
Property Settings

Object

Property

Setting

Label

Name

Label1

Text

Tables

Name

Label2

Text

Columns

Name

Label3

Text

SQL String

Name

Label4

Text

Data Display

ListBox

Name

lstTables

ListBox

Name

lstColumns

SelectionMode

MultiSimple

Name

txtSQLString

MultiLine

True

Button

Name

btnView

DataGrid

Name

dgDisplay

Label
Label
Label

TextBox

Tip
Notice that the lstTables list box only allows the user to pick one
table at a time, whereas lstColumns allows you to choose multiple
columns.
A great enhancement to this tool would be to allow the user to
select multiple tables and have the application figure out the
relation between tables.

2. In the class module for the form, add the following Private declaration just below the line of code that
reads Windows Form Designer generated code:

Dim mcnn As New OleDb.OleDbConnection(BuildCnnStr("(local)", "Northwind"))

This line of code declares and assigns an OleDBConnection object that will be used throughout
the form.

3. On the form, add the code in Listing 8.12 to the Load event. The first thing this code routine does is
create a new OleDbCommand called ocmdTables and assign the built-in SQL Server stored procedure
called sp_Tables. After establishing the CommandType as being CommandType.StoredProcedure
and then opening the connection, the data reader called odrTables is created by calling the
ExecuteReader method off ocmdTables.
Listing 8.12 frmHowTo8_3.vb: Executing a SQL Server-Supplied Stored Procedure That Lists
the Tables in the Database

Private Sub frmHowTo3_8_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
'-- Create the connection and specify the stored procedure to use.
Dim ocmdTables As New OleDb.OleDbCommand("sp_Tables", mcnn)
Dim odrTables As OleDb.OleDbDataReader
Try
'-- Specify the type of command being performed
ocmdTables.CommandType = CommandType.StoredProcedure
mcnn.Open()
'-- Create the DataReader object
odrTables = ocmdTables.ExecuteReader()
'-- Loop through and add table-type object names
' to the lstTables list box.
Do While odrTables.Read
If odrTables.GetString(3) = "TABLE" Then
Me.lstTables.Items.Add(odrTables.GetString(2))
End If
Loop
mcnn.Close()
Catch excpData As Exception
MessageBox.Show("Error Occurred: " & excpData.Message)
End Try
End Sub

Next, the code loops though each of the items returned by the command. Those of type TABLE
are added to the lstTables items. Then the connection is closed.
As mentioned, you will see a comparison to the literal "TABLE." The reason for this is that the
fourth column returned is the same table type as the current table. The other two types are
SYSTEMTABLE and VIEW. To see the data returned by the sp_tables stored procedure, open
the Query Analyzer, located on the Start menu, in Programs, Microsoft SQL Server. After opening
up the Query Analyzer, highlight the Northwind database, and then type execute sp_tables
into the Query Edit window and press F5 to execute the query. The results will be shown in the
bottom of the window. Page down through the data until you see some of the type "TABLE" (see
Figure 8.6).
Figure 8.6. Testing the built-in stored procedure called sp_tables.

4. On lstTables, add the code in Listing 8.13 to the SelectedIndexChanged event. This routine
performs a similar feat to the previous routine in that it calls a built-in stored procedurein this case,
sp_Columns. However, the next task in this step is to pass a parameter, TableName, which is the
table chosen in lstTables. After the connection is opened, the data reader called odrColumns is loaded
with the ExecuteReader command. After the lstColumns.Items.Clear() method is called to clear
the list, the new columns are added to lstColumns Items collection. Last, the connection is closed.
Listing 8.13 frmHowTo8_3.vb: Executing a SQL Server Built-In Stored Procedure That Lists
the Columns of a Supplied Table in the Database

Private Sub lstTables_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles lstTables.SelectedIndexChanged
'-- Create the connection and specify the stored procedure to use.
Dim ocmdColumns As New OleDb.OleDbCommand("sp_Columns", mcnn)
Dim odrColumns As OleDb.OleDbDataReader
Try
'-- Specify the type of command being performed
ocmdColumns.CommandType = CommandType.StoredProcedure
ocmdColumns.Parameters.Add("@TableName", Me.lstTables.Text)
mcnn.Open()
'-- Create the DataReader object
odrColumns = ocmdColumns.ExecuteReader()
'-- Clear the current items in the list

Me.lstColumns.Items.Clear()
'-- Loop through and add table type object names
'
to the lstTables list box.
Do While odrColumns.Read
Me.lstColumns.Items.Add(odrColumns.GetString(3))
Loop
mcnn.Close()
Catch excpData As Exception
MessageBox.Show("Error Occurred: " & excpData.Message)
End Try
End Sub

5. On lstColumns, add the code in Listing 8.14 to the SelectedIndexChanged event. This routine
iterates through the SelectedItems collection of the lstColumns ListBox control, adding the
chosen column names to a string variable called strTemp . The length of the string is checked; if the
length is greater than 0, the Text property of txtSQLString is set to the following expression:
"Select " & strTemp & " From " & Me.lstTables.Text .
Listing 8.14 frmHowTo8_3.vb: Creating the SQL String

Private Sub lstColumns_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles lstColumns.SelectedIndexChanged
Dim strTemp As String
Dim intNumColumns As Integer
Dim oCurr As Object
'-- Cycle through each of the selected columns of the table chosen
'
and combine them into a string.
For Each oCurr In Me.lstColumns.SelectedItems()
If Len(strTemp) > 0 Then
strTemp &= ", "
End If
strTemp &= oCurr
Next
'-- Take the string created and add it to the table
'
name for a SQL String
'
if columns are chosen.
If Len(strTemp) = 0 Then
Me.txtSQLString.Text = ""
Else
Me.txtSQLString.Text = "Select " & strTemp & " From " & _
Me.lstTables.Text

End If
End Sub

6. On btnView, add the code in Listing 8.15 to the Click event. This routine creates the new data
adapter called odaDisplay passes the Text property of txtSQLString , and then fills the dtDisplay
data table. dtDisplay is then set to the DataSource property of the data grid called dgDisplay.
Listing 8.15 frmHowTo8_3.vb: Loading the DataGrid Control with the Specified Data

Private Sub btnView_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnView.Click

Dim odaDisplay As OleDb.OleDbDataAdapter


Dim dtDisplay As New DataTable()
Try
'-- Take the txtSQLString text and create a data table; then set the
'
data source of the data grid.
odaDisplay = New OleDb.OleDbDataAdapter(Me.txtSQLString.Text, mcnn)
odaDisplay.Fill(dtDisplay)
Me.dgDisplay.DataSource = dtDisplay
Catch excData As Exception
MessageBox.Show(excData.Message)
End Try
End Sub

Figure 8.5. You can set the sorting of the data grid displayed here by clicking on the desired
column.

How It Works
When the form is opened, the lstTables ListBox control is loaded with the tables from the Northwind
database. When the user selects a table from the list, that table name is passed to the stored procedure that
lists the columns in a table located in the database specified in the connectionin this case, Northwind.
These columns are loaded into lstColumns.
The user can then click on multiple columns in lstColumns. The columns are then added to the SQL Select
string that is created and stored in txtSQLString . When the btnView button is clicked, the string is passed
to a DataAdapter control, filling a data table. The data is then displayed when the data source of the
DataGrid control is set to the data table.

Comments
You can enhance this tool in a number of ways:

Allow users to click on multiple tables and automatically create the join.
Add a list of columns for the user to choose to use for criteria, and allow the user to input the criteria.
Use this tool as a base for editing or reporting the records that are returned.
Let the users specify the sorting order using a combo box.

Tip

This last enhancement isn't necessary using the DataGrid control


because you can click on the column heading and have it sort the
columns for you.

The goal of this technique, as with others in this book, is to push you into thinking about the possibilities of
what you can accomplish with Visual Studio .NET and your databases.

[ Team LiB ]

[ Team LiB ]

8.4 Make a Generic Search Form in a Visual Basic .NET Desktop Application
Another useful utility that takes advantage of being data driven is a standard search form that you can use
for any number of tables, such as customers, employees, or products. This How-To shows you how to create
such a Windows Form so that all you need to use it with different tables is to set four custom properties of
the form in code.
You like to be able to provide a usable search form for my users, without having the hassle of creating a
custom form for every topic that the users are maintaining. In this How-To, you will see how to create a form
that provides a quick lookup for records and can be used with various topics, such as Customers and
Employees, by setting up only a few custom properties on the search form.

Technique
The forms package of Visual Basic has a class module behind it where you can add your own properties and
methods. The .NET version of it is no exception.
In this How-To, you will see a simple use for custom properties that are being added to a form. Properties
can be specified on a form by adding the following syntax to your form:

Public Property PropertyName() As DataType


Get
PropertyName = ModuleLevelMemoryVariable
End Get
Set(ByVal Value As DataType)
ModuleLevelMemoryVariable = Value
End Set
End Property

With the ModuleLevelMemoryVariable being declared in the module declaration area of the form's class
module, you can see the properties created for the search form, called frmHowTo8_b.vb, in Listing 8.16.
Listing 8.16 frmHowTo8_4b.vb: Creating Custom Properties for the Search Form

Private
Private
Private
Private
Private

mstrDisplayName As String
mstrRecordSource As String
mstrSearchField As String
moResultValue As Object
mstrKeyField As String

Public Property DisplayName() As String


Get
DisplayName = mstrDisplayName
End Get
Set(ByVal Value As String)
mstrDisplayName = Value
End Set

End Property
Public Property SearchRecordSource() As String
Get
SearchRecordSource = mstrRecordSource
End Get
Set(ByVal Value As String)
mstrRecordSource = Value
End Set
End Property
Public Property SearchField() As String
Get
SearchField = mstrSearchField
End Get
Set(ByVal Value As String)
mstrSearchField = Value
End Set
End Property
Public Property KeyField() As String
Get
KeyField = mstrKeyField
End Get
Set(ByVal Value As String)
mstrKeyField = Value
End Set
End Property
Public Property ResultValue() As Object
Get
ResultValue = moResultValue
End Get
Set(ByVal Value As Object)
moResultValue = Value
End Set
End Property

By assigning values to these properties after initiating an instance of the form, you can utilize the properties
and the data stored in those properties from within the forms properties and methods, as well as the
procedures assigned to the events within the form.
For more information on creating custom classes, properties, and methods for use with your database
application, see Chapter 9, "Using Classes with Databases to Make Life Easier."

Steps
Open and run the VB.NET Chapter 8 solution. From the main Windows Form, click on the command button
with the caption How-To 8.4a. This form is a simple one that contains text boxes for the Customer table in
Northwind. Click on the Search button to open the search form. Click on the button labeled B. You will see
the data grid displayed in the bottom of the form filled with the CompanyName column of the Customer
table, beginning with the letter B (see Figure 8.7).
Figure 8.7. This form can be used for searching within any of the tables in your databases.

Place the cursor in one of the customers displayed in the grid, and then click Accept. The search form will be
hidden, and the fields in the first form will be filled in with the data from the chosen record.

1. Create a Windows Form. Then place the controls on the form shown in Figure 8.7, with the properties
set forth in Table 8.5.

Table 8.5. Label, TextBox, and Command Button Controls Property Settings for the
Calling Form

Object

Property

Setting

Label

Caption

Customer ID

Label

Caption

Company Name

Label

Caption

Contact

Label

Caption

Contact Title

Label

Caption

Address

Label

Caption

City

Label

Caption

Region

Label

Caption

Country

Label

Caption

Phone

Label

Caption

Fax

TextBox

Name

txtCustomerID

TextBox

Name

txtCompanyName

TextBox

Name

txtContact

TextBox

Name

txtContactTitle

TextBox

Name

txtAddress

TextBox

Name

txtCity

TextBox

Name

txtRegion

TextBox

Name

txtPostalCode

TextBox

Name

txtCountry

TextBox

Name

txtPhone

TextBox

Name

txtFax

Button

Name

btnSearch

Caption

&Search

2. On btnSearch, add the code in Listing 8.17 to the Click event. This routine shows the power of
creating custom properties. After instantiating an instance of the Search formin this case,
frmHowTo8_4b.vbthe four custom properties shown in Listing 8.17 are set before the form is
displayed. This is powerful in letting you get the form set up exactly the way you want to before the
user even sees it. After setting up the custom properties, the ShowDialog method is called off of
frmSearch. By calling this method, code execution is halted until the form is closed or hidden. This
same line of code compares the DialogResult property of the form to the value; if it matches, the code
calls the LoadIndividual routine, passing the ResultValue custom property of frmSearch. Both the
DialogResult and ResultValue properties are set in frmSearch and will be shown later in these
steps.
Listing 8.17 frmHowTo8_4a.vb: Executing a SQL Server-Supplied Stored Procedure That Lists
the Tables in the Database

Private Sub btnSearch_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnSearch.Click
'-- Instantiate the search forms.
Dim frmSearch As frmHowTo8_4b
frmSearch = New frmHowTo8_4b()

'-- Set the custom data properties on the search form.


'
This is what makes it so data driven.
frmSearch.DisplayName = "Customers"
frmSearch.SearchRecordSource = "Customers"
frmSearch.SearchField = "CompanyName"
frmSearch.KeyField = "CustomerID"
'-'
'
'

Open the search form as dialog.


Based on the DialogResult property, use the
custom property ResultValue property and load
the requested record.

If frmSearch.ShowDialog() = DialogResult.OK Then


LoadIndividual(frmSearch.ResultValue)
End If
End Sub

3. Create the LoadIndividual routine by entering the code shown in Listing 8.18 into the form. Taking
the strKeyValue passed from the results of the search, a data adapter is created and a DataSet is
filled. Next, the individual data row is created. Last, each of the TextBox controls is loaded with the
value from the column with the corresponding name. Notice the use of the TryCatchEnd Try to
ignore controls that don't have a like column in the data row.
Listing 8.18 frmHowTo8_4a.vb: Loading an Individual Record into Text Boxes on the Form

Private Sub LoadIndividual(ByVal strKeyValue As String)


Dim strSQL As String
Dim strName As String
Dim oCtl As Object
Dim dsCustIndiv As New DataSet()
Dim odaCustIndiv As OleDb.OleDbDataAdapter
Dim drCustIndiv As DataRow
Try
'-- Load the individual record into the dataset
strSQL = "Select * from Customers Where CustomerID = '" &
strKeyValue & "'"
odaCustIndiv = New OleDb.OleDbDataAdapter(strSQL, _
BuildCnnStr("(local)", "Northwind"))
'-- Fill the dataset
odaCustIndiv.Fill(dsCustIndiv, "Customers")
'-- Grab the individual data row
drCustIndiv = dsCustIndiv.Tables("Customers").Rows(0)
Catch oexpData As OleDb.OleDbException

MessageBox.Show("Error loading individual data: " & _


oexpData.Message)
Exit Sub
End Try
'-- Run through the text boxes on the form.
'-- If they match up with a field from the record, load them.
For Each oCtl In Me.Controls
If TypeOf oCtl Is TextBox Then
strName = Mid(oCtl.Name, 4)
'-- By trapping the exception this way, errors are ignored.
Try
oCtl.text = drCustIndiv(strName).ToString
Catch oexp As Exception
End Try
End If
Next
End Sub

4. Create the next Windows Form and call it whatever name you referred to in the search form in step 2.
Then place the controls shown in Figure 8.7 of the search form, with the properties set as in Table 8.6.

Table 8.6. Label, TextBox, DataGrid, and Command Button Controls Property
Settings for the Calling Form
Object

Property

Setting

GroupBox

Name

GroupBox1

Text

Click on a Letter

Name

btnA

Caption

Name

btnB

Caption

Name

btnC

Caption

Name

btnZ

Caption

Name

btnAll

Caption

All

Button
Button
Button
...
Button
Button

DataGrid

Name

dgSearch

Button

Name

btnAccept

Caption

&Accept

Name

btnCancel

Caption

&Cancel

Button

5. Create the custom properties discussed in the "Technique" section of this How-To and found in Listing
8.19. Each of the properties is self-explanatory.
Listing 8.19 frmHowTo8_4b.vb: Creating Custom Properties for the Search Form

Private
Private
Private
Private
Private

mstrDisplayName As String
mstrRecordSource As String
mstrSearchField As String
moResultValue As Object
mstrKeyField As String

Public Property DisplayName() As String


Get
DisplayName = mstrDisplayName
End Get
Set(ByVal Value As String)
mstrDisplayName = Value
End Set
End Property
Public Property SearchRecordSource() As String
Get
SearchRecordSource = mstrRecordSource
End Get
Set(ByVal Value As String)
mstrRecordSource = Value
End Set
End Property
Public Property SearchField() As String
Get
SearchField = mstrSearchField
End Get
Set(ByVal Value As String)
mstrSearchField = Value
End Set
End Property
Public Property KeyField() As String
Get
KeyField = mstrKeyField
End Get
Set(ByVal Value As String)
mstrKeyField = Value
End Set
End Property

Public Property ResultValue() As Object


Get
ResultValue = moResultValue
End Get
Set(ByVal Value As Object)
moResultValue = Value
End Set
End Property

6. On the form, add the code in Listing 8.20 to the Load event. This routine ensures that the calling form
set the DisplayName custom property; thus, this routine can assume that the others were set as
well. If not, a message box is displayed. If so, the Text property of the form, which is displayed in the
Title bar, is set to DisplayName.
Listing 8.20 frmHowTo8_4b.vb: Executing a SQL Server-Supplied Stored Procedure That Lists
the Tables in the Database

Private Sub frmHowTo8_4b_Load(ByVal sender As Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
If Len(Me.DisplayName) = 0 Then
MessageBox.Show("Form specific properties not set.")
Me.Close()
Else
Me.Text = Me.Text & " " & Me.DisplayName
End If
End Sub

7. For each of the command buttons that has a single letter, add the first subroutine displayed in Listing
8.21 to each of the Click events. For the btnAll Button control, add the second subroutine to the
Click event. Each Button control will pass the letter it represents to the subroutine called SetData ,
discussed in the next step. The btnAll code simply passes the empty string.
Listing 8.21 frmHowTo8_4b.vb: Click Events for Each of the Letter Button Controls

Private Sub btnA_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnA.Click
SetData("A")
End Sub
Private Sub btnAll_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles btnAll.Click
SetData("")
End Sub

8.

8. Add the subroutine in Listing 8.22 to the class module of the form. This routine takes the letter value
passed in strFilterLetter as a parameter. A SQL Select string is created that takes the literal values
Select, From, and Where and uses the custom properties KeyField, SearchField, and
SearchRecordSource. The SearchField property is used with the Like clause, also using the
strFilterLetter and the % (wildcard). Note that if "" is passed to strFilterLetter, all the
records will be listed. Finally, odtSearch is filled and set as the data source for dgSearch, which is the
DataGrid control.
Listing 8.22 frmHowTo8_4a.vb: Filling the Results Set Based on the Letter Button That Was
Pressed

Sub SetData(ByVal strFilterLetter As String)


Dim odaSearch As OleDb.OleDbDataAdapter
Dim dtSearch As DataTable = New DataTable()
odaSearch = New _
OleDb.OleDbDataAdapter("Select " & Me.KeyField & ", " &
Me.SearchField & " From " & Me.SearchRecordSource & " Where " &
Me.SearchField & " Like '" & strFilterLetter & "%'",
(BuildCnnStr("(local)", "Northwind")))
odaSearch.Fill(dtSearch)
dgSearch.DataSource = dtSearch
End Sub

Note
This routine more than any in this How-To shows the flexibility of
this technique. You can use any table values for these properties.
Just think of how many places you can use this form without
changing a line of code in the form.

9. On the buttons called btnAccept and btnCancel, add the code in Listing 8.23 to the appropriate
Click event of each. The btnAccept_Click routine creates a DataTable object from the data grid's
DataSource property. Then it derives the data row that is currently selected from that data table. The
KeyField property is used to store the individual column value of drCurr into the ResultValue
custom property. The DialogResult property is set to OK, and the form is hidden with the Hide
method. By hiding the form, you can still read the properties of the form without the user seeing it.
In the btnCancel_Click routine, the DialogResult is set to No , and the form is closed. This action
tells the calling form that the search was canceled.
Listing 8.23 frmHowTo8_4b.vb: Storing the Resulting Key Value to the ResultValue Custom
Property and Setting the DialogResult

Private Sub btnAccept_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnAccept.Click
Dim dtFromGrid As DataTable
Dim drCurr As DataRow
Try
'-- Using the DataRow and DataTable objects of the DataGrid control,
'
get the selected result and assign it to the custom property
'
ResultValue. Then set the DialogResult
'
property to DialogResult.OK,
'
and hide the form so that the calling form can still access it.
dtFromGrid = CType(dgSearch.DataSource, DataTable)
drCurr = dtFromGrid.Rows(Me.dgSearch.CurrentRowIndex())
Me.ResultValue = drCurr(Me.KeyField).ToString
Me.DialogResult = DialogResult.OK
Me.Hide()
Catch exp As Exception
Me.DialogResult = DialogResult.No
Me.Close()
End Try
End Sub
Private Sub btnCancel_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnCancel.Click
Me.DialogResult = DialogResult.Cancel
Me.Close()
End Sub

How It Works
When the user clicks on the search button, the calling form sets the custom properties of the search form,
telling it what record source to use, as well as other information used for searching for the specific record
and domain desiredin this case:

Customers for the property DisplayName


Customers for the property SearchRecordSource
CustomerID for the property KeyField
CompanyName for the property SearchField

After the search form is loaded, the user presses one of the letters to narrow down the records to look for, a
data adapter is passed a SQL String made up of the properties just mentioned, a data table is filled, and the
data grid's DataSource property sets the data table.
When the user clicks Accept, the data table is retrieved from the data grid, which then produces the data
row that contains the key field. This is stored into the ResultValue property of the search form, the
DialogResult property is set to DialogResult.OK, and the form is hidden.
Back on the calling form, the LoadIndividual routine is called and passed the ResultValue property
from the search form. The text boxes are then loaded with the data row results.

Comments
This technique shows a number of ways that the various ADO.NET objects can be used. Take a close look at
the use of the dialog style form, forcing code execution to halt until you hide or close the form. This is a
technique that you will use throughout your applications after you get used to it.
Again, you can enhance this tool in a number of ways. One way is to allow the user to enter a string value to
type in, narrowing down the choices even more, and another is to add a property that could be used to
specify multiple columns to be displayed.

[ Team LiB ]

[ Team LiB ]

8.5 Work with Data-Bound Multi-Select List Boxes Using Web Forms
As with How-to 8.1, this example will show you how to take advantage of multi-select list boxes, only with a
Web Form instead of a Windows Form.
You need to be able to manipulate multi-select list boxes in your Web applications using ASP.NET as well as
in your Visual Basic .NET desktop applications. This How-To shows you how to use just about the same
coding techniques as in How-To 8.1, but with the change of using the Web Form.

Technique
When you are performing a task in a Web Form that you have created in a Windows Form, you would think it
would take the same effortif not moreto accomplish the task. However, this is not the case for this HowTo. The commands available to the Windows Form ListBox control will give better performance because
you have a SelectedIndexes collection to work with, and in the Web Form you iterate through all the
items in the ListBox control and check the Selected property. Nonetheless, coding on the Web Form is
simpler.
Unlike the Windows Form version of the ListBox Control, which has four different settings for the
SelectionMode property, the Web Form version has two: Single or Multiple.
Another thing to keep in mind when developing with data and the Web Form is that you will need to use the
DataBind method off the ListBox control to bind the data at runtime. In the Load event of the page, you will
want to use the IsPostBack method of the page to ensure that you perform certain tasks only when the
page is initially loaded, and not on a round trip that pages take back to the server.

Steps
Open and run the VB.NET Chapter 8 solution. From the main Web Form, click on the hyperlink with the
caption How-To 8.5: Work with Data-Bound Multi-Select List Boxes Using a Web Form. You will then see the
page displayed in Figure 8.8.
Figure 8.8. This Web Form uses controls bound at runtime and takes advantage of multi-select
list boxes.

When the page loads, you will see the Beverages category chosen in the top combo box. The Selected and
Unselected Products ListBox controls are filled in with the appropriate products.
If you click on a product in the Unselected Products list box and then click on the arrow button pointing to
the right (>), the item is moved to the Selected Products list box. If you select items in the Selected
Products list box and click on the arrow button pointing to the left (<), those items are moved to the
Unselected Products list box.
If you click on the Unassigned Products Only check box at the bottom of the form, the Unselected Products
list box is filled with products that are not assigned to any category.

Note
If you check the Unassigned Products Only check box when you are
first getting into Northwind and running this example, you probably
won't see unassigned items. You will need to unselect products from a
category.

1. Create a Web Form. Then place the controls shown in Figure 8.8 with the properties set as seen in
Table 8.7.

Table 8.7. Label, ComboBox, ListBox, and Command Button Control Property
Settings
Object

Property

Setting

DOCUMENT

bgColor

buttonface

Label

Name

Label1

Text

Category:

DropDown

Name

ddCategories

Label

Name

Label2

Text

Unselected Products

Name

lstUnSelected

ListBox

SelectionMode Multiple
Label
ListBox

Name

Label3

Text

Selected Products

Name

lstSelected

SelectionMode Multiple
Command Button

Name

btnSelect

Text

>

Name

btnUnSelect

Text

<

CheckBox

Name

chkUnAssignedOnly

Label

Name

Label4

Text

UnAssigned Products Only

Name

hplReturnToMain

Text

Return To Main

Command Button

HyperLink

NavigateURL wfrmMain.aspx

Note

HyperLink is optional. It is just used to get back to the main


sample page for this chapter.

2. As with some of the other chapters' projects, before creating the code that will be attached to the Load
event of the form, you need to build a support routine to create the Connection string. Called
BuildCnnStr, the function can be seen in Listing 8.24. This function takes a server and database
names that are passed to it and creates a Connection string.
Listing 8.24 modGeneralRoutines.vb: Creating a Connection String

Function BuildCnnStr(ByVal strServer As String, _


ByVal strDatabase As String) As String
Dim strTemp As String
strTemp = "Provider=SQLOleDB; Data Source=" & strServer & ";"
strTemp &= "Initial Catalog=" & strDatabase & ";"
strTemp &= "Integrated Security=SSPI"
Return strTemp
End Function

Although you could create a routine that would pass back a Connection object, a more versatile
method would be to pass back a string. Some objects ask you for a Connection object, but
others just want a string. You will see BuildCnnStr called in the next step.

3. On the form, add the code in Listing 8.25 to the Load event. In this code, the first task is to make sure
the code is run only once in the page, when it is first loading. The Not IsPostBack check performs
this task. Next, you will create a data adapter called odaCategories and load the categories SQL
Statement into it. The dtCategories data table is filled and set as the DataSource property of
ddCategories . The DataTextField and DataValueField of ddCategories are then set. After
that, the DataBind method of the DropDown is called. This is necessary for binding data to the server
controls on Web Forms. Finally, two new subroutines called LoadUnSelectedProducts and
LoadSelectedProducts are called to populate the appropriate list boxes. These routines are
discussed in the next two steps.
Listing 8.25 wfrmHowTo8_5.aspx : Loading Categories into a List Box

Private Sub Page_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
Dim odaCategories As OleDb.OleDbDataAdapter
Dim dtCategories As New DataTable()
'-- Make sure this code is only executed when the page is first loaded.
If Not Page.IsPostBack Then
'-- Load up the Categories DropDown control
odaCategories = New _
OleDb.OleDbDataAdapter("Select CategoryID, CategoryName " & _
" From Categories",
BuildCnnStr("(local)", "Northwind"))
odaCategories.Fill(dtCategories)
Me.ddCategories.DataSource = dtCategories
Me.ddCategories.DataTextField = "CategoryName"
Me.ddCategories.DataValueField = "CategoryID"
'-- This is necessary for Web Forms, but not used on Windows Forms.
Me.ddCategories.DataBind()

LoadUnSelectedProducts()
LoadSelectedProducts()
End If
End Sub

4. Create the LoadUnSelectedProducts routine, shown in Listing 8.26, by entering the following code
in the Web Form that you created for this How-To. This routine starts off by testing the check box
called chkUnAssignedOnly. Based on that value, a SQL string is created that grabs the products that
are either not assigned to any product, if chkUnAssignedOnly = True, or all products that are not
assigned to the chosen category are retrieved. The SQL String is stored in the variable called strSQL.
Next, the DataAdapter object called odaUnselected is set to strSQL and the SQL Server connection
string. The DataTable object called dtUnSelected is then filled.
The Dispose method of the ListBox control is called to remove current items, and dtUnSelected is
assigned to the DataSource property of lstUnSelected. Then the DataTextField and DataValueField
properties are set. Last, the DataBind and ClearSelected methods are called to bind the
lstUnSelected and ensure that no entries are left selected.
Listing 8.26 wfrmHowTo8_5.aspx : Populating the List Box That Displays Unselected Products

Sub LoadUnSelectedProducts()
Dim odaUnSelected As OleDb.OleDbDataAdapter
Dim dtUnSelected As New DataTable()
Dim strSQL As String
'-- If the check box for unassigned only is checked, then
'
grab the product items where the category is null; otherwise, load
'
it up with those products not assigned to the current category.
If chkUnAssignedOnly.Checked Then
strSQL = "Select ProductID, ProductName From Products " &
"Where CategoryID IS NULL Order By ProductName"
Else
strSQL = "Select ProductID, ProductName From Products " & _
Where CategoryID <> " & ddCategories.SelectedItem.Value & _
" Or CategoryID IS NULL Order By ProductName"
End If
'-- Load up the lstUnselected based off the SQL string.
odaUnSelected = New OleDb.OleDbDataAdapter(strSQL,
BuildCnnStr("(local)", "Northwind"))
odaUnSelected.Fill(dtUnSelected)
Me.lstUnSelected.Dispose()
Me.lstUnSelected.DataSource = dtUnSelected
Me.lstUnSelected.DataTextField = "ProductName"
Me.lstUnSelected.DataValueField = "ProductID"

'-- Needed on Web Forms.


Me.lstUnSelected.DataBind()
Me.lstUnSelected.ClearSelection()
End Sub

5. Create the LoadSelectedProducts routine by entering the code in Listing 8.27 into the form you
created for this How-To. This routine basically performs the same tasks that the routine listed in the
previous step does, except that it performs the tasks using the lstSelected ListBox control. This
routine also doesn't need to test the CheckBox control.
Listing 8.27 wfrmHowTo8_5.aspx : Populating the List Box That Displays Selected Products

Sub LoadSelectedProducts()
Dim odaSelected As OleDb.OleDbDataAdapter
Dim dtSelected As New DataTable()
Dim strSQL As String

'

'-- Create the SQL string for the category chosen in the
ddCategories dropdown. Then load the data table with the data
' and bind the lstSelected list box.
strSQL = "Select ProductID, ProductName From Products " & _
"Where CategoryID = " & _
ddCategories.SelectedItem.Value & " Order By ProductName"
odaSelected = New _
OleDb.OleDbDataAdapter(strSQL, _
BuildCnnStr("(local)", "Northwind"))
odaSelected.Fill(dtSelected)
Me.lstSelected.Dispose()
Me.lstSelected.DataSource = dtSelected
Me.lstSelected.DataTextField = "ProductName"
Me.lstSelected.DataValueField = "ProductID"
Me.lstSelected.DataBind()
Me.lstSelected.ClearSelection()

End Sub

6. Add the code in Listing 8.28 to the SelectedIndexChanged event of the ddCategories drop-down.
Listing 8.28 wfrmHowTo8_5.aspx : Repopulating the List Boxes Based on the Current Category
That Is Selected

Private Sub ddCategories_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) _
Handles ddCategories.SelectedIndexChanged
LoadUnSelectedProducts()
LoadSelectedProducts()
End Sub

7. Add the code in Listing 8.29 to the CheckChanged event of the chkUnAssignedOnly check box.
Listing 8.29 wfrmHowTo8_5.aspx : Calling the Routine to Reload the lstUnSelected List Box If
This Check box Is Changed

Private Sub chkUnAssignedOnly_CheckedChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles chkUnAssignedOnly.CheckedChanged
LoadUnSelectedProducts()
End Sub

8. Add the code in Listing 8.30 to the Click event of the btnSelect command button. This and the next
step contain the most code as well as some new objects and properties. First, the number of
highlighted items (SelectedIndices.Count) is stored to an Integer variable called intItemsNum. A
1 is subtracted off the figure because the collections in .NET are zero based.
Next, the code iterates through the Items collection of the lstUnSelected list box, testing the Selected
property for selected items. The Value property of the item is converted to a string and added to a
string variable called strItems. strItems is then used to create the criteria for an IN clause of a SQL
Update statement, which is passed to the Command object called ocmdSelect. This Command object is
executed, and the selected products are updated to reflect the category chosen. Last, both the list
boxes are reloaded to reflect the changes.
Listing 8.30 wfrmHowTo8_5.aspx : Updating the Server with Products That Are Selected for
the Given Category

Private Sub btnSelect_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnSelect.Click
Dim intItemsNum As Integer
Dim strItems As String
Dim oCurr As Object
'-- Iterate through each of the items in lstUnSelected
'
and check the Selected property.
'
If selected, store into a string with other selected product IDs.
For Each oCurr In Me.lstUnSelected.Items

If oCurr.Selected() = True Then


If Len(strItems) > 0 Then
strItems = strItems & ", "
End If
strItems = strItems & CType(oCurr.Value, String)
End If
Next
'-- Run an update query to assign the category to the desired products,
'
using an IN clause in the SQL statement.
Try
Dim ocnn As New OleDb.OleDbConnection(BuildCnnStr("(local)", _
"Northwind"))
Dim ocmdSelect As New _
OleDb.OleDbCommand("Update Products Set CategoryID = " &
Me.ddCategories.SelectedItem.Value & _
" Where ProductID IN (" & strItems & ")", ocnn)
ocmdSelect.CommandType = CommandType.Text
ocnn.Open()
ocmdSelect.ExecuteNonQuery()
Catch excpCommand As Exception
End Try
LoadUnSelectedProducts()
LoadSelectedProducts()
End Sub

9. Add the code in Listing 8.31 to the Click event of the btnUnSelect command button. Again, this code
is similar to the previous step, but it is used to set the CategoryID column to null if the product was
highlighted in the lstSelected list box and btnUnSelect was clicked.
Listing 8.31 frmHowTo8_5.aspx: Updating the Server with Products That Are Unselected for
the Given Category

Private Sub btnUnSelect_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnUnSelect.Click
Dim intItemsNum As Integer
Dim strItems As String
Dim oCurr As Object
'-- Iterate through each of the items in lstSelected
'
and check the Selected property.
'
If selected, store into a string with other selected product IDs.

For Each oCurr In Me.lstSelected.Items


If oCurr.Selected() = True Then
If Len(strItems) > 0 Then
strItems = strItems & ", "
End If
strItems = strItems & CType(oCurr.Value, String)
End If
Next
Try
Dim ocnn As New OleDb.OleDbConnection(BuildCnnStr("(local)", _
"Northwind"))
Dim ocmdUnSelect As New _
OleDb.OleDbCommand("Update Products Set CategoryID = Null " & _\\
"Where ProductID IN (" & _
strItems & ")", ocnn)
ocmdUnSelect.CommandType = CommandType.Text
ocnn.Open()
ocmdUnSelect.ExecuteNonQuery()
Catch excpCommand As Exception
End Try
LoadUnSelectedProducts()
LoadSelectedProducts()
End Sub

How It Works
When the user chooses a category, the appropriate items are loaded into the two list boxes. Unselected
items are in the list box on the left, and the selected items are in the list box on the right.
If the check box is selected, then those only those items that are not currently assigned to categories are
displayed in the list box on the left, which is titled Unselected Products.
When the btnSelect button is clicked, any items that are highlighted in the lstUnSelected list box are used in
a query that updates the server with the new category they now belong to. Similarly, when the btnUnSelect
is clicked, items that are listed in the lstSelected list box are used in a query that updates the CategoryID of
the products to null.

Comments
As mentioned in How-To 8.1, this example is not the smartest to create in real life because you want
products to be assigned to a category. However, this example does a good job of showing the properties and
methods you can use to work with the multi-select features of the ListBox control.

[ Team LiB ]

[ Team LiB ]

8.6 Use a Single Web Form to Update Multiple Lookup Tables


As with the second tutorial(8.2), this example will show you how to update multiple lookup tablesthis time
using a Web Form.
Creating a Web Form for viewing multiple lookup tables would take about the same if not less code than
performing the same task using the Windows Form. Updating, adding, and deleting data takes a bit more
work, though. This How-To will show you how to accomplish this task by using the DataGrid control and
show you how to take advantage of Session variables and paging within the data grid when you're
manipulating data.

Technique
The DataGrid control is a powerful control, as you saw in Chapter 5, "Working with Data on Web Forms,"
but when programming in Web Forms, it takes some getting used to. Because the Web Forms are stateless,
you need to keep reminding the data grid what it is bound to.
Also, even though you declare a variable at the module level behind the form, you will notice that whenever
the form goes back to the server for information, you lose the values of your variables. The workaround for
this is the use of the Session object.

Note
A full discussion of State management in .NET and the various
options is presented in Chapter 5. This also includes how to use the
options for the data grid manipulation portion of this How-To.

The other major issue with this How-To is managing the paging of the DataGrid control, covered in Chapter
4. You will quickly learn the steps of creating the Web Form that allows users to update Lookup tables.

Steps
Open and run the VB.NET Chapter 8 solution. From the main Web Form, click on the hyperlink with the
caption How-To 8.6: Use a Single Web Form for Updating Multiple Lookup Tables. Click on the first choice,
Categories, in the list box labeled Lookup Table to Edit. The data grid will then appear. The grid will be filled
in with the data of the table you chose. Your page will then look like the page displayed in Figure 8.9.
Figure 8.9. On this page, you can add, edit, and delete information for all your simple lookup
tables.

You can now add data into the data grid by clicking on the Add New button, located under the Error box.
When you click on the Add New button, an entry is added to the data grid, and you are placed in Edit mode,
shown in Figure 8.10.
Figure 8.10. Adding new data to your lookup tables using the DataGrid control.

After entering the data into the fields, you will click Update. The values are then saved back to the server. If
you don't want to save the new entry, click the Cancel button, and the data grid makes the entry disappear.

Tip

You will notice that the look of the columns is a little congested and changes when you go to edit
the data. You can avoid this by creating and using templates with the data grid. Of course, if you
are using templates with the data grid, you have to change the template based on the lookup
table you were using.

Tip
You will also notice that the CategoryID field has been disabled and
can't be edited. This is through the use of a method, FillSchema,
which fills data table with schema information from the record source.
In this case, FillSchema passed on the information that the
CategoryID was an AutoIncrement field, and the data grid was smart
enough to handle it.

When you click on the Edit button, the page will look similar to Figure 8.10, except that data already will be
present in the fields. When you click Delete, the entry is deleted.
Any errors that might occur, such as from data integrity errors, will appear in the text box labeled Errors. If
you try to delete a current category and Products uses that category, for example, then SQL Server causes
an error to occur, and the page reports the error because of code created.

1. Create a Web Form. Then place the controls shown in Figure 8.9 with the properties shown in Table
8.8.

Table 8.8. Label, TextBox, ListBox, and Command Button Control Property Settings

Object

Property

Setting

DOCUMENT

bgColor

buttonface

Label

Name

Label1

Text

Lookup Table to Edit:

Name

lstLookupTables

AutoPostback

True

Name

Label2

Text

Lookup Table Data:

Name

dgLookupData

AllowPaging

True

PageSize

Name

Label3

Text

Errors:

Name

txtError

ForeColor

Red

ReadOnly

True

TextMode

MultiLine

Name

btnAdd

Text

Add New

Name

hplReturnToMain

NavigateURL

wfrmMain.aspx

ListBox
Label
DataGrid

Label
TextBox

Button
HyperLink

2. On the newly created lstLookupTables ListBox control, click the Build button next to the Items
property, which is a collection of names of the lookup tables to edit. After you have clicked the Build
button, the ListItem Collection Editor opens. Enter the values Categories, Regions, and Territories, as
shown in Figure 8.11. Click OK to accept the entries.
Figure 8.11. These values allow you to update multiple lookup tables from a single Web
Form.

Tip
To make this truly data driven, you could have these entries in a
table in your database. Then you could point the DataSource
property of the list box to the table. You could also have the table
contain the names of the templates you wanted to use.
A different approach was taken here so that you would not have to
modify your copy of the Northwind database.

3. On the dgLookupData DataGrid control, click the Build button next to the Columns property. You
will then be brought into the Columns tab of the dgLookupData Properties dialog box. Click on the plus
sign by the Button column in the Available Columns list. You will then see the list of available button
types you can use. Select the Edit, Update, Cancel, and Delete buttons. Set each of these buttons to
have the PushButton button type. After you have made these selections, the dialog box will look like
Figure 8.12. Click OK to accept the entries.
Figure 8.12. Add some buttons to your DataGrid control.

4. Now that you have added some buttons to the DataGrid control, you still have to tell the control how
to react to the buttons. You will do that using events in code, but we need to add some tags to the
HTML. The tags you will add are as follows:

OnUpdateCommand="dgLookupData_Update"
OnCancelCommand="dgLookupData_Cancel"
OnEditCommand="dgLookupData_Edit"
OnDeleteCommand="dgLookupData_Delete"

Click on the HTML tab of the Web Form in Visual Studio. Then you can see the HTML and insert
the tags. By looking at the HTML shown in Figure 8.13, you can see where to put the tags. Of
course, your HTML won't be as nicely laid out as this figure because Visual Studio scrunches it up.
Figure 8.13. Add the tags in this step to tie in the events to the buttons in the DataGrid
control.

5. Now it's time for the code. The first items to add are the code for the module level DataTable object
variable declaration and the code that you want to add for the Load event of the page. Both are shown
in Listing 8.32. The load event tests for the Session variable MyLookupData ; if the variable exists,
the event creates a reference to the data table using the mdtLookupData DataTable object.
Listing 8.32 frmHowTo8_6.aspx: Tracking the DataTable Object Between Trips to the Server

Private mdtLookupData As New DataTable()


Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
If Not (Session("MyLookupData") Is Nothing) Then
mdtLookupData = CType(Session("MyLookupData"), DataTable)
End If
End Sub

6. Add the code in Listing 8.33 to the SelectedIndexChanged event of lstLookupTables. This code
starts off by clearing the txtError TextBox control that is used to store the Exception object's
Message property that is caught later in the routine. Next, the odaLookupData DataAdapter object is
built by creating a SQL Select statement from the currently selected table in lstLookupTables.
Now it's time to fill in the dtNew DataTable object, which is done using FillSchema and Fill. You have

seen the Fill method before. FillSchema tells .NET to do just thatreturn the Schema to the data table,
thus having your DataTable object use properties such as AutoIncrement, DataTypes, and even
Constraints.
Next, the code reassigns the mdtLookupTable reference to point to dtNew. This works well for using
the DataTable and DataGrid objects with different tables, which not only clears the data, but also
resets what columns are being used in the data table object.
You can see that mdtLookupData is being stored to a Session object entry called MyDataTable, and
a Boolean variable called IsAdding is set to False. This last variable will be set to True when the
btnAdd is clicked and used for special handling when updating and canceling the editing of the data
grid.

Tip
You really need to watch where you are storing values to the
Session object and other state management objects. Make sure
you do store these objects before calling methods or accessing
properties of server controls such as the DataGrid control.

Next, the EditItemIndex property is set to 1 to unselect any item that is being edited. Then the
CurrentPageIndex property is reset to 0 to reflect the first page in the data grid.
The data grid is then filled and the subroutine BindTheGrid() is called. You can find this subroutine
at the bottom of the listing. This routine sets the DataSource property of the DataGrid control to
mdtLookupData and calls the DataBind method of the DataGrid control.
Listing 8.33 frmHowTo8_6.aspx: Tracking the DataTable Object Between Trips to the Server

Private Sub lstLookupTables_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) _
Handles lstLookupTables.SelectedIndexChanged
Dim odaLookupData As OleDb.OleDbDataAdapter
Dim dtNew As New DataTable()
Me.txtError.Text = ""
Try
'-- Take the txtSQLString text and create a data table; then set the
'
data source of the data grid.
odaLookupData = New OleDb.OleDbDataAdapter("Select * From " & _
Me.lstLookupTables.SelectedItem.ToString,
BuildCnnStr("(local)", "Northwind"))
'-- Test for identity and display any other notes.
odaLookupData.FillSchema(dtNew, SchemaType.Source)

'-- Get the data and put it in the data table.


odaLookupData.Fill(dtNew)

'-- Save the data table to a session variable so that


'
you don't lose it on a trip back to the server.
'
Set the flag for adding new records to False.
Session("IsAdding") = False
Session("MyLookupData") = dtNew
mdtLookupData = dtNew
Me.dgLookupData.EditItemIndex = -1
Me.dgLookupData.CurrentPageIndex = 0
'-- Bind the data grid to the data table
BindTheGrid()
Catch excp As Exception
'-- If an error occurs, stash the error message to a text box.
Me.txtError.Text = excp.Message
End Try
End Sub
Sub BindTheGrid()
'-- Bind the data grid
Me.dgLookupData.DataSource = mdtLookupData
Me.dgLookupData.DataBind()
End Sub

7. Add the code in Listing 8.34 to the Edit command of dgLookupData . This is one of the events
specified in step 4. This code sets the EditItemIndex of the DataGrid object to the selected item
and then binds the data.
Listing 8.34 frmHowTo8_6.aspx: Telling the DataGrid Object to Use the Selected Item and Go
into Edit Mode (Display Wise)

Sub dgLookupData_Edit(ByVal sender As Object, _


ByVal e As DataGridCommandEventArgs)
'-- The data grid does most of the work; just set the EditItemIndex
'
to the ItemIndex and bind the grid again.
Me.txtError.Text = ""
dgLookupData.EditItemIndex = e.Item.ItemIndex
BindTheGrid()

End Sub

8. Add the code in Listing 8.35 to the Click event of btnAdd. Notice that the first task invokes the
BeginLoadData method for mdtLookupData. This turns off the schema checking that will occur when
adding the new row to the DataTable object. You need to turn this off because you don't want to have
it check for required fields until you actually edit the record. The editing of the record is started by the
line of code setting the EditItemIndex property of the DataGrid object.
The GetPageNum() routine helps to synchronize the DataGrid page with the position the pointer is in
the DataTable object. If you add a record, you have to know whether to have it be on the current
page or on a new page in the data grid. You can see GetPageNum after btnAdd_Click in Listing 8.35.

GetPageRows(), found at the bottom of Listing 8.35, returns the number of actual rows based on the
page in the data grid.

Note

GetPageRows() returns the current page number


(dgLookupData.CurrentPageIndex) times the page size
(dgLookupData.PageSize). In this case, GetPageRows() returns
5, as set in step 1. If EditItemIndex were used alone, it would
return only the position of the item that was being edited for the
current page.
Last, the EditItemIndex of dgLookupData is set. The data grid is bound to the data table using the
BindTheGrid routine.
Listing 8.35 frmHowTo8_6.aspx: Adding a Record to the Data Table and Having the Data Grid
Reflect It

Private Sub btnAdd_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnAdd.Click
Dim intColCnt As Integer
Dim drCurr As data row
Me.txtError.Text = ""
mdtLookupData.BeginLoadData()
'-- Add the row to the data table via the data row
drCurr = mdtLookupData.NewRow
mdtLookupData.Rows.Add(drCurr)
'-- Set the Adding flag.
Session("MyLookupData") = mdtLookupData
Session("IsAdding") = True
'-- Set the current page based on the new number of rows
dgLookupData.CurrentPageIndex = GetPageNum(mdtLookupData)

'-- Set the item index based on the rows on this page only.
dgLookupData.EditItemIndex = mdtLookupData.Rows.Count - _
GetPageRows() - 1
BindTheGrid()
End Sub
Function GetPageRows() As Integer
'-- This helps synchronize the data table rows
' with the DataGrid page and row.
GetPageRows = dgLookupData.PageSize * dgLookupData.CurrentPageIndex
End Function
Function GetPageNum(ByVal dt As data table) As Decimal
Dim decTemp As Decimal
'-- Calculate the number of pages
decTemp = ((dt.Rows.Count - 1) / dgLookupData.PageSize)
GetPageNum = decTemp.Truncate(decTemp)
End Function

9. Add the code shown in Listing 8.36 to the Cancel command of dgLookupData . This is one of the
events specified in step 4. If the code is in the middle of adding an entry, it uses the EditItemIndex
of the DataGrid object to the selected item and adds this to the value returned by GetPageRows(),
shown just after the dgLookupData_Cancel subroutine.
The value that EditItemIndex and GetPagerows() returns is used to position the pointer in
mdtLookupData so that the Delete method can be called.
After accepting the changes, the session variables are resaved. Then the page index for the DataGrid
object is cleaned up by comparing the current page number relative to the pointer of the data table
position to the CurrentPageIndex property. Regardless of whether the item is being added or edited,
the EditItemIndex is cleared by setting it to 1. The data grid is bound again by calling
BindTheData().
Listing 8.36 frmHowTo8_6.aspx: Canceling Editing/Adding a Record in the DataGrid Object

Sub dgLookupData_Cancel(ByVal sender As Object,


ByVal e As DataGridCommandEventArgs)
Dim blnAdding As Boolean
'-- If you're canceling while adding a record, you need to back the
'
row out of the data table and data grid.
' You don't have to send it to the
'
server because it really was never added to it.

Me.txtError.Text = ""
If CType(Session("IsAdding"), Boolean) Then
mdtLookupData.Rows(dgLookupData.EditItemIndex + _
GetPageRows()).Delete()
mdtLookupData.AcceptChanges()
Session("IsAdding") = False
Session("MyLookupData") = mdtLookupData
'-- Reset the paging if it has been affected
If GetPageNum(mdtLookupData) < dgLookupData.CurrentPageIndex Then
dgLookupData.CurrentPageIndex -= 1
End If
End If
dgLookupData.EditItemIndex = -1
BindTheGrid()
End Sub

10. Add the code in Listing 8.37 for the Delete command of dgLookupData. This is one of the events
specified in step 4. This code is a lot like the code in the previous step when the record was added. The
big difference in this step's code listing is that the deletion is posted back to the server, and in the
previous step, it wasn't. It wasn't posted back to the server in the previous step because the server
never knew anything about the record. The record had only been added to the data table and was not
sent back to the server. You can see that in the next step.
Another item to note is the RejectChanges method called in the Catch of the exception handling
code. If an error occurs, the change is undone, the message is noted, and life goes on. The rest of this
code pretty closely follows what was done in the previous step.
Listing 8.37 frmHowTo8_6.aspx: Deleting a Record from the Data Grid and Posting It Back to
the Server

Sub dgLookupData_Delete(ByVal sender As Object, _


ByVal e As DataGridCommandEventArgs)
Dim intColCnt As Integer
Dim cnn As New OleDb.OleDbConnection(BuildCnnStr("(local)", _
"Northwind"))
'-- Create the command builder to update (post) the data
' in the data grid
'
back to the server.
Dim odaTableData As OleDb.OleDbDataAdapter
Me.txtError.Text = ""

Try
'-- Take the txtSQLString text and create a data table. Then set the
'
data source of the data grid.
odaTableData = New OleDb.OleDbDataAdapter("Select * From " & _
Me.lstLookupTables.SelectedItem.ToString, cnn)
Dim ocbTableData As OleDb.OleDbCommandBuilder = _
New OleDb.OleDbCommandBuilder(odaTableData)
'-- Delete the row from the data table.
mdtLookupData.Rows(e.Item.ItemIndex + GetPageRows()).Delete()
'-- Commands are necessary to actually post back to the server.
cnn.Open()
odaTableData.Update(mdtLookupData)
mdtLookupData.AcceptChanges()
cnn.Close()
Session("MyLookupData") = mdtLookupData
Session("IsAdding") = False
'-- This is just in case they were editing and press Delete, Clear.
dgLookupData.EditItemIndex = -1
'-- Adjust the page according to the number of rows.
If GetPageNum(mdtLookupData) < dgLookupData.CurrentPageIndex Then
dgLookupData.CurrentPageIndex -= 1
End If
Catch excp As Exception
Me.txtError.Text = excp.Message
mdtLookupData.RejectChanges()
End Try
BindTheGrid()
End Sub

11. Add the code in Listing 8.38 for the Update command of dgLookupData . This is one of the events
specified in step 4. This routine starts off by declaring DataAdapter and CommandBuilder objects to
update your data back to the server. Before the actual update, however, the current row that is being
edited in the data grid is assigned to a DataRow object. Then each of the items in the row is saved
from the data grid cells to the column in the data row.
Thanks to using the FillSchema method when filling the data table, the AutoIncrement property
will reflect whether a column was an Identity column. If the FillSchema method were not used, you
would have to handle the exception that would occur when you tried to write the value to the column.
When writing the cells into the columns in the data row, the Trim function is used; because of using

the FillSchema method, the values are padded out as SQL Server columns generally are.
The rest of the code pretty well runs like it does in step 9. The changes are accepted, written back to
the server, and so forth.
Listing 8.38 frmHowTo8_6.aspx: Deleting a Record from the Data Grid and Posting It Back to
the Server

Sub dgLookupData_Update(ByVal sender As Object, _


ByVal e As DataGridCommandEventArgs)
Dim
Dim
Dim
Dim

intColCnt As Integer
intColCurr As Integer
drCurr As DataRow
cnn As New OleDb.OleDbConnection(BuildCnnStr("(local)", _
"Northwind"))
Dim blnAdding As Boolean
Dim strCurrValue As String

'-- Create the command builder to update (post) the data


' in the data grid
'
back to the server.
Dim odaTableData As OleDb.OleDbDataAdapter
Me.txtError.Text = ""
Try
'-- Take the txtSQLString text and create a data table. Then set the
'
data source of the data grid.
odaTableData = New OleDb.OleDbDataAdapter("Select * From " & _
Me.lstLookupTables.SelectedItem.ToString, cnn)
Dim ocbTableData As OleDb.OleDbCommandBuilder = _
New OleDb.OleDbCommandBuilder(odaTableData)
drCurr = mdtLookupData.Rows(dgLookupData.EditItemIndex + _
GetPageRows())
'-- Update the fields in the rows.
intColCnt = e.Item.Cells.Count
For intColCurr = 2 To intColCnt - 1
If mdtLookupData.Columns(intColCurr - 2).AutoIncrement = _
False Then
drCurr.Item(intColCurr - 2) = _
Trim(CType(e.Item.Cells(intColCurr).Controls(0), _
TextBox).Text)
End If
Next

'-- Commands are necessary to actually post back to the server.


cnn.Open()
odaTableData.Update(mdtLookupData)
mdtLookupData.AcceptChanges()
cnn.Close()
Session("MyLookupData") = mdtLookupData
Session("IsAdding") = False
dgLookupData.EditItemIndex = -1
BindTheGrid()
Catch excp As Exception
Me.txtError.Text = excp.Message
End Try
End Sub

12. Add the code in Listing 8.39 for the PageIndexChanged command of dgLookupData . This code
simply sets the CurrentPageIndex property of the data grid to the page that is chosen. The code
then calls BindTheGrid().
Listing 8.39 frmHowTo8_6.aspx: Updating the Data Grid Page Index

Private Sub dgLookupData_PageIndexChanged(ByVal source As Object,


ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs)
Handles dgLookupData.PageIndexChanged
'-- Set the current page in the data grid.
Me.dgLookupData.CurrentPageIndex = e.NewPageIndex
BindTheGrid()
End Sub

How It Works
When the form opens, the user clicks on an entry in the Lookup Table to Edit list box. When this occurs and
when a user selects a new item in the list, a Select statement is generated and loaded into a data adapter,
which fills a data table. This, in turn, is used for the data source of the data grid, and the DataBind method
is called. The data is then displayed.
If the user clicks on the Edit button, the controls in the data grid are put into Edit mode. If the user clicks the
Add button, a data row is added to the data table, and the data grid reflects this, including handling the
paging of row locations.
When the user clicks the button with the caption Update, a CommandBuilder object is generated off the
DataAdapter object. The Update command for the data adapter is then invoked, with the Update method

called. The data table is then referenced from the data source of the data grid control, and the
AcceptChanges method is called. If the user clicks Cancel, then the changes are ignored if a record is being
edited, but the data row is deleted if the record was in the middle of being added.
When the user clicks Delete, the DataAdapter and CommandBuilder objects are created to remove the
data row from the data table and reflect the changes back to the server. The data grid is also re-bound to
the data table, and the paging is adjusted.

Comments
Whew, this was a long one. The good news is that the code is already created for you with the book.
Remember, though, as with other techniques and examples, this is a starting point for you to run with and
expand on. This example doesn't provide all the error trapping that is necessary, but it definitely gives a
good start.
Before spending too much time enhancing this example, make sure you get the performance out of it, just
as you should when trying different data-driven techniques.

[ Team LiB ]

[ Team LiB ]

8.7 Create a Point-and-Click Query Tool for Users Using a Web Form
As usefulif not more sothan the example shown in the 8.3 How-to, this exercise will show you how to add
a data-driven query tool to your ASP.NET application.
When creating database applications, even on the Web, one of your clients inevitably wants to be able to
examine the data in his database, and not necessarily in edit pages. The client wants to be able to list his
data out and examine the data at his leisure.
Giving the user the flexibility to do this via the Web is not as big of a hassle as it sounds. This How-To will
show you how to create a page to view the tables in your database, using a nice point-and-click interface.

Technique
To accomplish the task just presented, you will be using the OleDbCommand and DataReader objects. Along
with these objects, you will be using some stored procedures that SQL Server supplies. Those stored
procedures list the various objects within a SQL Server databasein this case, Northwind's tables and
columns.
You will take the elements returned in the DataReader object and load the Add method of the ListBox
object.
You will also use the Session object as you did in the previous How-To to save a DataTable object for use
over trips to the server.
Finally, the ViewState object will be used to store a string variable when going to the server and back. The
ViewState object is a good .NET state object to use for small pieces of data, such as strings.

Steps
Open and run the VB.NET Chapter 8 solution. From the main Web Form, click on the hyperlink with the
caption How-To 8.7: Create a Point-and-Click SQL Server Query Tool for Users Using a Web Form. When the
new page opens, the first list box you see to the left is populated with the tables from Northwind. Click on
the Customer table, and you will see the columns listed in the list box labeled Columns. Click on the
CompanyName and ContactName, and you will see the SQL String text box filled in. After clicking on the
View button, the Web page will look like the one displayed in Figure 8.14.

1. Create a Web Form. Then place the controls shown in Figure 8.14 with the properties set forth in Table
8.9.

Table 8.9. Property Settings for Controls on the Point-and-Click Web Form

Object

Property

Setting

DOCUMENT

bgColor

buttonface

Label

Name

Label1

Text

Tables

Name

Label2

Text

Columns

Name

Label3

Text

SQL String

Name

Label4

Text

Data Display

Name

lstTables

AutoPostback

True

Name

lstColumns

SelectionMode

Multiple

AutoPostback

True

Name

ddSortBy

AutoPostback

True

Name

txtSQLString

MultiLine

True

Button

Name

btnView

DataGrid

Name

dgDisplay

AllowPaging

True

Name

hplBackToMain

Text

Return To Main

NavigateURL

wfrmMain.aspx

Label
Label
Label
ListBox
ListBox

DropDown
TextBox

Hyperlink

Tip
Notice that the lstTables list box allows the user to choose only one
table at a time, whereas lstColumns allows the user to choose
multiple columns.
A great enhancement to this tool would be to allow the user to
choose multiple tables and have the application figure out the
relationship between tables and create joins automatically.

2. In the class module for the form, add the following Private declaration just below the line of code that
reads Web Form Designer Generated Code :

Private mdtDisplay As DataTable

3. On the Web Form, add the code in Listing 8.40 to the Load event. The first task is to load the tables list
box, performed by the subroutine LoadTables() , which is also in this listing. The form only calls this

3.
routine if it is the first time into the page, by checking for Not Me.IsPostBack . The form then tests
to see whether the Session object has an entry called MyDisplayDataTable. If the entry exists,
then mdtDisplay is referenced to it, meaning that this time through the Load event is probably
occurring on a trip back from the server. The entry exists, and the code needs to set a reference to it.
In LoadTables, the routine first creates a new OleDbConnection object called ocnn, an
OleDbCommand object called ocmdTables. It then assigns the built-in SQL Server stored procedure
called sp_Tables when instantiating ocmdTables. After establishing the CommandType as being
CommandType.StoredProcedure and then opening the connection, the data reader called
odrTables is created by calling the ExecuteReader method off ocmdTables.
Listing 8.40 frmHowTo8_7.vb: Executing a SQL Server-Supplied Stored Procedure That Lists
the Tables in the Database

Private Sub Page_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
If Not Me.IsPostBack Then
LoadTables()
End If
If Not (Session("MyDisplayDataTable") Is Nothing) Then
mdtDisplay = CType(Session("MyDisplayDataTable"), DataTable)
End If
End Sub
Sub LoadTables()
'-- Create the connection and specify the stored procedure to use.
Dim ocnn As New OleDb.OleDbConnection(BuildCnnStr("(local)", _
"Northwind"))
Dim ocmdTables As New OleDb.OleDbCommand("sp_Tables", ocnn)
Dim odrTables As OleDb.OleDbDataReader
'-- Specify the type of command being performed.
ocmdTables.CommandType = CommandType.StoredProcedure
ocnn.Open()
'-- Create the DataReader object.
odrTables = ocmdTables.ExecuteReader()
'-- Loop through and add table type object names
' to the lstTables list box.
Do While odrTables.Read
If odrTables.GetString(3) = "TABLE" Then
Me.lstTables.Items.Add(odrTables.GetString(2))
End If
Loop

End Sub

Next, the code loops through each of the items returned by the command. Those of type TABLE
are added to the lstTables items. Then the connection is closed.
As mentioned, you will see a comparison to the literal TABLE. This is because the fourth column
returned matches the current table type. The other two types are SYSTEMTABLE and VIEW. To
see the data returned by the sp_tables stored procedure, open the Query Analyzer, as described
in How-To 8.3.

4. On lstTables, add the code in Listing 8.41 to the SelectedIndexChanged event. This routine
performs a similar feat as the previous routine; it will call a built-in stored procedurein this case,
sp_Columns. However, the next task in this step is to pass a parameter, TableName, which is the
table chosen in lstTables.SelectedItem.Text . After the connection is opened, the DataReader
called odrColumns is loaded with the ExecuteReader command. After the
lstColumns.Items.Clear() method is called to clear the list, the new columns are added to the
lstColumns Items collection. Then the connection is closed.
Listing 8.41 frmHowTo8_7.vb: Executing a SQL Server Built-In Stored Procedure That Lists
the Columns of a Supplied Table in the Database

Private Sub lstTables_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles lstTables.SelectedIndexChanged
'-- Create the connection and specify the stored procedure to use.
Dim ocnn As New OleDb.OleDbConnection(BuildCnnStr("(local)", _
"Northwind"))
Dim ocmdColumns As New OleDb.OleDbCommand("sp_Columns", ocnn)
Dim odrColumns As OleDb.OleDbDataReader
'-- Specify the type of command being performed
ocmdColumns.CommandType = CommandType.StoredProcedure
ocmdColumns.Parameters.Add("@TableName", Me.lstTables.SelectedItem.Text)
ocnn.Open()
'-- Create the DataReader object
odrColumns = ocmdColumns.ExecuteReader()
'-- Clear the current items in the list
Me.lstColumns.Items.Clear()

'

'-- Loop through and add table type object names


to the lstTables list box.
Do While odrColumns.Read
Me.lstColumns.Items.Add(odrColumns.GetString(3))
Loop

End Sub

5. On lstColumns, add the code in Listing 8.42 to the SelectedIndexChanged event. After clearing the

5.
items from ddSortBy, this routine iterates through the Items collection of the lstColumns ListBox
control, adding the chosen column names (those items with the Selected property set to True) to a
string variable called strTemp. The DropDown control called ddSoryBy adds the column name to its
Items collection.
After the string is finished iterating through the lstColumns Items, it is stored to a ViewState entry
called SQLFields. The LoadSQLString routine is then called, which is also in this listing.
In the routine LoadSQLString, the length of the string is checked. If the length is greater than 0, the
Text property of txtSQLString is set to the following expression: "Select " & strTemp & "
From " & Me.lstTables.Text & " Order By " & Me.ddSortBy.SelectedItem.ToString . If
the length is 0, then the Text property of txtSQLString is set to the empty string.
Listing 8.42 frmHowTo8_7.vb: Creating the SQL String

Private Sub lstColumns_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles
lstColumns.SelectedIndexChanged
Dim
Dim
Dim
Dim

intNumColumns As Integer
oCurr As Object
blnIsSelected As Boolean
strTemp As String

Me.ddSortBy.Items.Clear()
'-- Cycle through each of the selected columns of the table chosen
'
and combine them into a string.
For Each oCurr In Me.lstColumns.Items
If oCurr.Selected() = True Then
If Len(strTemp) > 0 Then
strTemp &= ", "
End If
strTemp &= oCurr.ToString
Me.ddSortBy.Items.Add(oCurr.ToString)
End If
Next
ViewState("SQLFields") = strTemp
LoadSQLString()
End Sub
Sub LoadSQLString()
'-- Take the string created and add it to the
'
table name for a SQL String, if columns are chosen.

If Len(ViewState("SQLFields")) = 0 Then
Me.txtSQLString.Text = ""
Else
Me.txtSQLString.Text = "Select " & ViewState("SQLFields") & _
" From " & Me.lstTables.SelectedItem.ToString & _
" Order By " & Me.ddSortBy.SelectedItem.ToString
End If
End Sub

6. On btnView, add the code in Listing 8.43 to the Click event. This routine creates the new data
adapter called odaDisplay, passes the Text property of txtSQLString , and then fills the
dtDisplay DataTable. The public variable, called mdtDisplay, references dtDisplay so that it will be
seen in other routines. The code then stores a new entry in the Session object called
MyDisplayDataTable, which is loaded back into mdtDisplay upon reloading of the page. Last, the
routine BindTheGrid is called to set to the DataSource property of the data grid called dgDisplay.
Listing 8.43 frmHowTo8_7.vb: Loading the DataGrid Control with the Specified Data

Private Sub btnView_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnView.Click
Dim odaDisplay As OleDb.OleDbDataAdapter
Dim dtDisplay As New DataTable()
Try
'-- Take the txtSQLString text and create a data table. Then set the
'
data source of the data grid.
odaDisplay = New OleDb.OleDbDataAdapter(Me.txtSQLString.Text, _
BuildCnnStr("(local)", "Northwind"))
odaDisplay.Fill(dtDisplay)
mdtDisplay = dtDisplay
Session("MyDisplayDataTable") = mdtDisplay
BindTheGrid()
Catch excData As Exception
End Try
End Sub
Sub BindTheGrid()
Me.dgDisplay.DataSource = mdtDisplay
'-- Must databind for ASP.NET
Me.dgDisplay.DataBind()
End Sub

7.

7. On the ddSortby control, attach the first routine in Listing 8.44 to the SelectedIndexChanged event.
Then add the second routine to the PageIndexChanged event of dgDisplay.
Figure 8.14. You can set the sorting of the data grid that is displayed here by choosing from the
drop-down list.

Listing 8.44 frmHowTo8_7.vb: Loading the DataGrid Control with the Specified Data

Private Sub ddSortBy_SelectedIndexChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles ddSortBy.SelectedIndexChanged
LoadSQLString()
End Sub
Private Sub dgDisplay_PageIndexChanged(ByVal source As Object, _
ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs)
Handles dgDisplay.PageIndexChanged
'-- Set the current page in the data grid
Me.dgDisplay.CurrentPageIndex = e.NewPageIndex
BindTheGrid()
End Sub

How It Works
When the form is opened, the lstTables ListBox control is loaded with the tables from the Northwind
database. When the user selects a table from the list, that table name is passed to the stored procedure.
That procedure lists the columns in a database table that is specified in the connectionin this case,
Northwind. These columns are loaded into lstColumns.
The user can click on multiple columns in lstColumns. Those columns then are added to the SQL Select string
that is created and stored in txtSQLString . When the btnView button is clicked, the string is passed to a
DataAdapter control, filling a data table. From there, the data is displayed when the data source of the
DataGrid control is set to the data table.
Users can change the sort order by changing the value in the DropDown object.

Comments
You can enhance this tool in several ways:

Let users click on multiple tables and automatically create the join.
Add a list of columns for the user to choose to use for criteria, and allow the user to input the criteria.
Use this tool as a base for editing or reporting the records that are returned.
This technique's goal, as with others in this book, is to push you into thinking about the possibilities of what
you can accomplish with Visual Studio .NET and your databases.

[ Team LiB ]

[ Team LiB ]

8.8 Make a Generic Search Form in an ASP.NET Web Application


This tutorial will show you how to add a unique search Web Form to your Web application that can be set up
using session variables and used for various tables.
Creating search forms for Web applications is pretty common. You would like to be able to use the same
Web Form for different tables in the same application.
This How-To will demonstrate using the same Web Form for searching various tables, taking advantage of
the Session object.

Technique
This How-To will use the Session object with a Web Form, something that has been done throughout the
book. You will see a good example of using the Session object to pass valuesin this case, the record
source, the key search field, and the display search fieldto the search form. Finally, the return value will be
passed back to the calling Web Form using the Session object.
One new control that is used in this How-To is the Panel control. It will be used to group the controls on the
search Web Form. The Panel control will be used simply by throwing controls into it and letting it control the
layout.
You will see the paging used with the DataGrid control as well.

Steps
Open and run the VB.NET Chapter 8 solution. From the main Web Form, click on the hyperlink with the
caption How-To 8.8: Make a Generic Search Form Using a Web Form. This page is a simple one that contains
text boxes for the Customer table in Northwind (see Figure 8.15).
Figure 8.15. This Customers page is a common one used to demonstrate calling the search form.

Click on the Search button to open the search page, and then click on the button labeled B. You will see the
DataGrid object displayed in the bottom of the form filled with the CompanyID and CompanyName fields of
the Customer table, which begin with B (see Figure 8.16).
Figure 8.16. This form can be used for searching within any of the tables in your databases.

Click the Select button for one of the customers who is displayed in the grid, and then click Accept. The
customer page will be displayed, and the fields will be filled in with the data from the chosen record.

1. Create a Web Form. Then place the controls shown in Figure 8.16 of the form calling the search form,
with the properties set forth in Table 8.10.

Table 8.10. Label, TextBox, and Command Button Controls Property Settings for the
Calling Form
Object

Property

Setting

DOCUMENT

bgColor

buttonface

Label

Caption

Customer ID

Label

Caption

Company Name

Label

Caption

Contact

Label

Caption

Contact Title

Label

Caption

Address

Label

Caption

City

Label

Caption

Region

Label

Caption

Country

Label

Caption

Phone

Label

Caption

Fax

TextBox

Name

txtCustomerID

TextBox

Name

txtCompanyName

TextBox

Name

txtContactName

TextBox

Name

txtContactTitle

TextBox

Name

txtAddress

TextBox

Name

txtCity

TextBox

Name

txtRegion

TextBox

Name

txtPostalCode

TextBox

Name

txtCountry

TextBox

Name

txtPhone

TextBox

Name

txtFax

Button

Name

btnSearch

Caption

&Search

2. On btnSearch, add the code in Listing 8.45 to the Click event. This sets up the search Web Form as far
as telling it what it needs to know, including how to get back to this page, which is the calling page.
The search page, in this case "wfrmHowTo8_8b.aspx," is then opened.
Listing 8.45 frmHowTo8_8a.vb: Storing Values to the Session Object for Use on Another Page

Private Sub btnSearch_Click(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles btnSearch.Click


Session.Item("SearchRecordSource") = "Customers"
Session.Item("SearchField") = "CompanyName"
Session.Item("KeyField") = "CustomerID"
Session.Item("CallingPage") = "wfrmHowTo8_8a.aspx"
Server.Transfer("wfrmHowTo8_8b.aspx")
End Sub

3. Add the code in Listing 8.46 to the Load event of the Web Form. If the Session object has an entry
for ResultValue, then the LoadIndividual routine is executed and the ResultValue is passed.
The LoadIndividual routine is described in the next step. This routine is coded so that when the
page is reloaded after the search form has been used, the Session object entry will exist. When you
first come into the page, the entry doesn't exist.
Listing 8.46 frmHowTo8_8a.vb: Loading an Individual Record into Text Boxes on the Form

Private Sub Page_Load(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles MyBase.Load
If Not (Session("ResultValue") Is Nothing) Then
LoadIndividual(Session("ResultValue"))
End If
End Sub

4. Create the LoadIndividual routine by entering the code shown in Listing 8.47 in the form. Taking
the strCustID passed from the results of the search, a data adapter is created, and a data table is
filled. Last, each of the TextBox controls is loaded with the value from the column with the
corresponding name.
Listing 8.47 frmHowTo8_8a.vb: Loading an Individual Record into Text Boxes on the Form

Private Sub LoadIndividual(ByVal strCustID As String)


Dim odaCustIndiv As New _
OleDb.OleDbDataAdapter("Select * From Customers Where CustomerID = '" &
strCustID & "'", BuildCnnStr("(local)", "Northwind"))
Dim dtCustIndiv As New DataTable()
odaCustIndiv.Fill(dtCustIndiv)
With dtCustIndiv.Rows(0)
Me.txtCustomerID.Text = .Item("CustomerID").ToString
Me.txtCompanyName.Text = .Item("CompanyName").ToString
Me.txtContactName.Text = .Item("ContactName").ToString

Me.txtContactTitle.Text = .Item("ContactTitle").ToString
Me.txtAddress.Text = .Item("Address").ToString
Me.txtCity.Text = .Item("City").ToString
Me.txtRegion.Text = .Item("Region").ToString
Me.txtCountry.Text = .Item("Country").ToString
Me.txtPostalCode.Text = .Item("PostalCode").ToString
Me.txtPhone.Text = .Item("Phone").ToString
Me.txtFax.Text = .Item("Fax").ToString
End With
End Sub

5. Create the next Web Form, and call it whatever name you referred to in the search Web Form in step
2. Then place the controls shown in Figure 8.16 of the search page, with the properties set forth in
Table 8.11.

Table 8.11. Label, TextBox, DataGrid, and Command Button Controls Property
Settings for the Calling Form
Object

Property

Setting

DOCUMENT

bgColor

buttonface

TextBox

Name

Label1

Text

Click on a Letter

Panel

Name

Panel1

Button

Name

btnA

Caption

Name

btnB

Caption

Name

btnC

Caption

Name

btnZ

Caption

Name

btnAll

Caption

All

Name

dgSearch

AllowPaging

True

Name

btnAccept

Caption

&Accept

Name

btnCancel

Caption

&Cancel

Button
Button
...
Button
Button
DataGrid
Button
Button

6. In the class module for the Web Form, add the following Private declaration just below the line of code
that reads Web Form Designer Generated Code :

6.

Private mdtSearch As New DataTable()

7. On the Web Form, add the code in Listing 8.48 to the Load event. This routine loads the data table
stored in the Session object if it exists.
Listing 8.48 frmHowTo8_8b.vb: Executing a SQL Server-Supplied Stored Procedure That Lists
the Tables in the Database

Private Sub Page_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
If Not (Session("SearchDataTable") Is Nothing) Then
mdtSearch = CType(Session("SearchDataTable"), DataTable)
End If
End Sub

8. For each of the command buttons that has a single letter, add the first subroutine displayed in Listing
8.49 to each of the Click events. For the btnAll Button control, add the second subroutine to the Click
event. Each Button control will pass the letter it represents to the subroutine called SetData,
discussed in the next step. The btnAll code simply passes the empty string.
Listing 8.49 frmHowTo8_8b.vb: Click Events for Each of the Letter Button Controls

Private Sub btnA_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnA.Click
SetData("A")
End Sub
Private Sub btnAll_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnAll.Click
SetData("")
End Sub

9. Add the subroutine in Listing 8.50 to the class module of the form. This routine takes the letter value
passed in strFilterLetter as a parameter. A SQL Select string is created that takes the literal
values Select, From, and Where and uses the Session object entries KeyField, SearchField, and
SearchRecordSource. The SearchField property is used with the Like clause, also using the
strFilterLetter and the % (wildcard). Note that if " is passed to strFilterLetter, all the records will be
listed. Finally, mdtSearch is filled in the routine BindTheGrid set as the data source for dgSearch, which
is the DataGrid control. The routine called BindTheGrid is located at the end of the listing.
Listing 8.50 frmHowTo8_8a.vb: Filling the Results Set Based on the Letter Button Pressed

Sub SetData(ByVal strFilterLetter As String)


Dim odaSearch As OleDb.OleDbDataAdapter
odaSearch = New _
OleDb.OleDbDataAdapter("Select " & Session.Item("KeyField") &
", " & Session.Item("SearchField") & " From " & _
Session.Item("SearchRecordSource") & " Where " & _
Session.Item("SearchField") & " Like '" & _
strFilterLetter & "%'", (BuildCnnStr("(local)", "Northwind")))
mdtSearch.Clear()
odaSearch.Fill(mdtSearch)
Session("SearchDataTable") = mdtSearch
BindTheGrid()
End Sub
Private Sub BindTheGrid()
dgSearch.DataSource = mdtSearch
dgSearch.DataBind()
End Sub

Note
This routine, more than any in this How-To, shows the flexibility of
this technique. You can use any table values for these properties.
This is a major benefit when you think of how many places you can
use this form without changing a line of code in the form.

10. Right-click on the data grid and choose Property Builder. Go to the Columns tab and add a Select
button. Set the Button Type to be PushButton (see Figure 8.17) and click OK.
Figure 8.17. There is no code required for this button.

11. On the buttons called btnAccept and btnCancel, add the code in Listing 8.51 to the appropriate Click
event of each. The btnAccept_Click routine derives the data row that is currently selected from
mdtSearch using the data grid's SelectedIndex property and adding the paging that has to occur using
GetPageRows(), which returns the actual rows given the current page that the user is on in the
DataGrid object. You can see the GetPageRows() routine at the bottom of the listing. The KeyField
Session object entry is then used to store the individual column value of drCurr in the ResultValue
Session object entry. The calling page is reloaded.
The ResultValue is not set in the btnCancel_Click routine. The calling page is simply reloaded.
Listing 8.51 frmHowTo8_8b.vb: Storing the Resulting Key Value to the ResultValue Session
Object Entry and Returning to the Calling Page

Private Sub btnAccept_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnAccept.Click
Dim drCurr As DataRow
Try
drCurr = mdtSearch.Rows(dgSearch.SelectedIndex + GetPageRows())
Session("ResultValue") = drCurr.Item(Session("KeyField")).ToString
Catch exc As Exception
End Try

Server.Transfer(Session("CallingPage"))

End Sub
Private Sub btnCancel_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles btnCancel.Click
Server.Transfer(Session("CallingPage"))
End Sub
Function GetPageRows() As Integer
'-- This helps synchronize the data table rows
'
with the data grid page and row.
GetPageRows = dgSearch.PageSize * dgSearch.CurrentPageIndex
End Function

12. The last step is to add the code in Listing 8.52 for synching up pages in the data grid when the user
switches pages.
Listing 8.52 frmHowTo8_8b.vb: Updating the Data Grid with the New Page Number

Private Sub dgSearch_PageIndexChanged(ByVal source As Object,


ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs)
Handles dgSearch.PageIndexChanged
'-- Set the current page in the data grid.
Me.dgSearch.CurrentPageIndex = e.NewPageIndex
BindTheGrid()
End Sub

How It Works
When the user clicks on the Search button, the calling page stores values in the Session object for use in the
search page, telling it what record source to use, as well as other information used for searching for the
specific record and table desired:

"Customers" for the Session object entry SearchRecordSource


"CustomerID" for the Session object entry KeyField
"CompanyName" for the Session object entry SearchField
"wfrmHowTo8_8a" for the Session object entry CallingPage

After the search page is loaded, the user clicks one of the letters to narrow down the records to look for. A
data adapter is passed a SQL String made up of the Session object entries just mentioned, a data table is
filled, and the data grid's DataSource property sets the data table.
When the user clicks Accept, the DataTable is retrieved from the data grid, which then produces the data
row that contains the key field. The key field is then stored in the ResultValue Session object entry of the
search form, and the calling page is reloaded.
Back on the calling form, the LoadIndividual routine is called. Then the routine is passed the
ResultValue Session object entry from the search form. The text boxes are loaded with the data row
results.

Comments
This technique shows a number of ways that the various ADO.NET objects can be used. Using the Session
object and other State management methods is handy for creating pages that you want to use for more than
one table. You will find use for this technique throughout your applications after you become accustomed to
it.
Again, you can enhance this tool in several ways. One way is to allow the user to enter a string value to type
in, and narrow down the choices even more. Another way is to add a property that could be used to specify
multiple columns to be displayed.

[ Team LiB ]

[ Team LiB ]

Chapter 9. Using Classes With Databases to Make Life Easier


In this chapter you will

Define a class in Visual Basic .NET


Create a class that implements the interface you defined
Use Visual Studio .NET tools to speed up writing ADO.NET code
Control the creation and behavior of classes
Implement the methods that update the database
Validate data passed to properties and communicate errors to developers
Write data validation code that can be reused in other classes
Writing database-access code is never a simple task, whether you use ADO.NET, DAO, RDO, db-lib, ct-lib, or
OCI. Although ADO.NET makes life much easier, data still needs to be validated for size, type, or range;
referential integrity must be maintained; business rules must be obeyed; exceptions must be handled; and
so on. Moreover, writing the code can be tedious. Think of how many lines of code are needed to set up a
DbCommand object with ten parametersand maintaining different code bases that access the same
database objects can turn into weeks of busywork.
The solution for any complex programming problem is to place all the related code into a flexible, reusable
code component, or class. This chapter shows you how easy it is to create a class that handles all the
common tasks you need to access data in the table, and more important, how this will simplify writing code
for the rest of your application.
This chapter also touches on several of Visual Basic .NET's objects-oriented features. Because Microsoft has
made every .NET languageincluding Visual Basica fully object-oriented language, no book on .NET
programming is complete without some discussion of object-oriented programming. This book, however, is
all about database access using .NET technologies, so we shouldn't get bogged down with discussions filled
with words like polymorphism, encapsulation, and inheritance. In fact, this chapter will do its best to avoid
using these terms (at least until the concept is already explained) because, frankly, if you're new to objectoriented programming (OO), these terms can be confusing and intimidating.
This chapter uses three terms frequently that need definition. A class is a software component that combines
related data, function, and type definitions into a new type. In other words, it is a description of some
properties and methods, but nothing more. The properties, methods, types, data, and so on of a class are
referred to jointly as members. To use a class, you need to instantiate it with a specific set of data. An
instance of a class is called an object.
This chapter takes for granted that you understand how DataAdapter, DataSet , and Command objects
interact to assist client applications and databases communicate. Instead, it focuses on the tools that
Microsoft has provided for you to automatically generate code that creates these objects for use with specific
database tables, as well as the basic principles for creating data access classes.

[ Team LiB ]

[ Team LiB ]

9.1 Define a Class in Visual Basic .NET


Before writing code, you should take a moment to decide what data the class needs to contain and what
actions a developer (whether it is you or a teammate) would want to perform on that data. For the moment,
we'll keep it fairly simple by limiting the class to properties representing the columns in the Customers table
and to methods that insert, delete, and update data.

Technique
Visual Basic .NET has a specific kind of code file that you can use to describe the public members of a class:
an interface. An interface simply lists all the public variables, methods, and properties that any class
implementing that interface must expose. Interfaces are an enormously powerful and flexible feature of
object-oriented programs, and this chapter just scratches the surface. Because of the fact that an interface
has no code, you can focus on the design of your application without the clutter of hundreds of lines of
source code.

Steps

1. First, create a new Windows Application project and name it CustomerClasses.


2. To create a new interface, add a new class to your project by right-clicking on the Solution Explorer
window and selecting Add Class. Name the new file CustomerInterface9_1.vb.
3. Change the class block declaration so that it reads interface instead of class, and name the class
ICustomer. You should have a code file like that shown in Listing 9.1.
Listing 9.1 CustomerInterface9_1.vb: An Empty Interface

Public Interface ICustomer


End Interface

Add Properties to the Interface


The first items you need to add to your interface are properties to represent the columns in the Customers
table.

1. Using the Server Explorer, drill down to the Customers table in the Northwind database so that you
have an easy reference to the Customers table.
2. To make your code more readable, add a #Region/#End Region block with the name Properties
so that you can expand and collapse your property declarations as needed.
3.

2.

3. For each column in the table, add a property declaration to your interface that matches the datatype of
the column and its name(see Listing 9.2).
Listing 9.2 CustomerInterface.vb: The ICustomer Interface with Properties

Public Interface ICustomer


#Region "Properties"
'Define properties for this interface
'that match the table structure in name and data type
Property CustomerID() As String
Property CompanyName() As String
Property ContactName() As String
Property ContactTitle() As String
Property Address() As String
Property City() As String
Property Region() As String
Property PostalCode() As String
Property Country() As String
Property Phone() As String
Property Fax() As String
#End Region
End Interface

More About Properties


ICustomer is a simple interface. It has only one kind of datatypea stringand all of the properties are
read/write. When you begin to write more complex classes, you might need more than just read/write
properties. Visual Basic .NET also has read-only and write-only properties, parameterized properties, and
default properties.

Read-Only and Write-Only Properties


If you have written classes in Visual Basic 6, you might be scratching your head, wondering how the code
sample in Listing 9.2 declares your properties read/write. In Visual Basic 6, all that mattered was whether
you wrote the appropriate Property Get and Property Let/Set blocks in your class. If you did not have a
Public or Friend Property Let/Set, the property was read-only. If you did not have a Public or Friend Property
Get, the property was write-only.
In Visual Basic .NET, you must explicitly declare a property as read-only or write-only using the ReadOnly
and WriteOnly keywords (see Table 9.1).

Table 9.1. Visual Basic .NET Property Modifiers

Object

Purpose

ReadOnly The property cannot be modified outside of the class.


WriteOnly The property can only be modified, but only methods that are internal to the class can read the
property.
In our example, a developer who is using this class could change the CustomerID. Without the original
CustomerID, the class won't be able to find that record in the database when updating the table, so perhaps
you should make the CustomerID property read-only. To do this, simply add the ReadOnly keyword to the
beginning of the property declaration as in Listing 9.3.
Listing 9.3 CustomerInterface9_1.vb: Using the ReadOnly Keyword

'Adding the ReadOnly keyword before the property


'declaration makes the property read-only.
ReadOnly Property CustomerID() As String

Parameterized Properties
You can also create properties that accept a parameter. For example, in the Northwind database, a customer
might have many orders. A parameterized property would be a perfect way to access Orders information
from the Customer class.
A parameterized property (see Listing 9.4) takes one or more parameters that can be used to qualify the
data that the property returns.
Listing 9.4 A Parameterized Property

'The nIndex parameter would be used to determine which


'item from a collection of orders should be returned.
Property CustomerOrders(ByVal nIndex As Integer) As COrder

Tip
One of the advantages to interfaces is the ability to rapidly define all of
the classes you plan to use in your application. For the previous code
example, you could create an interface called IOrder that was similar
to the ICustomer interface in Listing 9.2. Instead of returning an
actual class from the CustomerOrders property, you could return the
IOrder interface. The interface would act as a placeholder for any
object that implemented the interface. This way, you could program
the Customer class and put off writing code for the Order class until
later.

Default Properties

In Visual Basic 6, any property in a class could be defined as the default property for that class. For example,
if the CustomerID property were declared the default property, both of the examples in Listing 9.5 would
return the customer ID.
Listing 9.5 Default Properties in Visual Basic 6

Dim oCustomer As CustomerData


Dim strCustomerID As String
'Example 1: This code places the value of the customer ID property
'into a variable.
strCustomerID = oCustomer.CustomerID
'Example 2:This code does the same thing.
strCustomerID = oCustomer

In Visual Basic .NET, the only kind of property that you can declare as the default property for a class is a
parameterized property.
To declare a property as the default property, simply add the Default keyword prior to the property
declaration, as shown in Listing 9.6.
Listing 9.6 Declaring a Default Property

'Adding the Default keyword before the property declaration


'makes this parameterized property the default property for
'the class.
Default Property CustomerOrders(ByVal nIndex As Integer) As COrder

Add Methods to the Interface


Now that you have added all the properties you need, you need to add the various methods that a developer
uses to retrieve, insert, update, and delete data from the Customers table.

1. To make your code more readable, add a #Region/#End Region block with the name Methods so
that you can expand and collapse your method declarations as needed.
2. The point in wrapping a table in a class is to make access to this table as simple as possible. You could
have one method to insert new rows to the database and one to update existing rows, leaving the task
of determining the state of the object to the code that instantiates it. The actions of the class,
however, should be as transparent to other developers as possible. To this end, the interface should
only define two methods: save and delete. (The retrieve method will be defined in a subsequent
section.) Add the save and delete methods to your interface, as in Listing 9.7.
Listing 9.7 CustomerInterface9_1.vb: Declaring the Delete and Save Methods

#Region "Methods"
'To update or delete a record, a user would have needed to
'retrieve data before modifying any of the properties.

Function Save() As Boolean


Function Delete() As Boolean
#End Region

As I mentioned in the introduction to this chapter, one of the great advantages to wrapping up all
access to a database table within a class is that you only have to write complex error-handling
code once. To keep things simple, we are returning a Boolean value from each of these methods,
letting other developers know whether the action failed without requiring them to write complex
error-handling code.

3. Finally, add a ToString method to output all the data of the class in a string. This is extraordinarily
useful for debugging. Your finished interface should look like Listing 9.8.
Listing 9.8 CustomerInterface9_1.vb: The Final ICustomer Interface

Public Interface ICustomer


#Region "Properties"
'Define properties for this interface
'that match the table structure in name and data type
ReadOnly Property CustomerID() As String
Property CompanyName() As String
Property ContactName() As String
Property ContactTitle() As String
Property Address() As String
Property City() As String
Property Region() As String
Property PostalCode() As String
Property Country() As String
Property Phone() As String
Property Fax() As String
#End Region
#Region "Methods"
'To update record, a user would have needed to
'retrieve data before modifying any of the properties.
Function Save() As Boolean
Function Delete() As Boolean
Function ToString() As String
#End Region
End Interface

How It Works
You might have noticed that an interface has no actual code. An interface is designed for one purpose: to
define a set of public methods and interfaces that are related to a specific entity. To put this in objectoriented terminology, an interface represents an abstraction of an entityin this case, a customer.
Interfaces can't function by themselves; a class must implement all of its properties and methods, which is
what you will do in the next sections.

Comments
Of course, you don't really need to write an interface before writing the class, and in our current example, it
might be overkill. The power of an interface really comes into play when you have multiple classes that might
implement the same interface in different ways.
An interface cannot exist as an object: It has no code and no place to store object data. A class that
implements an interface has an explicit contract with the interface: The class must implement all the
properties and methods of the interface. If you declare a variable typed to an interface, that variable could
represent any object of any class that implements the interface.
You've already seen several examples of interfaces in this book, such as the DataAdapter, DataReader,
Connection, Command , and other ADO.NET objects. Take, for example, the Command interface. At present,
two types of Command classes exist: the OleDbCommand and the SqlCommand. Both of these classes
implement the IDbCommand interface. The OleDbCommand is responsible for handling data access to any
database provider with an OleDB driver, and the SqlCommand connects directly to SQL Server. If you wrote
a function that processed Command objectssuch as a debugging function that wrote all the properties of a
Command to an error logyou might need to handle both kinds. Instead of writing two functions, one for
each Command type,

Public Sub WriteCommandErrorToLog(ByVal pCommand as SqlCommand)


Public Sub WriteCommandErrorToLog(ByVal pCommand as OleDbCommand)

you could simply declare a function that takes a variable of type IDbCommand, which would accept an
instance of either command type:

Public Sub WriteCommandErrorToLog(ByVal pCommand as IDbCommand)

The second solution is far more elegant because, before long, you could end up with Commands for Oracle,
Sybase, MySql, SqlAnywhere, DB2, and so on, all of which would implement the IDbCommand interface.
It might look like you're missing a few items in ICustomer. What method do you call to add new records or
retrieve data from the database? If you're working with a new customer row, how does a developer set the
CustomerID? A new type of method called a constructor is executed when a class is instantiated. Because
constructors can't be declared in an interface, we will cover this subject in section 9.4.

[ Team LiB ]

[ Team LiB ]

9.2 Create a Class That Implements the Interface You Defined


Now that you have defined the public interface of your class, you need to create a class that will implement
that interface, along with all of its methods and properties.
The place to start is with the properties of your class. You'll also need to create some code to test your class,
so you'll need to create a form that interacts with the instances of the class.

Technique
This section uses a form with text boxes that mirror the properties of the CCustomer class. Visual Basic .NET
allows you to have classes and forms within the same .vb file, so this section will have both in one file to
make editing and debugging easier.
After setting up the form, you need to implement the class properties. If you have worked with Property
statements before, this technique will be old hat (although the syntax will be new). If you have not worked
with properties and classes, you will need the following:

A private variable for each property to store class data


Code to modify and return that data

Steps

1. Create a Windows Form and name it frmHowTo9_2. Then place text boxes for each of the properties of
the class, naming the text boxes with the property name, prefaced by txt. For example, the postal
code text box should be named txtPostalCode. Next add command buttons called cmdDelete,
cmdSave, cmdRetrieve, and cmdNew. Finally, add a RichTextBox control called rtbToString to contain
the output of the ToString method. (This enables you to see how the Form maps to the data of the
class. See Figure 9.1)
Figure 9.1. Arrange the controls on the form you created to look like this form.

2. Add a class declaration block to frmHowTo9_2 and name the class CCustomer. After the line Public
Class CCustomer, type Implements ICustomer. This tells Visual Basic that the CCustomer class
includes all of the properties and methods of the ICustomer interface as shown in Listing 9.9.
Listing 9.9 frmHowTo9_2.vb: The Empty Customer Class

Public Class CCustomer


Implements ICustomer
End Class

3. Copy all of the property and method declarations in the ICustomer interface, and paste them within
the CCustomer class block.
4. Visual Basic .NET requires that you specify which property or method in your class implements which
public member of your interface. After each property or method declaration that you just pasted into
the CCustomer class block, you must add Implements ICustomer.[Property/Method Name].

Note
Visual Basic .NET allows you to have property and method names in
your class that differ from the public member names listed in your
interface. This feature exists because one class can implement
many interfaces, and those interfaces can have public members of
the same name. Although typing Implements
ICustomer.[Property/Method Name] after a property and
method with the same nameas in our examplemight seem
frustrating, it will come in handy when you write more complex
code.

5. By default, all methods, properties, and module-level variables are Public unless an access modifier,

5.
such as Private or Protected, is used (see Table 9.2). It is, however, good programming practice to use
the Public access modifier, so be explicit and add Public before each member.

Table 9.2. Visual Basic .NET Access Modifiers


Object

Purpose

Private

You can only access the member within the class.

Protected

You can only access the member from classes that are derived from
(inherit from) the member's class.

Friend

You can only access the member by objects within the same project.

Protected
Friend

You can only access the member by derived classes within the same
project.

Public

You can access the member by any object.

When you are finished, your code should look like Listing 9.10.
Listing 9.10 frmHowTo9_2.vb: The Empty CCustomer Class

Public Class Customer


Implements ICustomer
Public ReadOnly Property CustomerID() As String _
Implements ICustomerData.CustomerID
Public Property CompanyName() As String Implements ICustomerData.CompanyName
Public Property ContactName() As String Implements ICustomerData.ContactName
Public Property ContactTitle() As String _
Implements ICustomerData.ContactTitle
Public Function Delete() As Boolean Implements ICustomerData.Delete

Note
Copying and pasting the code from the interface will result in code
with an invalid syntax because of the lack of End Property and End
Function/Sub lines. Although this doesn't matter for the moment, it
does disable Intellisense, which normally appears after typing both
Implements and the period after the Interface name. If you want
to enable Intellisense, press Enter at the end of each property and
method declaration. Doing so inserts the appropriate End tag.
When all the tags are in the class, Intellisense is reenabled.

6. Add private variables directly below the class declaration to store class data, as shown in Listing 9.11.
You should have one variable for each property. As mentioned in Chapter 3, "Viewing Data with
ADO.NET," prefacing class-level variables with "m" is a Visual Basic programming convention.
Listing 9.11 frmHowTo9_2.vb: Class Variable Declarations

Public Class CCustomer


Implements ICustomer
#Region "Class Variables"
Private mstrCustomerID As String
Private mstrCompanyName As String
Private mstrContactName As String
Private mstrContactTitle As String
Private mstrAddress As String
Private mstrCity As String
Private mstrRegion As String
Private mstrCountry As String
Private mstrPostalCode As String
Private mstrPhone As String
Private mstrFax As String
#End Region

7. Write code to set and return data for the properties. Place your cursor at the end of a property
declaration line, and press Enter. Visual Studio .NET automatically inserts code blocks for setting and
getting your property. If you have worked with properties before, you might notice that Microsoft has
changed the syntax. Listing 9.12 shows Visual Basic 6 property get and let declarations, each with its
own separate block.
Listing 9.12 Property Declarations in Visual Basic 6

Public Property Get PropertyName() As String


PropertyName = mstrClassVariable
End Property
Public Property Let PropertyName(ByVal strValue As String)
mstrClassVariable = strValue
End Property

In Visual Basic .NET, the property declaration has been combined in the format shown in Listing
9.13.
Listing 9.13 Property Declarations in Visual Basic .NET

Public Property PropertyName() As String


Get
Return mstrClassVariable
End Get
Set(ByVal Value As String)
mstrClassVariable = Value
End Set
End Property

For each of your properties, add Return and the variable you declared in step 1 in the Get block.

In the Set block, type the name of a variable from step 1 and = Value . All of your property
declarations should now look like Listing 9.14.
Listing 9.14 frmHowTo9_2.vb: Some Property Declarations for the CCustomer Class

Public ReadOnly Property CustomerID() As Customers.CCustomerID


Implements ICustomer.CustomerID
Get
Return mCustomerID
End Get
End Property
Public Property CompanyName() As String Implements ICustomer.CompanyName
Get
Return mstrCompanyName
End Get
Set(ByVal Value As String)
mstrCompanyName = Value
End Set
End Property

8. To test your properties, you need to write a bit more code. First, you might want to implement the
ToString method that returns all the object's properties as a string. Also, you will want a method that
clears the form for use with a new object. Listing 9.15 shows one way to implement ToString, with
each property printing on a separate line, as well as a ClearAllTextBoxes method.
Listing 9.15 frmHowTo9_2.vb: The ToString Method Outputting Property Information

Public Overrides Function toString() As String Implements ICustomer.ToString


Dim strReturn As String
strReturn = Me.CustomerID & vbCrLf
strReturn = strReturn & "Company:
" & Me.CompanyName & vbCrLf
strReturn = strReturn & "Contact:
" & Me.ContactName & vbCrLf
strReturn = strReturn & "Title:
" & Me.ContactTitle & vbCrLf
strReturn = strReturn & "Address:
" & Me.Address & vbCrLf
strReturn = strReturn & "City:
" & Me.City & vbCrLf
strReturn = strReturn & "Postal Code: " & Me.PostalCode & vbCrLf
strReturn = strReturn & "Country:
" & Me.Country & vbCrLf
strReturn = strReturn & "Phone:
" & Me.Phone & vbCrLf
strReturn = strReturn & "Fax:
" & Me.Fax & vbCrLf
strReturn = strReturn & "Region
" & Me.Region & vbCrLf
Return strReturn
End Function
Protected Sub ClearAllTextBoxes()
Me.txtAddress.Text = ""
Me.txtCity.Text = ""
Me.txtCompanyName.Text = ""
Me.txtContactName.Text = ""
Me.txtContactTitle.Text = ""

Me.txtCountry.Text = ""
Me.txtCustomerID.Text = ""
Me.txtFax.Text = ""
Me.txtPhone.Text = ""
Me.txtRegion.Text = ""
Me.txtPostalCode.Text = ""
Me.rtbToString.Text = ""
End Sub

The Overrides keyword is necessary because your class already has a ToString method. You
didn't write that method, but it's there. This is because every class inherits from the Object
class, which defines a ToString method. Don't worry about this: You'll learn about inheritance
and overriding methods later in this chapter.
Now you need to add three pieces of code to the form that will instantiate the CCustomer class,
and you need to allow its properties to be modified through the text boxes.

9. First, add a form-level variable called mCustomer of type CCustomer to the form. Use the new keyword
in the declaration to create a new instance of CCustomer.
10. Next, add code to each TextBox control's TextChanged event that updates property values in
mCustomer. Note that Listing 9.16 does not actually refer to mCustomer. Instead, Listing 9.16 calls an
as-yet-undefined method called TextBoxChange that handles changes to mCustomer. This method is
defined and explained in step 11.
Listing 9.16 frmHowTo9_2.vb: The TextChanged Event of the txtPostalCode Text Box That
Calls the TextBoxChange Method

Protected Sub txtPostalCode_TextChanged(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles txtPostalCode.TextChanged
TextBoxChange("PostalCode", txtPostalCode.Text)
End Sub

11. Finally, add the TextBoxChange method that takes the value passed from each text box's
TextChanged event and writes it to the appropriate property of the mCustomer object. Listing 9.17
shows a sample of this code, although it does not list a case for each text box. Centralizing all access
to the mCustomer object in one method allows you to have one exception-handling area.
Listing 9.17 frmHowTo9_2.vb: An Excerpt of the TextBoxChange Method That Writes Values
in Text Boxes to Object Properties

Protected Sub TextBoxChange(ByVal pstrProperty As String, _


ByVal pstrValue As String)
Try
Select Case pstrProperty
Case "Address"

mCustomer.Address = pstrValue
Case "CompanyName"
mCustomer.CompanyName = pstrValue
Case "PostalCode"
mCustomer.PostalCode = pstrValue
End Select
' Write the class' properties to the RichTextBox
' to aid debugging.
Me.rtbToString.Text = mCustomer.ToString()
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub

How It Works
An instance of the CCustomer class is created when frmHowTo9_2 is created. When the user types in a text
box, the TextChanged event fires, calling the TextBoxChange method. The TextBoxChange method sets
the corresponding property of the class handles any exceptions, and then writes the output of the
CCustomer.ToString method to the RichTextBox.

Note
In the samples for this chapter, I want to use the same form for each
section's code samples without having to re-create the form each
time. To this end, I have used frmHowTo9xBase as a base form class,
and all the frmHowTo9_x forms inherit from this class. Some common
methods, including the TextChanged events, are defined in
frmHowTo9xBase rather than in each child form.

Comments
Microsoft has made several changes to property declarations, and it's worth enumerating them here.
First, the Get and Set code blocks are wrapped up in one Property declaration, making neater and easierto-read code.
Second, the Return keyword is now used instead of the Property name in the Get block. Remember that no
code is executed after the Return keyword.
Third, there is no longer a distinction between Set and Let. In Visual Basic 6, Set was used for Object
properties, and Let was used for base datatypes, such as Strings and Integers. Microsoft has eliminated this

confusing distinction, and now Set is used in all cases.


Just like Visual Basic 6, however, read-only properties will have only a Get block, whereas write-only
properties will have only a Set block.

[ Team LiB ]

[ Team LiB ]

9.3 Use Visual Studio .NET Tools to Speed Up Writing ADO.NET Code
The code you've written up to this point doesn't do that much. It doesn't even access the database. The next
task is to write code that populates the class with data from the database, and the first step in doing this is
setting up database access objects.

Technique
In Chapter 3 , you learned how to fill a dataset to store data in a disconnected fashion. In this chapter, you
will use a strongly typed datasetthat is, a dataset with data rows that match the name and datatypes of
the columns. You will learn how to use the DataAdapter Configuration Wizard to autogenerate code that
initializes Command and DataAdapter objects for use with a specific table.

Steps

1. Right-click on your project and select Add New Item from the Add menu. Choose DataSet and name it
dsCustomers.xsd.
2. Visual Studio .NET opens dsCustomers.xsd in Design mode. Expand the Server explorer and drill down
to Data Connections, Northwind, Tables. Drag the Customers table onto the Design view.
3. Visual Studio might process for a few seconds, but afterward, you'll have a strongly typed dataset.
The result? Instead of writing dataset code like this:

strCustomerID = CType(ds.Tables("Customers").Rows(0).Item("CustomerID"), String)

you'll have code that looks like this:

strCustomerID = ds2.Customers(0).CustomerID

A strongly typed dataset is a combination of two documents. One is a .vb file with the same name
as the dataset. Visual Studio .NET will not show you the contents of this file (unless you step into
it while debugging), and the contents don't appear in the Solution Explorer. The other file is an
.xsd file, or a XML Schema Definition (XSD), which defines a data structure. The .vb file reads the
XSD to properly instantiate a strongly typed dataset.
You should always take a look at any code that is generated automatically by a tool because the
tool might generate code that doesn't do precisely what you want it to do. If you have never seen
an XSD, this is also a good time to learn something new. Listing 9.18 shows the XSD for the
dsCustomers dataset. You can view the XSD you created by opening dsCustomers.xsd from the
Solution Explorer and then clicking the XML button at the bottom of left corner of the screen.
Listing 9.18 dsCustomers.xsd : The Customers XSD

<?xml version="1.0" encoding="utf-8" ?>


<xsd:schema
id="dsCustomers"
targetNamespace="http://tempuri.org/dsCustomers.xsd"
elementFormDefault="qualified"
xmlns="http://tempuri.org/dsCustomers.xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="dsCustomers" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="Customers">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="CustomerID" type="xsd:string" />
<xsd:element name="CompanyName" type="xsd:string" />
<xsd:element name="ContactName" type="xsd:string" minOccurs="0" />
<xsd:element name="ContactTitle" type="xsd:string" minOccurs="0" />
<xsd:element name="Address" type="xsd:string" minOccurs="0" />
<xsd:element name="City" type="xsd:string" minOccurs="0" />
<xsd:element name="Region" type="xsd:string" minOccurs="0" />
<xsd:element name="PostalCode" type="xsd:string" minOccurs="0" />
<xsd:element name="Country" type="xsd:string" minOccurs="0" />
<xsd:element name="Phone" type="xsd:string" minOccurs="0" />
<xsd:element name="Fax" type="xsd:string" minOccurs="0" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
<xsd:unique name="dsCustomersKey1" msdata:PrimaryKey="true">
<xsd:selector xpath=".//Customers" />
<xsd:field xpath="CustomerID" />
</xsd:unique>
</xsd:element>
</xsd:schema>

It is beyond the scope of this chapter and this book to fully explain an XSD. The subject requires
an entire book of its own. But it's important to point out a few areas of this XSD.
The element tags are really nothing more than the properties you have already declared. Each
element has a name that corresponds to a column in the Customers table, as well as a type that
loosely corresponds to the datatype of the column.
The element tag also has a minOccurs attribute. This attribute actually defines whether a value
is required for that element. The default for the minOccurs attribute is 1, which means that the
element does not allow Null values.
Also, take a close look at the lines at the end of the XSD that begin with <xsd:unique . This XML
block refers back to the CustomerID element, and it declares that that element is the primary key
of the dsCustomers XSD.
A dataset is just one part of what you need. Without a data adapter, the dataset just sits there.
You could create all the data adapter code yourself, but again, Visual Studio .NET can do a lot of
the work for you.

4.

4. Right-click on your project and select Add New Item from the Add menu. Choose Component Class and
name it CCustomerData.vb.
5. Visual Studio opens CCustomerData.vb in Design mode. Expand the Toolbox on the left side of the
screen. Choose the Data tab, and drag an OleDbConnection onto the Design view.
6. Click on OleDbConnection1 and rename it oleCnn.
7. Set the ConnectionString property. You should have an existing connection to the Northwind
database. (You set this up in section 9.1 .) If you don't have an existing connection, create a connection
by selecting New Connection from the ConnectionString menu.
8. Drag an OleDbDataAdapter from the Data tab of the Toolbox. Doing so opens the DataAdapter
Configuration Wizard.
9. First, in the Choose Your Data Connection page of the wizard, shown in Figure 9.2 , select a connection
for the data adapter, and then click Next.
Figure 9.2. The Choose Your Data Connection page of the DataAdapter Configuration Wizard.

10. Now, you have the option of choosing the type of query to use with the data adapter. For the purposes
of this example, use SQL statements. The other choices, shown in Figure 9.3 , allow you to specify
existing stored procedures, or have the wizard create new database stored procedures for you.
Figure 9.3. The Choose a Query Type page of the DataAdapter Configuration Wizard.

11. The next page of the wizard, shown in Figure 9.4 , asks for a SQL statement to retrieve data from the
database. Choose Query Builder to create a new query.
Figure 9.4. The Generate the SQL Statements page of the DataAdapter Configuration Wizard.

12. Using the Query Builder , shown in Figure 9.5 , select the Customers table, and return all the columns
in the table. Add a WHERE condition to select a single customer row based on the Customer ID as in
Listing 9.19 . Click Next and then Finish.
Listing 9.19 CustomerData.vb : The SELECT Command Text to Use in the DataAdapter
Configuration Wizard

SELECT CustomerID, CompanyName, ContactName, ContactTitle,


Address, City, Region, PostalCode, Country, Phone, Fax
FROM Customers
WHERE CustomerID = ?

Figure 9.5. The Query Builder allows you to graphically build a SQL statement.

13. Again, Visual Studio picks some undescriptive and bland names for the data adapter and its Select ,
Update , Insert , and Delete commands. In this example, OleDbAdapter1 will be renamed to
odaCustomers, and OleDb[Action]Command1 will be renamed to [Action]Customer.
14. Finally, right-click on odaCustomers and select Properties. In the Properties window, expand the
TableMappings property, which will open a dialog box like that shown in Figure 9.6 . This property
tells Visual Studio .NET where to look for datatype and column information. The DataAdapter
Configuration Wizard will have referenced the Customers table because it was not aware of the XSD you
created earlier in this section. Check on the Use a Dataset to Suggest Table and Column Names check
box, and select dsCustomers from the DataSet combo box. Make sure that the DataSet Table combo
box references the Customers table of the dataset.
Figure 9.6. The Table Mappings collection of the data adapter.

Based on the query you entered, Visual Studio .NET will generate Update, Delete, and Insert SQL statements
for the Customers table, as well as code that populates the parameter collections and other properties of
Command objects based on those SQL statements. Most important, if you modify the SQL statements, Visual
Studio .NET updates the Visual Basic code setting up their command objects.
The SQL statements that Visual Studio .NET generates might be a bit more complex than you need. In
Listing 9.20 , the UPDATE command has a WHERE condition that references every column instead of just the
primary key.
Listing 9.20 CustomerData.vb : The Update Command Text from the Visual Studio .NETGenerated Data Adapter

UPDATE Customers
SET CustomerID = ?,
ContactName = ?,
Address = ?,
Region = ?,
Country = ?,
Fax = ?
WHERE (CustomerID = ?)
AND (CompanyName = ?)
AND (Address = ?
AND (City = ?
AND (ContactName = ?
AND (ContactTitle = ?

CompanyName = ?,
ContactTitle = ?,
City = ?,
PostalCode = ?,
Phone = ?,

OR
OR
OR
OR

?
?
?
?

IS
IS
IS
IS

NULL
NULL
NULL
NULL

AND
AND
AND
AND

Address IS NULL)
City IS NULL)
ContactName IS NULL)
ContactTitle IS NULL)

AND
AND
AND
AND
AND

(Country = ?
(Fax = ?
(Phone = ?
(PostalCode = ?
(Region = ?

OR
OR
OR
OR
OR

?
?
?
?
?

IS
IS
IS
IS
IS

NULL
NULL
NULL
NULL
NULL

AND
AND
AND
AND
AND

Country IS NULL)
Fax IS NULL)
Phone IS NULL)
PostalCode IS NULL)
Region IS NULL)

This SQL statement is guaranteed to make sure you update only the record you retrieved. If the record has
changed in another session since the data was retrieved by the current session, that row won't be updated.
This might be the behavior you need in some situations. Still, the whole point of a primary key is that it fully
represents all the non-key values in the table (and, of course, none of the non-key values should rely on
each other for definition), so perhaps that WHERE condition is overkill.
You might also have noticed that the SET clause of the UPDATE statement that Visual Studio .NET generated
updates the value of the CustomerID column. In the case of the Customers table, this SQL statement could
not execute because DRI is enforcing the relationship with the Orders table. However, not all databases use
DRI; if the CustomerID were changed in that scenario, the code would execute and orphan rows in other
tables that contain the old CustomerID value.
In that case, it would be best to modify the Visual Studio .NET-generated SQL statements to reflect the
primary key and make the code more sensible. For example, the UPDATE statement from Listing 9.20 would
read as shown in Listing 9.21 .
You can access the Insert, Update, and Delete SQL statements used by the data adapter by expanding the
related command in the Properties window. Then look for the CommandText property and click on the Ellipsis
button , as shown in Figure 9.7 .
Listing 9.21 CustomerData.vb : A Simpler Update Command Text from the Visual Studio .NETGenerated Data Adapter

UPDATE Customers
SET CompanyName = ?,
ContactName = ?,
Address = ?,
Region = ?,
Country = ?,
Fax = ?
WHERE (CustomerID = ?)

ContactTitle = ?,
City = ?,
PostalCode = ?,
Phone = ?,

Figure 9.7. The Properties window allows you to access the CommandText property by expanding
the data adapter's Update command.

When you do modify those statements, you'll notice that a SELECT SQL statement follows the Insert and
Update commands.
In ADO 2.x , a cursor was maintained on the server using server resources or on the client with a connection
to the server. This meant that an updateable recordset would receive notification of data changes, such as
the value of an IDENTITY or other auto-incrementing column after inserting a new row or the values of
updated data after an UPDATE trigger made additional changes. The open connection and cursor, however,
consume substantial resources and can adversely affect server performance. ADO.NET's disconnected
architecture is an attempt to resolve this resource problem. Still, refreshing the dataset with the most
current data can be quite helpful in databases that have triggers or complex stored procedures, so this
second SQL command will retrieve the updated data from the database and refresh the dataset to reflect the
database-side changes to submitted data.

Tip

If you're working with a Web application on SQL Server, you might not need this
level of concurrency. All you might need is the new IDENTITY value when
inserting records. Instead of selecting back the inserted row, you could end the
SQL statement with this:

SELECT @@IDENTITY

which returns the last identity value created in the current transaction. You execute the Insert

command using the ExecuteScalar method, which returns this integer value to your Visual Basic
code.
This requires dropping the Visual Studio .NET-generated data adapter code, so you need to weigh
this slight performance gain against the time needed to maintain manually generated code.
However, as you'll see in the following pages of this section, the code that Visual Studio .NET
generates is nothing you haven't read about in the earlier chapters of this book.

As mentioned earlier, before you start writing code that utilizes this data adapter, you should take a look at
the code that Visual Studio .NET has generated. You can view the generated code, like the excerpts shown in
Listing 9.22 , by right-clicking on the gray area of CCustomerData.vb and selecting View Code.
Listing 9.22 CustomerData.vb : A Sample of the Code Generated by the Visual Studio .NET
DataAdapter Configuration Wizard

Private Sub InitializeComponent()


' instantiate the connection object
Me.oleCnn = New System.Data.OleDb.OleDbConnection()
' instantiate the data adapter
Me.odaCustomers = New System.Data.OleDb.OleDbDataAdapter()
' instantiate the OLE DB command we will use for updating data
Me.updateCustomer = New System.Data.OleDb.OleDbCommand()
' set the table mapping properties for the data adapter. This defines
' which columns' names in your client code map to which columns in the table.
' This allows you to alias terse and obscure column names with longer,
' more descriptive names.
Me.odaCustomers.TableMappings.AddRange(New _
System.Data.Common.DataTableMapping()
{New System.Data.Common.DataTableMapping("Table", "Customers",
New System.Data.Common.DataColumnMapping()
{New System.Data.Common.DataColumnMapping("CustomerID", "CustomerID"),
New System.Data.Common.DataColumnMapping("CompanyName", "CompanyName"),
New System.Data.Common.DataColumnMapping("ContactName", "ContactName"),
New System.Data.Common.DataColumnMapping("ContactTitle", "ContactTitle"),
New System.Data.Common.DataColumnMapping("Address", "Address"),
New System.Data.Common.DataColumnMapping("City", "City"), _
New System.Data.Common.DataColumnMapping("Region", "Region"), _
New System.Data.Common.DataColumnMapping("PostalCode", "PostalCode"),
New System.Data.Common.DataColumnMapping("Country", "Country"),
New System.Data.Common.DataColumnMapping("Phone", "Phone"),
New System.Data.Common.DataColumnMapping("Fax", "Fax")
})
})
' make the OLE DB command we instantiated above the update command
' for the data adapter.
Me.odaCustomers.UpdateCommand = Me.updateCustomer

' set the connection for the update command


Me.updateCustomer.Connection = Me.oleCnn
' set the command text of the update command to the SQL statements from
' Listing 9.16.
Me.updateCustomer.CommandText = _
"UPDATE Customers SET CompanyName = ?, ContactName = ?, ContactTitle = ?, " & _
"Address = ?, City = ?, Region = ?, PostalCode = ?, Country = ?, " & _
"Phone = ?, Fax = ? WHERE" (CustomerID = ?); SELECT CustomerID, " & _
"CompanyName, ContactName, ContactTitle, Address, City, Region, " & _
"PostalCode, Country, Phone, Fax FROM Customers " & _
"WHERE (CustomerID = ?)"
' add all of the parameters for the update command. I have included only
' two parameters in this text.
Me.updateCustomer.Parameters.Add(New _
System.Data.OleDb.OleDbParameter("ContactName",
System.Data.OleDb.OleDbType.VarWChar, 30,
System.Data.ParameterDirection.Input,
True, CType(0, Byte), CType(0, Byte), "ContactName", _
System.Data.DataRowVersion.Current, Nothing))
Me.updateCustomer.Parameters.Add(New
System.Data.OleDb.OleDbParameter("Select2_CustomerID",
System.Data.OleDb.OleDbType.WChar, 5, _
System.Data.ParameterDirection.Input, False,
CType(0, Byte), CType(0, Byte), "CustomerID", _
System.Data.DataRowVersion.Current, Nothing))
End Sub

This code is simple. Visual Studio .NET has created a data adapter based on the dsCustomers XSD, four
Command objects with their parameters collection already populated, and a connection object that the data
adapter and the four Command objects will use to connect to the database. In other words, Visual Studio
.NET has written more than 100 lines of code, or four pages of dull, repetitive code in a matter of minutes.
More important, Visual Studio .NET will update this code automatically whenever changes are made to the
data adapter's properties.

How It Works
With the updates you have made to the Delete , Update , and Insert commands, as well as to the
strongly typed dataset, you have all the code you need to retrieve and modify data using the CCustomer
class.
In previous chapters, you saw how these objects work together to handle database access. By using Visual
Studio .NET's code-generation tools, you can rapidly create these components, allowing you to write simple
and concise database access code.

Comments
Two areas concerning automatically generated code require further comment: XSDs and strongly typed
datasets, and auto-generated SQL.
XSDs and Strongly Typed Datasets

As you would expect, the XSD mirrors the structure of the Customers table. A few things are missing,
however. For example, the XSD does not define the maximum and minimum length of the columns. Nor will
the primary key declaration prevent you from adding a row with an existing primary key value if all of the
rows from the table are not provided to the dataset.
Unfortunately, the strongly typed dataset exposes many properties whose values are not necessarily defined
in the automatically generated XSD. It's tempting to utilize these dataset properties in your code, but if you
do, you'll find out that those properties aren't always accurate. The code that follows is a perfect example: It
looks like you'll get the maximum length of the CompanyName column, but all you'll really get is 1.

Dim mdsCustomers As New dsCustomers


MsgBox(mdsCustomers.Customers.CompanyNameColumn.MaximumLength)

Although it is possible to create an XSD that is almost identical to the structure of the Customers table (and
your database in general), this task is beyond the scope of this book.
Using a strongly typed dataset does have some potential drawbacks. The properties and methods of strongly
typed datasets are written with complex exception-handling calls, and most of them utilize typecasting calls
as well. As you know, throwing exceptions and casting objects to different types impacts performance
slightly.
On the other hand, the code you need to write for production systems requires exception throwing and
typecasting. Therefore, the results of the strongly typed dataset will be similar to the code you need to write
anyway, and they will save you the time needed to write that code.
Also, if your application is using XML sources directly instead of from a database, a strongly typed dataset
will have better performance when loading that XML than a regular dataset. That's because a regular dataset
has to shred the XML document twicefirst to infer the schema, and second to extract the data. A strongly
typed dataset, in contrast, already knows its schema, so it has to shred the document only once.
Visual Studio .NET Auto-Generated SQL
Some larger issues stem from the UPDATE SQL statement in Listing 9.19 , which referenced every column in
the table.
Many of these columns might not be indexed, so having a WHERE condition that references each row forces
SQL Server (as well as Oracle, Sybase, MySQL, and so on) to perform a table scan unless an index is created
on every row in the table. In a table with thousands of rows, a covering index of this sort would give you
fabulous results when querying the database, but could perform horrendously when inserting, updating, or
deleting data.
In an even worse scenario, some of those columns might contain images, long text data, and so on.
Needless to say, performance in this situation would be pathetic.
Remember that Visual Studio .NET also created an UPDATE statement that could have modified the primary
key of the Customers table. That's a problem in and of itself, but consider the potential issues with an
UPDATE statement that tries to update an auto-incrementing identity column. Visual Studio .NET will
generate the same type of UPDATE statement and attempt to change the value of that column. However,
because the value of an auto-incrementing identity column cannot be updated (DRI or no), the SQL
statement fails to execute and an exception is thrown.
Any time that Visual Studio .NET generates code for you, examine that code thoroughly to make sure it does
precisely what you want. You should also be wary when you ask ADO.NET to generate commands on-the-fly,
for example, when using a CommandBuilder object. ADO.NET creates the same kind of SQL statements that
the DataAdapter Configuration Wizard generates.

This discussion is not meant to denigrate or downplay Visual Studio .NET's code-generation capabilities. On
the contrary, Microsoft has provided some extraordinarily powerful tools. As you saw in Listing 9.22 in this
section, the code that Visual Studio .NET creates is repetitive to write and tedious to maintain. With just a
little bit of direction from you, Visual Studio .NET can save you hours of mundane programming, liberating
you to focus on more challenging and interesting tasks.

[ Team LiB ]

[ Team LiB ]

9.4 Control the Creation and Behavior of Classes


Now that you have code that can prepare all of the objects you need to access and update data in the
database, it's time to write code that can load information for the database into your class. In Visual Basic 6,
developers typically used one of two techniques to populate a class from a database. One was to have a
collection class with a LoadData method that would open a recordset and iterate through the rows, creating
instances of the class representing the table and setting the object's properties with data from the row. The
second was to have a Retrieve method that would accept a primary key value and load the data from that
one row.
The problem with both of these techniques was that you could never be sure if a developer who was using
your component would call the methods in the proper order. For example, that developer might attempt to
access properties of the class before calling the Retrieve method. If those properties were strings and
numbers, the developer would receive a 0-length string and a 0, respectively. These could both be valid
values within your database and would not raise an error. How do you control what is happening? You could
have an internal Boolean variable representing whether data was loaded into the object, and add
If...Then statements in every property and method that would raise an error if that Boolean variable was
False. This is not, however, a neat solution.
The Class_Initialize Event was no help either because it wouldn't accept parameters. You could use a
global variable that the initialize event would access, but this would only work if a single instance of the class
could exist in your application.
In short, in VB 6, you didn't have control over the creation of objects.

Technique
Visual Basic .NET does give you this kind of control with a new kind of method called a constructor. A
constructor is a method that is called in conjunction with the New keyword when you're creating an object
instance. It is similar to the Class_Initialize event that is available in Visual Basic 6, except that it
accepts parameters. If the parameters that are passed to a constructor are not of the proper type, or if a
developer neglects to specify those parameters, the code does not compile.
In this section, you will learn how to write a constructor that accepts a primary key value and uses that value
to populate the properties of a CCustomer object.

Steps
First, the CCustomer class needs access to the data adapter code you created in the previous section. You
could paste all of the code you have written so far in the CCustomer class into the CCustomerData class,
but you may want to use the CCustomerData objects elsewhere. That leaves you two options: you could
have a module-level CCustomerData variable within the CCustomer class, or you could have the
CCustomer class "inherit" all of the code in the CCustomerData class. You've probably done the former
before, so try the latter.

1. In the declaration of the CCustomer class, add "Inherits CCustomerData".


2. Add module-level variables of type dsCustomers and dsCustomers.CustomersRow, and name them

3.

1.
2.

mdsCust and mdrCust , respectively.


3. Add the following constructor to the CCustomer class, as shown in Listing 9.23.
Listing 9.23 frmHowTo9_4.vb: A Constructor for the CCustomer Class

Public Class CCustomer


Inherits CCustomerData
Implements ICustomer
#Region "Constructors"
Public Sub New(ByVal pCustomerIDToRetrieve As String)
' This constructor takes a valid CustomerID as a parameter,
' and retrieves that row. If the row does not exist, an
' Exception is thrown.
' Create an instance of the Customers dataset
mdsCust = New dsCustomers()
Try
' Set the parameter for the select command so that we retrieve
' the proper row from the table.
' MyBase is used to refer to members of the inherited class.
' It is only required in certain circumstances, such as when
' calling a constructor in the inherited class.
MyBase.odaCustomers.SelectCommand.Parameters(0).Value = _
pCustomerIDToRetrieve
' A strongly typed dataset could contain multiple tables,
' so you must specify the table name to fill.
odaCustomers.Fill(mdsCust, "Customers")
Catch ex As Exception
Throw New ApplicationException("The customer row could not be
retrieved." & ex.Message)
End Try
If mdsCust.Customers.Rows.Count = 1 Then
' Option Strict disallows implicit type conversions.
' Even though the row returned by dsCustomers.Rows(0) is
' actually of the type CustomersRow, the method is declared
' as returning System.Data.DataRow.
mdrCust = CType(mdsCust.Customers.Rows(0), dsCustomers.CustomersRow)
ReadValuesFromDataRow()
Else
' Throw an exception if no data was returned
Throw New ApplicationException("The customer " & _
pCustomerIDToRetrieve & " was not found.")
End If
End Sub
#End Region

Note
According to Microsoft, you should use ApplicationException
for all exceptions that custom applications throw. This is because
exceptions are typically defined as one of two groups: system
exceptions and application exceptions. System exceptions are
exceptions that are related to execution of code and the .NET runtime, including things as standard as NullPointerExceptions as
well as more critical issues, such as OutOfMemoryExceptions.
ApplicationExceptions are designed for unexpected
circumstances in your code. If you only throw exceptions, it will be
more difficult for other developers to determine how severe the
error is and how to recover properly.

4. Finally, add the private method called ReadValuesFromDataRow from Listing 9.24 that reads the
values from the data row and writes to the class properties. ReadValuesFromDataRow translates null
values from the database into values that don't cause NullPointerExceptions in Visual Basic code.
Listing 9.24 frmHowTo9_4.vb: Excerpts from the ReadValuesFromDataRow Method

Private Sub ReadValuesFromDataRow()


With mdrCust
' Because the CustomerID and the CompanyName are both required
' values, we will not have handling for zero-length strings.
mstrCustomerID = .CustomerID
Me.CompanyName = .CompanyName
If .IsContactNameNull Then
Me.ContactName = ""
Else
Me.ContactName = .ContactName
End If
If .IsContactTitleNull Then
Me.ContactTitle = ""
Else
Me.ContactTitle = .ContactTitle
End If
End With

5. To test this code, you need to add code to the click event of the Retrieve button to use the constructor,
as well as a method that copies the values of the object to the form's text boxes. Listing 9.25 shows
the code behind btnRetrieve, as well as the GetProperties to write the object's properties to the
text boxes.
Don't forget that you originally declared the mCustomer variable by creating a new instance of the
class. That declaration called the class's default, parameterless constructor, which the .NET runtime
creates for you if you do not declare a constructor. As soon as you do declare a constructor, however,

that default, parameterless constructor will no longer exist. You will need to change the declaration to
exclude the instantiation of a new Ccustomer class, as written at the beginning of Listing 9.25.
Listing 9.25 frmHowTo.vb: Testing the New Constructor

Private mCustomer As CCustomer


Private Sub btnRetrieve_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnRetrieve.Click
Try
mCustomer = New CCustomer(Me.txtCustomerID.Text)
GetProperties()
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Private Sub GetProperties()
Me.txtAddress.Text = mCustomer.Address
Me.txtCity.Text = mCustomer.City
Me.txtCompanyName.Text = mCustomer.CompanyName
Me.txtContactName.Text = mCustomer.ContactName
Me.txtContactTitle.Text = mCustomer.ContactTitle
Me.txtCountry.Text = mCustomer.Country
Me.txtCustomerID.Text = mCustomer.CustomerID
Me.txtFax.Text = mCustomer.Fax
Me.txtPhone.Text = mCustomer.Phone
Me.txtRegion.Text = mCustomer.Region
Me.txtPostalCode.Text = mCustomer.PostalCode
End Sub

How It Works
By adding this constructor, other developers must provide a CustomerID when they attempt to create an
instance of the CCustomer class. If a corresponding row is in the Customers table, the properties of the
class are populated using data from the database. If not, an exception is thrown that notifies the developer
of the problem.
Of course, a developer could ignore your exception and attempt to access the properties of the object, but
you did give those other developers ample warning. Also, none of the class-level variables would have been
instantiated. In Visual Basic 6, this would not have made a difference; base datatypes have a default value.
(Strings are 0-length strings, numbers are 0, and so on.) In Visual Basic .NET, however, base datatypes are
objects just like everything else, so a NullPointerException will be thrown at runtime if a developer
should churlishly ignore your previous exception.

Note

Exactly when a constructor executes can be a little confusing. Two


steps are involved in creating an object with a constructor.
(Technically, all objects have at least one constructor, even if you
didn't implement one.) First, the .NET runtime allocates space in
memory for the object and returns a pointer to your code. Second,
before control is passed back to your code, the constructor is
executed. Why? Your constructor wouldn't be able to initialize the
properties of the object if memory hadn't been allocated.
The important point is that, even if you throw an exception in the
constructor, the object already exists in memory and is accessible to
the client code.

Take a closer look at the ReadValuesFromDataRow method. The technique used here allows consumers of
this class to modify the object's properties (the class-level variables) without modifying the values in the
data row, and thus the dataset. In short, the data row holds the original state of the Customers row,
whereas the class-level variables hold the current state. At the moment, the ReadValuesFromDataRow
method is just used to initialize the class, but, because it sets the class-level variables to the state of the
data row, you could also use it as the basis of a cancel or reset method.
Remember: The dataset stores data as XML, and it does not require an active database connection. With
ADO, retaining recordsets and rows in memory results in a heavy load on your database servers. With
ADO.NET, the cost is minimal.
In the next section, you will add a matching method that writes the current state of the object to the data
row just prior to saving changes to the database.

Comments
Inheritance is
class. Instead
Visual Basic 6
Inheritance"),
declaration of

another OO term that simply means that all the members of one class are now part of another
of copying and pasting code from one module into another module like you would do with
(the copy-and-paste method is sometimes referred to in the OO world, jokingly, as "Editor
you simply add a reference to the class containing the code you want to use to the class
another class.

One of the most fundamental problems in Visual Basic database development is how to interpret null values
in the database within Visual Basic code, and viceversa. In the database, a null value is a null value, and it
won't cause database runtime errors (unless nulls are not allowed). In Visual Basic, base datatypes, such as
strings and dates, do not have null states. You can, however, have a null pointer in Visual Basic .NET, which
means that the variable was never initialized. An uninitialized variable is quite different from a database null
value and could just as easily signify a runtime bug as a null value. In either case, other developers would
have to include error handling for NullPointerExceptions.
In Listing 9.24, I used 0-length strings to represent null. This is, perhaps, a bad habit held over from Visual
Basic 6. The definition of a null value is, incongruously, undefined. A null value represents nothingnot even
a datatype. In other words, null represents the absence of data. A 0-length string represents a specific
datatype and a specific valuethat is, a string of no characters. In short, a null value and a 0-length string
are two entirely different things.
The issue becomes more complex when you add numbers, Booleans, dates, and so on. Does 0 represent
null? Does January 1, 1901 represent a null for a date? When you begin to write production applications in
.NET, you should make a choice about how you will handle null values and stick to it. Microsoft has made the

choice in .NET that a null is a null and nothing else, and, as you can see from the ReadValuesFromDataRow
method, all the code that is generated in .NET provides methods to check if a value is null, as well as
methods to set a value to null. You might want to follow this recommendation, but to keep the examples in
this chapter as simple as possible, we will continue to use 0-length strings to represent null values.

[ Team LiB ]

[ Team LiB ]

9.5 Implement the Methods That Update the Database


Now that you have a class that can retrieve a specific row from the database, you will need to implement a
method that will save changes to the database.
You'll also need a method to insert new rows intoas well as delete existing rows fromthe Customers
table.

Technique
In the case of updating and deleting rows in the database, you will need to implement the Save and Delete
methods that are defined in the ICustomer interface.
To insert new rows into the database, you'll need to add additional constructors to the class. Having multiple
methods with the same name is a new feature in Visual Basic .NET called overloading . In this example, you
will only overload constructors, but you can also overload functions and subs.

Steps
The first task is to implement the Save and Delete methods that were defined in your interface in section 9.1
. You have already set up all the database-access objects you will need, so it's just a matter of adding the
relevant code.

1. You will need two helper methods for the Save and Delete methods: a method whose sole
responsibility is to call the update method of the data adapter, and a method that clears the properties
of the object. Both methods are defined in Listing 9.26 .
Listing 9.26 frmHowTo9_5.vb : The WriteChangesToDB and Clear Methods

Private Function WriteChangesToDB (ByVal pStateToHandle _


As DataRowState) As Boolean
Try
' Create a dataset of only those rows that have been
' changed (specifically, those changed in the manner
' specified by the DataRowState).
Dim dsChanges As DataSet = mdsCust.GetChanges(pStateToHandle)
' Pass this subset of the dataset to the data adapter
' to write the changes to the database. The data adapter
' will handle all calls to the Delete, Insert, and Update
' commands.
odaCustomers.Update(dsChanges)
Catch ex As Exception
' If the update fails, communicate this back to
' the calling function by returning False.

mdsCust.RejectChanges()
Return False
End Try
' Update the customer's dataset to reflect the changes
' you have made.
mdsCust.AcceptChanges()
Return True
End Function
Private Sub Clear()
' Consumers of this class should be savvy enough to dispose
' of the object after they call the Delete method, but
' to be sure, let's set all the pointers to null, so if a
' developer forgets... NullPointerException
mdsCust = Nothing
mdrCust = Nothing
mstrCustomerID = Nothing
mstrCompanyName = Nothing
mstrContactName = Nothing
mstrContactTitle = Nothing
mstrAddress = Nothing
mstrCity = Nothing
mstrRegion = Nothing
mstrCountry = Nothing
mstrPostalCode = Nothing
mstrPhone = Nothing
mstrFax = Nothing
End Sub

Adding code to insert new rows into the database requires a bit more work. The first problem is
that you have only one way to create an instance of the CCustomer class: by passing a
CustomerID value to the constructor that retrieves an existing row from the database. You will
need to add a second constructor to your class that allows you to create new Customer objects.
Having two methods with the same name is called overloading . In Visual Basic 6, you had limited
support for overloading with properties. Think about it: Properties are pairs of methods with the
same name, except that one is a sub and accepts a parameter, whereas the other is a
parameterless function that returns a value. Each independent Visual Basic 6 function or sub,
however, must have a unique name.
Visual Basic .NET lifts this restriction by allowing you to overload any method, be it function, sub,
or constructor. For constructors, all you need to do is add another New sub. (For functions and
subs, you need to preface the declaration with the Overloads keyword.)
You can add as many overloaded methods as you like, as long as the parameter list is different. Be
aware that .NET determines which overloaded method to callconstructor or otherwisebased on
the order of the datatypes in the parameter list, and not the names of the parameters. In other
words, you can have two overloaded methods that take an integer and a string as their
parameters, as long as one method has a string as the first parameter, and the other has an
integer as the first parameterlike the first two methods in Listing 9.27 . It doesn't matter what
you name the parameter: Two overloaded methods with a single integer parameter but with
different nameslike the second pair of methods in Listing 9.27 are not allowed.

The name of the method, the return type, and the order of its parameters make up what is called
the method signature . When it comes to overloading, however, Microsoft only takes into account
the argument signature. In other words, the return type doesn't matter.
Listing 9.27 Overloaded Methods: The Datatypes of the Parameters Are What Matter

'These two methods can be declared because the order


'of the datatypes differs. If you do this, make sure
'your parameters have descriptive names. Otherwise,
'your code can be confusing to other developers.
Overloads Sub method1(ByVal IntegerValue As Integer,
ByVal StringValue As String)
Overloads Sub method1(ByVal StringValue As String,
ByVal IntegerValue As Integer)
'These two methods cannot be declared, even though the
'parameters have different names.
Overloads Sub method2(ByVal IntegerValue As Integer)
Overloads Sub method2(ByVal AnotherIntegerValue As Integer)

Tip

You should always use Option Strict in applications in which you use
overloaded methods. Option Strict prevents you from implicitly converting
between datatypes when data might become lost as a result of the conversion.
For example, if you have two overloaded methodsone that accepts a string
and one that accepts an integerwithout Option Strict , .NET might get confused and
convert the integer value to a string and call the wrong method. With Option Strict , not
only will this not happen, but you will not even be able to compile your code with an implicit type
conversion.

2. Now that you understand what an overloaded method is, add one more constructor, as shown in Listing
9.28 , that accepts the two required values for the Customers table: CustomerID and CompanyName.
You will also need to declare a private, class-level Boolean variable to track whether you have a new or
existing record loaded into the class.
Listing 9.28 frmHowTo9_5.vb : A New Constructor for Inserting New Rows into the Customers
Table

Private mfNew As Boolean ' By default, this value will be false


Public Sub New(ByVal pCustomerID As String,
ByVal pCompanyName As String)
' This constructor is used to create new Customer records

' in the database.


mdsCust = New dsCustomers()
mfNew = True
mstrCustomerID = pCustomerID
Me.CompanyName = pCompanyName
mstrContactName = ""
mstrContactTitle = ""
mstrAddress = ""
mstrCity = ""
mstrRegion = ""
mstrCountry = ""
mstrPostalCode = ""
mstrPhone = ""
mstrFax = ""
End Sub

Take a look at the first two lines of the constructor. First, you need to make sure that an instance
of the Customers dataset is created. Second, you need some form of flag to keep track of whether
the instance is a new or existing row. This example will use a class-level Boolean variable called
mfNew to fill this role.

3. Next, implement a method that will take the values of your class' properties and write them to the data
row, as shown in Listing 9.29 .
Listing 9.29 frmHowTo9_5.vb : Implementation of the Save Method

Private Sub WriteValuesToDataRow()


' This technique allows consumers to modify properties (class' level variables) but leaves the DataRow intact until the
' save method is called. In essence, the DataRow holds the
' original state of the Customers row, while the class-level
' variables hold the current state. This method synchronizes
' the two.
With mdrCust
.CompanyName = mstrCompanyName
If mstrAddress.Length = 0 Then
.SetAddressNull()
Else
.Address = mstrAddress
End If
If mstrRegion.Length = 0 Then
.Set_RegionNull()
Else
._Region = mstrRegion
End If
If mstrCountry.Length = 0 Then
.SetCountryNull()
Else
.Country = mstrCountry

End If
If mstrPostalCode.Length = 0 Then
.SetPostalCodeNull()
Else
.PostalCode = mstrPostalCode
End If
If mstrPhone.Length = 0 Then
.SetPhoneNull()
Else
.Phone = mstrPhone
End If
If mstrFax.Length = 0 Then
.SetFaxNull()
Else
.Fax = mstrFax
End If
If mstrContactTitle.Length = 0 Then
.SetContactTitleNull()
Else
.ContactTitle = mstrContactTitle
End If
If mstrContactName.Length = 0 Then
.SetContactNameNull()
Else
.ContactName = mstrContactName
End If
If mstrCity.Length = 0 Then
.SetCityNull()
Else
.City = mstrCity
End If
End With
End Sub

4. Now implement the Save method by pasting the code from Listing 9.30 into the class. To make your
code as easy to use as possible, have the Save method manage both new and existing ecords.
Listing 9.30 frmHowTo9_5.vb : Implementation of the Save Method

Public Function Save() As Boolean Implements ICustomer.Save


Dim nState As DataRowState
If mfNew Then
' If this is a new Customer, you need to add a data row
' to the dataset.
mdrCust = mdsCust.Customers.AddCustomersRow(mstrCustomerID, _

mstrCompanyName, mstrContactName, mstrContactTitle, _


mstrAddress, mstrCity,mstrRegion, mstrPostalCode, _
mstrCountry, mstrPhone, mstrFax)
nState = DataRowState.Added
mfNew = False
Else
nState = DataRowState.Modified
End If
' Begin editing the data row
mdrCust.BeginEdit()
' If you have a problem writing values to the data row, you want to
' catch the exception, cancel editing, reject the changes, and
' immediately dump out of the method with a return value of false.
Try
writeValuesToDataRow()
Catch ex As Exception
mdrCust.CancelEdit()
mdrCust.RejectChanges()
Return False
End Try
' If you succeed in writing the new values to the data row,
' end the editing block, and then write the changes to the
' database. If you have a problem writing to the DB, then
' reject the changes to the row and return false.
mdrCust.EndEdit()
If WriteChangesToDB (nState) Then
Return True
Else
mdrCust.RejectChanges()
Return False
End If
End Function

5. Finally, implement the Delete method. The Delete method flags the data row for deletion and then
calls the WriteChangesToDB method with a DataRowState of Deleted. Paste the Delete method from
Listing 9.31 into your class.
Listing 9.31 frmHowTo9_5.vb : Implementation of the Delete Method

Public Function Delete() As Boolean Implements ICustomer.Delete


If mfNew Then
' If you're working with a new record, you don't need to
' communicate with the database, so clear the properties
' of the object and return true to indicate that the delete
' succeeded.
Clear()

Return True
Else
' If this is an existing record, flag the data row as
' deleted, then write the changes to the DB.
mdrCust.Delete()
If WriteChangesToDB (DataRowState.Deleted) Then
Clear()
Return True
Else
mdrCust.RejectChanges()
Return False
End If
End If
End Function

6. To test this code, try creating a new CCustomer instance, set its properties, and save it to the database.
Then instantiate a new CCustomer instance, loading the new row into the object. Make a change to that
instance, and save the changes back to the database. Last, load that new row into a third CCustomer
instance and delete it. Add the code in Listing 9.32 to the click events of the Delete, Save, and New
buttons of frmHowTo9_5 to test the new code in your class.
Listing 9.32 frmHowTo9_5.vb : Testing the New Constructor and the Delete and Save Methods

Private Sub btnNew_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnNew.Click
Try
' These two properties are the minimum required to insert a new row.
If txtCustomerID.Text.Length > 0 Then
If txtCompanyName.Text.Length > 0 Then
mCustomer = New CCustomer(txtCustomerID.Text, txtCompanyName.Text)
Else
MsgBox("A company name is required for a new Customer.")
End If
Else
MsgBox("A CustomerID is required for a new Customer.")
Exit Sub
End If
GetProperties()
Me.rtbToString.Text = mCustomer.ToString
Catch ex As Exception
MsgBox(ex.Message)
End Try

End Sub

Private Sub btnSave_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnSave.Click
Try
If mCustomer.Save() Then
MsgBox("Save succeeded.")
Else
MsgBox("Save failed.")
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub

Private Sub btnDelete_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnDelete.Click
Try
If mCustomer.Delete() Then
MsgBox("Delete succeeded.")
mCustomer = Nothing
ClearAllTextBoxes()
Else
MsgBox("Delete failed.")
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub

How It Works
When a consumer of the CCustomer class instantiates an object using the new constructor, the required
properties of the object (the CustomerID and CompanyName) are set using the parameters of the
constructor, whereas the optional properties are all set to zero-length strings. This constructor also sets an
internal flag saying that the current instance is a new record. When the Save method is called, the internal
flag tells the class that a new data row should be added to the dsCustomer dataset. Finally, the
WriteChangesToDB is called and a new row is inserted into the Customers table.
If the Save method is called with an existing record, the internal flag lets the class know that the data row
already exists in the dsCustomer dataset, so the WriteChangesToDB method is called to update that row in
the Customers table.
The behavior of the Delete method also changes based on the value of the internal new-record flag. If the
object instance is a new record, you don't need to delete the row from the database; therefore, the only
necessary action is to dispose of the class-level variables. If the object instance is an existing record, the

delete method of the data row is called. Finally, the WriteChangesToDB method is called to physically
delete the row from the Customers table.
Both the Save and Delete methods use the AcceptChanges and RejectChanges methods of the data
row to react to exceptions thrown when making changes to the database.

Comments
One thing to note is that the Delete and WriteChangesToDB methods only return Boolean values and do
not throw exceptions. The data validation code that you will see in the next section should prevent most of
the exceptions that could be thrown when updating the database, so a Boolean return value is sufficient.
Depending on your needs, however, this might to be too simple of an implementation. In the next section,
you will learn how to create custom exceptions that will provide more error detail to consumers of your
classes.

[ Team LiB ]

[ Team LiB ]

9.6 Validate Data Passed to Properties and Communicate Errors to Developers


To make a class that wraps up access to a table, it is critical that the classand not the developer who is
using the classmakes sure that all data is valid before writing it to the database.
For example, in the Customers table, the CustomerID field must be five characters in lengthno more, no
lessand, of course, it must be unique.
Phone numbers and fax numbers must also be validated. Although we don't necessarily know how many
digits are in a phone number (France has eight-digit numbers, Spain has nine, and the U.S. has ten), we do
know that only numbers and characters such as periods, parentheses, and hyphens are allowed.
You also need to communicate with other developers when their data is not valid. It is the data class's job to
make sure that invalid data doesn't make it to the database.
This section presents three tasks: adding code to make sure that data passed to an object matches the
column properties in the database, adding code that validates complex data types and business rules, and
communicating errors to the class's consumers.

Technique
First, you need to pass errors back up the call stack. In Visual Basic 6, the accepted method was to use
Err.Raise, which propagates an error number and an error message. This technique is still available to Visual
Basic developers, but it retains all the old problems. If you aren't careful, programmers who are working on
different components can raise the same error numbers, making error handling a potential nightmare. (If
you aren't careful, you can end up raising the same error number in your own code.)
You learned how to use structured exception handling in earlier chapters, but the beauty is that you can
define your own exceptions as needed, with almost no code. Also, because exceptions have a name as
opposed to a number, it is far easier for developers to work with your code. Finally, because exceptions are
defined with a Namespace, even if two projects define an InvalidCustomerIDException , each exception
will be unique.
Second, you need to write code to check whether a value is valid. For the CustomerID, you simply need to
check the length of the string. (Later on, you'll need to add code to check whether the ID already exists.) For
phone numbers, you'll need to examine the string for invalid characters.

Steps
Because validating the maximum allowable length for all of our properties is simplest, tackle this task first.

1. Define a new exception class by inheriting from System.ApplicationException . As mentioned before,


Microsoft recommends that all custom exceptions inherit from ApplicationException rather than
Exception. System.ApplicationException has most of the methods and properties that you will need
to communicate an exception to consumers of this class. The only property that a consumer might find
useful is one that exposes what the maximum length of the property is. This would allow the consumer to
communicate the error back to the user or truncate the string without having to hard-code the maximum
length into his code. Adding the name of the property and the value is also a good idea. Some developers
who are using your class might write one long Try...Catch block, so this information will help debug going
forward. Paste the code in Listing 9.33 defining the MaximumStringLengthExceededException into

your code.
Listing 9.33 frmHowTo9_6.vb : Class Declaration for the

MaximumStringLengthExceededException

Public Class MaximumStringLengthExceededException


Inherits System.ApplicationException
Private nMaxLen As Integer
Public Sub New(ByVal pMaxLen As Integer, ByVal pPropertyName As String,
ByVal pValue As String)
' You need to initialize the base class of this exception.
' If you do not specifically call a constructor of the base
' class, the default constructor (the one without parameters)
' will be called, if it exists. MyBase must precede the call to
' the base class's constructor so that the .NET runtime knows not
' to call a constructor in the derived class.
MyBase.new("The value specified, " & pValue & _
", exceeds the maximum " &
"length of " & pMaxLen & " allowable by the " & _
pPropertyName & " property.")
End Sub
Public ReadOnly Property MaxLength() As Integer
Get
Return nMaxLen
End Get
End Property
End Class

2. Next, modify the set block of each property in the class to check the length of the new value to see if it
exceeds the maximum length of the column in the Customers table. If the length of the new value does
exceed the maximum length, throw a new instance of the MaximumStringLengthExceededException .
To do this, simply create an If...Then block that checks the maximum length into your class.
When you have modified all of your properties, they should look like the ContactName property in Listing
9.34 .
Listing 9.34 frmHowTo9_6.vb : The ContactName Property Validates for the Maximum Length of
the Column and Throws an Exception if the Value Passed to the Property Exceeds That Maximum
Value

Public Property ContactName() As String Implements ICustomer9_6.ContactName


Get
Return mstrContactName
End Get
Set(ByVal Value As String)
If Value.Length <= 30 Then
mstrContactName = Value

Else
Throw New MaximumStringLengthExceededException(30, "ContactName", Value)
End If
End Set
End Property

3. Validating a phone number or fax number requires more than just checking the maximum length of the
column. You need to make sure that only numbers and other allowable characters are in the value. First,
create a new exception for invalid phone numbers by adding the code from Listing 9.35 to
frmHowTo9_6.vb .
Listing 9.35 frmHowTo9_6.vb : The InvalidPhoneNumberException

Public Class InvalidPhoneNumberException


Inherits System.ApplicationException
Public Sub New(ByVal pstrPhone As String)
MyBase.New("The phone number specified, " & pstrPhone & ", is not valid.")
End Sub
End Class

4. Next, add a private method called ValidatePhoneNumber to check a phone number string for invalid
characters, such as letters or punctuation marks, as shown in Listing 9.36 .
Listing 9.36 frmHowTo9_6.vb : A Function That Validates Phone Numbers

Private Function ValidatePhoneNumber(ByVal pstrPhone As String) As Boolean


' Create a string array that contains the numbers 0 to 9, as well as
' a hyphen, period, space, and parentheses.
Dim cValidChars() As String
cValidChars = New String(14) {"1", "2", "3", "4", "5", _
"6", "7", "8", "9", "0", "(", ")", "-", " ", "."}
Dim i As Integer = 0
Dim nUBound As Integer = cValidChars.GetUpperBound(0)
Dim nLBound As Integer = cValidChars.GetLowerBound(0)
' Loop through the array of valid characters and remove them
' from a phone number string. If characters are left
' in the string, the phone number is invalid.
For i = nLBound To nUBound Step 1
pstrPhone = pstrPhone.Replace(cValidChars(i), "")
Next
pstrPhone = pstrPhone.Trim()
If pstrPhone.Length > 0 Then
Return False
Else

Return True
End If
End Function

5. Modify the set blocks of the Fax and Phone properties to call the ValidatePhoneNumber method and
throw an InvalidPhoneNumberException if the phone number is not valid. Your code should look like
Listing 9.37 .
Listing 9.37 frmHowTo9_6.vb : The Phone Property That Validates Phone Numbers

Public Property Phone() As String Implements ICustomer.Phone


Get
Return mstrPhone
End Get
Set(ByVal Value As String)
If Value.Length <= 24 Then
If ValidatePhoneNumber(Value) Then
mstrPhone = Value
Else
Throw New InvalidPhoneNumberException(Value)
End If
Else
Throw New MaximumStringLengthExceededException(24, "Phone", Value)
End If
End Set
End Property

6. The last piece of data that you need to validate is the CustomerID. You need to validate for the string
length, and for new customers, you need to validate for the uniqueness of the proposed CustomerID.
Validating for the proper length of a CustomerID is simple. First, add a new exception called
InvalidCustomerIDException , as shown in Listing 9.38 .
Listing 9.38 frmHowTo9_6.vb : Declaration of the InvalidCustomerIDException

Public Class InvalidCustomerIDException


Inherits System.ApplicationException
Public Sub New(ByVal pstrID As String)
MyBase.New("The customer ID specified, " & pstrID & ", is not valid")
End Sub
End Class

7. Then add the method from Listing 9.39 , which checks the length of a CustomerID string and ensures that
the string has no whitespace.

7.

Listing 9.39 frmHowTo9_6.vb : The ValidateCustomerID Method

Private Function ValidateCustomerID(ByVal pstrID As String) As Boolean


' Strip out any leading or trailing spaces.
pstrID = pstrID.Trim
' A CustomerID must have five characters.
If pstrID.Length = 5 Then
Return True
Else
Return False
End If
End Function

8. Now add code like that in Listing 9.40 to your constructor that validates the CustomerID, and, if that value
is invalid, throws an InvalidCustomerIDException .
Listing 9.40 frmHowTo9_6.vb : An If...Then Block to Wrap Around Your Constructor Code

If ValidateCustomerID(pCustomerIDToRetrieve) Then
' Your original constructor code goes here.
Else
Throw New InvalidCustomerIDException(pCustomerIDToRetrieve)
End If

9. The final piece of validation code you need to add is a function that checks for the existence of a
CustomerID before a new Customer object is instantiated. You could use the data access objects you
defined in CCustomerData, but for performance purposes, you should create a new command object and
use the ExecuteScalar method as shown in Listing 9.41 ; this method requires less interaction with the
database.
Listing 9.41 frmHowTo9_6.vb : The DoesCustomerIDExist Function

Private Function DoesCustomerIDExist(ByVal pstrID As String) As Boolean


Dim strSQL As String = "SELECT COUNT(*) FROM Customers " & _
"WHERE CustomerID = '" &
pstrID & "'"
Dim cmd As New System.Data.OleDb.OleDbCommand(strSQL, oleCnn)
Dim fExists As Boolean
oleCnn.Open()
fExists = CBool(cmd.ExecuteScalar())

oleCnn.Close()
Return fExists
End Function

Add an extra If...Then block in the constructor used to create a new Customer row and you're
ready to start testing your new code.
Your existing code for frmHowTo9_6 should suffice for testing. You made sure to handle exceptions
thrown from properties in section 9.2 .

How It Works
Much of the code in this section qualifies and extends the properties you have already defined and
implemented. This section has two key concepts.
Validating data is a critical part of any application, although the examples in this section use validation
techniques you should already be familiar with. The next section will take data validation to a new level.
The most important concept is declaring your own exceptions. Communicating errors to other parts of the
application is, perhaps, more important than the business logic you implement. When it works, it works, but
when something goes wrong, providing enough information about the error is far more important. Creating
new exceptions is an elegant and readable way to communicate and handle errors.

Comments
Hardcoding the maximum column length into the property set block as recommended in this section isn't
necessarily the best solution to this problem. If you decide to increase or decrease the length of the column
in the database, you must search through all of your source code to find every place that value was hard
coded. Using constants is one way around this, but your options still are fairly limited.
The strongly typed dataset you created earlier has the potential to provide an elegant solution to this
problem. The XSD that underlies the dataset could define many of the data rules in your tables, including the
maximum length of columns. Also, the actual dataset exposes this information in its properties and methods.
The problem is that Visual Studio .NET does not generate XSDs with such strict definitions.

[ Team LiB ]

[ Team LiB ]

9.7 Write Data Validation Code That Can Be Reused in Other Classes
As you were writing the PhoneNumber and CustomerID validation code in the previous section, you might
have thought that this code would be extraordinarily useful in other parts of the Northwind application. For
example, the Suppliers and Employees tables also have Phone and Fax fields, and the CustomerID column is
also defined in the Orders table.

Technique
In this section, you will pull the data validation code you wrote for both the PhoneNumber and CustomerID
columns and create independent objects that encapsulate your existing validation logic. Then you will update
the CCustomer class to make use of these new classes.
The CCustomerID class will be a simple class that performs two functions: checking the length of the ID and
looking in the database to see if the CustomerID exists.
For the PhoneNumber data validation, you will learn how to create an entire object model that will provide
you with data validation for different types of phone numbers with a bare minimum of code. And, just for the
fun of it, you will learn how to use the same base class you use to validate phone numbers to validate Social
Security numbers.

Steps

1. Add a new class file to your project by right-clicking on the project in the Solution Explorer window and selecting
Add Class from the Add submenu. Name the new class CCustomerID.vb .
2. Copy the DoesCustomerIDExist and ValidateCustomerID methods from the CCustomer class into the
CCustomerID class. Rename them Exists and Validate , respectively.
3. Copy the InvalidCustomerIDException from CustomerClass.vb and paste it inside the CCustomerID
class. This makes your exceptions directly related to the CCustomerID.
4. One gap in splitting off the CustomerID property into its own class is that some parts of your application might
want a read/write CustomerID property, whereas others, such as the CCustomer class, need a ReadOnly
property. Instead of having a Boolean set to lock the property, a more flexible way is to add an event to your
class that is called before setting the property, allowing a containing class to cancel the change.

Public Event BeforeUpdate(ByVal pstrCustomerID As String, ByRef pfCancel As Boolean)

5. Now add a new property called CustomerID to the CCustomerID class, as shown in Listing 9.42 . This property
should raise the BeforeUpdate event, and if the changes are not cancelled, call the Validate method and
throw an InvalidCustomerIDException if the CustomerID is invalid.
Listing 9.42 CCustomerID.vb : The CustomerID Property of the CCustomerID Class

Property CustomerID() As String


Get
Return mstrCustomerID
End Get
Set(ByVal Value As String)
Dim fCanceled As Boolean
RaiseEvent BeforeUpdate(Value, fCanceled)
If Not fCanceled Then
mfValid = Validate(Value)
If mfValid Then
mstrCustomerID = Value
Else
Throw New InvalidCustomerIDException(Value)
End If
End If
End Set
End Property

6. Then add a new constructor that accepts a CustomerID as a parameter. That constructor, shown in Listing 9.43
, should call the property statement, which will handle validation.
Listing 9.43 CCustomerID.vb : The Constructor for the CCustomerID

ClassPublic Sub New(ByVal pID As String)


Me.CustomerID = pID
End Sub

7. You're almost finished with the CCustomerID class, except for one subtle issue: pointers. Everything you've
written in this chapter so far has used base datatypes, so you haven't had to worry about copying values
between function calls. But objects work differently than base datatypes do.

The issue is with ByVal and ByRef . With base datatypes, the distinction is fairly straightforward: ByVal
passes a copy of the value of the variable to the function. The called function could do whatever it pleased to
the passed value without impacting the value of the variable in the calling function. ByRef passes the called
function a pointer instead of the value, so any change made to the variable in the called function is made to the
variable in the calling function.
With objects such as CCustomerID , it's completely different. Whether you use ByVal or ByRef , you're still
working with a pointer. The called function will always modify the object that the calling function passes.

The difference is in reassigning the pointer. ByVal passes a pointer to an object. If the called function changes
the pointer to a new object, the calling function will still point at the original object. ByRef passes a pointer to a
pointer to an object. If the called function changes the pointer to a new object, the calling function will now
point at the new object instead of the original object.

The point(er) here is in the constructors. You want to provide a way for other developers to create copies of the
class easily, or you might end up with the same CCustomerID object being used simultaneously in a potentially

conflicting fashion. The solution? Add a constructor to the CCustomerID class that accepts a CCustomerID
object as a parameter, as shown in Listing 9.44 .
Listing 9.44 CCustomerID.vb : An Object-Based Constructor for the CCustomerID Class

Public Sub New(ByVal pID As CCustomerID)


' Both ByVal and ByRef pass object pointers, so if you
' want to create a new CustomerID instance based on an
' existing instance, you need to copy the values of the
' base datatypes to a new instance.
Me.CustomerID = pID.CustomerID
End Sub

8. Updating the CCustomer class is not difficult, but it does require many small, similar changes, including the
following:

Retyping variables
Removing code validating CustomerIDs and related exception throwing
Calling CCustomerID's object-based constructor from Listing 9.43 each time a CCustomerID object is
passed into a CCustomer object
Adding .CustomerID after every CCustomerID variable where the string value is needed

The constructor in Listing 9.45 that you use to create new customers in the database is a good example of all of
these changes.
Listing 9.45 CCustomerID.vb : An Excerpt of a CCustomer Constructor as Modified to Use the
CCustomerID Class

Public Sub New(ByVal pCustomerID As CCustomerID,


ByVal pCompanyName As String)
' Change A: retyping variables
' Instead of accepting a string that has to be validated,
' this constructor accepts a CCustomerID object that by
' definition is valid.
If pCustomerID.Exists Then
' Change B: removing code validating CustomerIDs
' If you recall, there used to be an additional condition
' in this If... Then that checked the length of the CustomerID.
' Now, the CCustomerID guarantees a valid CustomerID.
mdsCust = New dsCustomers()
mfNew = True
'
'
'
'

Change C: using the object-based constructor


Just to make sure, create a new CCustomerID object. If you
don't, consumers of this class will retain a pointer to this
object instance, allowing that consumer to change the value

' of the shared CCustomerID object unbeknownst to the CCustomer object.


mCustomerID = New CCustomerID(pCustomerID)
Me.CompanyName = pCompanyName
Else
' Change D: calling the CustomerID property when you need a string.
' When you throw an InvalidCustomerIDEException, you won't necessarily
' have a valid CustomerID, so this exception has to accept a string
' instead of an object. This InvalidCustomerIDException is used to express
' that the CustomerID for the new customer already exists in the database.
Throw New CCustomerID.InvalidCustomerIDException(pCustomerID.CustomerID)
End If
End Sub

Tip

One way to identify all these changes is to change the type of the CustomerID
property in the ICustomer interface, as well as the name of the class-level variable in
the CCustomer class that holds CustomerID values. Because this will invalidate much
of the code utilizing CustomerIDs, Visual Studio .NET will put a wavy blue line under
the code you need to modify.

9. You will also need to redeclare the mCustomerID variable using the WithEvents keyword and add an event
handler that cancels the change, as shown in Listing 9.46 .
Listing 9.46 frmHowTo9_7.vb : Adding Event Handlers to CCustomer to Handle the CCustomerID
BeforeUpdate Event

Private WithEvents mCustomerID As CCustomerID


Private Sub mCustomerID_BeforeUpdate(ByVal pstrCustomerID As String,
ByRef fCancel As Boolean) Handles mCustomerID.BeforeUpdate
fCancel = True
End Sub

The next task in this section is to provide similar validation functionality for phone numbers that can be
used throughout your application. At the end of this task, you will have four separate classes, all of which
extend the functionality of a fifth base class. Before you begin coding so many classes, it is always a good
idea to plan precisely what you want to write.

Figure 9.8 is a class diagram that describes the classes you're going to write and their relationship to each
other. The class at the top of the diagram is our base class. The base class has only one purpose: It
contains a method that checks a string of numbers to see whether the string has invalid characters. All of
the other classes will inherit this validation method, but each class will change the definition of valid
characters in the string.

Figure 9.8. A class diagram describing the classes to be developed in section 9.7 .

According to the class diagram, this class has only seven members: two variables (cValidChars and
mstrValue ), one property (StringValue ), three methods (IsValid , ThrowException , and
DefineValidChars ), and one member class (InvalidNumberStringException ).
You might have noticed a symbol before each member declaration. This symbol refers to the accessibility
of the member. A minus sign () means the member is Private, a number sign (#) means Protected, and
a plus sign (+) means Public. If this doesn't make sense at the moment, don't worry. It will be much
clearer when you see the code.

10. Because all of the classes rely on code in the base class, you should start by defining the CNumberString class
This class will be the most complex in this hierarchy, so you will walk through it step-by-step. First, right-click
on your project in the Solution Explorer and select Add Class from the Add submenu. Name the class
PhoneDatatypes.vb .
11. Declare the CNumberString class block, as well as the InvalidNumberStringException member class,
using the MustInherit keyword , as shown in Listing 9.47 . The MustInherit keyword means that the
CNumberString class cannot be instantiated directly. Instead, the class must be inherited by another class for
its members to be accessed.
Listing 9.47 PhoneDatatypes.vb : Declaration of the CNumberString Abstract Class

Public MustInherit Class CNumberString


Public MustInherit Class InvalidNumberStringException
Inherits System.ApplicationException
Protected Sub New(ByVal pstrMessage As String)
MyBase.New(pstrMessage)
End Sub
End Class
End Class

A class that is declared with the MustInherit keyword is known as an abstract class, and it is best
described as a hybrid between an interface and a class(see Table 9.3 ). Like an interface, instances of an
abstract class cannot be created directly, and its methods and properties need not have code. Like a
regular class, an abstract class does contain some implemented methods and properties, and even though
it cannot be instantiated, it can have constructors.

MustInherit
Instances of the class cannot be created directly, and the class must be inherited to be used.

NotInheritable
The class is in a finalized state and cannot be used as a base class.

Table 9.3. Visual Basic .NET Class Inheritance Permission Keywords


Keyword

Definition

12. Declare the two class-level variables: cValidChars and mstrValue . Note that both variables are declared as
protected. This means that the variables will only be accessible to derived classes because those classes will
need to modify the list of valid characters and might need to access the string value.
13. Declare the DefineValidChars , IsValid , and ThrowException methods as shown in Listing 9.48 . You
might also want to add an Event declaration that fires before updating the StringValue property. (If you do
not, you won't be able to catch changes made to properties in CCustomer; thus, you won't be able to check for
the maximum length of the Phone and Fax properties.)
Listing 9.48 PhoneDatatypes.vb : Declaration of the IsValid , DefineValidChars , and
ThrowException Methods

Public Sub New()


DefineValidChars()
End Sub
Public Event StringValueBeforeUpdate(ByVal pstrValue As String, _

ByRef pCancel As Boolean)


Protected Overridable Sub DefineValidChars()
cValidChars = New String(9) {"1", "2", "3", "4", "5", _
"6", "7", "8", "9", "0"}
End Sub
Public Overridable Function IsValid(ByVal pstrNumber As String) As Boolean
Dim i As Integer = 0
Dim nUBound As Integer = cValidChars.GetUpperBound(0)
Dim nLBound As Integer = cValidChars.GetLowerBound(0)
For i = nLBound To nUBound Step 1
pstrNumber = pstrNumber.Replace(cValidChars(i), "")
Next
pstrNumber = pstrNumber.Trim()
If pstrNumber.Length > 0 Then
Return False
Else
Return True
End If
End Function
Public MustOverride Function ThrowException() As InvalidNumberStringException

The IsValid method is the key to the whole thing, and is really just the ValidatePhoneNumber method
from the previous section. The DefineValidChars method populates the list of valid characters. It has
been separated from the IsValid method so that derived classes can easily redefine the list of valid
characters without having to rewrite the IsValid method.

Both of these methods have been declared as Overrideable. This means that a derived class has the
option of redefining either of these methods. In previous sections, you regularly overrode constructors
when you created custom exceptions based on the ApplicationException . (Actually, constructors of a
base class are never exposed as constructors of a derived class, so they are overridden by default). If you
recall, you can still access the overridden constructor within your class by using the MyBase keyword. The
same goes for methods: You can call the overridden method within your class by using the MyBase
keyword.

The ThrowException method is declared using the MustOverride keyword (see Table 9.4 ), and it is an
abstract method. Any class that inherits from the CNumberString class must implement this method.
This works just like a method that is declared in an interface, except that you do not need to use the
Implements keyword in the method declaration.

Overrides
The method overrides the member of the base class with the same signature.

Overrideable
The method can be overridden in a derived class.

MustOverride
The method must be overridden in every derived class.

NotOverrideable
The method can never be overridden by a derived class.

Table 9.4. Visual Basic .NET Member Override Keywords


Keyword

Definition

14. The last member to implement is the StringValue property defined in Listing 9.49 . Note that properties
cannot be overridden.
Listing 9.49 PhoneDatatypes.vb : The StringValue Property

Public Property StringValue() As String


Get
Return mstrValue
End Get
Set(ByVal Value As String)
Dim fCancelChange As Boolean
If isValid(Value) Then
RaiseEvent StringValueBeforeUpdate(Value, fCancelChange)
If Not fCancelChange Then
mstrValue = Value
End If
Else
Throw ThrowException(Value)
End If
End Set
End Property

It doesn't look like much is going on here, but this is the most interesting member in the class. Why? This
member is responsible for all data validation, including validation kicked that constructors kick off. All of
the methods called in this member are overrideable, though. In other words, the IsValid method that it
looks like you're calling could be any IsValid method from any derived class. And the ThrowException
method isn't even defined in this class. The actual method called always will be defined in a derived class.

15. The simplest derivation of the CNumberString class is a class that handles phone number extensions. (You wil
use this as part of a BusinessPhone number class later.) A phone extension contains only numbers, so you
don't need to modify the IsValid method or the DefineValidChars method. As you can see in Listing 9.50 ,
all you need is to define an exception, a method to throw it, and a constructor that calls the constructor of the
CNumberString class and the StringValue property.
Listing 9.50 PhoneDatatypes.vb : A Simple Class for Phone Number Extensions Derived from the
CNumberString Class

Public Class CExtension


Inherits CNumberString
Public Class InvalidPhoneExtensionException
Inherits CNumberString.InvalidNumberStringException
Public Sub New(ByVal pstrPhoneNumber As String)
MyBase.New("The phone extension specified, " & pstrPhoneNumber &
", contains invalid characters.")
End Sub
End Class
Sub New(ByVal pstrExtension As String)
MyBase.New()
Me.StringValue = pstrExtension
End Sub
Public Overrides Function ThrowException(ByVal pstrInvalidExt As String)
As InvalidNumberStringException
Throw New InvalidPhoneExtensionException(pstrInvalidExt)
End Function
End Class

So what's going on? The CExtension constructor calls the constructor of the CNumberString class, which
calls CNumberString.DefineValidChars . Then, the string value is passed to the
CNumberString.StringValue property, where its validity is checked. (If that doesn't make sense,
create an instance of the class and step through it line by line.)

16. To take the example a step further, create a phone number class that inherits from the CNumberString class.
As in the CExtension class from Listing 9.50 , , you will need to declare an exception, a method to throw it,
and a constructor to create the class. The only substantive change in Listing 9.51 is the addition of several new
valid charactersincluding parentheses, periods, and hyphensto the list of valid characters.
Listing 9.51 PhoneDatatypes.vb : An International Phone Number Extension of the CNumberString
Class

Public Class CPhoneNo


Inherits CNumberString
Public Class InvalidPhoneNumberException
Inherits CNumberString.InvalidNumberStringException
Public Sub New(ByVal pstrPhoneNumber As String)
MyBase.New("The phone number specified, " & pstrPhoneNumber & _
", contains invalid characters.")
End Sub
End Class
Sub New(ByVal pstrPhoneNo As String)

MyBase.new()
Me.StringValue = pstrPhoneNo
End Sub
Sub New(ByVal pPhoneNo As CPhoneNo)
Me.New(pPhoneNo.StringValue)
End Sub
Public Overrides Function ThrowException(ByVal pstrInvalidPhone As String) _
As InvalidNumberStringException
Throw New InvalidPhoneNumberException(pstrInvalidPhone)
End Function

' This is the only substantive change. This sub redefines the DefineValidChars
' method so that when this method is called in the base class's constructor, it will
' call this method instead of the original method.
Protected Overrides Sub DefineValidChars()
cValidChars = New String(14) {"1", "2", "3", "4", "5", _
"6", "7", "8", "9", "0", _
"(", ")", " ", ".", "-"}
End Sub
End Class

What's going on? The CPhoneNo constructor calls the constructor of the CNumberString class, but
because you overrode DefineValidChars , CNumberString.New actually calls
CPhoneNo.DefineValidChars instead of CNumberString.DefineValidChars .

Tip

You can combine the CExtension and CPhoneNo classes to create a


CBusinessPhone class that would be far more useful in an application of this sort. A
sample of this code is included in PhoneDatatypes.vb .

17. Utilizing the phone number class in your existing code is a process similar to what you did to integrate the
CCustomerID class in earlier in this section.
Again, you will need to do the following:

Retype variables.
Remove code validating phone numbers and related exception throwing.
Add .StringValue after every CPhoneNo variable where the string value is needed.

Calling CPhoneNo's object-based constructor from Listing 9.51 each time a CCustomerID object is passed
into a CCustomer object.

18. More important, you will need to move the maximum length validation to event handlers, as shown in Listing
9.52 . The value of the phone numbercontained in the CphoneNo classcan be changed without changing the

18.
phone number property, which is just a pointer.
Listing 9.52 PhoneDatatypes.vb : An Event Handler for the mFax Variable in the CCustomer Class

Private Sub mFax_StringValueBeforeUpdate(ByVal pstrValue As String,


ByRef pCancel As Boolean) Handles mFax.StringValueBeforeUpdate
If pstrValue.Length <= 24 Then
pCancel = False
Else
pCancel = True
Throw New MaximumStringLengthExceededException(24, "Fax", pstrValue)
End If
End Sub

19. The final code example of this chapter will take another look at the CNumberString base class and suggest
another potential use for it: Social Security numbers. A Social Security number is similar to a phone number in
that it is a string of numbers formatted with a limited set of characters. In the Northwind database, you did not
have the option of strictly validating a phone number because you could not be sure of the specific format that
the customer's country might use. A Social Security number is a different story: Two hyphens must be present
at specific locations in the string. The complete CsocialSecurityNo class is defined under Listing 9.53 .

Listing 9.53 PhoneDatatypes.vb : Another Extension of the CNumberString Class, This Time for SSNs

Public Class CSocialSecurityNo


Inherits CNumberString
Public Class InvalidSSNException
Inherits CNumberString.InvalidNumberStringException
Sub New(ByVal pstrSSN As String)
MyBase.New("The SSN specified, " & pstrSSN & ", is not valid.")
End Sub
End Class
Sub New(ByVal pstrSSN As String)
MyBase.new()
StringValue = pstrSSN
End Sub
Sub New(ByVal ssn as CSocialSecurityNo)
MyBase.New(ssn.StringValue)
End Sub
Public Overrides Function IsValid(ByVal pstrSSN As String) As Boolean
If MyBase.IsValid(pstrSSN) Then
Dim nFirstHyphen As Integer = pstrSSN.IndexOf("-")

' An SSN must have a hyphen at index 3 and 6.


If nFirstHyphen = 3 Then
If pstrSSN.IndexOf("-", nFirstHyphen + 1) = 6 Then
If pstrSSN.Chars(0) = "0" Then
Return False
Else
Return True
End If
Else
Return False
End If
Else
Return False
End If
End If
End Function
Public Overrides Function _
ThrowException(ByVal pstrInvalidSSN As String)
As InvalidNumberStringException
Throw New InvalidSSNException(pstrInvalidSSN)
End Function
Protected Overrides Sub DefineValidChars()
cValidChars = New String(10) {"1", "2", "3", _
"4", "5", "6", "7", "8", "9", "0", _
"-"}
End Sub
End Class

Just like the CPhoneNo example, the DefineValidChars is overridden to allow a hyphen to be valid
character.

The real change is in the overridden IsValid method. First, the IsValid method for the base class
(CNumberString.IsValid ) is called to verify that the string only contains numbers and hyphens. Then,
the string is checked to verify that the hyphens are in the proper location.
When the StringValue property is set, the overridden IsValid method in CSocialSecurityNo is
called, which in turn calls the IsValid method in CNumberString .

How It Works
The CCustomerID example in the beginning of this section is a perfect example of encapsulation and a
simple example of the goal of object-oriented code. One piece of business logicthe definition of a
CustomerID and a way to determine its uniqueness and/or existenceis wrapped up in one piece of small,
reusable code. Any other class that represents a table containing a CustomerID can utilize the CCustomerID
class and is guaranteed to always have a valid value whose existence in the database can be verified
painlessly.
The CNumberString class, in combination with its derived classes, extends the reusability concepts
exhibited in the CCustomerID class. Inheritance might be a new concept and a new way of thinking about
code, but the implementation of inheritance is simple. Any time that you inherit from another class, all the

code in that base class is immediately available to you.

Comments
Inheritance is a powerful, flexible way to write code, not only quickly, but in a way that is much easier for
other developers to understand. If the earlier samples make sense, you might be tempted to design
extraordinarily complex object models utilizing a substantial amount of inheritance, polymorphism, and
abstraction.
Two notes of caution:

There is slightly more runtime overhead with derived classes because the CLR must resolve which
members of the object must actually be called. This cannot be done at compile time, because in many
cases, the specific type of the object is ambiguous in static source code.
The clarity and readability of object-oriented code can be countered by an overly complex object
model. Always keep it simple. If that means a bit of "editor inheritance" here and there, so be it. It's
better than object-oriented spaghetti code.

[ Team LiB ]

[ Team LiB ]

Chapter 10. Creating Reports Using Crystal Reports


In this chapter you will

Create a report using Crystal Reports report expert


Display a report that was created
Add calculated fields to the Crystal Reports report
Select whether the report will be displayed, printed, or exported using Visual Basic .NET code
Determine which records will be printed at runtime
Print labels and control the order in which records will be printed
Create an onscreen report that contains hyperlinks
Crystal Reports is the report writer that has been included in Visual Basic since 1993. It uses a banded
approach, as do many other report designers, such as Access's report writer and dBASE.
Crystal Reports is included in Visual Studio .NET for use with both Windows Forms and Web Forms. You can
deploy your reports and have them store locally or even using a Report Web Service. This chapter uses
Windows Forms to show how to create and run Crystal Reports.

Note
Be sure to verify the licensing before deploying reports that were
created using Crystal Reports. In Visual Studio help, look up Crystal
Reports and then licensing.

[ Team LiB ]

[ Team LiB ]

10.1 Create a Report Using Crystal Reports Report Expert


I have created quite a few applications that allow the users to manipulate data in various ways. Now I need
to create some reports so that I can present information for my clients and users. How do I create a report
with Crystal Reports using one of these Experts I have heard about?

Technique
You can create a report and include it in your projects as needed.

Getting Started Creating a Crystal Report


After right-clicking on your project in the Solution Explorer, you need to choose Add New Item from the Add
menu item. You then can pick Crystal Report from the templates and name it appropriately.

Note
If you name your reports with the prefix of rptReportName, you will be
able to find all your reports in one location.

After you have clicked the Open button on the dialog box, you are presented with the Crystal Report Gallery
dialog box, shown here in Figure 10.1.
Figure 10.1. These Experts can save you time when you're creating reports.

Crystal Reports includes wizards called Experts. These Experts assist you in performing certain tasks, from
creating a whole report to creating an individual formula for a field. Experts are actually made up of other
Experts. For instance, the Report Expert utilizes the Formula Expert within itself; however, you also can take
advantage of the Formula Expert from within the Report Designer.

Types of Reports You Can Create with the Report Expert


This How-To will discuss the Report Expert. This Expert creates various types of reports, listed in Table 10.1,
in alphabetical order.

Table 10.1. Types of Reports Created by the Report Expert

Report
Type

Description

Crosstab

Crosstab reports give you a cross-tabulation of your data, such as sales of customers by years.

Drill Down This report is broken down into sections that start out as hidden, and then allow the user to
"drill down" further into the information. An example of this would be a report that lists
customers. When the user clicks on a particular customer, the invoices for that customer are
displayed. Next, when you click on a particular invoice, its line items (or details) can be
displayed.
Form

This report helps to create preprinted forms that use company logos and forms. Examples of
this type include invoices.

Form
Letter

Forms letters, such as late notices and sales letters, are created using this expert.

Mail Labels This expert allows you to create mailing labels that are any size and any number of columns.
Standard

The standard report is just thata standard list report that lists your information. It allows you
to group and sort information, include formulas, and set an overall format for the report. This is
the report that will be demonstrated for this How-To.

SubReport This report helps you to create main reports that utilize subreports. An example is invoices for
customers.
If the Standard report type is highlighted, click OK, and the Standard Report Expert dialog opens, as shown
in Figure 10.2.
Figure 10.2. The tabs on this dialog box are actually experts in their own rights.

Tabs Included in the Standard Report Wizard


Included on the Standard Report Wizard are the following tabs:

Data. Specify the database and table that you will be using for this report. You will have a number of
different choices from datasets within the current application to OLEDB connections. For this chapter,
you will be creating an OLEDB connection to the Northwind database.
Fields. After you have selected your record source, you then get a list of the fields from within your
record source (see Figure 10.3).
Figure 10.3. Besides fields from your table, you can create formulas (expressions) as well.

In addition to creating formulas, you can view the data using the Browse Data option and locate fields
using the Find Field option.
Group. This tab allows you to specify the group levels you want to include. An example you will see
here groups the customer by region (see Figure 10.4).
Figure 10.4. It is often useful to be able to organize your reports based on groupings, such
as the Region field in this report.

Tip
If you want to have a field used for grouping, then you will most
likely not want to have it listed in the detail section. It is important
that you do not choose it back on the Field tab; just choose it from
the Group tab.
Of course, if you accidentally include it in the detail section after
the report is created, you can always delete it later from the
report while you're in the Design view.

Total. This tab allows you to specify which fields you want to summarize in the group footers and report
footer.
Top N. You can set how you want the groups sorted, based on the totals set by the last tab, Total.
Chart. Create a chart of your data.
Select. Filter which records you want to have reported based on field values. You will see how to do
this at runtime in How-To 10.5.
Style. You can give your report a number of different looks. You also can specify the title of your report
(see Figure 10.5.)
Figure 10.5. This chapter will stick to using the Red/Blue border style.

After you have finished specifying your report features, click Finish. Your report will now be displayed in
Design view (see Figure 10.6).
Figure 10.6. The finished product.

For this How-To, you want to get to the point where you have the report created. The next How-To describes

how to actually view the report on a Windows Form.


You can find all the examples in this chapter in the Solution called Visual Basic .NETChapter 10 on the Web
site.

Steps
Open the Visual Basic .NETChapter 10 solution. In the Solution Explorer, you see the report
rptHowTo10_3.rpt. You then see a report that looks similar to the one in Figure 10.7.

1. Right-click on your project in the Solution Explorer, and choose Add New Item menu item from the Add
menu item. Type a name for the report in the Name field, and click Open. The Crystal Report Gallery
dialog box opens.
2. Leaving the defaults as they are, which is to use the Report Export and create a Standard report, click
the OK button. You are taken to the Report Expert, with the Data tab displayed.
3. Double-click on the OLE DB (ADO) node in the Available Data Sources tree if you haven't chosen a data
source before. The OLE DB (ADO) dialog box opens the option and asks you to choose an OLE DB
provider. Choose Microsoft OLE DB Data Provider for SQL Server (see Figure 10.7) and click Next.
You are requested to enter connection information. For this page, type (Local) for the server, check
Integrated Security, and type Northwind for the database (see Figure 10.8). Click Finish. Your
Available Data Sources will now include (Local) under the OLE DB (ADO) node and Northwind under the
local connection.
Figure 10.8. Information that is needed to create the Northwind connection.

4.

4. Expand the tree under Northwind to get to the first DBO, and then expand Tables. You then see the
tables listed, including the Customers table. Highlight the Customers table, and click Insert Table. The
table is displayed in the Tables in Report list (see Figure 10.9.) Click Next.
Figure 10.9. After you have the Tables node expanded, it is easy to tell where your record
sources come from.

5. From the Fields tab, add the fields as displayed in Figure 10.10, and then click Next. You are taken to
the Group tab.
Figure 10.10. These fields will be displayed in the Details section of the report.

6. Under the (local) node in the field tree, select Region from the Customers table, and click Add. Now
you're ready to choose the style. Skip over to the Style tab by clicking on it directly.
7. Type My First Report for the title of the report, and then select Red/Blue Border for the style. Click
Finish, and your report is displayed in Design mode.
Figure 10.7. Choosing the OLE DB (ADO) Data provider.

Comments
The next How-To explains how to display your report.
You can modify your report by both clicking on controls and changing them in the Design mode, or rightclicking on one of the section bodies (not the gray bands), and choosing Report, Report Expert. This takes
you right back into the Report Expert to let you make some tweaks when you have to.
You will also see the various tabs of the Report Expert as you right-click and choose some of the individual
Experts to give you a hand. Try these right now. Take some time to right-click on some of the section bodies,
and select some of the choices from the pop-up menu to play with, such as Report, then Style Expert.

[ Team LiB ]

[ Team LiB ]

10.2 Display a Report That Was Created


The report doesn't do me much good in Design mode. How do I go about viewing the report?

Technique
You can review a report in a couple of ways. You can view a report either using a Windows Form or a Web
Form. Either way, you will also use a control called the Crystal Report Viewer.

Introducing the Crystal Report Viewer


The Crystal Report Viewer is a control that is available in the Toolbox. It is the last control by default. Crystal
Report Viewer does far more than just let you view your document on the form. With the Viewer, you can do
any of the following:

View the report on the form.


Zoom in and out on the document.
Expand and collapse groupings on the document.
Print from the Viewer.
Export from the Viewer.
Move around in the document
Search for text within the report.
You can see an example of the Crystal Report Viewer on a Windows Form in Figure 10.11, where the
example form from this How-To is displayed.
Figure 10.11. The Crystal Report Viewer provides the user with several features, but most of the
features can be disabled if necessary.

Sometimes you might not want the user to have all the features that come with the Viewer. The good news
is that the Viewer actually has its own object model whereby you can set some of the properties at design
time or runtime. The Viewer even has an event model that you can program. You can see the object model
for the Windows Form CrystalReportViewer in Figure 10.12.
Figure 10.12. Using the object model displayed here, you have almost complete control over the
Viewer behavior.

Although you can make the Viewer stand on its head using the object model, to get started, you really only
have to set one property: the ReportSource .
Just as it sounds, the ReportSource tells the Viewer which report to display. You can set the
ReportSource in a variety of ways. You can set the ReportSource property to the file path and name of a
report, or you can create what is called a strongly typed report document.

Specifying the ReportSource by Using the Report


To specify a report file, click on the down arrow in the ReportSource property, and choose (Browse). The
Open an Existing Crystal Report dialog box opens. There, you can look for the report that you want to assign
within your project folder.
The bad part about assigning the report file directly is that when you move the application, the file path is
not updated. A better way to assign the ReportSource is to use a Report Document to strongly type the
report.

Specifying the ReportSource Using a Strong Typed Report


When you're using strong typed reports, you can move your application and the report file without having to
re-establish the file path to the report. To accomplish this, you use what is called a Report document. You
can find the Report document in the Component list. In there, it is called ReportDocument.
When you pull the ReportDocument component onto your form, you are asked to supply a report to be
strong typed. After you have chosen your report, the ReportDocument appears in your Component tray.
When you assign the ReportSource, the ReportDocument appears first in the list (see Figure 10.13).

Figure 10.13. Using the ReportDocument is the way to go when you're assigning reports to
Viewers.

Steps
Open and run the Visual Basic .NETChapter 10 solution. Click on the button labeled How-To 10.2. You
immediately see the report show up that was created in the last How-To. Play with the toolbar buttons on
the Viewer to see how they work. When you maximize the form, you the Viewer expands as well. That is
because the Viewer is anchored (see Figure 10.14).

1. Create a Windows Form.


2. Drag a CrystalReportViewer control from the toolbox onto the form.
3. Drag a Report document from the Component list onto the form. Type the report name you created in
the first How-To (see Figure 10.15).
Figure 10.15. This report is strongly typed.

Tip
After you have clicked OK to accept the Report document, you
might want to set the Name property of the control to a name with
an rd prefix. I named mine rdHowTo10_2. That way, in later code, I
know I am dealing with a ReportDocument control.

4. Set the ReportSource property of the CrystalReportViewer control to the name of your Report
document.
5. Set the Anchor property of the Viewer to be Top, Bottom, Left, Right. This ensures that the Viewer fills
the form, regardless of the size of the form.
Figure 10.14. The report displayed in the Crystal Report Viewer.

Comments
That's it, if you open the form now, you will see the report displayed in the form. You get so many features
with the Crystal Report Viewer just by dumping it on the form with no code.

[ Team LiB ]

[ Team LiB ]

10.3 Add Calculated Fields to the Crystal Reports Report


Although creating reports based on given fields in tables is fine, it just does not give the flexibility I need. To
have that, I need to know how to add calculated fields to my reports.

Technique
In Crystal Reports, calculated fields are known as formulas. To create and edit formulas, you will use the
Formula Editor (see Figure 10.16).
Figure 10.16. You can display this Formula Editor from either the Report Expert or the Report
design.

When you're in the Report Expert, you can add a new formula by clicking on the Formula button, located on
the Fields tab.
In Report design, you can add a field by opening the Field Explorer, located to the left of the IDE with the
toolbox, and expanding the Formula Fields tree. You can also right-click on the Formula Fields base node and
choose New to add a new formula.
Formulas are similar to T-SQL expressions in that you can combine fields or values with operators to create a
Formula field. For example, a formula called @TotalPricePerUnit is being created for this How-To. You saw it
displayed in Figure 10.16. The expression used is this:

{Invoices.UnitPrice}*{Invoices.Quantity}

You can use Formula fields in summary sections and grand totals. The best way to verify a formula is to
create it when you use the Report Expert to create a report.

Steps
Open the Visual Basic .NETChapter 10 solution. In the Solution Explorer, you will see the report
rptHowTo10_3.rpt. Scroll over to the left so that the last column is visible. This is the Formula field.

1. Right-click on your project in the Solution Explorer and choose Add New Item from the Add menu item.
Type the name of the report in the Name field and click Open. The Crystal Report Gallery dialog box
opens.
2. Leaving the defaults as they are, which is to use the Report Export and create a Standard report, click
the OK button. You are taken to the Report Expert, with the Data tab displayed.
3. Double-click on the OLE DB (ADO) node in the Available Data Sources tree. If you haven't chosen a
data source before, the OLE DB (ADO) dialog box opens asking you to choose an OLE DB provider.
Choose Microsoft OLE DB Data Provider for SQL Server and click Next.
You are requested to enter connection information. For this page, type (local) for the server, check
Integrated Security, and type Northwind for the database. Click Finish. Your Available Data Sources
will now indicate (Local) under the OLE DB (ADO) node and Northwind under the local connection.
4. Expand the tree under Northwind to get to the first DBO, and then expand Views. You will see the
views listed, including the Invoices view. Highlight the Invoices view and click Insert Table. The view is
displayed in the Tables in Report list (see Figure 10.17). Click Next.
Figure 10.17. The Report Expert considers both tables and views as tables.

5.

5. From the Fields tab, add the following fields: Invoices.CustomerName, Invoices.OrderID,
Invoices.UnitPrice, and Invoices.Quantity.
6. Click on the Formula button. Type TotalPricePerUnit . Crystal Reports puts the @ before the
formula name from now on. You are then brought into the Formula Editor, shown back on Figure 10.16.
7. Type the formula {Invoices.UnitPrice}*{Invoices.Quantity}; then press Ctrl+S to save and
close the dialog box. An alternative is to double-click on the fields you want to include.
8. Now select the formula you just created, and add it to the fields to include.
9. You can now go on to select how you want the columns totaled and what you want the style of the
report to be. Click Finish when you are done.

Comments
The Formula Editor works similarly to other builders, and it's pretty straightforward to use. Formulas are
another step to making sure you can give your clients the full-featured reports they want.

[ Team LiB ]

[ Team LiB ]

10.4 Select Whether the Report Will Be Displayed, Printed, or Exported Using Visual Basic .NET
Code
I know I can use the Crystal Report Viewer to print and export my reports, but I want to be able to have
control over that, and maybe not even include the Viewer in some cases.

Technique
For this How-To, you will use a Tab control to display the three options for the user: Print, Export, and View
(see Figure 10.18 ).
Figure 10.18. You have flexibility when it comes to letting your users work with reports.

The first tab, Print, allows you to specify the number of copies to print, along with the starting and ending
pages.
Printing Using the Report Document
This page will use the PrintToPrinter method shown here:

Me.rdHowTo10_4.PrintToPrinter(Me.txtNumOfCopies.Text,
False,
Me.txtStartPage.Text, Me.txtEndPage.Text)

The PrintOptions object , found on the ReportDocument , is very useful. The PrintOptions object has
the following properties: PaperOrientation , PaperSize , PaperSource , PrinterDuplex , and

PrinterName. You can set these properties either at design time using the property sheet, or at runtime
using code.
Exporting Using the Report Document
When you're exporting using the Report document, you will be using the ExportOptions object. The
ExportOptions object is made up of four other properties/objects:

DestinationOptions . This is made up of one of three possible objects:


DiskFileDestinationOptions , ExchangeFolderDestinationOptions , or
MicrosoftMailDestinationOptions .
ExportDestinationType . This gets or sets the export destination type. This will be DiskFile ,
ExchangeFolder , MicrosoftMail , or NoDestinationType .
ExportFormatType . This gets or sets the export format type. It can be one of the following: Excel ,
HTML32 , HTML40 , NoFormat , PortableDocFormat , RichText , or WordForWindows .
FormatOptions . This gets or sets the FormatOptions . It can be ExcelFormatOptions ,
HTMLFormatOptions , or PdfRtfWordFormatOptions .
To execute the export, you use the Export method of the DocumentReport object. You can see the page
for exporting in Figure 10.19 .
Figure 10.19. You can control which types of files you want the user to export.

The View tab uses a CrystalReportViewer object on the tab page.

Steps
Open and run the Visual Basic .NETChapter 10 solution. Click on the button labeled How-To 10.4. Clicking

on the tabs, you can see the three options you have to work with. Clicking on the Print button prints your
report to the default printer. When you go to the Export tab and click the Export button, a message appears
stating that the data has been exported. The last tab, View, displays the report in a Viewer.

1. Create a Windows Form. Then place a Tab control on your form.


2. Add pages for Print, Export, and View, using the TabPages property of the Tab control.
3. Drag on a ReportDocument object, and set it to point to the report you created in How-To 10.1. Then
name your report document rdHowTo10_4.
4. Place the controls shown in Figure 10.18 and 10.19 onto the tab page with the properties set forth in
Table 10.3 .

Print
Label
Text
# of Copies

Label
Text
Start Page

Label
Text
End Page

TextBox
Name
txtNumOfCopies

Text
1

TextBox

Name
txtStartPage

Text
0

TextBox
Name
txtEndPage

Text
0

Button
Name
btnPrint
Export
Label
Text
Export Type

Label
Text
File Path

TextBox
Name
txtExportFilePath

Label
Text
File Name

TextBox
Name
txtExportFileName

ListBox
Name
lstExportType

Button
Text
btnExport
View
CrystalReportViewer
Anchor
Top, Bottom, Left, Right

ReportSource
rdHowTo10_4

Table 10.3. Controls for the Tab Pages


Tab Page

Object

Property

Setting

5. Type Excel and Word Document into the Items collection of lstExportType.
6. Add the code in Listing 10.1 to the Click event of btnPrint.
Listing 10.1 frmHowTo10_4.vb : Printing the Report

6.

Private Sub btnPrint_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnPrint.Click
Dim po As PrintDialog
Me.rdHowTo10_4.PrintToPrinter(Me.txtNumOfCopies.Text, False, _
Me.txtStartPage.Text, Me.txtEndPage.Text)
End Sub

7. Add the code in Listing 10.2 to the Click event of btnExport. This code tests the value of lstExportType
and assigns the appropriate ExportFormatType and file extension (strExt). The
DiskFileDestinationOptions information is supplied, and the Export method is called to complete the
export.
Listing 10.2 frmHowTo10_4.vb : Exporting the Report

Private Sub btnExport_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnExport.Click
Dim strExt As String
Try
With Me.rdHowTo10_4.ExportOptions
Select Case lstExportType.SelectedItem
Case "Excel"
.ExportFormatType = _
CrystalDecisions.[Shared].ExportFormatType.Excel
strExt = ".xls"
Case "Word Document"
.ExportFormatType = __
CrystalDecisions.[Shared].ExportFormatType.WordForWindows
strExt = ".Doc"
End Select
.ExportDestinationType = _
CrystalDecisions.[Shared].ExportDestinationType.DiskFile
Dim ddo As New CrystalDecisions.Shared.DiskFileDestinationOptions()
ddo.DiskFileName = Me.txtExportFilePath.Text & _
Me.txtExportFileName.Text & strExt
.DestinationOptions = ddo
End With

Me.rdHowTo10_4.Export()
MessageBox.Show("Report Exported!")
Catch excp As Exception
MessageBox.Show(excp.Message)
End Try
End Sub

Comments
As you can see, very little code is needed to provide a great deal of functionality.
Sometimes when you're using tools such as the Viewer in a tabbed form, the tab pages can become
cluttered. If you want to limit the power of the Viewer or make it more streamlined, you can turn off the
toolbar and group display by setting DisplayToolbar and DisplayGroupTree to False on the
CrystalReportViewer.

[ Team LiB ]

[ Team LiB ]

10.5 Determine Which Records Will Be Printed at Runtime


I can use the Select Wizard to help limit the records that I want to be displayed on the report, but that
doesn't do my users much good when they want control over that when the application is running. How do I
determine which records are displayed at runtime?

Technique
To handle this, use the SelectionFormula of the CrystalReportsViewer. When you use the
SelectionFormula to limit your records, you are taking advantage of Crystal Report's Database
optimization. This means that the query you generated by setting the SelectionFormula and the original
record source for the report will be "pushed down" to the server.

SelectionFormula Syntax
You can see the syntax for the SelectionFormula here:

CrystalReportViewer1.SelectionFormula = "{Table.Field} = value"

Value can be various data types, delimited by the usual delimiters, such as single quotes for strings.
For the new criteria to take effect, after setting the SelectionFormula, you need to call the
RefreshReport method of the CrystalReportViewer.

Setting Report Options


To get optimal use out of the SelectionFormula, you need to keep some things in mind. You need to set
an option of the report that will ensure it uses the record source indexes if possible.
To do this, you right-click on the report and choose Report Options from the Report menu. On the Options
page, make sure to check Use Indexes or Server for Speed (see Figure 10.20).
Figure 10.20. Customize your report by using the Report Options dialog box.

Another tip is to make sure you use indexed fields whenever possible.

Tip
Take some time now to check out some of the other report options.

Steps
Open and run the Visual Basic .NETChapter 10 solution. Click on the button labeled How-To 10.5. You can
select Regions from the ComboBox control on the top of the form; the report then reflects the selection (see
Figure 10.21).

1. Create a Windows Form.


2. Drag on a ReportDocument object, and set it to point to the report you created in How-To 10.1. Then
name your report document rdHowTo10_5.
3. Place the controls shown in Figure 10.21 onto the form with the properties set in Table 10.4.

3.

Table 10.4. Label, Combo, and CrystalReportViewer Controls and Their Property
Settings
Object

Property

Setting

Label

Text

Pick a Region

ComboBox

Name

cboRegions

CrystalReportViewer Name
Anchor

cvwRegionReport
Top, Bottom, Right, Left

ReportSource rdHowTo10_5
4. Add the code in Listing 10.3 into the Load event of the form. This code performs a DISTINCT SQL
statement to get all of the regions used in the Customers table. The data adapter then fills dtRegions.
Before binding dtRegions to the combo box cboRegions, a new data row is added to allow the user to
specify All Regions.
Listing 10.3 frmHowTo10_5.vb: Populating the Selection Combo Box

Private Sub frmHowTo10_5_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
Dim odaRegions As New _
OleDb.OleDbDataAdapter("SELECT DISTINCT Region FROM Customers",
BuildCnnStr("(local)", "Northwind"))
Dim dtRegions As New DataTable()
odaRegions.Fill(dtRegions)
'-- Add the All Regions feature
Dim drAll As DataRow = dtRegions.NewRow
drAll(0) = "<< ALL Regions >>"
dtRegions.Rows.Add(drAll)

With Me.cboRegions
.DataSource = dtRegions
.ValueMember = "Region"
End With

End Sub

Note

As with a number of other examples, the BuildCnnStr() function


has been created in the modGeneralRoutines module to create a
connection string.

5. Add the report in Listing 10.4 to the SelectedIndexChanged event of cboRegions. One of the
values that is in the Region field of the Customer table is NULL. This code tests for that first by testing
this:

Me.cboRegions.SelectedItem(0) Is System.DBNull.Value

If the selected item is NULL, which uses System.DBNull.Value for the comparison, then the
value of IsNull({Customers.Region}) is stored in the SelectionFormula. If All Reasons
was chosen, then the SelectFormula is set to the empty string so that all items are chosen.
Otherwise, the Customers.Region field is compared to the literal region, supplied by cboRegions.
The last task performed is refreshing the report.
Listing 10.4 frmHowTo10_5.vb: Setting the SelectionFormula Property

Private Sub cboRegions_SelectedIndexChanged(ByVal sender As System.Object, _


ByVal e As System.EventArgs) _
Handles cboRegions.SelectedIndexChanged
With Me.cvwRegionReport
If Me.cboRegions.SelectedItem(0) Is System.DBNull.Value Then
.SelectionFormula = "IsNull({Customers.Region})"
ElseIf Me.cboRegions.SelectedItem(0) = "<< ALL Regions >>" Then
.SelectionFormula = ""
Else
.SelectionFormula = "{Customers.Region} = '" & _
Me.cboRegions.SelectedItem(0) & "'"
End If
.RefreshReport()
End With
End Sub

Figure 10.21. You can let your user control how much data he sees in the report.

Comments
You can limit the selection displayed on the report in other ways, too. This How-To presented just one way to
get you started. Letting your user choose the records gives him the perceived control he wants.

[ Team LiB ]

[ Team LiB ]

10.6 Print Labels and Control the Order in Which Records Will Be Printed
I need to be able to have my application print labels for my user's customer list. Sometimes my user needs
to print labels based on the postal code, and other times he needs to print labels alphabetically by company
name. How do I do this at runtime?

Technique
To accomplish this task, you will use the Report Expert to create mailing labels. You then will use code to
update the sort order at run-time.

Creating the Mailing Labels


To create the mailing labels, you create a new report and choose Mailing Labels for the type of Expert to use.
You then fill in the Data tab, which uses Customers for the table, and grab the following fields:
CompanyName, ContactName, ContactTitle, Address, Formula (@CityRegionPostal= { Customers.City} & ", "
& { Customers.Region} & " "& { Customers.PostalCode} ), and Country. You can see how this will look in
Figure 10.22.
Figure 10.22. Fields that will be used in the mailing labels.

After clicking Next, you are taken to the tab that allows you to choose which type of label you want to use.
For this How-To, the Address (Avery 5160) is used. Everything else on the page is chosen for you (see
Figure 10.23).

Figure 10.23. You have control over how your labels look.

Now you can just click Finish, and the final mailing label report is created (see Figure 10.24).
Figure 10.24. These appear in nice mailing label format when they're viewed on a form.

Controlling the Sort Order at Runtime


To control the sort order at runtime, you use properties, methods, and objects of the ReportDocument.
Specifically, you traverse down the ReportDocument object model, looking at the Database object and
moving down to the Fields level. You do this first when you're loading up a combo box with fields that are
being used in the report. The following lines of code accomplish this:

For Each dfCurr In Me.rdHowTo10_6.Database.Tables.Item(0).Fields


Me.cboSortFields.Items.Add(dfCurr.Name)
Next

dfCurr is a FieldDefinition object, which means it is a field definition for a given field in the report.
The other object that will be used from the Report definition is the DataDefinition object, and the
SortFields collection off of that. You can see this with the following lines of code, which set the sorting for
the report based on which field they chose in the combo box:

With Me.rdHowTo10_6
dfSort = .Database.Tables.Item(0).Fields.Item(Me.cboSortFields.Text)
.DataDefinition.SortFields.Item(0).Field = dfSort
End With
Me.cvwCustomerLabels.RefreshReport()

You can see once again that the RefreshReport method is called after updating the SortFields item.

Steps
Open and run the Visual Basic .NETChapter 10 solution. Click on the button labeled How-To 10.6. You can
select fields from the ComboBox control on the top of the form, and the report reflects the sorting selection
(see Figure 10.25).

1. Create a new Crystal Report. Choose Mail Labels for the Report Expert to use.
2. Fill in the Data tab, choosing Northwind for the database, and Customers for the table to use.
3. Choose the fields as specified by the "Technique" section: CompanyName, ContactName, ContactTitle,
Address, Formula (@CityRegionPostal= { Customers.City} & ", " & { Customers.Region} & " " & {
Customers.PostalCode} ), and Country.
4. On the Label tab, choose Address (Avery 5160) for the mailing label type. Click Finish.
5. Create a Windows Form.
6. Drag on a ReportDocument object, and set it to point to the report you created in the past few steps.
Then name your report document rdHowTo10_6.
7. Place the controls shown in Figure 10.22 onto the form with the properties set forth in Table 10.5.

7.

Table 10.5. Label, Combo, and CrystalReportViewer Controls and Their Property
Settings
Object

Property

Setting

Label

Text

Pick a Field to Sort

ComboBox

Name

cboSortFields

CrystalReportViewer Name
Anchor

cvwCustomerLabels
Top, Bottom, Right, Left

ReportSource rdHowTo10_6
8. Add the code in Listing 10.5 to the Load event of the form. As described in the "Technique" section,
this code iterates through each of the fields in the table that the report is based on and loads them into
the Items collection of cboSortFields.
Listing 10.5 frmHowTo10_5.vb: Populating the Selection Combo Box

Private Sub frmHowTo10_6_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
Dim dfCurr As CrystalDecisions.CrystalReports.Engine.FieldDefinition
' Iterate through the table that the report is based on
'
and load the fields into a combo box.
For Each dfCurr In Me.rdHowTo10_6.Database.Tables.Item(0).Fields
Me.cboSortFields.Items.Add(dfCurr.Name)
Next
End Sub

9. Add the code in Listing 10.6 to the SelectedIndexChanged event of cboSortFields. This code
takes the selected item from cboSortFields and locates it in the table that the report is based on.
The DataDefinition object is retrieved and then assigned to the first items in the SortFields
collection. Last, the report is redisplayed using the RefreshReport method.
Listing 10.6 frmHowTo10_5.vb: Populating the Selection Combo Box

Private Sub cboSortFields_SelectedIndexChanged(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles cboSortFields.SelectedIndexChanged
Dim dfSort As CrystalDecisions.CrystalReports.Engine.FieldDefinition
' Use the field that is picked in the combo box in
'
the SortFields collection.
With Me.rdHowTo10_6
dfSort = .Database.Tables.Item(0).Fields.Item(Me.cboSortFields.Text)
.DataDefinition.SortFields.Item(0).Field = dfSort

End With
Me.cvwCustomerLabels.RefreshReport()
End Sub

Figure 10.25. Let your user sort the mailing labels as needed.

Comments
If you have more than one field you want to sort on, then you could use the same technique, just adding
code such as this:

dfSort = .Database.Tables.Item(0).Fields.Item(Me.cboSortFields2.Text)
.DataDefinition.SortFields.Item(1).Field = dfSort

The 0 is replaced by 1, and you have another combo box from which you must pick the second sort field.

[ Team LiB ]

[ Team LiB ]

10.7 Create an Onscreen Report That Contains Hyperlinks


My user has a text field in his Customers table that is actually customers' Web sites. Users want to be able
to access the site as a hyperlink on an onscreen report. How can I use Crystal Report to create a report that
uses hyperlinks?

Technique
For this How-To, you won't be doing coding. You'll just adjust the formatting of a report field to tell it that it
needs to treat the field like a hyperlink. Before doing this, you need to modify Northwind.

Adding a Web Site Field to Northwind


To accomplish this, you open the Northwind database in the Server Explorer. You then add a field called
Website to the Customers table, with the Data Type of nchar and a size of 50.
You then modify the first two records in the Customers table so that the Website field contains
www.appsplus.com and www.microsoft.com respectively, as shown in Figure 10.26.
Figure 10.26. These will be displayed as hyperlinks on the new report you will create.

Now you are ready to create the report.

Telling the Report That a Field Is a Hyperlink


You create a simple report that is based off the Customers table and contains the fields CompanyName,
ContactName, and Website. Then you right-click on the Website field in the report and choose Format. The
Format dialog box opens, where you can click on the Hyperlink tab. For the hyperlink type, you should
choose Current field value (see Figure 10.27).
Figure 10.27. These are the options that you can have for hyperlink actions.

Now that you have told the field to act like a hyperlink, you need to have it look like one. To perform this
task, you set the following properties:

BorderColor. This is set to RoyalBlue to give it the color of a hyperlink.


BottomLineStyle. This is set to crLSSingleLine.
EnableTightHorizontal. This is set to True so that it doesn't highlight the full length of the
fieldjust to the text.
That's it! When you display this in a Viewer, as shown in Figure 10.28, you can click on the Website field, and
the Viewer will bring you to the Web site you chose.
Figure 10.28. This is a real-time hyperlink.

Steps
Open and run the Visual Basic .NETChapter 10 solution. Click on the button labeled How-To 10.7. You can
click on the hyperlinks, displayed for the Web site, to go to the Web site.

1. Modify Northwind as described in the "Technique" section, adding the Website field to the Customers
table.
2. Create a new Crystal Report. Choose Standard for the Report Expert to use.
3. Fill in the Data tab, choosing Northwind for the database, and Customers for the table to use.
4. Choose the fields as specified by the "Technique" section: CompanyName, ContactName, and Website.
Click Finish.
5. With the report open in Design mode, right-click on the Website field in the Details section, and choose
Format.
6. Make the modifications to the format of the field as described in the "Technique" section, setting the
hyperlink type. Then click OK.
7. Make the rest of the format changes as described, setting the BorderColor, BottomLineStyle, and
EnableTightHorizontal.
8. Create a Windows Form.
9. Drag on a ReportDocument object, and set it to point to the report you created in the past few steps.
Then name your report document rdHowTo10_7.
10. Place a CrystalReportViewer on the form, setting the Anchor property to Top, Bottom, Right, Left and
the ReportSource property to rdHowTo10_7.

Comments
Now when you run the report, you get your hyperlinks. You could have performed additional tasks with
hyperlinks, such as using the report for reviewing those you want to email, and having the field be clickable
that way.

[ Team LiB ]

[ Team LiB ]

Chapter 11. Managing SQL Server Security


In this chapter you will

Create Windows NT/2000 users


Create Windows NT/2000 groups
Establish a Windows NT/2000 authentication mode
Establish mixed-mode authentication
Create a standard login
Create a Windows NT/2000 login
Use a fixed server role
Create a database user account
Use statement permissions
Use object permissions
Use fixed database roles
Create custom database roles
Create application roles
Set permission states on tables
This chapter discusses the SQL erver security scheme and explains the options available to you as a Visual
Basic developer. It starts by discussing authentication modes and then moves on to the various permissions
you can set on SQL Server objects.
SQL Server supports a layered security system. From bottom to top, these layers add to the overall
protection of the database objects and the data they contain. Keep in mind how SQL Server works. SQL
Server is an engine that maintains an environment for database objects and data. Through this environment,
SQL Server provides services for searching and sorting data, adding or updating records, and performing
other data-oriented tasks.
If you look at SQL Server as a multilayered or multidimensional entity, the role played by each of the
security layers becomes clear. Each layer and each data-management task has its own set of rules that must
be followed. For instance, simply entering the SQL Server environment does not automatically grant access
to database. Similarly, the ability to read data stored in the table does not imply the ability to add new
records or change existing data.
Security is an important issue, and a certain amount of complexity is the price for the high degree of data
security and integrity that SQL Server provides.

[ Team LiB ]

[ Team LiB ]

11.1 Create Windows NT/2000 Users


Although it might seem odd to begin a discussion of SQL Server security by describing the process of adding
users to Windows NT/2000, it's important to note that SQL Server security is tightly integrated with the
Windows security registry. SQL Server administrators are pleased with the fact that SQL Server
automatically recognizes Windows NT and Windows 2000 users, saving a considerable amount of
administrative time.
This section explains the process of adding new users to Windows 2000. The next section describes how to
add functional groups to Windows. Although these sections deal with Windows 2000, the process is nearly
identical for Windows NT.
Windows must recognize users before they can log in to the network and use the computer's resources. You
need to add a number of users to the Windows user registry. How do you make sure that all users are
required to provide a network password before they can access the network?

Technique
Windows users cannot be added to the system from within SQL Server's Enterprise Manager. You'll have to
use the Windows Control Panel applets to add new users and groups.

Note
The settings you choose on this page of the Add New User dialog box do
not affect the SQL Server settings you establish for the user. The
settings you see in Figure 11.4 relate only to Windows and not SQL
Server or the database data.
Figure 11.4. Most of your users will be set up as Standard Users
with no special capabilities.

Note
The first step in the following example is different on Windows NT 4.0
and Windows 2000. For Windows NT 4.0, you go to Start, Programs,
Administrative Tools, User Manager. For Windows 2000, you go to
Start, Settings, Control Panel, Administrative Tools, Computer
Management; then you right-click on Users.
Also, you must have administrative rights on the computer to add
users to it.

Steps
SQL Server security is tightly integrated with Windows NT/2000 security. SQL Server recognizes the users
and groups that are added to a Windows NT or Windows 2000 domain. The first step toward security for SQL
Server, of course, is to add users to the computer system. This section describes how to add individual users
to a Windows NT or Windows 2000 computer.
SQL Server consults the registered users on the computer when you create logins, discussed in How-To
11.5. The user's name and description are accessible from the SQL Server security dialog boxes and help
you recognize each user as you create SQL Server logins and establish security profiles.

1. Choose Start, Settings, Control Panel and open the Users and Passwords Control Panel applet.
2. Make sure the check box labeled Users Must Enter a User Name and Password to Use This Computer is

1.
2.
checked, as shown in Figure 11.1.
Figure 11.1. This dialog box allows you to add new Windows users and manage their group
membership.

3. Click the Add button to open the Add New User dialog box.
4. Fill in the username, the full name of the user, and a description for the new user, as shown in Figure
11.2.
Figure 11.2. The information you enter in the Add New User dialog box will be accessible
from the SQL Server security dialog boxes.

5. After you have provided the required user information, click the Next button to move to the second
page of the dialog box.
6. Enter the new user's initial password into the password text box. Then type the password a second
time in the Confirm Password text box (see Figure 11.3). When you're ready, click the Next button.
Figure 11.3. Specify the user's initial password in this dialog box. The user will be able to
change it later, if necessary.

7. On the third page of the Add New User dialog box (see Figure 11.4), you specify the type of Windows
access you want to provide this user. Although this information is not directly related to SQL Server,
most often you will want to leave this setting at its default to Standard User.

7.

In most cases, you do not want to provide a user with more access than necessary.

Note
The settings you choose on this page of the Add New User dialog
box do not affect the SQL Server settings you establish for the user.
The settings you see in Figure 11.4 relate only to Windows and not
SQL Server or the database data.

8. When all settings are complete, click the Finish button to add the new user to the collection of users on
your computer.

Comments
SQL Server's integration with Windows security is one of the major reasons that SQL Server has grown in
popularity. Its ability to cooperate with Windows when authenticating users and providing database services
dramatically reduces the administrative burden that is normally associated with server database engines.

Caution
Be sure to remove your temporary users after you are finished with
them so that you do leave them hanging around.

[ Team LiB ]

[ Team LiB ]

11.2 Create Windows NT/2000 Groups


Windows 2000 recognizes individual users as well as functional groups. You can, for instance, set up a group
called Marketing or Sales, and then add new users to the computer. In addition, each user can be added to
any of the groups that you've created. Therefore, you might have a marketing manager who belongs to both
the Marketing and Management groups.
One advantage of using groups is that SQL Server automatically recognizes the registered Windows users.
SQL Server is tightly integrated with Windows security, and you are able to use this integration as you set up
SQL Server security. This means, for instance, that you can provide the marketing group with access to
tables and stored procedures that are related to marketing data, yet deny the salespeople access to those
same tables and procedures. The marketing manager mentioned earlier in this chapter is able to work with
marketing and management data (such as employee human resources data).
Because you are treating multiple users as a single group, the administrative effort is considerably less than
if you had gone to the trouble of providing access to each individual user. As you'll soon see, treating users
as members of groups greatly simplifies the security administration task.
Handling individual users is a real hassle. In large installations with hundreds of users, you spend inordinate
amounts of time managing SQL Server security on a user-by-user basis. Instead, you'd like to use Windows
groups to add groups of users to the database.

Technique
Use the Administrative Tools in the Control Panel to create groups and add the users you've created to those
groups.

Steps
Most often, users are arranged into logical groupings. For instance, all the people in the marketing
department are likely to belong to a group named Marketing. Similarly, managers probably belong to a
Management group. In this section, you'll learn how to specify the groups on your computer and add the
user accounts you've created to those groups.
Later, as users log in to SQL Server, they'll be able to log in as themselves or as a group. Although this
might sound a bit strange, to SQL Server, an individual user is the same as a group of users. All that SQL
Server sees is an identifier ("TonyS" or "Marketing"), and it matches that identity with a Windows network
login.

1. Choose Start, Settings, Control Panel to open the Control Panel.


2. Double-click on the Administrative Tools applet to access the Windows 2000 administration options.
3. Select the security settings by double-clicking on the Computer Management option and opening the
dialog box you see in Figure 11.5.
Figure 11.5. Each Windows group usually contains a number of individual users.

4. Use the + next to Local Users and Groups to reveal the Users and Groups icons. Right-click on the
Groups icon and select New Group from the shortcut menu that appears. (Alternatively, use the New
Group menu item under the Action menu). You'll see the New Group dialog box as shown in Figure
11.6. Provide a name for the new group, such as Shift Supervisors, in the Group Name text box at the
top of the New Group dialog box.
Figure 11.6. Fill in the Group name and Description text boxes. You'll add users to this
Windows group in a minute.

5.

5. Provide a verbose description for the group in the Description text box.
Near the bottom left of the New Group dialog box, you'll see two buttons labeled Add and Check Names
(see Figure 11.7). Click on the Add button to open the list of all users who are registered on this
computer.
Figure 11.7. This list shows all the users within the local Windows domain. The red X
indicates disabled Windows accounts.

6. The top half of the Select Users or Groups dialog box contains an alphabetically sorted list of all the
users in the local domain. Use the scrollbar if necessary to locate the user you want to add to the
group. Select the user and use the Add button (or double-click on the user) to add the user to the
group. As users are added to the group, their names appear in the lower half of the Select Users or
Groups dialog box. When you have completed the selection process, click the OK button.
You'll be returned to the New Group dialog box. You should see the users you've added to the group
displayed in the list at the middle of the dialog box.
7. When you're finished adding groups to the computer, click the Close button to dismiss the New Group
dialog box.

Comments
Normally, as a database developer, you won't be creating Windows 2000 groups. However, in many small
environments, developers are required to take on more than a single role. Also, you might find it useful to
create a group login just for the applications you write.

[ Team LiB ]

[ Team LiB ]

11.3 Establish a Windows NT/2000 Authentication Mode


SQL Server is a much more complex database engine than desktop systems such as Jet. SQL Server is a
true server engine; it provides access to multiple databases or even hundreds of users simultaneously.
Furthermore, unlike Jet, which is tightly bound into Microsoft Access, SQL Server exists as a separate entity
on a computer, which most often is located across a network from the user's desktop computer.
A building that contains multiple offices is a good analogy for the way that SQL Server is built. The building is
secured, and each office within the building is locked. Carrying the analogy further, each office contains
locked desk drawers and filing cabinets. A person who wants to use information in one of those file cabinets
must enter the building, gain entrance to a locked office, and have a key or other permission to open a
locked file cabinet.
SQL Server requires each user to be authenticated before it permits the user to open a database.
Authentication is somewhat like having to show an employee ID card to a guard at the front door of a
building. Without proper credentials, you don't get into the building. Similarly, you don't get "into" SQL
Server unless you have been properly authenticated.
SQL Server supports two different authentication modes:

Windows NT/2000 authentication. This mode means that SQL Server explicitly trusts Windows to
authenticate the user. Anyone who has a valid Windows user ID and password is allowed to access SQL
Server. This is known as a trusted connection because SQL Server trusts Windows to allow only
qualified people into the computer system and onto SQL Server. Windows NT/2000 Authentication
works only with Windows NT or Windows 2000, and it has the distinct advantage of being quite simple
to administer. Furthermore, the Windows NT/2000 security provides for expiration and other controls
over password usage. In fact, SQL Server even recognizes the user's membership in Windows security
groups.
SQL Server and Windows NT/2000 authentication. Using this mode means that both SQL Server and
Windows are involved in authenticating the user. The user is not able to access SQL Server unless both
Windows and SQL Server are able to verify the user's identity and password. SQL Server and Windows
NT/2000 Authentication mode is often referred to as mixed-mode authentication.
The selected authentication mode simply directs SQL Server where to look for the user's credentials. In other
words, authentication is performed by SQL Server, and describes how SQL Server verifies the user's identity.
You've decided that mixed-mode authentication is too much work, and users are likely to forget either their
Windows password or their SQL Server password. In fact, some users have resorted to writing down their
passwords on sticky notes attached to their computer monitors. Obviously, this creates a security risk. You'd
like to make it as easy as possible for users to get into SQL Server so that they can use the databases you've
created for them.

Technique
Enterprise Manager includes all the dialog boxes that are required to set SQL Server's authentication mode.
Changing the authentication mode takes only a few moments, and it affects all databases that SQL Server
manages.

Steps

SQL Server takes a layered approach to securing the data in its tables. These layers include
authenticationwhereby a user is identified by username and passwordand permissions on the objects
(such as tables and views) stored in the database.
As a developer or SQL Server administrator, you must choose which authentication mode your users use to
access SQL Server.
In Windows NT/2000 authentication mode, any user who is able to log in to Windows is able to access SQL
Server.Windows NT/2000 Authentication mode is the easiest possible way for users to access SQL Server. As
long as a user is an authorized user of Windows NT or Windows 2000, he'll be able to get into SQL Server.
No extra password or user identity is required for admittance to SQL Server, which reduces the risk that a
user will store a password in an insecure place.

1. Choose Start, Programs, Microsoft SQL Server, Enterprise Manager to open Enterprise Manager.
2. Open the SQL Server group and right-click on the server you want to configure.
3. Select Properties from the context menu that appears. (The Properties command should be highlighted
in the context menu.)
4. Select the Security tab.
5. Select Windows Only from the authentication options near the top of the Security tab, as shown in
Figure 11.8.
Figure 11.8. Select Windows Only from the Authentication options in the Properties dialog
box.

6. Click the OK button to complete the process and close the Properties dialog box.

Comments
Windows NT/2000 authentication simply means that SQL Server trusts Windows to authenticate users.
Windows determines the user's identity and group member as the user logs into his computer. From that
point on, the user has full access to SQL Server installations that are configured to accept Windows NT/2000
authentication.
The main problem with Windows NT/2000 authentication mode is that it doesn't work with Windows 95,
Windows 98, or non-Windows computers. Anyone who is connected to your network from a Unix or Apple
Macintosh computer will not be able to use Windows NT/2000 authentication. You'll have to set up mixedmode authentication for these users.
Another problem with Windows NT/2000 authentication is that in many environments, more than one person
shares the same computer. For instance, consider a point-of-sale terminal in a retail environment or an
industrial computer located on a factory floor. Multiple users typically access these computers, and because
SQL Server admits anyone logged into the computer, SQL Server can't distinguish between individual users.
This can be a problem in environments where some users must be prevented from viewing certain tables or
accessing certain data within a database.

Note
Although this discussion makes it sound as though SQL Server is
passive when working under Windows NT/2000 authentication mode,
there is more to the story than discussed here. SQL Sever actually
calls Windows NT or 2000 to retrieve the user's Windows login identity
and group membership. If the user's group membership changes while
he is logged into Windows, SQL Server sees the changes as soon as
the user tries to access SQL Server.

[ Team LiB ]

[ Team LiB ]

11.4 Establish Mixed-Mode Authentication


The alternative to Windows NT/2000 authentication is mixed-mode authentication. In this arrangement, a
user is first authenticated by Windows, and is again authenticated by SQL Server as he tries to access a
database. Mixed-mode is the only authentication for users who are working on Windows 98 computers or
Macintoshes, Unix, and other non-Windows machines.
My network includes a number of non-Windows computers, such as Apple Macintoshes. In addition, several
users are working from Windows 98 computers. I discovered that these users are unable to access the SQL
Server databases that I created. It appears that mixed-mode authentication might be the only way for these
users to access SQL Server.

Technique
Again, Enterprise Manager provides the dialog boxes that are necessary to set mixed-mode authentication.
After mixed-mode is set, users must first log in to Windows (or, at least, access the SQL Server machine
from a Windows network) and then provide a username and password to SQL Server before using a
database.

Steps
In mixed-mode authentication, the user first logs into Microsoft Windows and then into SQL Server. At each
step, the user's credentials are authenticated.
Mixed-mode authentication means that SQL Server keeps a record of users who are allowed to log in to SQL
Server. It matches their password with the password that is stored with their user record.
SQL Server stores the user's login information in a system table named syslogins in the master database.
This table includes an encrypted security ID (SID) and the user's encrypted password. The other information
that is stored in the syslogins table is discussed in the next two sections.

1. Use Start, Programs, Microsoft SQL Server, Enterprise Manager to open Enterprise Manager.
2. Open the SQL Server group and right-click on the server you want to configure.
3. Select Properties from the context menu that appears. (The Properties command should be highlighted
in the context menu.)
4. Select the Security tab.
5. Select SQL Server and Windows (for Windows 2000 and NT, SQL Server and Windows NT is displayed)
from the authentication options near the top of the Security tab (see Figure 11.9).
Figure 11.9. Setting mixed-mode authentication is similar to setting Windows NT/2000
authentication.

6. Click the OK button to complete the process and close the Properties dialog box.

Comments
Although it might sound bothersome to provide two passwords before accessing SQL Server, most often the
SQL Server password is provided by the front-end application with which the user is working. Users rarely, if
ever, actually open Enterprise Manager or Query Analyzer and directly access the data in SQL Server
databases. Instead, they'll use front-end applications that are written in Visual Basic, Access, or another
database tool. Most often, the user opens the application and the application passes the user's identity and
password to SQL Server as the database connection is made.
Although mixed-mode authentication might be more of a hassle to your users, it results in considerably
better security for your database. Just because a user is able to access your network by using mixed-mode
authentication does not mean that he is able to access SQL Server. The paradox is that some users might
write down their network or SQL Server password in a location where unauthorized people can see it.
However, the same could be said of any security system, and such security breaches must always be
avoided.

[ Team LiB ]

[ Team LiB ]

11.5 Create a Standard Login


The previous sections discussed authentication, which is nothing more than the process of how SQL Server
recognizes users. Authentication is much like the method used to allow people to get through the front door
of an office building. The building may use a simple key card lock, or the company may use a guard to check
IDs as people enter the building. In either case, the person's identity is verified before entry is granted.
Earlier in this chapter, you read about the Windows NT/2000 authentication process. When using Windows
NT/2000 authentication, a trusted connection is established between SQL Server and the user's computer.
However, trusted connections cannot be established between SQL Server and Windows 95 or 98 machines,
or Apple Macintoshes, Unix, or other non-Windows computers.
After you determine which type of SQL Server authentication to use, you must establish a login for each of
your users. A login is how the user gains access to SQL Server after the authentication process. A login is
how SQL Server establishes your identity within SQL Server and how SQL Server determines your
permissions to use data and database objects. You can choose from two different types of logins:

Standard SQL Server login


Windows NT/2000 login
When no trusted connection exists between the user's computer in SQL Server, SQL Server has no way of
obtaining the user's identity from the operating system. Therefore, mixed mode application must be used to
validate the user, and a standard SQL Server login must be created so that SQL Server recognizes the user.
The login you establish only provides access to SQL Server; it does not provide access to databases, data, or
database objects.
Not every SQL Server user will be working with Windows NT and Windows 2000. Some users might be
connected to the network from a Unix or Macintosh machine. These users will not be able to use a Windows
NT/2000 login, and you have to establish a standard SQL Server login for them. This is also true of users
who are working under Windows 95 and Windows 98.

Technique
SQL Server Enterprise Manager provides the dialog boxes that are necessary to set up standard logins. This
section describes these dialog boxes and how to provide the information necessary to create standard SQL
Server logins.
Creating a login can affect only one person or a group of people; it does not affect how SQL Server operates.

Steps
If the user is connecting to SQL Server from a Unix, Macintosh, or other non-Windows machine, or if the user
is running Windows 98, you must create a standard login for him. SQL Server maintains a record of each
person's logins and a table named sysxlogins in the master database. This table stores the user's login ID,
encrypted password, and other critical information. If you'd like to view the data in this table, use the
syslogins view in the master database. The syslogins view uses a SQL statement to arrange the data in a
more readable format.

As the user logs in, regardless of which authentication mode he uses, his user information is compared
against the data that is stored in the syslogins table. As long as the user appears to be valid, SQL Server
allows him to try to access tables, stored procedures, and data.

1. Open Enterprise Manager and select your server.


2. Use the plus sign (+) next to your server's name to expand the server's object list. Then expand the
security icon.
3. Right-click on the Login icon and select New Login from the shortcut menu.
4. You'll now see the new Login dialog box (see Figure 11.10). Enter the new login name in the Name text
box.
Figure 11.10. Add new Windows NT/2000 logins with the SQL Server Login Properties
dialog box.

5. Select the SQL Server Authentication option button, and enter a password for the login.
6. Select which database this login will normally use from the Database drop-down list in the Default
section near the bottom of the New Login dialog box.

Comments

The SQL Server standard login does not permit you to use the user's NT/2000 identity. Therefore, you
cannot take advantage of a user's membership in a Windows group to simplify login creation. You can,
however, create a shared login. (CS would be named something like Marketing for accounting practices used
by everyone who is a member of that group.) The problem is that everyone within that group will use the
same password in login name, which might make security an issue.

[ Team LiB ]

[ Team LiB ]

11.6 Create a Windows NT/2000 Login


Any user who is accessing SQL Server from a Windows NT or Windows 2000 computer (but not Windows 98
or ME) can be given a SQL Server Windows NT/2000 login. Although setting up the NT/2000 login is almost
the same process as creating a standard login, you have the option of adding the user as any of the
following:

An individual user
A member of an NT/2000 group
A member of the SQL Server built-in group
I want to take advantage of the fact that the users on my system have all been assigned to functional
groups (such as marketing and sales) in Windows NT or Windows 2000. Because these groups parallel the
user's SQL Server activity, I'd like to use each person's network identity as his SQL Server login.
Using a person's Windows NT/2000 group saves a lot of administrative time. Rather than creating a custom
login for each user, I can assign a login to a Windows NT/2000 group, and SQL Server can recognize any
individual as valid who belongs to the group.

Technique
You first create a Windows 2000 user or group, and then create a SQL Server login for that user or group.
Earlier in this chapter, you read how to add users and groups to Windows NT/2000. You'll now use the
Windows Enterprise Manager dialog boxes to establish SQL Server logins for those users and groups.

Steps
If the user is working with Windows NT or 2000, you are able to establish a Windows NT/2000 login identity
for him. As you'll see, using a Windows NT/2000 login is less work than using standard logins.
When a user logs in to a SQL Server or Windows NT/2000 account, his identity is verified against the account
information that Windows manages. SQL Server security is tightly integrated with Windows login information,
and SQL Server knows and understands the user's identity.
SQL Server recognizes the user's personal identity as well as his Windows group membership. Even if no
personal SQL Server login is established for the user, as long as a SQL Server login is established for at least
one of the Windows groups to which he belongs, he will be able to access SQL Server.
The person's identity and Windows group membership is established as he logs in to Windows. As you'll see
later in this chapter, these identities play important roles with regard to object permissions and access to
data that SQL Server manages.

1. Open Enterprise Manager and select the Security icon. Open this icon by clicking on the plus sign (+)
next to it.
2. Select New Login from the Actions menu, or click on Logins with the right mouse button and select New

1.

2.
Login from the context menu. Regardless of which method you use, the SQL Server Login Properties
dialog box (see Figure 11.10) opens.
3. Use the button next to the Name text box to display the list of Windows NT/2000 user and group logins
that SQL Server recognizes. The list is alphabetically sorted first by groups, and then by users. In
Figure 11.11, you see the list scrolled downward to show the last several groups and the first users in
the list.
Figure 11.11. The Names list includes both groups and users.

4. Select a group or user for the new login. If the user or group does not appear in the list, use the dropdown list at the top of the dialog box shown in Figure 11.11 to select another computer on the
network.

Comments
Establishing a Windows NT/2000 login account is similar to creating a standard SQL Server login. The biggest
difference is that SQL Server recognizes the user's Windows identity,including the password and group
membership.
The user's identity is how SQL Server determines the individual's access to database data and objects. You'll
learn about these important permissions in the sections titled A and B later in this chapter.
Don't automatically create logins for every Windows NT/2000 group. This would mean that virtually every
user has access to SQL Server. You should provide access to SQL Server only to those users with a
legitimate need to use the data that is stored in SQL Server, or to people who are established as SQL Server
system administrators.

[ Team LiB ]

[ Team LiB ]

11.7 Use a Fixed Server Role


Managing a server database engine such as SQL Server can be a time-consuming process. Administrative
tasks include backing up the data, adding new users, modifying tables and views, and so on. Most SQL
Server administrators find it useful from time to time to permit other people to perform these tasks.
Assigning other people tasks such as backing up the databases or administering security allows the system
administrators to devote more time to other responsibilities.
As a system administrator, I am overwhelmed by the administrative tasks that are required to keep several
databases operating efficiently. There's too much to do between database administration, adding new users,
performing backups, and other tasks. I'd like to be able to allow an assistant to assume some of these
responsibilities so that I won't have to be personally involved in performing these tasks.
However, I'm not looking forward to assigning various permissions to all of my administrative assistants. For
instance, some people are to be designated as security administrators whereas others will be responsible for
updating table designs.

Technique
SQL Server supports the notion of fixed server roles that define certain administrative profiles. Each fixed
server role is accompanied by the appropriate permissions to perform the administrative tasks that are
associated with the role.
Enterprise Manager provides all the dialog boxes that are necessary to assign user accounts to the fixed
server roles that SQL Server recognizes.
It is important to note that each fixed server role is global within SQL Server. This means, for instance, that
the dbcreator fixed server role is able not only to create new databases, but also to make changes to
existing databases in SQL Server.
SQL Server recognizes eight fixed server roles:

sysadmin. This is the most powerful fixed server role. Members of this role are able to perform all the
tasks included with the other roles.
serveradmin. The serveradmin role adjusts the serverwide settings in SQL Server, such as memory
usage, authentication mode, and home directories.
setupadmin. This role administers linked servers. A SQL Server installation is able to share SQL Server
databases that are located on other computers. The setupadmin group is responsible for creating links
to SQL Server installations on other computers.
securityadmin. Members of the securityadmin role add new user and group logins, assign passwords,
and perform other security-oriented tasks.
processadmin. This role manages processes that are spawned by SQL Server.
dbcreator. Members of this role are responsible for creating and altering databases.
diskadmin. The diskadmin role adjusts the disk space that is available for databases, sets the database
growth increment (as a percent or in megabytes), and specifies the parameters for the SQL Server log

file.
bulkadmin. SQL Server 2000 includes a number of statements intended to perform bulk inserts to data.
Because the BULK INSERT statement can involve considerable amounts of processing time, SQL Server
does not allow anyone other than members of the sysadmin and bulkadmin roles to perform this
statement.

Steps
Often, you'll want individual users or groups of users to have database administrative responsibilities. For
instance, you might want the accounting group to manage its own login names and passwords.
In this case, you'll want to join the users in the accounting group to certain fixed server roles, which are
predefined special security groups. Each fixed server role defines a category of administrative tasks, and
each member of a fixed server role is able to perform those administrative tasks.
SQL Server recognizes members of fixed server roles as people who are authorized to perform these
administrative tasks. Each role is accompanied by the appropriate SQL Server permissions that are
necessary to perform those tasks.

1. Open Enterprise Manager and expand the Security icon.


2. Click on the Server Roles icon to show the eight fixed server roles in the right pane (see Figure 11.12).
Figure 11.12. The Server Roles icon reveals the eight SQL Server fixed server roles.

3. Right-click on any of the fixed server role entries and select Properties from the pop-up menu.
Alternatively, select one of the fixed server roles and use the Properties command under the Action
menu to open the Server Role Properties dialog box, as shown in Figure 11.13.
Figure 11.13. The Server Role Properties dialog box shows you the logins that are added to
the selected role.

4. Use the Add button to open the Add Members dialog box (see Figure 11.14). The Add Members dialog
box shows only those logins that have not already been added to the role. In Figure 11.14, only TonyS,
the Marketing group, and members of the BUILTIN/Administrators group are not members of the
securityadmin fixed server role.
Figure 11.14. The Add Members dialog box shows you only those logins that have not been
added to the selected role.

5. Select the login to add to the selected fixed server role and click the OK button.

Comments

Membership in a fixed server role does not grant access to a database or data within the databases. Fixed
server roles are intended for administrators and assistant administrators and do not automatically grant
access to any of the data that SQL Server manages. Database object permissions (discussed later in this
chapter in How-To 11.10) are required to gain access to data and database objects.
The special sysadmin role should be reserved for trusted and trained system administrators. Members of this
role are able to perform all SQL Server administrative tasks, and those tasks are applicable to all databases
that SQL Server manages. Obviously, this can lead to serious problems in the wrong hands.
Finally, when you add people to fixed server roles, make sure these people understand the consequences of
their actions as system administrators. Because fixed server roles are global within SQL Server, the actions
that fixed server role members perform could affect all the databases that SQL Server manages. Incorrectly
configuring SQL Server or failing to carefully implement security can have a dramatic negative impact on
every database that SQL Server manages.
Members of the SQL Server BUILTIN/Administrators group are automatically added to the sysadmin fixed
server role.

[ Team LiB ]

[ Team LiB ]

11.8 Create a Database User Account


The logins that you created in How-To 11.5 or 11.6 provide access to SQL Server, but not to any databases
within SQL Server. This is much like giving someone a key to a building, but not providing keys to offices
within the building.
The fixed server role that you might have specified in How-To 11.7 gives the person rights to perform
serverwide administrative tasks such as creating or modifying databases. Using the building analogy, this is
something like giving an electrician permission to rewire or modify the electrical service within the building.
However, neither of these settings actually grants access to databases within SQL Server. Before a user can
access a SQL Server database (somewhat like entering a locked office in the building), he must be provided
with the database user account.
I have been authenticated and logged into SQL Server and now I need to access data that is stored within a
SQL Server database. Without a specific database user account, I am unable to access and use data that
SQL Server manages.

Technique
Enterprise Manager provides the dialog boxes that are necessary to create user accounts in any of its
databases. Be sure to add the user to every database that he requires. Otherwise, the user will not be able
to use the data, run stored procedures, or otherwise access the database.

Steps
Simply logging in to SQL Server does not automatically establish a person's database identity. In other
words, accessing SQL Server does not mean that SQL Server recognizes the person as a valid database
user.
This is particularly true when Windows NT/2000 authentication is used. After all, this authentication mode
means that anyone who logs in to Windows is able to access the database. SQL Server needs to know
exactly who the person is and what data and database objects this person is allowed to access. A SQL Server
user account is needed for each user or group of users who is accessing SQL Server.
Each SQL Server database maintains an internal registry of user accounts that are permitted into the
database. This information is stored in the table named sysusers within the database. The account
information travels with the database's MDF file and is backed up when the database is backed up.

1. Open Enterprise Manager and expand the Northwind database icon.


2. Right-click on the Users icon and select New Database User from the shortcut menu that appears.
Alternatively, select New Database User from the Action menu. In either case, the Database User
Properties dialog box opens (see Figure 11.15).
Figure 11.15. You add new user accounts with the Database User Properties dialog box.

3. Select a user or group login from the drop-down list at the top of the Database User Properties dialog
box. If desired, you can provide a different username for the user account. Normally, however, you'll
want to avoid complications by using the default username.
4. Click the OK button to commit the new user account.

Comments
It is important to distinguish between a SQL Server login and a database user account. The SQL Server login
simply allows a person to access SQL Server, but it does not provide access to databases. A database user
account provides access to one and only one database that SQL Server manages. Each user, therefore, will
need an account with each database he intends to use. This is why creating database user accounts for
groups of users is much more efficient than adding user accounts for individual users.
Database user accounts can be established for individual users as well as groups. The Login name dropdown list in the User Account Properties dialog box contains all the SQL Server logins you have created.
The statement earlier that a user without a specific database account is unable to use the database is not
entirely correct. SQL Server declines to default user accounts: guest and dbo.
The guest account is used whenever a user seeks access to the database in which he has no specific
account. Under most situations, the SQL Server system administrator has severely limited the ability of the
default user account to access a database within SQL Server. Exactly how this is done is explained in How-To
11.10.
The database owner (dbo) account owns all the objects that are created by anyone who is a member of the

sysadmin fixed server role. You'll frequently see the dbo account listed as an object's owner simply because
the database construction is most often left up to SQL Server's system administrators.

[ Team LiB ]

[ Team LiB ]

11.9 Use Statement Permissions


SQL Server databases can become quite large and complex. It is important to control the number and type
of objects that are added to a SQL Server database so that its structure contains only those objects that are
actually required for the database's operation.
If object creation were not controlled, users could build temporary tables, views, stored procedures, and
other database objects that would become permanent additions to the database. There's no easy way to tell
whether a particular database object is required by some front-end applications, making it difficult to remove
these objects.
I want to control unwanted and unneeded object proliferation in my databases. Without some kind of
control, users might unnecessarily complicate the database's structure by adding unwarranted objects.

Technique
You will employ statement permissions to permit or disallow users to execute SQL statements that modify
the database structure. There are also statement permissions controlling SQL statements that back up the
database and its log file. These statements include the following:

CREATE DATABASE. Creates a new database (applicable only in the master database).
CREATE DEFAULT. Establishes default values for columns in tables.
CREATE FUNCTION. Creates a user-defined function that is saved as a Transact-SQL routine.
CREATE PROCEDURE. Creates a stored procedure.
CREATE RULE. Adds a rule to a column in a table. A rule specifies the acceptable values for the column.
CREATE TABLE. Creates a new table within the database.
CREATE VIEW. Adds a view to the database.
BACKUP DATABASE. Backs up the entire database to removable media.
BACKUP LOG. Backs up the database's log file to removable media.
Although this security task has been discussed only from the perspective of limiting the user's ability to
create database objects, statement permissions are also a way to ensure that users who really need to
modify the database structure are able to do so.
Statement permissions are applied at the database level. There are no global SQL Server statement
permissions.

Steps
Logging in to SQL Server does not mean that a user is actually able to access and use the data and other
objects that are stored in SQL Server. Each user account has certain permissions assigned to it that specify
the account's ability to use the database and its objects.

Statement permissions are one category of database permissions. Statement permissions specify which
types of SQL DDL (data definition language) statements a user is allowed to execute against the database.
DLL statements are frequently used to create and modify tables, add indexes to tables, and perform other
data structure operations on the database. Statement permissions limit a user's ability to perform operations
that could be dangerous to the database.
After statement permissions have been established, the user is able to execute object creation statements
(such as CREATE TABLE) only if the statements permission has been granted.
By default, SQL Server does not grant statement permissions. As the SQL Server system administrator, you
should grant these permissions only to users who require object creation ability.

1. Open Enterprise Manager and expand the Northwind database's icon.


2. Right-click on the Northwind database's icon and select Properties from the shortcut menu. This action
opens the database's Properties dialog box (see Figure 11.16).
Figure 11.16. The database Properties dialog box includes settings for statement
permissions.

3. Click on the Permissions tab. Notice that the leftmost column contains the roles and logins that you
have created within the database. Select a role or login, and check the statement permissions you
want to assign.

3.

Comments
Each statement permission has three different states:

Revoke. The user is not given the statement permission unless he is a member of a role that has been
given the permission. The graphic to depict this state is blank.
Grant. The user is given permission to run the statement. The graphic to depict this state is the check.
Deny. The user is denied the statement permission and cannot run the SQL Statement. The red X
depicts this state.
Statement permissions are a great way to permit assistants the ability to add tables, views, stored
procedures, and other objects as needed. Because the default setting is to deny these permissions, you do
not have to worry about unwarranted object proliferation in your databases.

[ Team LiB ]

[ Team LiB ]

11.10 Use Object Permissions


At this point, you have added SQL Server logins and established user accounts in each database within SQL
Server. In most situations, you want to restrict the user's access to tables, views, stored procedures, and
other objects within this database. This is done by setting permissions on these database objects.
I do not want every user to have read, write, and update permissions on every table within the database.
Otherwise, unauthorized users are able to view data that they are not permitted to see. Often, only certain
users are permitted to add new records or to delete existing records in a database table. Without object
permissions, I have no way of controlling individual user access to the data within a database.

Technique
You'll use the Enterprise Manager dialog boxes to assign permissions on the objects within a database. SQL
Server provides the following object permissions for tables, views, and stored procedures:

Select. Permission to issue SELECT statements against a table or view to retrieve data.
Insert. Permission that allows the user to execute the INSERT statement to add new records to a table
or view.
Update. Permission that allows the UPDATE statement to run, changing the data in a row of a table or
view.
Delete. Permission to run the DELETE statement and remove rows from a table or view.
Execute. Permission that allows the user to run stored procedures and functions within the database.

Steps
A SQL Server database contains a wide variety of database objects, such as tables, views, and stored
procedures. A user account can be assigned specific permissions on each object in a SQL Server database.
These permissions direct SQL Server to allow an account to run stored procedures, view and update data
that is contained in tables, and perform other database operations.
As you click on the individual object permissions, the check box changes from empty to a green check mark
to a red X, as mentioned in the previous How-To.

1. Open Enterprise Manager and expand the Northwind database's icon.


2. Expand the Northwind database's Tables icon to display all the tables in Enterprise Manager's right
pane.
3. Right-click on a table in the Tables list, and select Properties from the shortcut menu to open the Table
Properties dialog box (see Figure 11.17).
Figure 11.17. The Table Properties dialog box displays important information about the

table.

4. Select the Permissions button in the upper-right corner of the Table Properties dialog box to displaythe
Permissions tab (see Figure 11.18).
Figure 11.18. The Permissions tab contains all the object permission settings for the table.

5. Select a user or role from the leftmost column, and then click on the check box in any of the
Permissions columns.

Comments
Each permission on an object has three levels of access:

Grant. SQL Server permits all operations whose permission is set to Grant.
Revoke. The user is unable to perform the operation unless he's been implicitly granted permission
through membership in some role (discussed in How-To 11.11) or through a group. Revoke is the
default setting for all permissions.
Deny. The user cannot perform a denied operation, even if permission is implicitly granted by role or
group membership.
Most often, unless the user has a specific need to be granted or denied permission on an object, you'll leave
the permission set to Revoke. This means that the permission is not provided unless the user is given
permission through a database role (discussed in the next section). Generally speaking, it is better to
provide too little access to database objects than to grant too much access that could lead to confidentiality
or data integrity problems. This is why the default permission on SQL Server objects is set to Revoke by
default.

[ Team LiB ]

[ Team LiB ]

11.11 Use Fixed Database Roles


Creating database accounts and assigning permissions is obviously a lot of work. When you consider that
most SQL Server installations service dozens to hundreds of users, it's easy to see that a considerable
amount of time and effort goes into administering database security. All in all, creating and managing a
database security scheme is one of the most time- and effort-intensive tasks of any database administrator.
Fortunately, SQL Server provides a tool to considerably lighten this administrative load. As you'll see in this
section, SQL Server makes it easy to assign predefined permissions to groups of users rather than
individually handling each user.
Setting up database object permissions for individual users is a hassle. Every time a user account is added to
SQL Server, the permissions on database objects must be set for the user. I'd like to be able to minimize the
amount of time spent designing individual object permissions for my users.

Technique
SQL Server defines many built-in fixed database roles that grant or deny permissions on database objects.
Each fixed database role adds or subtracts permissions on all the tables, stored procedures, or other
database objects within the database. A user who is added to a fixed database role inherits all the
permissions specified by the role. A person can belong to multiple roles, if necessary.

Steps
A fixed database role is similar in some ways to the fixed server roles discussed earlier in this chapter. The
difference is that fixed database roles determine permissions to perform operations on objects within a
single database, whereas fixed server roles specify the administrative operations that are permitted on all
SQL Server databases.
SQL Server defines 10 different fixed database roles:

db_owner. As owners of the database, members of this role can perform any task that is granted to
the other fixed database roles. The db_owner role includes all administrative, design, and data access
permissions.
db_accessadmin. The db_accessadmin role manages the creation of new logins and accounts. These
logins and accounts include individual users as well as groups of users.
db_datareader. This role is able to view all data from all tables in the database.
db_datawriter. The db_datawriter role is able to add, update, or delete data from all the tables in the
database.
db_ddladmin. This role can modify objects within the database. This means that db_ddladmin users
can add or delete tables or modify the design of existing tables.
db_securityadmin. Members of the db_securityadmin role manage security on the database. This
means they can add new roles and manage statement and object permissions within the database.
db_backupoperator. This role is responsible for backing up the database.

db_denydatareader. Members of this role are unable to view data in the database. This role is useful
for data entry clerks whose job is inputting new data without viewing existing records.
db_denydatawriter. Use this role to prevent users from changing data in the database. This is useful,
for instance, for clerical and management staff who are supposed to be able to read, but not update,
data.
public. This role is for all users of the database that don't have specifically defined roles or permissions
in the database. You can edit the permissions of the public role, but be careful.
The db_prefix on each of these roles is significant. It's there to help distinguish between fixed server roles
(explained earlier in this How-To and discussed in the following section) from the fixed database roles
explained in this section.

1. Open Enterprise Manager and expand the Northwind database's icon.


2. Locate and expand the Northwind database's Roles icon to display the fixed database roles in
Enterprise Manager's right pane (see Figure 11.19).
Figure 11.19. SQL Server 2000 defines 10 different fixed database roles.

3. Right-click on a role (such as db_securityadmin) in the Roles list, and select Properties from the
shortcut menu. You'll see the Database Role Properties dialog box (shown in Figure 11.20) open in
response.
Figure 11.20. Use the Database Role Properties dialog box to assign login accounts to a SQL
Server's fixed database role.

4. Click on the Add button to open the Add Role Members dialog box (see Figure 11.21). This dialog
shows all database accounts that are not currently assigned to the selected role.
Figure 11.21. The Add Role Members dialog box shows everyone who is not currently
assigned to the selected role.

5.

6.

5. Click on any members you'd like to add to the selected role. The list box in the Add Role Members
dialog box allows you to select multiple logins at one time.
6. When you are satisfied with your selections, click the OK button to close the Add Role Members dialog
box; then close the Database Role Properties dialog box by clicking on its OK button.

Comments
The fixed database roles are not to be confused with the similar fixed server roles. Each fixed database role
applies only to a single database. The members you add to a role are only able to operate with the role
inside of the selected database. Fixed server roles, on the other hand, affect all databases within SQL Server
as well as SQL Server.
Therefore, fixed database role security is the ideal way to assign specific permissions on a single database.
This can be useful to allow departmental groups within a company to manage their own databases. Because
relatively small amounts of data are influenced by fixed database roles (this depends, of course, on the type
and size of the database), it isn't as likely that a poorly trained individual will damage the data within SQL
Server.

[ Team LiB ]

[ Team LiB ]

11.12 Create Custom Database Roles


Even though the 10 built-in fixed server roles might appear to cover all contingencies, it is possible to create
custom database roles for your SQL Server databases. One of the problems with the fixed database roles is
that they apply to all objects within the database. Therefore, assigning users to the db_datareader role
means that they'll be able to read data in all tables and views within the database. What should you do if you
want to provide access just to one or two tables instead of every table?
My database includes a mix of public and confidential information. I want to set up a role that permits read
access to the Categories and Products tables, but does not allow access to the Employees or Customers
tables.

Technique
You'll create a custom database role that specifies read permissions on the Products and Categories tables,
but does not give access to any other table in the database.

Steps
One of the essential qualities of SQL Server is its flexibility in dealing with almost any environment. As an
example of this flexibility, SQL Server provides custom database roles, which are freely modified to include
permissions to perform any administrative task. The SQL Server system administrator creates the custom
database roles and assigns them to any users who require the special combination of permissions on the
database objects.
SQL Server recognizes the security profiles that are established with custom database roles the same as
fixed database roles. Although considerable work is involved in setting up custom database roles, you are
assured that your users can view, edit, and add records only to those tables you want them to.

1. Open Enterprise Manager and expand the Northwind database's icon.


2. Right-click the Northwind database's Roles icon and select New Database Role from the shortcut menu.
The New Role dialog box (see Figure 11.22) opens in response. Notice how similar this dialog box is to
the Database Role Properties dialog box shown in Figure 11.20.
Figure 11.22. Add a custom database role with the New Role dialog box.

3. Provide a name such as ProdCatReadOnly for the custom database role.


4. Add users to the new role as you did in steps 4 and 5 in section 11.11. In this case, you'll add the
guest user so that all SQL Server users who are added to this role will have access to the tables.
5. Click OK to close the New Role dialog box and to establish the ProdCatReadOnly role.
6. Right-click on the ProdCatReadOnly role and select Properties from the shortcut menu. When the
Database Role Properties dialog box opens, click on the Permissions button in the upper-right corner to
display the Permissions tab (see Figure 11.23).
Figure 11.23. Set the database object permissions with the Permissions tab of the Database
Role Properties dialog box.

7. Locate the Categories and Products table and click on the check box in the SELECT column until a
green check mark appears for each table. Notice that as you continue to click on the check box, its icon
changes from the green check mark to a red X, and then empty again. These icons indicate the
permission states on the table: green means grant, the red X means deny, and the empty check box
means revoke.
8. Click the OK button to close the Permissions dialog box, and then close the Database Role Properties
dialog box.

Comments
You'll notice in Figure 11.23 that the list box on the permissions tab shows all the database objects, including
tables, views, and stored procedures. You are able to enable or disable the SELECT, INSERT, UPDATE,
DELETE, and EXEC permissions for any of the objects that appear in the list.
It's easy to go overboard on setting up a custom database role. You can overly complicate a custom
database role by overloading it with too many security settings. Generally speaking, you're better off with
several simple, easy-to-understand custom database roles than one or two massive, complicated custom
roles.

[ Team LiB ]

[ Team LiB ]

11.13 Create Application Roles


As if fixed database roles and custom database roles weren't enough, SQL Server provides a third category
of database role. Sometimes you want users to simply start up an application and have access to SQL
Server's data through the application. In these cases, you don't want to have to set up logins, user accounts,
and other security configurations to accommodate the application's users. This is the job of application roles.
I have users who routinely access SQL Server through an application that I've written in Visual Basic. I also
have users who use Web pages to access the data using ASP techniques. I'd like a simple and easy way to
provide these users with limited access to the database, but I don't want to spend a lot of time configuring
SQL Server.

Technique
Create a SQL Server 2000 application role for these users. The application logs into SQL Server and provides
the password that is required to access data. Typically, application roles are severely limited in their ability to
access data.

Steps
SQL Server provides for the creation of application roles. An application role is established to allow programs
that are written in Visual Basic and other programming languages to freely access and update SQL Server
data.
People cannot use application roles. You cannot add users, groups, or other roles to an application role. The
application role is not enabled until a password is provided.
At runtime, the user's application will provide the role's name and password to gain access to the data that is
granted by the application role. The application is unable to do anything other than the permissions you
established for the application role.

1. Open Enterprise Manager and expand the Northwind database's icon.


2. Right-click the Northwind database's Roles icon and select New Database Role from the shortcut menu.
The New Role dialog box opens in response (see Figure 11.24). Select the Application Role option
button near the bottom of this dialog box.
Figure 11.24. The New Role dialog box allows you to create application roles.

3. Provide a name and a password for the application role.


4. Click the OK button to save the application role.
5. Right-click the new application role, and select Properties from the shortcut menu.
6. Set the object permissions as you did in steps 6, 7, and 8 in section 11.12 in this chapter.

Comments
An application role bypasses the normal permissions that are applied to a database. A user who is not
otherwise able to access data will be able to use a program such as Excel, Word, or Visual Basic to get at the
data as long as an application role has been established for the program. The permissions that are
established for the application role exist only while the application maintains a connection to the database.
While the application role is active, all other permissions that are granted to the user on the database are
suspended, and only the permissions that the application role provides are enabled.
A user can't exploit an application role to access other databases that SQL Server manages. The only
permissions that an application role has on other databases are the permissions granted to the default guest
user. Under most circumstances, the system administrator will have disabled or severely limited the SQL
Server guest account.

[ Team LiB ]

[ Team LiB ]

Chapter 12. Utilizing XML Data In Your Visual Basic .NET Applications
In this chapter you will

Utilize XML in .NET


Use XML namespaces in .NET
Use XMLWriter to create an XML document
Use XMLReader to read an XML document
Work with the XML document object model
Retrieve XML from SQL Server 2000
Work with datasets and XML
Extensible Markup Language (XML) is to data what Hypertext Markup Language (HTML) is to presentation.
XML is similar to HTML; in fact, it is derived from the same standard.
As you deal with outside systems over the Internet or even by importing data, you will need to access XML
data at one time or another.

[ Team LiB ]

[ Team LiB ]

Ways of Utilizing XML in .NET


You can utilize XML from within .NET in hundreds of ways. The following list spells out some of those ways.
The first three ways will be shown in some of the How-Tos later in this chapter.

Utilizing the XML Document Object Model (DOM)


Reading XML with the XMLReader
Writing XML with the XMLWriter
Validating XML with Schemas
Using XPathNavigator in the .NET Framework
Integrating XML with Relational Data and ADO.NET
To use XML, you must use the XML namespaces that are available within .NET.

[ Team LiB ]

[ Team LiB ]

XML Namespaces in .NET


.NET utilizes XML for a number of purposes behind the scenes. As such, .NET has a number of namespaces
that not only can you use, but that .NET uses as well. Those namespaces are as follows:

System.XML. Provides standards-based support for processing XML.


System.XML.Xpath. Contains the XPath parser and evaluation engine.
System.XML.XSL. Provides support for Extensible Stylesheet Transformations (XSLT).
System.XML.Schema. Contains the XML classes that provide standards-based support for XML
Schemas Definition (XSD) language schemas.
You will see some classes within these namespaces utilized in this chapter. You can enhance the classes to
handle almost any task you might have that utilizes XML.

[ Team LiB ]

[ Team LiB ]

12.1 Use XMLWriter to Create an XML Document


Sometimes I need to take data that is in my database and write it out to an XML document. I heard that
XMLWriter is a good way to do this. What does XMLWriter do, and how do I create an XML document with it?

Technique
The XMLWriter provides a quick way to generate streams or files that contain XML data. The stream is not
cached; it is forward-only. The XML data that the XMLWriter generates conforms to W3C XML 1.0 and the
namespaces in XML recommendations.
With XMLWriter, you can accomplish the following:

Create well-formed XML.


Manage the outputincluding methods to determine the progress of the outputwith the WriteState
property.
Flush or close the output.
Write multiple documents to one output stream.
Encode binary bytes as base64 and as binhex, and write out the resulting text.
Report the current namespace prefix, xml:lang, or xml:space scope.
Write valid names, qualified names, and name tokens.
XMLWriter has one implementation: the XMLTextWriter.
To show you how to use the XMLTextWriter, the sample code will create a data table, allowing the user to
add names to it. Then the XMLTextWriter will be used to write the data from the data table into an XML
document.
Creating the Data Table
Rather than using a DataAdapter object to create and populate the data table from live data, the code will
create the data table from scratch, and the user will add data to it. To perform this task, add the properties
and methods shown in Table 12.1 . The first object that will be created and utilized is the DataColumn
object.

DataColumn
ColumnName
Specifies the Column name for the current data column that is being created.

DataColumn
Caption

Stores the Caption used to be displayed.

DataTable.Columns
Add
Adds the current DataColumn object to the collection of columns in the data table.

DataTable
NewRow
Creates a DataRow object.

DataRow
Item(ColumnName)
Replaces data in the specified column, in the current DataRow object.

DataTable.Rows
Add
Adds the data row to the collection of rows in the data table.

Table 12.1. Objects, Properties, and Methods for Creating a DataTable Object
Object

Property/Method

Description

Using the XMLTextWriter Implementation


The last task that the example performs is creating the XML document by using the properties and methods
of the XMLTextWriter class, shown in Table 12.2 .

WriteStartDocument
Writes the XML declaration with the version 1.0.

Formatting
Specifies how you want the XML file formatted. In this case, System.Xml.Formatting.Indented is used.

WriteDocType
Writes the DOCTYPE declaration with the specified name and optional attributes. This allows you to specify
the type of objects that this document represents.

WriteComment
Allows you to write comments into your XML document.

WriteStartElement

Used for both the rows and the columns, this lets you specify the starting element for a row that is
represented from a table.

WriteAttributeString
Writes columns and properties for data that is represented in the XML document.

WriteEndElement
Ends the row or column.

Flush
Flushes the stream from memory.

Close
Closes the string.

Table 12.2. XMLTextWriter Properties and Methods Used for This How-To
Property/Method

Description

Steps
Open and run the Visual Basic .NETChapter 12 solution. From the main Web page, click on the hyperlink
with the caption How-To 12.1: Use XMLWriter to Create an XML Document. When the page loads, you can
enter a few names by entering the last and first names and then clicking the button labeled Add to
DataTable . When you have added a few names, click the button labeled Create XML File. Using Explorer,
open the file created in C:\ called test.xml (see Figure 12.1 ).

1. Create a Web Form. Then place the Labels, TextBoxes, Buttons, and DataGrid objects as seen in Figure
12.1 on the form with the properties set as in Table 12.3 .

Label
Text
Last Name
TextBox

ID

txtLastName
Label
Text
First Name
TextBox

ID

txtFirstName
Button

ID

btnAdd

Text
Add to DataTable
Button

ID

btnCreateXMLFile

Text
Create XML File
DataGrid

ID

dgDataToWrite
HyperLink

ID

hplReturnToMain

NavigateURL
wfrmMain.aspx

Table 12.3. Label, TextBox, and Button Control Property Settings


Object

Property

Setting

2. Add the following line to the code module of the form. Place it under the line that reads Web Form
Designer Generated Code .

2.

Dim mdtData As New DataTable()

3. Add the code in Listing 12.1 to the Load event of the page. If the data table has not been saved to the
Session object, then you need to create it from scratch by first creating the data columns and then
adding them to the data table. The DataTable object is then saved to the Session object with the name
MyDataTable. If the Session object entry already exists, it is assigned back to the module variable
mdtData. Last, the data table is bound to the DataGrid object by calling the BindTheGrid routine , which
is described in the next step.
Listing 12.1 wfrmHowTo12_1.aspx.vb : Creating a DataTable Object from Scratch

Private Sub Page_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
If (Session("MyDataTable") Is Nothing) Then
Dim dcFirstName As New DataColumn()
dcFirstName.ColumnName = "FirstName"
dcFirstName.Caption = "First Name"
mdtData.Columns.Add(dcFirstName)
Dim dcLastName As New DataColumn()
dcLastName.ColumnName = "LastName"
dcLastName.Caption = "Last Name"
mdtData.Columns.Add(dcLastName)
Session("MyDataTable") = mdtData
Else
mdtData = CType(Session("MyDataTable"), DataTable)
End If
BindTheGrid()
End Sub

4. Create the routine BindTheGrid, shown in Listing 12.2 , in the code module for the page.
Listing 12.2 wfrmHowTo12_1.aspx.vb : Binding the Data Table to the Data Grid

Sub BindTheGrid()

dgDataToWrite.DataSource = mdtData
dgDataToWrite.DataBind()
End Sub

5. Add the code in Listing 12.3 to the Click event of the btnAdd button. This routine starts off by calling the
NewRow method off the mdtData data table, thus creating a new DataRow object. The two columns in
drNew are replaced with the values in txtLastName and txtFirstName. The new row is added to the data
table, and the text boxes are cleared. Last, mdtData is rebound to the data grid by calling BindTheGrid.
Listing 12.3 wfrmHowTo12_1.aspx.vb : Adding Data to the Data Table and Then Rebinding the
Data Grid

Private Sub btnAdd_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnAdd.Click
Dim drNew As DataRow
drNew = mdtData.NewRow()
drNew.Item("LastName") = Me.txtLastName.Text
drNew.Item("FirstName") = Me.txtFirstName.Text
mdtData.Rows.Add(drNew)
Me.txtLastName.Text = ""
Me.txtFirstName.Text = ""
BindTheGrid()
End Sub

6. Add the code in Listing 12.4 to the Click event of the btnCreateXMLFile button. The first task is to
declare an instance of the XMLTextWriter. Then the XMLTextWriter creates and opens the file c:\Text.xml.
Next, the XML document is created using the Write methods, including the writing of the individual rows
of the DataTable object. Last, the data is flushed, and the XMLTextWriter is closed.
Listing 12.4 wfrmHowTo12_1.aspx.vb : Creating the XML Document

Private Sub btnCreateXMLFile_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnCreateXMLFile.Click
Dim xtwMyData As System.Xml.XmlTextWriter = Nothing
Dim intCurrRow As Integer
Dim intNumRows As Integer
xtwMyData = New System.Xml.XmlTextWriter("c:\Test.XML", Nothing)
intNumRows = mdtData.Rows.Count - 1

With xtwMyData
.WriteStartDocument(False)
.Formatting = System.Xml.Formatting.Indented
.WriteDocType("Names", Nothing, Nothing, Nothing)
.WriteComment("This file represents names list")
.WriteStartElement("names")
For intCurrRow = 0 To intNumRows
'-- Start the current row
.WriteStartElement("name", Nothing)
'-- Write the fields
.WriteAttributeString("FirstName", _
mdtData.Rows(intCurrRow).Item("FirstName"))
.WriteAttributeString("LastName", _
mdtData.Rows(intCurrRow).Item("LastName"))
'-- Ending the row
.WriteEndElement()
Next
'-- Write the XML to file and close the writer
.Flush()
.Close()
End With
End Sub

Figure 12.1. This XML document was created using XMLTextWriter.

Comments

This is one of three ways described in this chapter of how to write data out to an XML document. This is
probably the second easiest method. The other two methods are using the XML DOM (described in How-To
12.3), which is the hardest method, and using the WriteXML method off the DataSet object (described in
How-To 12.5), which is the easiest method of all three. The amount of control you have over the document
matches the degree of difficulty in use.

[ Team LiB ]

[ Team LiB ]

12.2 Use XMLReader to Read an XML Document


In How-To 12.1, I learned how to write out data to an XML document by using the XMLWriter. How do I read
data using the XMLReader?

Technique
Whereas the XMLWriter has one implementation (class), the XMLReader has three, depending on the task
you need to perform. Those classes are listed in Table 12.4.

Table 12.4. XMLWriter Implementations


Class Name

Purpose/Descriptions

XMLTextReader

Reads character streams. This is a forward-only reader that has methods


returning data on content and node types. No validation occurs.

XMLNodeReader

Provides a parser over an XML Document Object Model (DOM) API, similar to the
XMLNode tree.

XMLValidatingReader Takes an XMLTextReader adding validation. In doing so, it provides a fully


compliant validating or non-validating XML parser with DTD, XSD schema, or XMLData Reduced (XDR) schema support.
For this How-To, you will be using the XMLTextReader to read the file and display the results. To do this,
you will use the following methods:

Read. Reads a line of the character stream out of the XMLReadReader.


AttributeCount. Gives the count of attributes. This depends on the type of data that is being read. In
this case, AttributeCount is used to display the individual attributes, which will be columns.
NodeType. Allows you to specify node types in the XML document. In the case of this How-To, the
System.Xml.XmlNodeType.XmlDeclaration is checked for so that no information is printed for that type
of node. You can see the other node types by looking at intellisense for the NodeType property.
Value. Represents the actual value of the line in the stream.
Close. Closes the stream.

Steps
Open and run the Visual Basic .NETChapter 12 solution. From the main Web page, click on the hyperlink
with the caption How-To 12.2: Use XMLReader to Read an XML Document. When the page loads, click the
button labeled Read XML File. The example then reads the XML file that is specified in the text box labeled
File to Read and displays the information in the text area located at the bottom of the form (see Figure
12.2).

1.

1. Create a Web Form. Then place the Label, TextBox, and Button objects as seen in Figure 12.2 on the
form with the following properties set as in Table 12.5.

Table 12.5. Label, TextBox, and Button Control Property Settings


Object

Property

Setting

Label

Text

File to Read

TextBox

txtFileToRead
ID

Button

btnReadFile
ID

Text
TextArea

Read XML File


taOutput

ID

HyperLink

hplReturnToMain
ID

NavigateURL

wfrmMain.aspx

Note
You will find the TextArea control in the HTML Components section
of the toolbox. After you have dragged this control onto the Web
Form, right-click and choose Run as Server Control from the pop-up
menu. This will then run this control as a server side control, and
you will be able to work with its properties and methods in code
behind.

2. Add the code in Listing 12.5 to the Click event of btnReadFile. To start off, the XXLTextReader is
initialized with the XML document specified in txtFileToRead. Then each node of the document is
read using the Read method. The node type is compared to make sure the current node is not the
XMLDeclaration. If it's not, then each of the attributes is displayed; in this case, the columns of each
name are entered. After each of the nodes (rows) have been read and added to strOut, then that
string is assigned to the InnerText property of the TextArea object called taOutput. Last, the
XMLTextReader object is closed.
Listing 12.5 wfrmHowTo12_2.aspx.vb: Reading an XML Document Using XMLTextReader

Private Sub btnReadFile_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnReadFile.Click
Dim
Dim
Dim
Dim
Try

xtrNames As System.Xml.XmlTextReader
strOut As String
intAtts As Integer
intCurrAtt As Integer

strOut = "Reading file " & _


Me.txtFileToRead.Text & "..." & vbCrLf
xtrNames = New System.Xml.XmlTextReader(Me.txtFileToRead.Text)
While xtrNames.Read()
intAtts = xtrNames.AttributeCount
If xtrNames.NodeType <> System.Xml.XmlNodeType.XmlDeclaration Then
If intAtts > 0 Then
For intCurrAtt = 0 To intAtts - 1
strOut &= xtrNames(intCurrAtt) & " "
Next
Else
strOut &= xtrNames.Value
End If
End If
End While
Catch excp As Exception
strOut &= "Following Error Occurred: " & excp.Message
Finally
strOut &= vbCrLf & "Done Processing " & Me.txtFileToRead.Text & ""
taOutput.InnerText = strOut
If Not xtrNames Is Nothing Then
xtrNames.Close()
End If
End Try
End Sub

Figure 12.2. The information displayed here was read from an XML document using XMLReader.

Comments
Again, as with XMLTextReader, this falls into the middle of complexity when it comes to reading XML
documents. If you want to actually validate the data, then you should use the XMLValidatingReader
implementation of the XMLReader, rather than the XMLTextReader.

[ Team LiB ]

[ Team LiB ]

12.3 Work with the XML Document Object Model


I want to have more control over the XML document as I create it. I heard that I can do this with XML DOM.
How do I work with the XML Document Object Model?

Technique
In How-Tos 12.1 and 12.2, you saw two ways to independently read and write XML documents. However,
what if you want to do both at the same time? To have this kind of flexibility, you need to use the XML
Document Object Model, also known as DOM.
The DOM class is an XML document that is represented in memory. It allows you not only to programmatically
read and write out XML documents, but also to modify those documents in memory.
Within the DOM, the XMLNode object is the base object in the DOM Tree, XMLDocument class that extends
it. XMLDocument has methods that allow you to perform operations on the document as a whole. It also lets
the developer work with the nodes in the entire XML document.
Both XMLNode and XMLDocument have performance and usability enhancements over prior versions.
The properties and methods of XMLNode and XMLDocument that will be used for this How-To are listed in
Table 12.6 .

XMLDocument
LoadXML
Loads an XML document into the XMLDocument object. In this case, it is a means to create the stub for the
XML document that will be created from the dataset.

XMLDocument
DocumentElement
Serves as the root element for the document. It is, in fact, of the type XMLElement.

XMLDocument
CreateNode
Creates an XMLNode object.

XMLElement
AppendChild
Appends the node created to the element specified as a child.

XMLDocument
CreateComment

Adds a comment to the XML document.

XMLNode
AppendChild
Appends a node to another node as a child.

XMLDocument
Save
Saves the current XML document.

Table 12.6. DOM Properties and Methods Used in This How-To


Class Name

Property/Method

Purpose/Description

Steps
Open and run the Visual Basic .NETChapter 12 solution. From the main Web page, click on the hyperlink
with the caption How-To 12.3: Working with the XML Document Object Model. As with How-To 12.1, when
the page loads, you can enter a few names. Enter the last and first names, and then click the button labeled
Add to DataTable . When you have added a few names, click the button labeled Create XML File. Using
Explorer, open the file created in C:\ called test.xml (see Figure 12.3 ).

1. Create a Web Form. Then place the Labels , TextBoxes , Buttons , and DataGrid objects as seen in
Figure 12.3 on the form with the properties set as in Table 12.7 .

Label
Text
Last Name
TextBox

ID

txtLastName
Label
Text
First Name
TextBox

ID

txtFirstName

Button

ID

btnAdd

Text
Add to DataTable
Button

ID

btnCreateXMLFile

Text
Create XML File
DataGrid

ID

dgDataToWrite
HyperLink

ID

hplReturnToMain

NavigateURL
wfrmMain.aspx

Table 12.7. Label, TextBox, and Button Control Property Settings


Object

Property

Setting

2. Add the following line to the code module of the form. I place it under the line that reads Web Form Designer
Generated Code.

Dim mdtData As New DataTable()

3.

3. Add the code in Listing 12.6 to the Load event of the page. If the data table has not been saved to the
Session object, then it is created from scratch by first creating the data columns and then adding them to the
data table. The DataTable object is then saved to the Session object with the name MyDataTable. If the
Session object entry already exists, it is reassigned to the module variable mdtData. Last, the data table is
bound to the DataGrid object by calling the BindTheGrid routine, which is described in the next step.
Listing 12.6 wfrmHowTo12_3.aspx.vb : Creating a DataTable Object from Scratch

Private Sub Page_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
If (Session("MyDataTable") Is Nothing) Then
Dim dcFirstName As New DataColumn()
dcFirstName.ColumnName = "FirstName"
dcFirstName.Caption = "First Name"
mdtData.Columns.Add(dcFirstName)
Dim dcLastName As New DataColumn()
dcLastName.ColumnName = "LastName"
dcLastName.Caption = "Last Name"
mdtData.Columns.Add(dcLastName)
Session("MyDataTable") = mdtData
Else
mdtData = CType(Session("MyDataTable"), DataTable)
End If
BindTheGrid()
End Sub

4. Create the routine BindTheGrid, shown in Listing 12.7 , in the code module for the page.
Listing 12.7 wfrmHowTo12_3.aspx.vb : Binding the Data Table to the Data Grid

Sub BindTheGrid()
dgDataToWrite.DataSource = mdtData
dgDataToWrite.DataBind()
End Sub

5.

5. Add the code in Listing 12.8 to the Click event of the btnAdd button. This routine starts off by calling the
NewRow method off the mdtData data table, thus creating a new DataRow object. The two columns in drNew
are replaced with the values in txtLastName and txtFirstName. The new row is added to the data table, and
the text boxes are cleared. Last, mdtData is rebound to the data grid by calling BindTheGrid.
Listing 12.8 wfrmHowTo12_3.aspx.vb : Adding Data to the Data Table and Then Rebinding the Data
Grid

Private Sub btnAdd_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnAdd.Click
Dim drNew As DataRow
drNew = mdtData.NewRow()
drNew.Item("LastName") = Me.txtLastName.Text
drNew.Item("FirstName") = Me.txtFirstName.Text
mdtData.Rows.Add(drNew)
Me.txtLastName.Text = ""
Me.txtFirstName.Text = ""
BindTheGrid()
End Sub

6. Add the code in Listing 12.9 to the Click event of the btnCreateXMLFile button. After getting the number of
rows in mdtData, an XML document is started using the LoadXML method. Next, the root element is retrieved
so that nodes can then be "hung" from it, using the CreateNode and AppendChild methods. A comment is
then added using the CreateComment method of xdMyData. Then, for each of the rows in the mdtData,
nodName is created using CreateNode and AppendChild methods, and node on nodName is added for the
LastName and FirstName. Again, these nodes are added using the CreateNode and AppendChild methods.
Last, the Save method is used to save the XML document.
Listing 12.9 wfrmHowTo12_3.aspx.vb : Creating the XML Document

Private Sub btnCreateXMLFile_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnCreateXMLFile.Click
Dim xdMyData As New System.Xml.XmlDocument()
Dim xeRoot As System.Xml.XmlElement
Dim intCurrRow As Integer
Dim intNumRows As Integer
intNumRows = mdtData.Rows.Count - 1
With xdMyData

'-- Start the XML document


.LoadXml("<?xml version='1.0' ?><Book></Book>")
xeRoot = .DocumentElement
Dim nodRoot As System.Xml.XmlNode = _
.CreateNode(System.Xml.XmlNodeType.Element, "Names", "")
xeRoot.AppendChild(nodRoot)
'-- Add the comment
.CreateComment("This file represents names list created using the XML DOM")
For intCurrRow = 0 To intNumRows
'-- Start the current row
Dim nodName As System.Xml.XmlNode = _
.CreateNode(System.Xml.XmlNodeType.Element, "name", "")
nodName = nodRoot.AppendChild(nodName)
'-- Add the attributes (columns)
Dim nodFirstName As System.Xml.XmlNode = _
.CreateNode(System.Xml.XmlNodeType.Element, "FirstName", "")
nodFirstName.InnerText = mdtData.Rows(intCurrRow).Item("FirstName")
nodName.AppendChild(nodFirstName)
Dim nodLastName As System.Xml.XmlNode = _
.CreateNode(System.Xml.XmlNodeType.Element, "LastName", "")
nodLastName.InnerText = mdtData.Rows(intCurrRow).Item("LastName")
nodName.AppendChild(nodLastName)
Next
'-- Write the XML to file and close the writer
.Save("c:\Test.xml")
End With
End Sub

Figure 12.3. This XML document was created using XMLTextWriter.

Comments
As you can see, working with the DOM takes a bit more time and work. However, when you really need to
massage the data, this is the way to go!

[ Team LiB ]

[ Team LiB ]

12.4 Retrieve XML from SQL Server 2000


Sometimes I have to pull data from my SQL Server database into an XML document format. How do I do
that with SQL Server 2000?

Technique
To accomplish this task, you will create a Command object with the Transact-SQL SELECT statement that you
want to execute. However, at the end of your SQL statement, you will add the clause FOR XML mode . The
mode can be any of these that are listed:

RAW. With this mode, each of the rows that is returned in the query result is made into a generic XML
element with <row /> as the identifier tag.
AUTO. Each of the rows and the columns are identified with tags for each of the elements and
attributes, using the column names as identifier tags.
EXPLICIT. Here, you have to nest and create your query in a particular way. For more information on
this mode, check out the SQL Server Books Online.
For this example, the code will use the RAW mode and look like this:

SELECT * FROM Customers FOR XML RAW

To execute the SQL statement in this case, you use the method ExecuteXMLReader. When you use this
method, an XMLReader is returned. You should then iterate through the elements as seen in How-To 12.2.

Steps
Open and run the Visual Basic .NETChapter 12 solution. From the main Web page, click on the hyperlink
with the caption How-To 12.4: Retrieving XML from SQL Server 2000. When the page loads, you will see an
example of a T-SQL statement that retrieves data from SQL Server 2000 in an XML format. Click on the
button labeled Retrieve XML, and the data will be listed in the TextArea at the bottom of the form (see Figure
12.4 ).

1. Create a Web Form. Then place the Label, TextBox, and Button objects as seen in Figure 12.4 on the form
with the properties in Table 12.8 set.

Label
Text
SQL To Execute
TextBox

ID

txtSQLToExecute

Text
SELECT * FROM Customers FOR XML AUTO, ELEMENTS
Button

ID

btnRetrieveXML

Text
Retrieve XML
TextArea

ID

taOutput
HyperLink

ID

hplReturnToMain

NavigateURL
wfrmMain.aspx

Table 12.8. Label, TextBox, and Button Control Property Settings

Object

Property

Setting

2. Add the code in Listing 12.10 to the Click event of btnRetrieveXML. Taking the SQL statement displayed in
the "Technique " section, the Command object cmdCust is created, and ExecuteXMLReader is invoked.
The XMLReader then iterates through each of the elements in the document, and they concatenate to a
string. Last, the string is assigned to the InnerText property of taOutput, and the connection to the
XMLReader object is closed.
Listing 12.10 wfrmHowTo12_4.aspx.vb : Reading an XML Document Using XMLTextReader

Private Sub btnRetrieveXML_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnRetrieveXML.Click
Dim cnn As New SqlClient.SqlConnection(BuildCnnStr("(local)", "Northwind"))
Dim cmdCust As SqlClient.SqlCommand = New SqlClient.SqlCommand( _
Me.txtSQLToExecute.Text, cnn)
Dim xrCust As System.Xml.XmlReader
Dim intAtts As Int32
Dim intCurrAtt As Int32
Dim strOut As String
Try
cnn.Open()
xrCust = cmdCust.ExecuteXmlReader()
While xrCust.Read()
intAtts = xrCust.AttributeCount
If xrCust.NodeType <> System.Xml.XmlNodeType.XmlDeclaration Then
If intAtts > 0 Then
For intCurrAtt = 0 To intAtts - 1
strOut &= xrCust(intCurrAtt) & vbCrLf
Next
Else
strOut &= xrCust.Value & vbCrLf
End If
End If
End While
Catch excp As Exception
strOut &= "Following Error Occurred: " & excp.Message
Finally
strOut &= vbCrLf & "Done Processing "
taOutput.InnerText = strOut
If Not xrCust Is Nothing Then
xrCust.Close()
End If
cnn.Close()
End Try
End Sub

Note

You have been using the BuildCnnStr() function throughout this book. You
should add this function to a module or copy it from other chapters. Here is the
code for the function:

Function BuildCnnStr(ByVal strServer As String,


ByVal strDatabase As String) As String
Dim strTemp As String
strTemp = "Data Source=" & strServer & ";"
strTemp &= "Initial Catalog=" & strDatabase & ";"
strTemp &= "Integrated Security=SSPI"
Return strTemp
End Function

Figure 12.4. The information displayed here was read from SQL Server in an XML format.

Comments
Normally, you would be taking the XML document that the command object returned and passing that on to
another system that requires the data to be in XML format. The data was displayed from the XMLReader for
demonstration purposes.

[ Team LiB ]

[ Team LiB ]

12.5 Work with Datasets and XML


Sometimes, I have to pull XML documents into datasets and vice versa. How do I accomplish this using
.NET?

Technique
.NET has developed a number of ways to utilize datasets and XML together. The simplest use is pushing data
between the two. To do this, you have the two methods belonging to the DataSet object: ReadXML and
WriteXML. For both of these methods, you need to provide a filename to read or write the XML document to.
To demonstrate how to take advantage of these methods, I created a form that looks similar to the other
How-Tos showing how to write XML documents. However, for this example, I also added another button and
data grid that will show the data after reading from the XML document.

Steps
Open and run the Visual Basic .NETChapter 12 solution. From the main Web page, click on the hyperlink
with the caption How-To 12.5: Working with Datasets and XML. As with How-To 12.1, when the page loads,
you can enter a few names. Enter the last and first names, and then click the button labeled Add to
DataTable. When you have added a few names, click the button labeled Create XML File. Using Explorer,
open the file created in C:\ called test.xml. If you click Read XML File, you will see the same data because it
was read from text.xml (see Figure 12.5).

1. Create a Web Form. Then place the Labels, TextBoxes, Buttons, and DataGrid objects as seen in Figure
12.5 on the form with the properties set as in Table 12.9.

Table 12.9. Label, TextBox, and Button Control Property Settings

Object

Property

Setting

Label

Text

Last Name

TextBox

txtLastName
ID

Label

Text

TextBox

First Name
txtFirstName

ID

Button

btnAdd
ID

Text
Button

Add to DataTable
btnCreateXMLFile

ID

Text
DataGrid

Create XML File


dgDataToWrite

ID

Button

btnReadFile
ID

DataGrid

dgResultsFromXML
ID

HyperLink

hplReturnToMain
ID

NavigateURL

wfrmMain.aspx

2. Add the following line to the code module of the form. Then place it under the line that reads Web
Form Designer Generated Code.

Dim mdtData As New DataTable()


Dim mdsData As New DataSet()

3. Add the code in Listing 12.11 to the Load event of the page. If the data table has not been saved to
the Session object, then it is created from scratch by first creating the data columns and then adding
them to the data table. The DataTable object is then saved to the Session object with the name
MyDataTable. A DataSet object is also created because some of the XML methods must be used from
the DataSet object, rather than at the DataTable level. If the Session objects entry already exists, it
is assigned back to the module variable mdtData and mdsData. Last, the data table is bound to the
DataGrid object by calling the BindTheGrid routine, which is described in the next step.
Listing 12.11 wfrmHowTo12_5.aspx.vb: Creating a DataTable Object from Scratch

Private Sub Page_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
If (Session("MyDataTable") Is Nothing) Then
Dim dcFirstName As New DataColumn()
dcFirstName.ColumnName = "FirstName"
dcFirstName.Caption = "First Name"

mdtData.Columns.Add(dcFirstName)
Dim dcLastName As New DataColumn()
dcLastName.ColumnName = "LastName"
dcLastName.Caption = "Last Name"
mdtData.Columns.Add(dcLastName)
mdsData.Tables.Add(mdtData)
Session("MyDataTable") = mdtData
Session("MyDataSet") = mdsData
Else
mdtData = CType(Session("MyDataTable"), DataTable)
End If
BindTheGrid()
End Sub

4. Create the routine BindTheGrid, shown in Listing 12.12, in the code module for the page.
Listing 12.12 wfrmHowTo12_5.aspx.vb: Binding the Data Table to the Data Grid

Sub BindTheGrid()
dgDataToWrite.DataSource = mdtData
dgDataToWrite.DataBind()
End Sub

5. Add the code in Listing 12.13 to the Click event of the btnAdd button. This routine starts off by calling
the NewRow method off the mdtData data table, thus creating a new DataRow object. The two
columns in drNew are replaced with the values in txtLastName and txtFirstName. The new row is added
to the data table, and the text boxes are cleared. Last, mdtData is rebound to the data grid by calling
BindTheGrid.
Listing 12.13 wfrmHowTo12_5.aspx.vb: Adding Data to the Data Table and Then Rebinding
the Data Grid

Private Sub btnAdd_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnAdd.Click
Dim drNew As DataRow
drNew = mdtData.NewRow()

drNew.Item("LastName") = Me.txtLastName.Text
drNew.Item("FirstName") = Me.txtFirstName.Text
mdtData.Rows.Add(drNew)
Me.txtLastName.Text = ""
Me.txtFirstName.Text = ""
BindTheGrid()
End Sub

6. Add the code in Listing 12.14 to the event of the btnCreateXMLFile button. After loading the dataset
from the Session object, the WriteXML method is invoked to save the data into an XML document.
Listing 12.14 wfrmHowTo12_5.aspx.vb: Creating the XML Document from the Dataset

Private Sub btnCreateXMLFile_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnCreateXMLFile.Click
mdsData = CType(Session("MyDataset"), DataSet)
mdsData.WriteXml("c:\Test.xml")
End Sub

7. Add the code in Listing 12.14 to the Click event of the btnReadFile button. Here, the code reads the
XML document by using the ReadXML method off the dsXMLData DataSet object and then binds it to
a DataGrid object.
Listing 12.15 wfrmHowTo12_5.aspx.vb: Reading the XML Document Back into the Dataset

Private Sub btnReadFile_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnReadFile.Click
Dim dsXMLData As DataSet = New DataSet()
dsXMLData.ReadXml("c:\Test.xml")
Me.dgResultsFromXML.DataSource = dsXMLData
Me.dgResultsFromXML.DataBind()
End Sub

Figure 12.5. This XML document was created using XMLTextWriter.

Comments
As you can see, for both reading and writing XML document from and to datasets, Microsoft has given us
some easy commands to accomplish the task. However, remember that you do have the control over the
format of the XML document that you have using the other methods, such as using the DOM.

[ Team LiB ]

[ Team LiB ]

Chapter 13. Creating XML Web Services


In this chapter you will

Overview of the XML Web Services infrastructure


Get started with XML Web Services
Create a simple XML Web Service using parameters
Consume XML Web Services
Pass a dataset back from an XML Web Service
Methods and other program logic available to applications both on the Web or the desktop are callled XML
Web Servers or simply Web Servers.
To use the service, or communicate, the consumer will use XML Messaging and HTTP. The same is true when
the Web Service is communicating back to the consumer.
One of the great things about using XML Web Services is that as long as the consumer can create and
consume messages defined for the Web Service, it doesn't matter what the consumer is written in, or even
what the platform is. The term used for this is loosely coupled, or in other words, nonproprietary (see Figure
13.1.)
Figure 13.1. Notice that no specific languages or platforms are named in this graphic, except to
point out ASP.NET, of course.

You also can use XML Web Services either internally or externally to an organization, as long as you can get
to the network or Internet.

[ Team LiB ]

[ Team LiB ]

Overview of the XML Web Services Infrastructure


The infrastructure of Web Services has four main areas:

XML Web Services Directories. Central location to locate XML Web Services that outside organizations
create. The UDDI registry is an example of one of these directories. Your Web Service client might not
even need to use these if you know the address of the Web Service you are accessing.
XML Web Service Discovery. Discovering documents that describe a particular XML Web Service using
the Web Services Description Language (WSDL). The DISCO specification defines an algorithm for
locating service descriptions. Again, if you know the location of the service description, you can avoid
this process.
XML Web Service Description. Defines what types of methods the XML Web Service uses. Tells clients
how to interact with an XML Web Service so that they know how to use it.
XML Web Service Wire Formats. To be able to communicate with all platforms and languages, XML Web
Services use open wire formats. These are protocols that any system that is capable of supporting the
most common Web standards can understand. SOAP is the main protocol used.
Figure 13.2. Don't panicthese steps are performed for you in some cases after you set up the
Web reference.

You can find all of the examples in this chapter in the Solution called Visual Basic .NETChapter 13 on the
Web site.

Note

You will find the Web Service solution in a separate location called
SecurityWebServices on the Web site. The Chapter 13 solution will
contain the sample forms that are created to call methods from the
Web Service.

[ Team LiB ]

[ Team LiB ]

13.1 Get Started with XML Web Services


I have heard some great things about XML Web Services, but I don't know how to get started. What do I do
to get started working with XML Web Services?

Technique
The best way to get started with XML Web Services is to simply create your own XML Web Service and start
playing with it. Visual Studio .NET makes it extremely easy to do just that. To achieve this task, you will be
creating your first Web Service, which, of course, will be Hello World. Now, before you start groaning, this
example will show you the basics of creating a Web Service without a lot of other fluff that gets in the way
and confuses things.

Creating a Web Service


The way you start to create a Web Service is to choose ASP.NET Web Service for a new project. When you
have done this, VS will create the Web Service project, and you will be brought to what looks like a blank
Web Form, as displayed in Figure 13.3.
Figure 13.3. Here is a newly created Web Service project.

Note

Remember: Web Services can be created in various ways. Because


you are creating this one using Visual Studio .NET, you will be using
ASP.NET to create it.

For the most part, you will be using Web Services to provide central processes or functionality that you want
to be consistent regardless of where you consume it from. This means that you will not be creating a user
interface that users will see; you are basically creating a class-like interface that will provide methods (hence
the use of Service1.asmx instead of *.aspx). You will use *.asmx as an entry point for your Web Service.
To really get going with working with your first Web Service, you will click on the View Code icon in the
Solution Explorer. When you do so, you will see the first method to create. That's rightit is Hello World. The
first thing you should do now is uncomment the lines of code that read as follows:

'<WebMethod()> Public Function HelloWorld() As String


'
HelloWorld = "Hello World"
'End Function

The screen should look like Figure 13.4.


Figure 13.4. Your first Web Service method.

That's it! You have created your first Web Service method. Now it's time to test it.

Testing the Web Service


After you have created your Web Service, click the Start toolbar button. Visual Studio will then compile and
build your Web Service. After which, Visual Studio will create a test page for your Web Service, as shown in
Figure 13.5.
Figure 13.5. This page that Visual Studio created gives you instructions on how to specify a
namespace for your Web Service within the calling application.

Note
As mentioned on this Web page, when you are developing your Web
Service, Visual Basic uses a temporary namespace called
http://tempuri.org/.
When you are going to make the Web service public, you will want to
create different namespace for the Web Service. I would recommend
just using the examples they give on your test Web page.

One of the items you see on this test Web page is the list of operations, otherwise known as methods you
created. If you click on the Hello World method, you will see another page that you will use to invoke the
actual method (see Figure 13.6.)
Figure 13.6. This page allows you to test your Hello World method.

You also will see example code for calling the method using SOAP, HTTP GET, and HTTP POST. However, you
will just be using this test page to check out the Hello World example, and you will be learning how to call
methods from Visual Basic behind Web Forms in How-To 3.3.
If you click on the Invoke button, you will see the following:

<?xml version="1.0" encoding="utf-8" ?>


<string xmlns="http://tempuri.org/">Hello

World</string>

This is the value that the method you created returns. That's it! That's all there is to the test page.

Note
Before telling yourself that this test page doesn't do much, remember
that if you did have a problem with the code in your Web Service, the
problem would have shown up here. This quick testing saves a lot of
hassle of writing code that actually calls the methods and then making
sure the calling code wasn't causing errors.
Using the test page allows you to debug your Web Service before you
integrate it. Okay, I am now off my SOAPbox. Pun intended.

Looking at the Description of the Web Service

Visual Studio .NET saves you from a lot of work. If you click the Back button and go to the main test page,
you will see a link to Description in the top sentence. If you click on this link, you will see the SOAP code
shown in Listing 13.1.
Listing 13.1 http://localhost/WebService1/Service1.asmx?WSDL : Soap Definition for Your First
Web Service

<?xml version="1.0" encoding="utf-8" ?>


- <definitions xmlns:http=
"http://schemas.xmlsoap.org/wsdl/http/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:s0="http://tempuri.org/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
targetNamespace="http://tempuri.org/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
- <types>
- <s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
- <s:element name="HelloWorld">
<s:complexType />
</s:element>
- <s:element name="HelloWorldResponse">
- <s:complexType>
- <s:sequence>
<s:element minOccurs="0" maxOccurs="1"
name="HelloWorldResult" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="string" nillable="true" type="s:string" />
</s:schema>
</types>
- <message name="HelloWorldSoapIn">
<part name="parameters" element="s0:HelloWorld" />
</message>
- <message name="HelloWorldSoapOut">
<part name="parameters" element="s0:HelloWorldResponse" />
</message>
<message name="HelloWorldHttpGetIn" />
- <message name="HelloWorldHttpGetOut">
<part name="Body" element="s0:string" />
</message>
<message name="HelloWorldHttpPostIn" />
- <message name="HelloWorldHttpPostOut">
<part name="Body" element="s0:string" />
</message>
- <portType name="Service1Soap">
- <operation name="HelloWorld">
<input message="s0:HelloWorldSoapIn" />
<output message="s0:HelloWorldSoapOut" />
</operation>
</portType>

- <portType name="Service1HttpGet">
- <operation name="HelloWorld">
<input message="s0:HelloWorldHttpGetIn" />
<output message="s0:HelloWorldHttpGetOut" />
</operation>
</portType>
- <portType name="Service1HttpPost">
- <operation name="HelloWorld">
<input message="s0:HelloWorldHttpPostIn" />
<output message="s0:HelloWorldHttpPostOut" />
</operation>
</portType>
- <binding name="Service1Soap" type="s0:Service1Soap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document" />
- <operation name="HelloWorld">
<soap:operation soapAction="http://tempuri.org/HelloWorld" style="document" />
- <input>
<soap:body use="literal" />
</input>
- <output>
<soap:body use="literal" />
</output>
</operation>
</binding>
- <binding name="Service1HttpGet" type="s0:Service1HttpGet">
<http:binding verb="GET" />
- <operation name="HelloWorld">
<http:operation location="/HelloWorld" />
- <input>
<http:urlEncoded />
</input>
- <output>
<mime:mimeXml part="Body" />
</output>
</operation>
</binding>
- <binding name="Service1HttpPost" type="s0:Service1HttpPost">
<http:binding verb="POST" />
- <operation name="HelloWorld">
<http:operation location="/HelloWorld" />
- <input>
<mime:content type="application/x-www-form-urlencoded" />
</input>
- <output>
<mime:mimeXml part="Body" />
</output>
</operation>
</binding>
- <service name="Service1">
- <port name="Service1Soap" binding="s0:Service1Soap">
<soap:address location="http://localhost/WebService1/Service1.asmx" />
</port>
- <port name="Service1HttpGet" binding="s0:Service1HttpGet">
<http:address location="http://localhost/WebService1/Service1.asmx" />
</port>

- <port name="Service1HttpPost" binding="s0:Service1HttpPost">


<http:address location="http://localhost/WebService1/Service1.asmx" />
</port>
</service>
</definitions>

The really nice thing to remember is that Visual Studio .NET generates all this code for you, so you don't
have to.

Steps
For this How-To, you will create the example Web Service that was discussed in the "Technique" section.

1. Open up Visual Studio .NET to the Start Page, with no projects or solutions opened.
2. From the File menu, choose New, Project. You will see the New Project dialog box. Highlight the
ASP.NET Web Service template (see Figure 13.7.) Then click OK. You will then be given a blank *.asmx
file.
Figure 13.7. You will be using the ASP.NET Web Service template for this project.

3. Click on the in the Solution Explorer. You will see the commented out method called Hello World.
Uncomment the lines of code that read as follows:

'<WebMethod()> Public Function HelloWorld() As String


'
HelloWorld = "Hello World"
'End Function

4.

4. Click the Start button. You are shown the test page as described in the "Technique" ssection.

Comments
Although this is definitely the simplest example to be given while creating a Web Service, if you look at the
description of even this simple Web Service and you see all the SOAP that is generated, you can appreciate
all the work Microsoft has done to make the generation of Web Services using Visual Studio as painless as
possible.

[ Team LiB ]

[ Team LiB ]

13.2 Create a Simple XML Web Service Using Parameters


I have seen how to create a Web Service using the sample that Microsoft provides. This was instructive but
not very useful. How do I create a Web Service that uses parameters?

Technique
For this How-To, you are going to create the start of a security Web Service. This security Web Service is
going to take in two parameters: Login Name and Password. It will then check against a table that you will
create of names, passwords, and security levels.
The method you will create first will then pass back True or False if the name and password are found.
Looking at the Security Table
The security table is included in the Web Service's Web folder. It is called WebServiceSecurity.MDB and
is, in fact, a jet database. You can see the table created, tblUsers , in Figure 13.8 .
Figure 13.8. Using the tblUsers, you can look up usernames and passwords

The method created for this first real example will take in the username and password and then look up the
username. If the username is found, the method will then compare the password. If the password matches,
then True will be returned from the method. Otherwise, False will be returned.
Passing Parameters

You will pass parameters to Web Service methods just as you would to any other methods or functions, as
shown in the function header for the Web Service method created in this How-To:

Public Function TestUser(ByVal strUserID As String, ByVal strPassword As String) As


Boolean

The return value is also assigned to the name of the function.


Specifying Descriptions for the Web Service and Methods
You can help developers who use your Web Service by adding descriptions to the Web Service and each of
the methods you create. This cuts down on the number of support requests you get, and is a good habit to
get into. For the Web Service, you will place it in the WebService header, where you will want to specify your
own namespace as well:

<WebService(Namespace:="http://appsplus.com/webservices/",
_
Description:="Testing of security routines.")> _
Public Class SecurityServices_

This causes the description specified to be displayed as the first line on the main test page.
For the method, you will include the description in the Web Method header:

<WebMethod(Description:="This method takes a user ID and Password " & _


"and returns True or False based on if the User and Password exist.")>
Public Function TestUserPassword(ByVal strUserID As String, _
ByVal strPassWord As String) As Boolean

You can see what the lines of code look like in the designer in Figure 13.9 .
Figure 13.9. Adding descriptions to the Web Service.

Now it's time to put it all together.

Steps
Open and run the SecurityWebService solution. From the main test page, click on the link for the method
TestUserPassword. You are presented with a page to input the user ID and password (see Figure 13.10 .) If
you type FSBarker for the strUserID and test for the strPassword, the value True is returned; otherwise,
False is returned.

1. Create an ASP.NET Web service project, calling it SecurityWebService.


2. Highlight the default .asmx file created in the Solution Explorer, renaming it to SecurityServices.asmx.
3. Click on the View Code button in the Solution Explorer. Change the WebService at the top of the code
to read as follows:

<WebService(Namespace:="http://appsplus.com/webservices/", _
Description:="Testing of security routines.")> _
Public Class SecurityServices

4. Add the code in Listing 13.2 to the code of the Web Service. (Double-click on the Web Service to bring
up the code.) You could replace the commented out lines that display for the Hello World Web method.
This code starts off by specifying the description for the Web Method and then declaring the function
header for the method called TestUserPassword. The parameters strUserID and strPassword are
passed, and a Boolean type value is returned. The rest of this routine should look somewhat familiar
because a DataAdapter object is created, and a DataTable object is filled, based on the username that
was passed in.

If a record is not found for the user, then False is passed back.
If a record is found and the password matches, then True is passed back. If the password for the user
does not match, then False is passed back.
Listing 13.2 SecurityServices.asmx.vb : Web Method to Validate Username and Password

<WebMethod(Description:="This method takes a user ID and Password " & _


"and returns True or False based on if the User and Password exist.")>
Public Function TestUserPassword(ByVal strUserID As String, _
ByVal strPassword As String) As Boolean
Dim ocnn As New _
OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=E:\InetPub\wwwroot\SecurityWebService\" & _
"WebServiceSecurity.MDB")
Dim odaUsers As New _
OleDb.OleDbDataAdapter("Select * from tblUsers " & _
"Where UserID = '" &strUserID & "'", ocnn)
Dim dtUsers As New DataTable()
odaUsers.Fill(dtUsers)
If dtUsers.Rows.Count = 0 Then
TestUserPassword = False
ElseIf dtUsers.Rows(0).Item("Password") = strPassword Then
TestUserPassword = True
Else
TestUserPassword = False
End If
End Function

Note

If you have a problem with sharing rights on the database, you might want to
include "Mode=Share Deny None;" in your connection string.

5. Press the Start button on the toolbar to test the service.


Figure 13.10. Testing a simple security Web Service.

Comments
When you are working in code for your Web Service, you can perform the majority of tasks, including
ADO.NET, that you can in ASP.NET, except for those depending on a user interface (UI).
You have now seen how to specify parameters. Now check out the next How-To to see how to consume, or
use, your Web Service in an application.

[ Team LiB ]

[ Team LiB ]

13.3 Consume XML Web Services


I have created this Web Service that takes the username and password and returns True or False based
on whether the username and password check out. I have even tested the Web Service to make sure it
works correctly. How do I use, or consume, this Web Service in another application?

Technique
To use a Web Service, you need to create a reference to it.

Setting Up the Web Reference to the Web Service


To set up a Web reference, you choose Add Web Reference from the Project menu. You are then presented
with the Add Web Reference dialog box, which allows you to browse for Web Services using the Universal
Description Discovery Integration (UDDI) directories, as described in the beginning of the chapter. However,
you will be able to browse to the Web Service you created and supply the name of the full URL using
localhost to the name of the .asmx file you created, in this case:

http://localhost/securitywebservice/SecurityServices.asmx
After you have specified this, you will see the methods appear for your Web Service (see Figure 13.11.)
Figure 13.11. You can test the Web Service right in this dialog box.

Note
You can see two methods for this Web Service: TestUserPassword and
GetUserInfo. The second method is discussed in How-To 13.4.

After you have clicked Add Reference to accept the new reference, you can use the methods in your
application. You can double-check that the reference is there by looking for it under the Web Reference node
that appears in the Solution Explorer (see Figure 13.12.)
Figure 13.12. You can see the reference created in the Solution Explorer.

Calling Web Service Methods


After you have created the reference to the Web Service, you will create a reference within your application,
much like you would to other object models. Following is a snippet of code that performs this very task:

Dim wsSec As localhost.SecurityServices


wsSec = New localhost.SecurityServices()

If wsSec.TestUserPassword(Me.txtUser.Text, Me.txtPassword.Text) Then

As you can see, the method TestUserPassword is called just as another other method or function is called.

Steps
To preview this How-To, open the solution called Visual Basic .NETChapter 13, located in the chapter
folder.

Note
You will probably have to re-establish the Web reference for the Web
Service that is used for this example. Locate where you have installed
SecurityWebServices and set the reference.

When you run the project, the first form that comes up is the main switchboard with each of the How-Tos
listed for this chapter. Click on How-To 13.3. The form for How-To 13.3 opens.
If you type FSBarker for the User and Test for the Password, you get a message box telling you that you
can continue into the application (see Figure 13.13).

1. Create a new Visual Studio .NET project using the Windows Application project template. Create a
Windows Form, and place the controls shown in Table 13.1 in the order displayed in Figure 13.13.

Table 13.1. Label, TextBox, and Command Button Control Property Settings
Object

Property

Setting

Label

Text

User

TextBox

Name

txtUser

Label

Text

Password

TextBox

Name

txtPassword

Button

Name

btnLogin

2. As described in the "Technique" section, set a reference to the Web Service you created in the previous
How-To. Remember to point to the *.asmx file created in the Web Service. Choose Add Web Reference
from the Project menu.
3. Add the code listed in Listing 13.3 to the Click event of btnLogin by double-clicking on the button. This
routine instantiates an instance of the Web Service. Then, using the wsSec object, the routine calls the
TestUserPassword method. This method is passed the username and password that were entered.

3.

Listing 13.3 frmHowTo13_3.vb: Web Method to Validate Username and Password

Private Sub btnLogin_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnLogin.Click
Dim wsSec As localhost.SecurityServices
wsSec = New localhost.SecurityServices()
If wsSec.TestUserPassword(Me.txtUser.Text, Me.txtPassword.Text) Then
MessageBox.Show("You may go into the applcation.")
Else
MessageBox.Show("You may not go into the application.")
End If
End Sub

Figure 13.13. This login form takes advantage of a Web Service for authentication.

Comment
Note that passing parameters and using the return value is just like using methods from other objects or
even functions from your own applications.

[ Team LiB ]

[ Team LiB ]

13.4 Pass a Dataset Back from an XML Web Service


I want to be able to get more data than just a single value. How do I retrieve a whole dataset from a Web
Service?

Technique
When you need to pass back a record or multiple records from a Web Service, you have to pass it back as a
DataSet object, rather than a DataTable object.
For this How-To, you will pass back the record of the user who is requested. Before returning the record,
however, the code removes the Password column. You don't particularly want that information going out
over the Net (see Listing 13.4 ).
Listing 13.4 SecurityServices.asmx.vb : Passing Back a DataSet Object

<WebMethod()> Public Function GetUserInfo(ByVal strUserID As String) As DataSet


Dim ocnn As New OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" &
_
"Data
Source=E:\InetPub\wwwroot\SecurityWebService\WebServiceSecurity.MDB")
Dim odaUsers As New _
OleDb.OleDbDataAdapter("Select * from tblUsers Where UserID = '" &
strUserID & "'", ocnn)
Dim dsUsers As New DataSet()
odaUsers.Fill(dsUsers)
'-- remove the password column
dsUsers.Tables(0).Columns.Remove("Password")
Return dsUsers
End Function

Note

Although you could limit the SELECT string to only return the necessary columns,
and not have to delete the Password column, there are two reasons for coding it
the way it was done. 1. It shows how to delete columns from a data table. 2. If
the goal is to include all the columns in the table and accept the Password
column, then when other columns are added, you will not have to touch the code
because the * is being used.

Tip

When referring to tables on the receiving end, as displayed in step 2 of this HowTo, you have to refer to tables in the dataset by their ordinal values. Therefore,
some information in the description of the Web Service method about the
dataset might be warranted.

Steps
To preview this How-To, open the solution called Visual Basic .NETChapter 13 , found in this chapter's
folder. On the main form, click on the button labeled How-To 13.4. The form for How-To 13.4 then opens. If
you type FSBarker for the User and Test for the Password, you can see the user's information listed in the
DataGrid object (see Figure 13.14 ). Otherwise, a message box appears saying that you cannot see the
data.

1. Open the SecurityWebServices Web Service project you created in How-To 13.2. Add the code from
Listing 13.4 in the "Technique " section to create the GetUserInfo method. Test the new Web
method. When you do so, you will see some XML, as shown here in Listing 13.5 .
Listing 13.5 Dataset Sent Back as XML

<?xml version="1.0" encoding="utf-8" ?>


- <DataSet xmlns="http://appsplus.com/webservices/">
- <xs:schema id="NewDataSet" xmlns=""
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xs:element name="NewDataSet" msdata:IsDataSet="true">
- <xs:complexType>
- <xs:choice maxOccurs="unbounded">
- <xs:element name="Table">
- <xs:complexType>
- <xs:sequence>
<xs:element name="UserID" type="xs:string" minOccurs="0" />
<xs:element name="UserName" type="xs:string" minOccurs="0" />
<xs:element name="SecurityTier" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>

- <diffgr:diffgram
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
- <NewDataSet xmlns="">
- <Table diffgr:id="Table1" msdata:rowOrder="0">
<UserID>FSBarker</UserID>
<UserName>F. Scott Barker</UserName>
<SecurityTier>3</SecurityTier>
</Table>
</NewDataSet>
</diffgr:diffgram>
</DataSet>

2. Open the Windows Project where you created the Login windows Form. Create a new Windows Form
and place the controls shown in Table 13.2 in the order displayed in Figure 13.14 .

Label
Text
User
TextBox
Name
txtUser
Label
Text
Password
TextBox
Name
txtPassword
DataGrid
Name
dgUserInfo
Button
Name
btnLogin

Table 13.2. Label, TextBox, DataGrid, and Command Button Control Property

Settings
Object

Property

Setting

3. Add the code in Listing 13.6 to the Click event of btnLogin. This code once again checks the username
and password by calling the TestUserMethod of the Web Service. If the username and password check
out, then the GetUserInfo method is called, passing the username once again. The first table from
the returned dataset is assigned to the DataSource property of dgUsers.
Listing 13.6 frmHowTo13_4.vb : Retrieving a Dataset from a Web Service

Private Sub btnLogin_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles btnLogin.Click
Dim wsSec As localhost.SecurityServices
wsSec = New localhost.SecurityServices()
If wsSec.TestUserPassword(Me.txtUser.Text, Me.txtPassword.Text) Then
dgUserInfo.DataSource = wsSec.GetUserInfo(Me.txtUser.Text).Tables(0)
Else
MessageBox.Show("No information may be presented.")
End If
End Sub

Figure 13.14. The information in the data grid was retrieved from a Web Service.

Comments
You can use Web Services in literally thousands of ways. This chapter just covers a couple, but it should be
enough to start you down the path of using them productively.

[ Team LiB ]

[ Team LiB ]

Appendix A. Desktop Development With ADO


You will learn about the following in this appendix:

When to use ADO (local database/single-tier applications)


Looking at the ADO Object Models
Using the Connection object
Working with the ADO Recordset object
Executing a SQL Server stored procedure by using ActiveX Data Objects
Executing batch updates with ADO and SQL Server
Creating SQL Server objects with ActiveX Data Objects
Perform a transaction using ActiveX Data Objects
Although it's true that in the .NET environment, you will want to use ADO.NET for the majority of your
development, developers have invested a lot of time and money into ActiveX Data Objects (ADO), and you
might want to use it for desktop applications.

[ Team LiB ]

[ Team LiB ]

When to Use ADO (Local Database/Single Tier Applications)


ADO was Microsoft's last data-access method, and it is now the standard for most Office, Web, and Visual
Basic environments prior to .NET. The following products ship ADO in the box:

Microsoft Office 200x


Visual Studio 6.0 (including all the languages in the box)
SQL Server 7.0, 2000
Data Access Objects (DAO), the standard prior to even ADO, was used throughout Office and Visual Basic,
but it wasn't designed to be used with Web environments such as Visual InterDev and other data servers.
Currently, no plan is underway to update DAO beyond DAO 3.6, which includes support for Unicode and
some bug fixes, but doesn't have any other new features included beyond 3.5.

[ Team LiB ]

[ Team LiB ]

Looking At the ADO Object Models


That's right, object models, plural. Unlike DAO, which consists of one object model, ADO has several
separate object models.

Note
With ADO.NET, you use Namespaces and classes rather than
references and object models. Although this takes some getting used
to, it isn't too bad after a while.

ADO's object models work together to give you the objects and collections that are necessary to work with
your data. A couple of the object models consist of the following:

ActiveX Data Objects 2.7 (ADODB) allows you to create and work with recordsets, as well as perform
error handling.
ADO Extensions 2.7 for DDL and Security (ADOX) is the data definition language, allowing you to work
with and modify the database schema. Security objects are also included in this object model.
Although these are separate object models and will be explained as such, you will also use them cohesively.
For instance, to modify the table's structure, you need to get to the Tables collection located off the
Catalog object in the ADOX library; however, this Catalog will have its ActiveConnection property set
to the Connection object (ADODB). Take a look at what makes up the individual object models.

The ActiveX Data Objects 2.7 (ADODB) Object Model


The ActiveX Data Objects object model (see Figure A.1) consists of the following:

Connection object. Equivalent to the Database object in DAO, this is where most of your work with
ADO begins. All the objects and collections that are mentioned after this come from the Connection
object.
Errors collection/ Error object. Identical to the DAO errors collection and error object, this allows
developers to manage error handling.

Command object. This allows you to run a query against a database and return records in a Recordset
object, to manipulate a database's structure, and to execute a bulk operation. A collection of
parameters is used with the Command object.

Recordset object. Similar to the DAO Recordset object, you can open the Recordset objects as
read-only or dynamic. Each Recordset object also has a Fields collection.
Stream object. This object allows you to read in special tree-structured hierarchies, such as e-mail
messages or file systems. You can even point the object to an URL, provided the system has set it up
in a consistent manner. This is another way that ADO allows developers to read outside data, such as
from other applications or over the Internet. You could not do this easily with DAO.
Figure A.1. The object model for ActiveX Data Objects 2.7.

Most of the work is done in the ADODB module when you use ADO. Whenever you use recordsets, this is also
the object model to use.

[ Team LiB ]

[ Team LiB ]

Referencing the Type Libraries


Before seeing how to use these collections and objects, you need to know how to reference the object model
libraries for use. The object models are found in type libraries in the file system. A type library contains the
information about the object models such as the collections, properties, and methods. Referencing the type
libraries isn't difficult, but it is still very important.
Because Visual Basic .NET uses ADO.NET by default, you will have to reference all the object models you are
going to need. To reference the object models, choose Add Reference from Visual Studio .NET's Project
menu. After you're in the Add Reference dialog box, click on the COM tab. You then have to find the type
libraries for the object models and select them. The type library name that you will be using is as follows:
Microsoft ActiveX Data Objects 2.7 Library. This is also referred to as the ADODB object model.
You can see this library, as well as other Microsoft ActiveX Data Objects Libraries, in Figure A.2.
Figure A.2. Registering the COM type libraries.

Tip

You can use both ADO libraries and ADO.NET classes in the same
application without worrying about the order of libraries in the
references. This is great when you're converting applications from
ADO to ADO.NET. To use all libraries and not worry about the order of
reference, you must prefix your objects with the library with which
they come. Here are some examples, with the specific library types:

Dim odaTest as OLEDB.OLEDBDataAdapter


Dim rstTestADO as ADODB.Recordset
Dim catTest as ADOX.Catalog
Although not all objects are shared between the libraries, it's a good
idea to specify the library while you're getting used to the objects.

After the library has been selected, you can see the ADODB reference entry in the Solution Explorer under
references.

[ Team LiB ]

[ Team LiB ]

Using the Connection Object


As with ADO.NET, you will be using the Connection object to set up a connection to the database of your
choice.
As you get started looking at the different examples, take a look at the main form that will be used to launch
each of them, shown in Figure A.3 .
Figure A.3. This form, called frmMain , is the Main form that is used for the examples in this
Appendix.

The button that is used for this example is called btnOpenConn, and the following is the code used for the
Click event, shown here in Listing A.1 .
Listing A.1 frmMain.vb : Code for Calling the Routine to Open and Display the ADO Connection

Private Sub btnOpenConn_Click(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles btnOpenConn.Click
OpenAndDisplayADOConnection(Me.txtCurrentResults)
End Sub

Each of the buttons calls examples, passing the text box called txtCurrentResults . This text box is
located at the bottom of the form. For clarity, the examples have been grouped in modules by section. In
this case, the first example routine, called OpenAndDisplayADOConnection , can be found in
basConnectionExamples.vb . The code for this routine is shown in Listing A.2 .
Listing A.2 basConnectionExamples.vb : Code for Opening and Displaying the ADO Connection

Sub OpenAndDisplayADOConnection(ByVal txtResults As TextBox)

Dim cnnNet As New ADODB.Connection()


Dim strConnect As String = _
"Provider=SQLOLEDB.1;Integrated Security=SSPI;" &
"Persist Security Info=False;Initial Catalog=Northwind;Data Source=(local)"
cnnNet.Open(strConnect)
txtResults.Text = "New connection string : " & _
vbCrLf & vbCrLf & cnnNet.ConnectionString
cnnNet.Close()
End Sub

As you can see from this example, using the ADO Connection object is virtually the same as ADO.NET. You
can see this example being executed in Figure A.4 .
Figure A.4. The text displayed here is the connection string that was set.

Listing A.3 provides two other examples of using the connection object, also found in
basConnectionExamples.vb. The first is code to open a connection to a Jet database by using a workgroup
file, username, and password. The second is to open a Jet version of the Northwind database.
Listing A.3 basConnectionExamples.vb : Two Examples of Opening Jet Databases

Sub DisplayProviderAndSecuredAccessDB(ByVal txtResults As TextBox)


Dim cnnNet As New ADODB.Connection()
cnnNet.Provider = "Microsoft.Jet.OLEDB.4.0"
cnnNet.Properties("Jet OLEDB:System database").Value = _
"c:\Books\VBNETHowTo\Examples\AppA\MySystem.mdw"
cnnNet.Open("Data Source=c:\Books\VBNETHowTo\Examples\AppA\MyMDB.mdb;" &
"User Id=Admin;Password=MyPW")
txtResults.Text = cnnNet.ConnectionString
End Sub
Sub OpenNorthwindADOConnection(ByRef cnnCurr As ADODB.Connection)
Dim strConnect As String = "Provider=SQLOLEDB.1;Integrated Security=SSPI;"
& "Persist Security Info=False;Initial Catalog=Northwind;Data
Source=(local)"
Try
cnnCurr.Open(strConnect)
Catch expADO As Exception
MessageBox.Show("The following error occurred: " & expADO.Message)
End Try
End Sub

In the last routine, you can also see how to use the TryCatchEnd Try block to trap any exceptions that
might occur. Next you will see how to use the Connection object with the Recordset object in VB .NET.

[ Team LiB ]

[ Team LiB ]

Working with the ADO Recordset Object


The central object in the ADODB object model is the Recordset object. You will be using this object when
you want to manipulate records one at a time or bring back a set of records with which to work. With ADO,
when you declare the type of variable you are going to use, you can instantiate it at the same time with the
New keyword.

Displaying Records by Using GetString


For debugging purposes, you can also dump the whole recordset into a string variable for display. Listing A.4
shows you how to do this.
Listing A.4 basRecordsetExamples.vb: Opening and Retrieving a Recordset for Display

Sub OpenRecordsetWithGetStringDisplayExample(ByVal txtResults As TextBox)


Dim cnn As New ADODB.Connection()
Dim rstCurr As New ADODB.Recordset()
OpenNorthwindADOConnection(cnn)
rstCurr.Open("Select * from Orders where OrderDate = '06/12/97'", cnn,
ADODB.CursorTypeEnum.adOpenForwardOnly)
txtResults.Text = rstCurr.GetString
rstCurr.Close()
End Sub

Using the OpenNorthwindADOConnection subroutine listing in Listing A.3, a connection object is


referenced, and that is passed to the Open method of a Recordset object.

Tip
Notice that the cursor type returned will be

ADODB.CursorTypeEnum.adOpenForwardOnly. Because all that is


happening is the data being dumped to a string, using this cursor type
makes sense. This will give you the best performance. Some other
choices include adOpenDynamic, adOpenKeyset , adOpenStatic ,
and adOpenUnspecified .

You can run this example by clicking on the button labeled Open a Recordset with GetString Display on the
main form. Figure A.5 shows what you will see.
Figure A.5. The GetString method is handy for checking out data.

Editing and Updating Records


You can use several methods to perform editing and updating of records in code.
Unlike the last section in which you could open the recordset as a Forward Only type cursor, you will want to
open it as a Dynamic type cursor. You can see this in Listing A.5.
Listing A.5 basRecordsetExamples.vb: Opening and Retrieving a Recordset for Display

Sub OpenRecordsetWithEditingExample(ByVal txtResults As TextBox)


Dim
Dim
Dim
Dim

cnn As New
rstCurr As
fldCurr As
strTemp As

ADODB.Connection()
New ADODB.Recordset()
ADODB.Field
String

OpenNorthwindADOConnection(cnn)

rstCurr.Open("Select * from Orders where OrderDate = '06/12/97'", cnn,


ADODB.CursorTypeEnum.adOpenDynamic, _
ADODB.LockTypeEnum.adLockOptimistic)

With rstCurr
Do Until .EOF
strTemp = strTemp & " Old Field Value: " & _
.Fields("ShippedDate").Value
'-- Updating the release date
.Fields("ShippedDate").Value = DateAdd(DateInterval.Day, 5, _
.Fields("ShippedDate").Value)
.Update()
strTemp = strTemp & " New Field Value: " & _
.Fields("ShippedDate").Value & vbCrLf
.MoveNext()
Loop
End With
txtResults.Text = strTemp
rstCurr.Close()
End Sub

You can see that the Update method is used after assigning the value that you want to the field you specify.
You could have specified other fields to be updated as well.

Note
Notice that when updating individual fields in the recordset, the Value
property is specified to be updated. You didn't have to do this in VB
6.0 or VBA, but .NET doesn't allow for default properties, which the
Value property is.

You don't have to explicitly use an Edit method; in fact, you won't find one like you could in previous
editions. To add a new record, you must use the AddNew method before updating field values. To delete a
record, you use the Delete method.
One last thing to discuss about recordsets is how to persist, or save a recordset to disk.

Persisting a Recordset

This example will open up two recordsets: one from the Orders table, and one from a file created from the
Orders table called OrdersForDate.rst.
The code, shown in Listing A.6, opens the Orders table for a specific date and uses the GetString method
to stash the contents to the results text box. The routine then saves that recordset using the Save method,
and passes the adPersistADTG format enumerator. You could save the recordset as XML by using
adPersistXML . The code opens the file into a recordset and prints it by saving it to the results TextBox
control.
Listing A.6 basRecordsetExamples.vb: Persisting a Recordset to Disk

Sub PersistingARecordset(ByVal txtResults As TextBox)


Dim cnn As New ADODB.Connection()
Dim rstOrig As New ADODB.Recordset()
Dim rstPersist As New ADODB.Recordset()
Const adCmdFile As Long = 256
OpenNorthwindADOConnection(cnn)
'-- Open forward only and readonly since we are just saving it to disk.
rstOrig.Open("Select * from Orders where OrderDate = '06/12/97'", cnn,
ADODB.CursorTypeEnum.adOpenStatic)
txtResults.Text = "Original Records : " & vbCrLf & rstOrig.GetString
'-- Delete any existing old copies.
' If you don't, you will get an error.
On Error Resume Next
Kill("OrdersForDate.rst")
'-- Create the persistent recordset in the applications directory
On Error GoTo 0
rstOrig.Save("OrdersForDate.rst", ADODB.PersistFormatEnum.adPersistADTG)
cnn = Nothing
rstOrig = Nothing
' Open the persisted recordset
rstPersist.Open("OrdersForDate.rst", Options:=adCmdFile)
txtResults.Text = txtResults.Text & vbCrLf & vbCrLf & _
"Persisted File : " & vbCrLf & rstPersist.GetString
rstPersist.Close()
End Sub

That's all there is to it. You will find that you can do most other things with recordsets using VB .NET that
you have been able to do in other languages. Now take a look at another common task that you must do if
you are using ADO with Visual Basic .NET: calling stored procedures.

[ Team LiB ]

[ Team LiB ]

Executing a SQL Server Stored Procedure By Using ActiveX Data Objects


If you are doing an ADO development with client server for backends, then you probably call stored
procedures. In doing so, you will use the ADO Command object, as well as the Parameter object if you are
passing parameters.
You will create a Command object and supply the command text, which in this case will be the name of the
stored procedure, called CustOrdersHist. You can see the T-SQL for CustOrderHist in Listing A.7. This
stored procedure returns product names and the total quantity purchased of those products for a given
customer.
Listing A.7 Northwind SQL Server Database: T-SQL for the Stored Procedure Called

CustOrdersHist

ALTER PROCEDURE CustOrderHist @CustomerID nchar(5)


AS
SELECT ProductName, Total=SUM(Quantity)
FROM Products P, [Order Details] OD, Orders O, Customers C
WHERE C.CustomerID = @CustomerID
AND C.CustomerID = O.CustomerID AND O.OrderID = OD.OrderID AND _
OD.ProductID = P.ProductID
GROUP BY ProductName

You will then specify the type of Command object you are creatingin this case by using the type of
ADODB.CommandTypeEnum.adCmdStoredProc .
The next step is to create a parameter that the Command object will use. This parameter will match the one
specified in CustOrdersHist, called CustomerID. You can see the actual code for this routine, called
UseAStoredProcedureWithAParameter, in Listing A.8.
Listing A.8 basCommandExamples.vb: Calling a Stored Procedure By Using Parameters

Sub UseAStoredProcedureWithAParameter(ByVal txtResults As TextBox)


Dim
Dim
Dim
Dim

cnn As New ADODB.Connection()


rstCurr As New ADODB.Recordset()
cmd As New ADODB.Command()
prm As ADODB.Parameter

Try
cmd.CommandText = "CustOrderHist"
cmd.CommandType = ADODB.CommandTypeEnum.adCmdStoredProc
prm = cmd.CreateParameter("CustomerID", ADODB.DataTypeEnum.adChar,
ADODB.ParameterDirectionEnum.adParamInput, 5)
cmd.Parameters.Append(prm)
prm.Value = "CHOPS"
OpenNorthwindADOConnection(cnn)

cmd.ActiveConnection = cnn
rstCurr.Open(cmd)
txtResults.Text = rstCurr.GetString
Catch excp As Exception
MessageBox.Show(excp.Message)
End Try
End Sub

The last thing that this routine does is open a recordset based on the Command object. This is to the use just
those records that are needed. In this case, the GetString method is used to assign it to the results text
box. If you are using a bulk query, shown in the next section, you would use the Execute method. To see
the routine in A.8 executed, click on the button with the caption Stored Procedure with Parameter, located
on the frmMain form for this Appendix project.

[ Team LiB ]

[ Team LiB ]

Executing Batch Updates with ADO and SQL Server


The biggest difference between this section and the previous one is the fact that in the last section, a
recordset being populated using a Command object. With the Command object, no data was affected. That is
one type of use for a Command object. What if you want to perform bulk tasks against a recordset? This is
the major use for using the Command object because you don't necessarily need it for populating a recordset.
The Parameter object is also not used. It was not necessary for the example, although you would want to
use it if criteria were being implemented.
To learn how to perform a bulk operation using ADO in Visual Basic .NET, see this simple Update SQL
statement, shown here:

Update Orders Set ShippedDate = DeliveryDate+1

This statement adds a day to the date in the ShippedDate column for all the records in the Orders table.
This statement is being assigned to the CommandText property of the Command object instead of to the
name of a stored procedure. Another important task is setting the CommandType property to be
ADODB.CommandTypeEnum.adCmdText. This tells ADO that you are performing a bulk operation. Last, the
Execute method is called from the Command object. This routine, called ExecuteABatchCommand, can be
seen in Listing A.9.
Listing A.9 basCommandExamples.vb: Creating and Executing a Bulk Query

Sub ExecuteABatchCommand(ByVal txtResults As TextBox)


Dim
Dim
Dim
Dim
Dim

cnn As
cmd As
prm As
rstOld
rstNew

New ADODB.Connection()
New ADODB.Command()
ADODB.Parameter
As New ADODB.Recordset()
As New ADODB.Recordset()

'-- In .NET, we can assign values as we declare variables


Dim strSQL As String = "Update Orders Set ShippedDate = ShippedDate+1"
Dim strDispSQL As String = _
"Select OrderID, ShippedDate From Orders Where OrderID < 10251"
'-- Open the connection
OpenNorthwindADOConnection(cnn)
'-- Open the Orders table and display the ShippedDate as they were.
rstOld.Open(strDispSQL, cnn)
txtResults.Text = "Old Values: " & vbCrLf & vbCrLf & rstOld.GetString
'-- Set up the Command object to use the SQL string.
cmd.ActiveConnection = cnn
cmd.CommandText = strSQL
cmd.CommandType = ADODB.CommandTypeEnum.adCmdText

'-- Execute the command


cmd.Execute()
'-- Reopen the Orders table and redisplay the ShippedDate Field
rstNew.Open(strDispSQL, cnn)
txtResults.Text = txtResults.Text & vbCrLf & vbCrLf & "New Values: " &
vbCrLf & vbCrLf & rstNew.GetString
End Sub

For this example recordset, objects were used merely to display the before and after data, as seen in Figure
A.6.
Figure A.6. Although they're not pretty, you can see the values of the OrderID and DeliveryDate
before and after the routine has been executed.

You can also use the Insert and Delete statements to perform other bulk operations with the Command
object. One of the last tasks that is useful to perform using ADO with SQL Server is to create objects such as
tables on-the-fly.

[ Team LiB ]

[ Team LiB ]

Creating SQL Server Objects with ActiveX Data Objects


It is worthwhile to mention an example of creating a new table object with fields and primary key specified,
even though the majority of the work of creating objects using ADO and SQL Server is done in the T-SQL
Statement that you create. You will execute this statement using the Command object, as shown in Listing
A.10.
Listing A.10 basStoredProcedureExamples.vb: Creating SQL Server Objects with ADO and T-SQL

Sub CreatingASQLServerObjectFromADO(ByVal txtResults As TextBox)


Dim
Dim
Dim
Dim
Dim

cnn As
cmd As
prm As
rstOld
rstNew

New ADODB.Connection()
New ADODB.Command()
ADODB.Parameter
As New ADODB.Recordset()
As New ADODB.Recordset()

'-- In .NET, we can assign values as we declare variables.


' This is where the hard work is.
Dim strSQL As String = "CREATE TABLE Test (PrimaryIntFld Integer
IDENTITY(1,1) PRIMARY KEY, Field2 Text)"
'-- Open the connection
OpenNorthwindADOConnection(cnn)
'-- Set up the Command object to use the SQL string.
cmd.ActiveConnection = cnn
cmd.CommandText = strSQL
cmd.CommandType = ADODB.CommandTypeEnum.adCmdText
'-- Execute the command
cmd.Execute()
End Sub

This routine is a lot like the previous example except for the SQL statement and the fact that results aren't
displayed. You can see the results by going to the Visual Studio .NET Server Explorer, zeroing in on the
tables for the Northwind database, and opening the new Test table in Design mode, as shown in Figure A.7.
Figure A.7. Creating objects such as this table is just a matter of learning the correct SQL syntax.

Note
If you already had the tables displayed for Northwind, you might need
to right-click on the Tables node and choose Refresh.

[ Team LiB ]

[ Team LiB ]

Conclusion
Well, there you have it. As you can see, you can use ADO in Visual Basic .NET under the covers in basically
the same way that you did in prior versions of VB.
Just as a parting word of advice for this Appendix: Convert as much of your code to take advantage of ADO
.NET as possible. However, if you have several developers and need to crank out a simple application with
Visual Basic .NET in a hurry, ADO is still available.

[ Team LiB ]

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]

[ Team LiB ]

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
"" (double quotation marks)
# (number sign)
% (percent sign)

& (ampersand)
variables, declaring
> (right arrow button)
< (left arrow button)
*.asmx file extension
+ (plus sign)

2nd

2nd

- (minus sign)

.NET
namespaces
XML (Extensible Markup Language)

2nd

System.XML namespace
System.XML.Schema namespace
System.XML.Xpath namespace
System.XML.XSL namespace

XML
(Extensible Markup Language)

2nd

.NET Framework Developer's Guide

.NET namespaces
bound list boxes, creating

2nd

3rd

4th

.vb file extension


.xsd file extension
? (question mark)

@ (at symbol)
parameters

2nd

[ ] (square brackets)
^ (caret)
_ (underscore)

1stBackupDevices list box


populating (code)

2nd

3rd

4th

1stCustomers
Click event, code

1stDatabases list box


populating (code)

2nd

3rd

1stLookUpTables
Items collection
data, adding

1stLookupTables
pointing to items (code)

2nd

1stUnSelected list box


reloading
code

1stUnselected list box


reloading
code
3621 error

[ Team LiB ]

4th

5th

5th

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
abstract classes
AcceptChanges method
access (Public) modifiers

2nd
2nd

access levels
deny (object permissions)
grant (object permissions)
revoke (object permissions)

accessing
command buttons on forms
CommandText property

2nd

2nd

libraries

accounts
database users, creating

2nd

3rd

4th

5th

6th

Windows NT/2000
disabled

2nd

Action menu commands


New Group
Properties
Action property

actions
execution on forms

2nd

Actions menu commands


New Login
ActivateEditing routine
creating (code)

2nd

ActiveEditing
frmHowTo1_4.vb
SaveRecord routing, calling (code)
text boxes, disabling (code)

2nd

2nd

ActiveEditing subroutine
calling (code)

2nd

ActiveX Data Objects 2.7 (ADODB)


ActiveX Data Objects 2.7 object model (ADODB)

2nd

3rd

[See ADO]2nd [See ADO]


adapters. [See also data adapters]2nd [See also data adapters]
Add button
ActiveX Data Objects.

clicking, effects
Add Class command (Add menu)
Add Members dialog box

2nd

Add menu commands


Add Class
Add New Item

2nd

Add method
Add New Item command (Add menu)
Add New User dialog box

2nd

2nd

3rd

Add Reference
command (Project menu)
dialog box
Add Reference command (Project menu)
Add Role Members dialog box

2nd

Add Table dialog box


Add to DataTable button

2nd

3rd

Add Web Reference command (Project menu)

adding
buttons

to DataGrid control

2nd

3rd

4th

2nd

3rd

4th

data
in DataGrid control

5th

6th

7th

8th

9th

10th

8th

9th

10th

11th

12th

13th

14th

15th

16th

26th
to Items collection
data to lookup tables

2nd

lookup table information


methods to interfaces

2nd

2nd

properties to interfaces
records

2nd

3rd

3rd

4th

2nd

4th

5th

6th

7th

8th

9th

10th

buttons
canceling in DataGrid object (code)
to data tables (code)

2nd

3rd

2nd

4th

5th

rows
in DataSet objects
AddNew method

2nd

3rd

4th

5th

6th

7th

2nd

AddObjectForTransfer method

Administrative Tools
groups and users, adding
ADO

2nd

(ActiveX Data Objects)

2nd

ADODB (ActiveX Data Objects 2.7 object model)


and ADO.NET, comparing

2nd

batch updates, executing


Connection object

2nd

2nd

3rd

2nd

3rd

3rd
3rd

4th

4th

5th

opening and displaying (code)

2nd

5th

connections
local databases

2nd

Microsoft Office 200x


object models

2nd

OLE DB data provider


choosing

2nd

Recordset object
records, displaying
records, editing

2nd

2nd

records, updating

2nd

recordsets, persisting
single tier applications

3rd

3rd

4th

3rd
2nd

4th
3rd

4th

2nd

SQL Server 2000


SQL Server 7.0
SQL Server objects, creating

2nd

3rd

4th

SQL Server stored procedures, executing


type libraries, referencing

2nd

2nd

3rd

4th

3rd

Visual Studio 6.0

ADO 2.x
cursors
ADO Extensions 2.7 for DDL and Security (ADOX)
ADO.NET

2nd

and ADO (ActiveX Data Objects), comparing

2nd

3rd

batch updates
on-the-fly, creating

2nd

on-the-fly, executing
objects

2nd

3rd

4th

3rd

2nd
5th

4th

3rd

5th

4th

5th

6th
6th

6th

7th

8th

9th

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

stored procedures
parameterized, executing

8th

Windows forms objects, using in Web forms

ADO.NET code
writing

2nd

3rd

4th

5th

SQL (auto-generated)
strongly typed datasets

6th

2nd
2nd

7th

3rd
3rd

12th

13th

14th

15th

16th

17th

18th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

XSDs
ADO.NET.

2nd

3rd

[See also DataSet object]

ADODB
(ActiveX Data Objects 2.7)
ADODB (ActiveX Data Objects 2.7 object model)

2nd

3rd

ADODB.CursorTypeEnum.adOpenForwardOnly cursor type

ADOX
(ADO Extensions 2.7 for DDL and Security)
Advanced Options button
Alfred's Fried Foods
Alias property
ALL clause
Allow Nulls
Allow Nulls property
ALTER TABLE statement

2nd

ampersand ( & )
variables, declaring
Anchor property

APIs
DMF (Distributed Management Framework)
references, setting

2nd

2nd

3rd

3rd

SQL-DMO
objects

2nd

AppendChild (XMLElement class)


AppendChild (XMLNode class)

applets
Windows NT/2000 Control Panel
groups and users, adding

Application object
methods
properties
Application Role button
application roles (SQL Server security)

2nd

3rd

4th

5th

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

ApplicationException

applications
assemblies

desktop (VB .NET)


generic search forms, creating
25th

26th

27th

exceptions

single tier
ADO (ActiveX Data Objects)

2nd

states

Web (ASP.NET)
generic search forms, creating
arrow buttons (right and left)

ASP.NET
Web applications
generic search forms, creating

Web Forms
developing
Windows forms objects, using in Web forms
ASP.NET Web Service templates

2nd

3rd

assemblies

assigning
reports
with ReportDocument

2nd

UDFs (user-defined functions)


code

2nd

variables

24th

syntax

assigning values
text boxes
code

2nd

at symbol (@)
parameters

2nd

Attach Database button


AttachDBWithSingleFile method

2nd

attaching
database file, code

2nd

files
to databases

2nd

routines
to SelectedIndexChanged event

SQL Server databases


code

2nd

AttributeCount method

attributes
AutoEventWireUp

2nd

authentication
login forms
XML Web Services
mixed-mode

2nd

3rd

4th

5th

authentication mode
SQL Server
Enterprise Manager
Windows NT/2000

2nd

3rd

4th

5th

6th

AUTO mode

auto-generated SQL
VB .NET tools, writing ADO.NET code
AutoEventWireUp attribute
AutoIncrement property
AutoPostBack property

[ Team LiB ]

2nd

2nd

3rd

7th

8th

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
backing up
databases
with SQL-DMO
SQL databases
SQL Server databases

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

BACKUP DATABASE statement

backup devices
1stBackupDevices list box
populating (code)

2nd

3rd

4th

names, retrieving (code)


BACKUP LOG statement
Backup object

2nd

methods
properties
Backup/Restore Wizard

BackupDevice object
methods
properties

BackupDevices object
methods
properties

backups
performing (code)

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

basCommandExamples.vb
bulk queries, creating and executing (code)
stored procedures, calling (code)

2nd

2nd

3rd

basConnectionExamples.vb
ADO connections
opening and displaying (code)

JET databases
opening (code)

2nd

basRecordsetExamples.vb
recordsets
opening and retrieving (code)
persisting to disks (code)

2nd

3rd

4th

2nd

basStoredProcedureesExamples.vb
SQL Server objects, creating (code)

batch updates
executing

2nd

3rd

4th

5th

on-the-fly, creating in ADO.NET

2nd

on-the-fly, executing in ADO.NET

3rd

2nd

4th

3rd

5th

4th

6th

5th

6th

BEGIN statement
BeginEdit method
BeginLoadData method

behavior
of Crystal Report Viewer, controlling
behaviorial control of classes

2nd

BETWEEN operator
syntax

2nd

BETWEEN statement
data, generating (code)
bigint data type (SQL Server)
binary data type (SQL Server)
BinderContext class

binding

2nd

3rd

2nd
4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

17th

18th

19th

20th

columns to controls
properties

2nd

data
to ComboBox control
to DataGrid control

2nd
2nd

3rd

4th

3rd

5th

4th

6th

5th

7th

6th

8th

7th

9th

8th

9th

data grids to DataView control (code)

data tables
code

2nd

3rd

4th

5th

6th

7th

8th

DataSet control
with ListBox control properties

2nd

filling and binding products to DataGrid object (code)


text boxes

2nd

3rd

to datasets

4th

5th

6th

7th

8th

9th

10th

2nd

to list boxes

2nd

BindingContext object

2nd

BindingManagerBase object
BindingManagerBase objects
BindTheGrid routine

2nd

bit data type (SQL Server)

blocks
If[ellipsis dots] Then
code

2nd

Try[ellipsis dots]Catch[ellipsis dots]End


Catch statements

Try[ellipsis dots]Catch[ellipsis dots]End Try


controls, ignoring
exceptions, trapping
Try[ellipsis dots]Catch[ellipsis dots]Finally
exceptions

2nd

blue wavy lines (code)


BorderColor property

borders
Red/Blue style

2nd

BottomLineStyle property

bound contols
on Web Forms at runtime

2nd

bound controls
at runtime

2nd

data
2nd

3rd

4th

5th

6th

7th

8th

error handling

editing and updating


2nd

3rd

4th

5th

6th

7th

8th

9th

10th

9th

on Web Forms

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

10th

11th

11th

AutoPostBack property
DataBind method
IsPostBack property

2nd

Web server and HTML controls, comparing

2nd

records
adding and deleting

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

bound list boxes, creating

2nd

3rd

4th

5th

6th

7th

8th

9th

Windows forms
bound list boxes, displaying data
data bound forms

2nd

data, binding to controls

3rd

4th

2nd

data, editing and updating

2nd
5th

3rd

4th

6th

4th

5th

7th

5th

6th

8th

6th

10th

11th

8th

6th

7th

8th

9th

10th

DataGrid control, drilling down to data

2nd

3rd

4th

5th

6th

7th

8th

9th

2nd
2nd

3rd

4th

text boxes, binding and viewing

bound list boxes

5th

2nd

6th

3rd

2nd

7th

4th
3rd

8th

5th
4th

9th

6th
5th

14th

9th

5th

records, adding and deleting

13th

9th

7th

4th

error handling

12th

7th

3rd

developing

2nd

3rd

10th

7th
6th

8th
7th

9th
8th

10th
9th

10th

10th

11th

12th

creating

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

data
displaying

2nd

3rd

4th

5th

6th

7th

boxes
bound list
creating

2nd

3rd

data, displaying

4th

2nd

5th

3rd

6th

4th

7th

8th

5th

6th

7th

2nd

3rd

4th

9th

10th

11th

12th

7th

8th

13th

14th

list
text boxes, binding and viewing

5th

6th

message
displaying

text
binding and viewing

2nd

3rd

4th

boxes.
[See also list boxes]

text boxes

brackets
square ([ ])

break points
setting in stored procedures

2nd

Web pages

browsers
Object Browser

btnAccept button
click event
code

2nd

btnAdd
Click event, code

2nd

3rd

2nd

3rd

btnAdd button
click event
code
Click event, code

btnAttach button
click event
code

btnBackup button
click event
code

btnCancel
Click event, code

2nd

btnCancel button
click event
code

2nd

btnClose button
click event
code

2nd

3rd

btnConnect button
click event
code

btnConnection button
disabling (code)

2nd

enabling (code)

2nd

btnCreateTable click event


PerformTask( ) function, calling (code)

btnCreateUDF click event


code

btnCreateXMLFile button
Click event, code

2nd

btnDelete
Click event, code
btnDelete button

2nd

3rd

5th

6th

7th

8th

9th

10th

9th

10th

enabled properties, toggling (code)

2nd

3rd

4th

5th

4th

5th

btnDeleteTable click event


PerformTask( ) function, calling (code)

2nd

btnDetach button
click event
code
toggling (code)

btnEdit
Click event, code

btnEdit button
enabled properties, toggling (code)

2nd

3rd

btnEdit command button


ActiveEditing subroutine, calling (code)

2nd

btnExecute
click event (code)
Click event, code

btnExport button
click event
code

btnLoadList
Click event, code
btnLoadList button

btnLocateFile button
click event
code

btnLogin button
Click event, code

2nd

btnModifyTable click event


PerformTask( ) function, calling (code)
btnNew button

btnOpenConn button
click event
code

btnPrint button
click event
code

btnReadFile button
Click event, code

2nd

btnRestore button
click event
code

btnRetrieve
code

btnRetrieveXML button
Click event, code

btnSave
Click event, code

btnSave click event


trapping save exceptions (code)

btnSearch button
click event
code

2nd

btnSelect button
click event
code

2nd

btnTestValidator
Click event, code

btnTransfer button
click event
code

2nd

2nd

btnUnSelect button
click event
code

2nd

btnUpdate button
click event
code

btnUseUDF click event


code

btnVerify button
click event
code

btnView
Click event, code

2nd

btnView button
click event
code

2nd

Build button
BuildCnnStr function

2nd

BuildCnnStr routine
BuildCnnStr( ) function

2nd

[See also Query Builder]


building
SQL statements
builders.

with Query Builder

SQL strings
code

built-in functions
T-SQL

2nd

built-in stored procedures


sp-tables
testing

2nd

bulk queries
creating and executing
code

2nd

bulkadmin (fixed server role)

business rules
data constraints

2nd

Button control
properties

2nd

3rd

4th

5th

6th

5th

6th

button controls
click events, adding (code)

Button object
properties

2nd

3rd

4th

28th

buttons
Add
clicking, effects
Add to DataTable

2nd

3rd

adding
to DataGrid control

2nd

3rd

Advanced Options
Application Role
arrow (right and left
Attach Database

btnAccept
click event (code)

2nd

btnAdd
click event (code)
Click event, code

btnAttach

2nd

3rd

4th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

27th

click event (code)

btnBackup
click event (code)

btnCancel
click event (code)

2nd

btnClose
click event (code)

2nd

3rd

btnConnect
click event (code)

btnConnection
disabling (code)

2nd

enabling (code)

2nd

btnCreateXMLFile
Click event, code

2nd

3rd

btnDelete
enabled properties, toggling (code)

2nd

3rd

4th

5th

2nd

3rd

4th

5th

btnDetach
click event (code)
toggling (code)

btnEdit
enabled properties, toggling (code)

btnEdit command
ActiveEditing subroutine, calling (code)

btnExport
click event (code)
btnLoadList

btnLocateFile
click event (code)

btnLogin
Click event, code

2nd

btnNew

btnOpenConn
click event (code)

btnPrint
click event (code)

btnReadFile
Click event, code

2nd

btnRestore
click event (code)

btnRetrieve
code

btnRetrieveXML
Click event, code

btnSearch
click event (code)

2nd

btnSelect
click event (code)

2nd

btnTransfer
click event (code)

btnUnSelect
click event (code)

2nd

btnUpdate
click event (code)

btnVerify
click event (code)

btnView
click event (code)
Build

Cancel
clicking, effects

2nd

2nd

command
accessing on forms

2nd

data (to edit, save, or cancel)

2nd

Command
properties

command
properties

Command
properties
properties, settings

2nd

3rd

4th

Command Button control


properties

2nd

3rd

property settings

4th

2nd

5th

3rd

4th

6th
5th

7th
6th

8th
7th

Command Button object


properties

2nd

3rd

4th

property

Connect
enabling
Create XML File

2nd

3rd

DataGrid control
events

2nd

Delete
Detach
Edit
Ellipsis
events on data grids
Execute

2nd

2nd

Export
Invoke

ItemCommand event
Repeater control events, programming

Label, TextBox, and Command


control property settings

2nd

Label, TextBox, ListBox, and Command


property settings

2nd

Letter Button controls


click events (code)

2nd

Letter Button result sets, filling (code)


Load List

2nd

3rd

Locate File
New Connection
Open a Recordset
Perform Backup
Permissions

2nd

Print

properties
setting
property settings
PushButton

2nd

2nd

Radio Button object


properties
rbDistinct Radio Button control
Read XML File

records
adding
deleting
RegionID, displaying
Run Query

Save
clicking, effects

2nd

3rd

4th

Search
SQL Server Authentication
Stored Procedure with Parameter
Use Distinct
Use UDF
View Code

2nd

[ Team LiB ]

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
calculated fields, adding to reports

2nd

3rd

4th

5th

calling
ActiveEditing subroutine
code

2nd

GenerateData routine
code

2nd

3rd

LoadProducts routine
code

PerformTask( ) function
code

2nd

3rd

RefreshIndividual routine
code

2nd

3rd

routines
code

2nd

3rd

4th

5th

6th

7th

8th

9th

SaveRecord routine
code

2nd

search forms

2nd

stored procedures
code

2nd

3rd

UDFs
(user-defined functions)
XML Web Services methods

2nd

3rd

4th

5th

2nd

calling pages, returning to (code)

2nd

calls
RefreshIndividual subroutine
adding (code)

2nd

Cancel button
clicking, effects

Cancel command
code

canceling
edits
code

2nd

canceling changes to data, code

2nd

canceling data
command buttons

2nd

canceling editing/adding
records
in DataGrid object (code)

cancelling
data grid edits (code)
caret (^)
Cascade Delete Related Records
cascade deletes

Catch statements
in Try[ellipsis dots]Catch[ellipsis dots]End Try block

catching
exceptions (code)

2nd

3rd

categories
DropDown control
loading (code)
loading;code

2nd

3rd

list boxes
repopulating (code)

loading

2nd

4th

6th

7th

8th

9th

10th

11th

12th

into list boxes (code)

2nd

3rd

4th

5th

CCustomer class
code

2nd

constructors (code)

2nd

mFax variable
event handlers (code)
property declarations (code)

CCustomerID class
constructors
code

2nd

CCustomerID.vb
constructors (object-based), code
constructors, code

2nd

3rd

CustomerID property, code

changes
updating to servers (code)

2nd

3rd

4th

chapters in book, source code (Web site)


char data type (SQL Server)

characters
Unicode

Chart tab
Standard Report Wizard

check boxes
Create Columns Automatically at Run Time, checking

Run Time
Create Columns Automatically, unchecking
Unassigned Products Only

2nd

Unassigned Products Only check


Users Must Enter a User Name and Password to Use This Computer

2nd

CheckBox object
properties

2nd

property

CheckChanged event
code

2nd

child records
cascade deletes
Choose a Query Type page (DataAdapter Configuration Wizard)
Choose Your Data Connection page (DataAdapter Configuration Wizard)
Choose Your Data Connection pge (DataAdapter Configuration Wizard)

class declarations
MaximumStringLengthExceededException
code

classes
abstract
ADO.NET code, writing

2nd

SQL (auto-generated)
strongly typed datasets
XSDs

2nd

3rd

2nd

4th

5th

6th

7th

8th

9th

6th

7th

8th

9th

10th

2nd

2nd

3rd

constructors (code)

2nd

4th

5th

CCustomer
2nd

mFax variable event handlers (code)


property declarations (code)

CCustomerID
constructors (code)

2nd

3rd

CNumberString
2nd

12th

13th

3rd

BinderContext

code

11th

3rd

behavioral control

code

10th

3rd

3rd

4th

international phone number extensions (code)

11th

12th

13th

14th

14th

15th

16th

17th

18th

code

2nd

3rd

creating to implement interfaces

2nd

3rd

4th

5th

6th

7th

creation control

5th

6th

7th

8th

9th

10th

2nd

3rd

4th

8th

9th

11th

10th

11th

12th

13th

14th

12th

13th

8th

9th

10th

11th

12th

11th

12th

13th

14th

15th

14th

Customer
code

data
errors, communicating to developers
validating

2nd

3rd

4th

validation code, writing


26th

5th

2nd

2nd

6th

3rd

3rd

7th

4th

4th

8th

5th

5th

9th

6th

6th

10th

7th

8th

7th

11th

12th

9th

10th

27th

data providers

2nd

default properties
defining

2nd

3rd

4th

5th

in interfaces
definition
diagrams

2nd

inheritance permission keywords

2nd

instances
definition

interfaces
implementing

2nd

3rd

member override keywords

4th

5th

6th

2nd

members
definition

methods
adding to interfaces

2nd

3rd

MustInherit keyword

objects
definition

OleDb
performance improvement

2nd

parameterized properties
pointers
properties

2nd

3rd

4th

adding to interfaces
read-only properties

2nd

SQLClient
performance improvement

2nd

variables
declarations (code)
write-only properties

2nd

XMLDocument
CreateComment
CreateNode
DocumentElement
LoadXML
methods

2nd

properties

2nd

Save

XMLElement
AppendChild

XMLNode
AppendChild
methods

2nd

properties

2nd

XMLNodeReader
XMLReader

2nd

XMLTextReader
XMLValidatingReader
classes.

[See also data providers]

7th

8th

9th

10th

11th

12th

13th

14th

15th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

clauses
ALL
DISTINCT
records, displaying

2nd

Clear method
code

2nd

click events
adding to button controls (code)

btnAccept button
code

2nd

btnAdd button
code

btnAttach button
code

btnBackup button
code

btnCancel button
code

2nd

btnClose button
code

2nd

3rd

btnConnect button
code

btnCreateTable click
PerformTask( ) function, calling (code)

2nd

btnCreateUDF
code

btnDeleteTable click
PerformTask( ) function, calling (code)

2nd

btnDetach button
code

btnExecute
code

btnExport button
code

btnLocateFile button
code

btnModifyTable click
PerformTask( ) function, calling (code)

btnOpenConn button
code

btnPrint button
code

btnRestore button
code

btnSave
trapping save exceptions (code)

btnSearch button
code

2nd

btnSelect button
code

2nd

btnTransfer button
code

btnUnSelect button
code

2nd

btnUpdate button
code

btnUseUDF
code

btnVerify button
code

2nd

2nd

btnView button
code

2nd

Click events
for 1stCustomers (code)
for btnAdd (code)

2nd

3rd

for btnAdd button (code)


for btnCancel (code)

2nd

3rd

2nd

for btnCreateXMLFile button (code)


for btnDelete (code)

2nd

3rd

2nd

for btnEdit (code)


for btnExecute (code)
for btnLoadList (code)
for btnLogin button (code)

2nd

for btnReadFile button (code)

2nd

for btnRetrieveXML button (code)


for btnSave (code)
for btnTestValidator (code)
for btnView (code)

2nd

click events
Letter Button controls
code

2nd

clicking
Add button, effects
Cancel button, effects
Delete key, effects
Save button, effects
client-side cursors

clients
state management

2nd

XML Web Services Description


Close (XMLTextWriter)
Close method

closing
forms
code

2nd

forms (code)

3rd

2nd

CNumberString class
code

2nd

3rd

international phone number extensions (code)

code
1stBackupDevices list box, populating

2nd

3rd

4th

5th

6th

7th

8th

1stCustomers Click event


1stDatabases list box, populating

2nd

3rd

3rd

4th

4th

5th

6th

1stLookupTables
pointing to items

2nd

1stUnSelected list box


reloading

1stUnselected list box


reloading

ActivateEditing routine
creating

2nd

3rd

ADO connections
opening and displaying
ADO.NET, writing

2nd

SQL (auto-generated)
strongly typed datasets
XSDs

2nd

2nd

3rd

4th

2nd
2nd

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

3rd
3rd

3rd

backup device names, retieving


backups, performing

2nd

3rd

4th

5th

basCommandExamples.vb

6th

7th

8th

9th

10th

11th

12th

13th

14th

16th

17th

18th

bulk queries, creating and executing


stored procedures, calling

2nd

2nd

3rd

basConnectionExamples.vb
ADO connections, opening and displaying
JET databases, opening

2nd

basRecordsetExamples.vb
recordsets, opening and retrieving
recordsets, persisting to disks

2nd

3rd

4th

2nd

basStoredProcedureesExamples.vb
SQL Server objects, creating
BEGIN statement
btnAccept button click event

2nd

btnAdd button click event


btnAdd button Click event
btnAdd Click event

2nd

2nd

3rd

3rd

btnAttach button click event


btnBackup button click event
btnCancel button click event
btnCancel Click event

2nd

2nd

btnClose button click event

2nd

3rd

btnConnect button click event

btnConnection button
disabling

2nd

enabling

2nd

btnCreateUDF click event


btnCreateXMLFile button Click event
btnDelete Click event

2nd

btnDetach button click event


btnDetach button, toggling

2nd

btnEdit Click event


btnExecute Click event
btnExecute click event
btnExport button click event
btnLoadList Click event
btnLocateFile button click event
btnLogin button Click event

2nd

btnOpenConn button click event


btnPrint button click event
btnReadFile button Click event

2nd

btnRestore button click event


btnRetrieve
btnRetrieveXML button Click event
btnSave Click event
btnSearch button click event
btnSelect button click event

2nd
2nd

btnTestValidator Click event


btnTransfer button click event
btnUnSelect button click event

2nd

btnUpdate button click event


btnUseUDF click event
btnVerify button click event
btnView button click event
btnView Click event

2nd

2nd

bulk queries
creating and executing

2nd

button controls, adding click events


calling pages, returning to
Cancel command
CCustomer class

2nd

constructors

2nd

3rd

2nd

2nd

3rd

property declarations

CCustomerID.vb
constructors

2nd

3rd

constructors (object-based)
CustomerID property

changes
updating servers

2nd

CheckChanged event

2nd

class variable declarations


classes

2nd

3rd

4th

2nd

3rd

creating to implement interfaces

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

default properties
defining

4th

5th

interfaces, implementing

2nd

3rd

2nd

methods, adding to interfaces

3rd

4th

2nd

3rd

5th

6th

parameterized properties
properties

2nd

3rd

4th

properties, adding to interfaces


read-only properties

2nd

write-only properties

2nd

Clear method

2nd

3rd

CNumberString class

2nd

3rd

4th

columns
listing in databases
columns, sorting

2nd

3rd

4th

2nd

ComboBox control, adding column names


ComboBox control, loading

2nd

2nd

3rd

3rd

connection strings
creating

2nd

establishing

3rd

4th

5th

6th

7th

8th

2nd

connection strings, creating

2nd

constructors
for CCustomerID class

2nd

inserting rows into tables

3rd

4th

2nd

object-based for CCustomerID class


testing
constructors, testing

2nd

ContactName property
Customer class

2nd

2nd

customer information
listing

2nd

CustomerData.vb
DataAdapter Configuration Wizard

2nd

SELECT command text


Update command text
CustomerID property

2nd

3rd

2nd

CustomerInterface.vb
ICustomer interface, properties (code)

CustomerInterface9_1.vb
delete method, declaring (code)
ICustomer interface
interfaces
ReadOnly keyword
save method, declaring (code)
Customers XSD

2nd

CustOrdersHist stored procedure, T-SQL

2nd

data
adding to data tables
generating
regenerating

2nd

2nd

3rd

4th

3rd

9th

7th

8th

9th

10th

11th

12th

13th

14th

15th

15th

saving

2nd

data grids
binding to DataView control
edits, cancelling
page indexes, updating
paging through

2nd

records, deleting

2nd

3rd

4th

5th

6th

records, posting

2nd

3rd

4th

5th

6th

setting to edit mode


sorting

2nd

updating

2nd

data tables
binding

2nd

3rd

records, adding

4th

2nd

5th

3rd

data tables, adding data

2nd

data tables, binding

2nd

3rd

data tables, loading

2nd

3rd

DataAdapter Configuration Wizard

2nd

database files, finding and attaching

3rd

4th

2nd

database list boxes


loading

2nd

3rd

4th

database names, retrieving

2nd

3rd

DataGrid control
loading

2nd

3rd

4th

5th

6th

DataGrid controls
populating

2nd

3rd

4th

5th

DataGrid object
loading

2nd

records, canceling editing/adding


special instructions

2nd

DataSet object
passing

datasets
loading

2nd

retrieving from XML Web Services


returning as XML

2nd

3rd

DataTable object
creating

2nd

3rd

4th

tracking

2nd

3rd

4th

DataTable object, creating

5th

2nd

DataView object, setting RowFilter property


default properties, declaring

DefineValidChars method
declaring
Delete command

delete method
declaring

2nd

Delete method
implementing
testing

detail pages
hyperlinks

2nd

DialogResult, setting

2nd

3rd

DoesCustomerIDExist function

DropDown control
loading

2nd

3rd

4th

dsCustomers.xsd
Customers XSD
Edit command

edits

2nd

5th

2nd

canceling

2nd

END statement
event handlers

2nd

for mFax variable

events
wiring for DataGrid control
exception handling

2nd

exceptions
catching

2nd

passing

2nd

3rd

formatting
UDFs (user-defined functions)

2nd

forms
closing

2nd

3rd

loading

2nd

3rd

opening

2nd

3rd

4th

5th

4th

frmHowTo.vb
constructors, testing

frmHowTo1_1
data sets, filling
frmHowTo1_2.vb

2nd

frmHowTo1_3.vb
dataset, refreshing

2nd

RefreshIndividual routine, calling

2nd

RefreshIndividual subroutine, adding calls

2nd

frmHowTo1_4.vb
ActiveEditing subroutine, calling
data, canceling changes
data, saving to servers

2nd

SaveRecord routine, calling

2nd

text boxes, disabling

3rd

2nd

text boxes, toggling

2nd

2nd

4th

2nd

frmHowTo1_5.vb
data, saving (code)

2nd

dsCustomerIndividual dataset, adding records (code)


edits, canceling (code)

2nd

2nd

list boxes, reloading (code)

2nd

3rd

mbAddNew variable, resetting (code)


records, deleting (code)

2nd

2nd

3rd

frmHowTo1_6.vb
btnSave click event, trapping save exceptions (code)
exceptions, catching

2nd

exceptions, passing

2nd

3rd

2nd

frmHowTo1_7.vb
enabled properties of buttons, toggling
forms, closing

2nd

2nd

records, saving before closing

2nd

frmHowTo1_8.vb
DataGrid control, refreshing
datasets, filling

2nd

3rd

2nd
4th

frmHowTo1_9
forms;opening (code)

2nd

frmHowTo1_9b
datasets;loading (code)

2nd

frmHowTo10_4.vb
reports, exporting

2nd

reports, printing

frmHowTo10_5.vb
Selection combo box, populating
SelectionFormula property, setting

2nd

3rd

3rd

4th

5th

frmHowTo13_3.vb
passwords, validating (code)
usernames, validating (code)

frmHowTo13_4.vb
datasets, retrieving from XML Web Services

frmHowTo3_1.vb
list boxes, loading

2nd

3rd

frmHowTo3_2.vb
list boxes, loading

2nd

frmHowTo3_3.vb
ComboBox control, loading
records, locating

2nd

3rd

2nd

text boxes, assigning values

2nd

frmHowTo3_4.vb
button controls, adding click events
columns, sorting

2nd

ComboBox control, adding column names


data tables, loading

2nd

2nd

3rd

3rd

DataView object, setting RowFilter property

2nd

frmHowTo6_1.vb
data, regenerating
forms, loading
SQL strings, building

frmHowTo6_2.vb
SQL statements, storing and executing

2nd

frmHowTo6_3.vb
data, generating

2nd

GenerateData routine, calling

2nd

3rd

frmHowTo6_4.vb
left outer joins

frmHowTo6_6.vb
PerformTask( ) function, calling (code)

2nd

3rd

SQL statements, executing (code)


SQL statements, loading (code)

2nd

frmHowTo6_7.vb
forms, loading (code)

2nd

SQL statements, storing (code)

2nd

frmHowTo6_8.vb
UDFs (user-defined functions), assigning
UDFs (user-defined functions), creating

2nd
2nd

3rd

4th

5th

UDFs (user-defined functions), displaying data


UDFs (user-defined functions), updating SELECT statement

frmHowTo7_1.vb
btnConnection button;disabling

2nd

btnConnection button;enabling

2nd

database list boxes, loading

2nd

3rd

4th

list boxes, repopulating


routines, reloading list boxes
routines;calling

2nd

2nd

SQL Servers, loading

2nd

frmHowTo7_2.vb
1stBackupDevices list box, populating
1stDatabases list box, populating
backups, performing (code)

2nd

3rd

routines, loading SQL Servers


routines;calling

frmHowTo7_3.vb
1stBackupDevices list box, populating
1stDatabases list box, populating
backups, performing (code)

2nd

3rd

4th

5th

6th

7th

2nd

routines, calling
SQL Servers, loading

frmHowTo7_4.vb
1stBackupDevices list box, populating
1stDatabases list box, populating

2nd

2nd

forms, closing
routines, calling
SQL Servers, loading
tables, copying

2nd

3rd

4th

5th

frmHowTo7_5.vb
1stDatabases list box, populating
btnDetach button, toggling
database files, finding and attaching
forms, closing

2nd

2nd

routines, calling
SQL Server database, detaching
SQL Server databases, attaching

2nd

SQL Servers, loading

frmHowTo8_1.vb
1stUnSelected list box, reloading
list boxes, loading categories
list boxes, populating

2nd

2nd
3rd

4th

5th

6th

routines, calling
servers, updating

2nd

3rd

4th

5th

6th

frmHowTo8_2.vb
1stLookupTables, pointing to items
connection strings, establishing
DataGrid control, populating

2nd

2nd

2nd

3rd

4th

5th

frmHowTo8_3.vb
columns, listing in databases

2nd

DataGrid control, loading


SQL strings, creating

2nd

stored procedures, executing


tables, listing in databases

2nd

2nd

3rd

4th

5th

3rd

frmHowTo8_4.vb
search forms, creating custom properties

2nd

stored procedures, executing


tables, listing in databases

frmHowTo8_4a.vb
Letter Button result sets, filling

2nd

records, loading into text boxes

2nd

3rd

frmHowTo8_4b.vb
DialogResult, setting
key values, storing

2nd
2nd

Letter Button controls, click events


search forms, creating custom properties

2nd

3rd

stored procedures, executing


tables, listing in databases

frmHowTo8_6.aspx
data grids, deleting records

2nd

3rd

4th

5th

6th

data grids, posting records

2nd

3rd

4th

5th

6th

data grids, updating page indexes


data tables, adding records

2nd

3rd

DataGrid object, canceling editing/adding records


DataGrid object, special instructions
DataTable object;tracking

2nd

3rd

frmHowTo8_7.vb
columns, listing in databases
DataGrid control, loading
SQL strings;creating

2nd

2nd

2nd
3rd

3rd
4th

4th

5th

stored procedures, executing


tables, listing in databases

2nd

3rd

4th

2nd

frmHowTo8_8a.vb
Letter Button result sets, filling
records, loading

2nd

2nd

3rd

Session object, storing values

frmHowTo8_8b.vb
calling pages, returning to

2nd

data grids;updating
key values, storing

2nd

Letter Button controls, click events


stored procedures, executing
tables, listing in databases

frmHowTo9_2.vb
CCustomer class

2nd

CCustomer class, property declarations


class variable declarations
Customer class
TextBoxChange method, writing values in text boxees

2nd

TextChanged event
ToString method, property information output

2nd

frmHowTo9_4.vb
CCustomer class, constructors

2nd

ReadValuesFromDataRow method

2nd

frmHowTo9_5.vb
Clear method

2nd

constructors, inserting rows into tables


constructors, testing
Delete method, implementing
Delete method, testing
Save method, implementing

2nd

3rd

4th

5th

Save method, testing


WriteChangesToDB method

2nd

frmHowTo9_6
ContactName property
InvalidPhoneNumberException
MaximumStringLengthExceededException;class declaration
phone numbers, validating

2nd

3rd

frmHowTo9_6.vb
DoesCustomerIDExist function
If[ellipsis dots] Then block
InvalidCustomerIDException, declaring
ValidateCustomerID method

frmHowTo9_7.vb
event handlers

frmMain.vb
ADO connections, opening and displaying
forms;opening (code)

2nd

routines, calling

GenerateData routine
calling

2nd

3rd

4th

HTML
repRegions Repeater control
repTerritories Repeater control

http[colon, no spaces]//localhost/WebService1/Service1.asmx?WSDL
XML Web Services, SOAP definition (code)
ICustomer interface
properties
If[ellipsis dots] Then block
international phone number extensions

2nd

3rd

4th

5th

InvalidCustomerIDException
declaring

2nd

InvalidPhoneNumberException

2nd

IsValid method
declaring

JET databases
opening

2nd

3rd

key values, storing


left outer joins

2nd

3rd

4th

2nd

Letter Button controls


click events

2nd

3rd

4th

Letter Button result sets, filling

2nd

3rd

4th

list boxes
categories, loading
populating
reloading

2nd
2nd

list boxes, loading

2nd

3rd
3rd

2nd

3rd

4th
4th

3rd

4th

5th

5th

6th

7th

8th

9th

10th

11th

12th

5th
4th

5th

list boxes, repopulating


Load event

2nd

LoadIndividual routine
creating

2nd

3rd

LoadList routine
creating

2nd

LoadProducts routine
calling
lstSQLServers list box
m (adding to)

MaximumStringLengthExceededException
class declaration

mbAddNew variable
resetting

2nd

mdtProducts DataTable object


rows, finding

2nd

modGeneralRoutines.vb
connection strings, creating

2nd

3rd

4th

modPublicVariables.vb
public variables, declaring

modSQLDMORoutines.vb
backup device names, retrieving
connection strings, creating

2nd

database names, retrieving

2nd

SQL Servers, loading

3rd

2nd

Northwind SQL Server database


CustOrdersHist stored procedure, T-SQL code
overloaded methods
PageIndexChanged command
parameterized properties

2nd

passwords
validating

2nd

3rd

PerformTask( ) function
calling

2nd

3rd

phone number extensions


phone numbers, validating

2nd
2nd

3rd

PhoneDatatypes.vb
CNumberString class

2nd

3rd

DefineValidChars method, declaring


event handlers
international phone number extensions
IsValid method, declaring
phone number extensions

5th

6th

7th

8th

9th

13th

StringValue property
ThrowException method, declaring
ProductID detail information, loading
property declarations (VB .NET)

2nd

2nd

property declarations (VB 6)


public varibles, declaring
ReadOnly keyword

2nd

ReadValuesFromDataRow method

2nd

records
adding to data tables
deleting

2nd

2nd

3rd

loading into text boxes


records, locating

2nd

3rd

2nd

3rd

2nd

recordsets
opening and retrieving
persisting to disks

4th

5th

2nd

RefreshIndividual routine
calling

reports
exporting
printing

2nd

3rd

2nd

repRegion Repeater control


loading
repTerritories Repeater control
loading
routines
calling

2nd

3rd

4th

list boxes, reloading

5th

6th

7th

8th

9th

10th

2nd

SQL Servers, loading

rows
deleting in data grids

2nd

3rd

finding
save exceptions, trapping

2nd

save method
declaring

Save method
implementing

2nd

3rd

4th

5th

testing

SaveRecord routine
creating

2nd

replacing

3rd

2nd

3rd

SaveRecord subroutine

search forms
custom properties, creating

2nd

3rd

4th

5th

SecurityServices.asmx.vb
DataSet object, passing
passwords, validating (code)
usernames, validating (code)
SELECT command text

SELECT statement
data, displaying
updating

2nd

SelectedIndexChanged
code

2nd

3rd

4th

5th

SelectedIndexChanged event

2nd

3rd

SelectedIndexChanged event, adding

Selection combo box


populating

2nd

3rd

SelectionFormula property
setting

4th

5th

6th

7th

11th

12th

servers
updating

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

Session object
values, storing

session variables
tracking

2nd

3rd

source code for chapters in book (Web site)


SQL Server database, detaching

2nd

SQL Server databases


attaching

2nd

SQL Server objects


creating

SQL Servers
loading

2nd

3rd

SQL Servers, loading

4th

2nd

3rd

4th

SQL statements
executing
loading

2nd

loading and executing


storing

2nd

storing and executing

2nd

SQL strings
building
creating

2nd

3rd

4th

5th

6th

stored procedures
calling

2nd

executing

3rd

2nd

3rd

StringValue property

4th

5th

6th

7th

8th

9th

10th

11th

12th

2nd

Table control
loading

2nd

3rd

4th

5th

Table data type


creating and returning

tables
copying

3rd

4th

5th

6th

7th

listing in databases

2nd

2nd

3rd

4th

5th

6th

7th

8th

7th

8th

9th

text boxes
toggling

2nd

3rd

4th

text boxes, assigning values

2nd

TextBoxChange method
text boxes, writing values
TextChanged event

2nd

2nd

3rd

ThrowException method
declaring

ToString method
property information output

2nd

3rd

UDFs (user-defined functions)


assigning
creating

2nd
2nd

3rd

4th

5th

data, displaying
SELECT statement, updating

2nd

Update command
Update command text

2nd

3rd

4th

UseAStoredProcedureWithAParameter routine

usernames
validating

2nd

ValidateCustomerID method
validation controls

2nd

validation, writing

2nd

27th

VB .NET

3rd

4th

5th

6th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

property declarations

VB 6
default properties
property declarations
wavy blue lines

Web pages
initializing

2nd

3rd

4th

5th

linking
loading

2nd

3rd

wfrmHowTo12_1.aspx.vb
data tables, adding data
data tables, binding

2nd

3rd

DataTable object, creating

2nd

XML documents, creating

2nd

wfrmHowTo12_2.aspx.vb
XML documents, reading

2nd

3rd

wfrmHowTo12_3.aspx.vb
data tables, adding data
data tables, binding

2nd

2nd

3rd

DataTable object, creating

2nd

XML documents, creating

2nd

wfrmHowTo12_4.aspx.vb
XML documents, reading

2nd

wfrmHowTo12_5.aspx.vb
data tables, adding data
data tables, binding

2nd

DataTable object, creating

2nd

XML documents, creating


XML documents, reading into datasets

wfrmHowTo5_1.aspx.vb
customer information, listing
RefreshIndividual routine, calling
Web pages, loading

wfrmHowTo5_2.aspx.vb
validation controls

wfrmHowTo5_3.aspx.vb
LoadProducts routine, calling
rows, finding
Web pages, initializing

2nd

3rd

wfrmHowTo5_4.aspx.vb
DropDown control, loading
Table control, loading

2nd

2nd

3rd

3rd

4th

wfrmHowTo5_5a.aspx
repRegions Repeater control, HTML
repTerritories Repeater control

wfrmHowTo5_5a.aspx.vb
DropDown control, loading
repRegion Repeater control, loading
Table control, loading

wfrmHowTo5_5b.aspx
repTerritories Repeater control, HTML

wfrmHowTo5_5b.aspx.vb
repTerritories Repeater control, loading

wfrmHowTo5_6.aspx.vb
data grids, binding to DataView control
data grids, paging through
data grids, sorting
Web pages, loading

wfrmHowTo5_7.aspx
events, wiring for DataGrid control

4th

wfrmHowTo5_7.aspx.vb
changes, updating servers

2nd

3rd

4th

data grids, cancelling edits


data grids, setting to edit mode
DataGrid object, loading

2nd

records, adding to data tables


rows, deleting in data grids

2nd

2nd

session variables, tracking

2nd

3rd
3rd

wfrmHowTo5_8a.aspx.vb
products, filling and binding to DataGrid object

wfrmHowTo5_8b.aspx.vb
ProductID, loading detail information

2nd

wfrmHowTo8_5.aspx
1stUnselected list box, reloading
list boxes, loading categories
list boxes, populating

2nd

2nd

3rd

3rd

4th

5th

6th

routines, calling
servers, updating

2nd

3rd

WriteChangesToDB method

4th

2nd

XML documents
creating

2nd

reading

2nd

3rd

4th

5th

6th

7th

reading into datasets


reading with XMLTextReader

2nd

3rd

XML Web Services


SOAP definition

2nd

3rd

4th

5th

code (VB .NET)


reports
displaying

2nd

3rd

4th

5th

6th

7th

exporting

2nd

3rd

4th

5th

6th

7th

printing

2nd

3rd

4th

5th

6th

7th

8th
8th

9th
9th

Code command (View menu)


collapsing routines

collections
Items
data, adding
Items (index)
SelectedIndices (index) collection
SQL-DMO

2nd

String Collection Editor


Table Mappings

2nd

2nd

Column Name property


Column property

columns
adding to tables

binding to controls
properties

2nd

Create Columns Automatically at Run Time check box, checking


Create Columns Automatically, unchecking
CustomerID (Customers table)
CustomerID (Orders table)

2nd

2nd

data
entering

data grids
templates

DataGrid Property Builder


Columns tab
DataItem.RegionURL
HyperLink

listing in databases

code

2nd

3rd

4th

names
adding to ComboBox control (code)

2nd

3rd

4th

5th

NULL
properties

2nd

3rd

SELECT string
setting for DataGrid control

2nd

sorting
sorting (code)

2nd

SQL Server database objects

2nd

3rd

Columns tab (DataGrid Property Builder)


columns.

[See also fields]

COM
type libraries, registering

combo boxes
customers, finding

Selection
populating (code)

2nd

3rd

4th

5th

6th

Combo control
property settings

2nd

ComboBox control
column names, adding (code)

2nd

3rd

data
binding to

2nd

3rd

4th

5th

6th

7th

8th

9th

datasets
filling (code)
loading (code)

2nd

2nd

3rd

properties
property settings

2nd

3rd

4th

5th

6th

7th

8th

9th

ComboBox object
properties

2nd

3rd

4th

property
property settings

2nd

Command button
properties

2nd

settings

2nd

3rd

4th

Command Button control


properties

2nd

3rd

property settings

4th

2nd

3rd

5th

6th

4th

7th

5th

6th

Command Button object


properties

2nd

3rd

4th

5th

property

command buttons
accessing on forms

2nd

data (to edit, save, or cancel)

2nd

properties
Command object

2nd

3rd

actions
executing with SQL statements

2nd

methods
properties

TextBox control
populating with stored procedures
CommandBuilder object
methods
properties

commands
Action menu
New Group
Properties

2nd

8th
7th

9th

10th

Actions menu
New Login

Add menu
Add Class
Add New Item

2nd

Cancel
code

context menu
New Login
Properties
CREATE FUNCTION

Delete
code

Edit
code

File menu
New

PageIndexChanged
code

pop-up menu
Copy
Generate Dataset

2nd

Paste
Run as Server Control

Project menu
Add Reference

2nd

Add Web Reference

Report menu
Report Options
SELECT command text, code

shortcut menu
New Database Role
New Database User
New Login
Update
code
Update command text, code

2nd

3rd

View menu
Code
Indexes/Keys
commands.

2nd

[See also T-SQL (Transact-SQL) commands]

CommandText property
CommandText property, accessing

2nd

companies
Alfred's Fried Foods
CompareValidator control

CompareValidator object
properties

comparing
ADO (ActiveX Data Objects) and ADO.NET

2nd

3rd

OleDb and SQLClient objects


Web server and HTML controls

2nd

compound indexes
Confirm Password text box

2nd

Connect button
enabling
connected data
Connection object

2nd

ADO (ActiveX Data Objects)


methods

2nd

3rd

4th

5th

properties

connection strings
BuildCnnStr( ) function

creating
code

2nd

creating (code)

3rd

4th

2nd

3rd

establishing (code)
text, displaying

5th

6th

7th

8th

2nd

2nd

connections
ADO (ActiveX Data Objects)
opening and displaying (code)

2nd

databases
with SQL-DMO objects

2nd

Northwind database
creating

2nd

trusted (SQL Server)

conrtrols
bound at runtime

2nd

constraints
defining

2nd

for data

2nd

3rd

4th

5th

6th

of indexes

constructors
CCustomer class
code

2nd

for CCustomerID class


code
for CCustomerID class (code)

2nd

object-based
for CCustomerID class (code)
objects

rows, inserting into tables


code

testing
code

2nd

consuming
XML Web Services

2nd

methods, calling

3rd

4th

5th

6th

7th

2nd

Web references

2nd

3rd

ContactName property
code

context menu commands


New Login
Properties

Control Panel applets (Windows NT/2000)


groups and users, adding

control property settings


Label, TextBox, and Command button

2nd

Control.ViewState property
definition

controlling
classes, creation and behavior
Crystal Report Viewer behavior
sort order at run-time

2nd

3rd

2nd

2nd

controls
arranging on forms

2nd

3rd

bound
AutoPostBack property
DataBind method
IsPostBack property

2nd

4th

5th

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

on Web Forms

6th

7th

Web server and HTML controls, comparing

2nd

3rd

4th

5th

2nd

8th

9th

10th

11th

button
click events, adding (code)

2nd

Button
properties

2nd

3rd

4th

5th

6th

4th

5th

columns, binding to
properties

2nd

Combo
property settings

2nd

ComboBox
data, binding to

2nd

3rd

datasets, filling (code)


loading (code)

2nd

6th

7th

8th

9th

2nd
3rd

properties
property settings

2nd

3rd

4th

5th

6th

7th

8th

9th

Command Button
properties

2nd

3rd

property settings

4th

2nd

5th

3rd

6th

4th

7th

5th

8th

6th

9th

10th

7th

CompareValidator

Crystal Report Viewer


reports, displaying

2nd

3rd

4th

5th

6th

7th

8th

9th

CrystalReportViewer
property settings

2nd

customer orders
header information, displaying
CustomValidator

2nd

2nd

data
DataSet
DataView
in Windows forms

2nd

3rd

OleDbCommand
OleDbConnection
OleDbDataAdapter
SqlCommand
SqlConnection
SqlDataAdapter

DataGrid
button events on data grids

2nd

buttons, adding

2nd

4th

buttons, events

2nd

columns, setting

3rd

2nd

data grids, hyperlinking from rows to detail pages


data, adding

2nd

data, binding to
data, deleting
27th

28th

29th

3rd

2nd
2nd

4th
3rd

3rd

5th
4th

4th

6th
5th

5th

7th
6th

6th

2nd

8th
7th

7th

3rd

9th
8th

8th

4th

10th
9th

9th

5th
11th

6th
12th

7th
13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

10th
10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

30th

data, displaying

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

data, drilling down to

2nd

3rd

4th

5th

6th

7th

8th

9th

data, editing

2nd

3rd

data, manipulating

4th

5th

6th

7th

8th

9th

10th

11th
10th

11th

12th
11th

12th

2nd

data, new items


data, paging through
data, sorting

2nd

2nd

3rd

datasets, filling (code)

3rd

4th
2nd

events, wiring (code)

2nd

General properties, setting


loading (code)

2nd

3rd

2nd
4th

lookup tables, adding data


objects, handling

4th

5th

2nd

3rd

2nd

5th

5th

6th

6th

7th

7th

8th

8th

9th

9th

10th

10th

11th

12th
13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

Paging properties, setting


populating (code)
properties

2nd

2nd
3rd

2nd
3rd

4th

5th

4th

property settings

2nd

refreshing (code)

2nd

3rd

4th

schemas;loading into data tables

5th

6th

2nd

DataGrid Web server


DataList Web server
DataRepeater
DataSet

2nd

binding with ListBox control properties


for frmHowTo1_9b forms

2nd

2nd

3rd

DataView
data grids, binding (code)

2nd

ddSortby
SelectedIndexChanged event, attaching routines

DropDown
loading (code)
populating

2nd

2nd

3rd

3rd

4th

4th

5th

5th

6th

7th

8th

9th

10th

11th

propeties

Export tab page


objects and properties

Hyperlink
properties

HyperLink
propeties
ignoring with Try[ellipsis dots]Catch[ellipsis dots]End Try block

Label
properties

2nd

3rd

properties, settings
property settings

4th

2nd

2nd

5th
3rd

3rd

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

9th

10th

11th

12th

13th

14th

15th

4th
4th

5th

6th

7th

8th

propeties

Label, TextBox, ListBox, and Command button


property settings

2nd

Letter Button
click events (code)

2nd

ListBox
loading
populating

2nd

3rd

4th

5th

6th

7th

properties

2nd

3rd

4th

5th

6th

7th

properties, setting to bind DataSet control


properties, settings
property settings

2nd

2nd

3rd

3rd

8th

9th

10th

11th

2nd

4th
4th

5th

6th

7th

8th

9th

ListBox Web server


ListBox Windows Forms

OleDataAdapter
for frmHowTo1_9b forms

2nd

3rd

OleDbDataAdapter
controls

2nd

on Web Forms
bound at runtime

2nd

OpenFileDialog
order information, displaying

2nd

Print tab page


objects and properties
properties

2nd

3rd

4th

5th

6th

7th

10th

11th

12th

9th

10th

11th

setting
settings
property settings
RangeValidator

2nd

3rd

4th

5th

6th

7th

8th

13th

14th

16th

rbDistinct Radio Button


Regular ExpressionValidator

Repeater
data, displaying

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

events, programming
lists, buttons and hyperlinks
lists, displaying regional territories

2nd

3rd

properties
repRegion, loading (code)

2nd

repRegions, HTML code


repTerritories, code
repTerritories, HTML code
repTerritories, loading (code)
templates

2nd

2nd

3rd

URLs (uniform resource locators), creating

2nd

Repeater Web server

repRegion Repeater
loading (code)

repRegions Repeater
HTML code

repTerritories Repeater
code
HTML code
loading (code)
RequiredFieldValidator
Selected Products ListBox
tab pages

2nd

Table
data, displaying
loading (code)

2nd
2nd

3rd
3rd

4th
4th

5th

6th

7th

8th

9th

7th

8th

9th

10th

5th

persistence
propeties
tables, displaying
Table Web server

2nd

2nd

objects, creating

3rd

2nd

TextArea

TextBox
populating

2nd

properties

2nd

3rd

properties, settings
property settings

4th

2nd

2nd

5th
3rd

3rd

6th

11th

12th

13th

4th
4th

unbound on forms
Unselected Products ListBox

validation
code
messages, displaying
on Web Forms

2nd

property settings

3rd

2nd

4th

5th

6th

3rd

validation (Web Forms)


property settings
validation errors

2nd

3rd

2nd

3rd

4th

5th

2nd

Validation Web server


ValidationSummary
lists, look of

validator
reaction inconsistencies

View tab page


objects and properties
Web server and HTML, comparing
controls.

2nd

[See also bound controls]

7th

8th

9th

10th

11th

12th

14th

14th

15th

16th

cookies
definition
Copy command (pop-up menu)

copying
tables
between SQL Server databases
code

2nd

3rd

4th

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

14th

15th

15th

16th

17th

18th

5th

Create Columns Automatically at Run Time check box, checking


Create Columns Automatically, unchecking
Create Database dialog box

2nd

CREATE DATABASE statement


CREATE DEFAULT statement
CREATE FUNCTION command
CREATE FUNCTION statement
CREATE PROCEDURE statement
Create Relationships dialog box

2nd

3rd

3rd

4th

CREATE RULE statement


CREATE TABLE statement

2nd

CREATE VIEW statement


Create XML File button

2nd

3rd

CreateComment (XMLDocument class)


CreateNode (XMLDocument class)

creatin
objectsd

2nd

creating
ActivateEditing routine
code

2nd

bound list boxes

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

2nd

3rd

4th

5th

6th

7th

8th

9th

12th

13th

14th

15th

bulk queries
code

2nd

classes
to implement interfaces

10th

11th

12th

13th

connection strings
code

2nd

3rd

4th

5th

6th

7th

8th

3rd

4th

5th

9th

10th

11th

custom properties
for search forms
data adapters
data tables

2nd

2nd

2nd

database user accounts

2nd

3rd

4th

5th

6th

DataTable object
code

2nd

3rd

4th

5th

6th

Delete Record
Detach/Attach SQL Server Database dialog box

2nd

dialog boxes

8th

2nd

3rd

4th

5th

6th

7th

3rd
9th

4th
10th

5th

6th

11th

7th

12th

8th

13th

9th
14th

10th
15th

11th
16th

12th
17th

13th
18th

14th

15th

16th

17th

19th

forms
GenerateData routine

generic search forms


data-driven techniques
26th

27th

28th

29th

30th

2nd

3rd

31st

32nd

GetBackupDevices routine
GetSQLDatabases routine

indexes
with Property Pages dialog box
interfaces
LoadIndividual routine
code

2nd

2nd

3rd

LoadList routine
code

2nd

LoadSelectedProducts routine

2nd

LoadUnSelectedProducts routine

2nd

4th

5th

33rd

6th

34th

7th
35th

8th
36th

9th
37th

10th
38th

11th
39th

12th
40th

13th
41st

14th
42nd

15th

16th

43rd

17th

44th

18th

45th

19th

46th

20th

47th

21st

48th

22nd

49th

50th

23rd

24th

25th

mailing labels

2nd

3rd

Northwind database connections

2nd

objects
with SQL syntax
on-screen reports with hyperlinks
fields as hyperlinks

2nd

Web site fields, adding

2nd

3rd

4th

5th

6th

3rd

2nd

on-the-fly batch updates in ADO.NET

2nd

3rd

4th

5th

6th

point-and-click query tool


data-driven techniques

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

8th

9th

10th

11th

12th

13th

14th

15th

16th

point-and-click SQL Server query tool


data-driven techniques

2nd

3rd

4th

5th

6th

7th

reports
with Report Expert

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

SaveRecord routine
code

2nd

3rd

SQL Server database

2nd

constraints, defining

3rd

default values, defining


fields, defining

2nd

indexes, defining

4th

2nd

2nd

3rd

2nd

4th

3rd

4th

3rd

primary keys, defining

5th

3rd

4th

5th

4th

2nd

6th
5th

6th

5th

6th

5th

6th

7th

6th

8th

9th

7th

8th

3rd

4th

5th

6th

2nd

3rd

4th

5th

tables, defining

4th

5th

6th

7th

2nd

3rd

views, creating

2nd

3rd

2nd

4th

3rd

5th

4th

6th

10th

8th

stored procedures, creating

tables, defining relationships

9th

7th

8th

5th

7th

9th

9th

6th

11th

10th

7th

8th

11th
9th

8th

SQL Server database objects


column properties
columns
rows

2nd

3rd

2nd

2nd

table properties
tables

2nd

3rd

2nd

SQL Server objects


code
SQL Server objects with ADO (ActiveX Data Objects)

2nd

3rd

4th

SQL statements

SQL strings
code

2nd

creating

3rd

4th

2nd

standard logins

2nd

3rd

stored procedures

2nd

T-SQL commands

2nd

4th

5th

3rd

4th

5th

6th

7th

8th

9th

10th

5th

6th

7th

8th

9th

10th

11th

4th

5th

6th

11th

12th

Table data type


tables

2nd

3rd

4th

CREATE TABLE statement

2nd

12th

13th

14th

3rd

tblCustomerPhones table
UDFs

2nd

(user-defined functions)
calling

2nd

3rd

2nd

code, formatting

2nd

parameters, passing
scalar types, returning

2nd

table types, returning

2nd

UDFs (user-defined functions)


code

2nd

3rd

4th

5th

URLs (uniform resource locators)


views

2nd

3rd

4th

5th

2nd

6th

7th

8th

Web Forms
Windows NT/2000 groups
Windows NT/2000 logins

2nd
2nd

3rd
3rd

4th
4th

5th
5th

6th
6th

7th

8th

9th

10th

15th

16th

17th

17th

18th

19th

XML documents
code

2nd

3rd

4th

5th

data tables, creating

2nd

with XMLTextWriter

2nd

3rd

with XMLWriter

2nd

3rd

XMLTextWriter

2nd

3rd

XML Web Services

3rd

5th

6th

7th

4th

5th

6th

7th

XML Web Services with parameters

2nd

3rd

4th

descriptions

2nd

4th

4th

8th

9th

5th

10th

11th

12th

13th

6th

2nd

methods, descriptions

2nd

parameters, passing
security tables

2nd

2nd

creation control of classes

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

6th

7th

8th

7th

8th

9th

11th

12th

13th

14th

Criteria property
Crosstab reports

Crystal Report
Gallery dialog box
Crystal Report Gallery dialog box

Crystal Report Viewer


behavior, controlling

2nd

reports
displaying

2nd

3rd

4th

ReportSource property

5th

2nd

6th

7th

8th

9th

3rd

Crystal Reports
Experts
licensing

reports
calculated fields, adding

2nd

3rd

4th

5th

creating with Report Expert

2nd

3rd

4th

displaying

5th

6th

7th

2nd

3rd

4th

displaying with Viewer


exporting

2nd

3rd

fields as hyperlinks
labels, printing

2nd
4th

2nd

2nd

4th

8th

9th

4th

2nd

5th

6th

7th

8th

2nd

3rd

records, printing

2nd
2nd

3rd

4th

5th

4th

5th

6th

7th

8th

9th

2nd

3rd

4th

5th

6th

7th

2nd

ReportSource property
SelectionFormula syntax

6th

3rd

4th

2nd

3rd

5th

6th

7th

8th

9th

8th

2nd

sort order, controlling at run-time

2nd

CrystalReportViewer control
2nd

CrystalReportViewer object
property settings

2nd

CType( ) function
cursor data type (SQL Server)

cursors
ADO 2.x
client-side
Dynamic type
Forward Only type

types
ADODB.CursorTypeEnum.adOpenForwardOnly
custom database roles (SQL Server security)

Customer class
code

11th

2nd

printing at run-time

property settings

10th

3rd

on-screen, creating with hyperlinks

printing

6th

7th

on-screen, adding Web site fields

options, setting

5th

6th

9th

3rd

3rd

mailing labels, creating

3rd
5th

5th

2nd

3rd

4th

5th

6th

10th

11th

12th

12th

13th

14th

15th

customer information
listing
code

customer orders
header information, displaying

2nd

information
controls to display

2nd

displaying (code)

2nd

3rd

4th

CustomerData.vb
DataAdapter Configuration Wizard, code

2nd

3rd

SELECT command text, code


2nd

3rd

CustomerID column (Customers table)

Update command text, code

2nd

CustomerID column (Orders table)

2nd

CustomerID property
code

CustomerInterface.vb
ICustomer interface
properties (code)

CustomerInterface9_1.vb
delete method
declaring (code)

ICustomer interface
code

interfaces
code
ReadOnly keyword, code

save method
declaring (code)

customers
finding
in combo boxes

RefreshIndividual routine
calling (code)

customers orders
details, drilling down to

Customers page
search forms, calling

2nd

Customers table
CustomerID column

2nd

list
Server Explorer
Customers XSD, code

2nd

Customers, CustomerCustomerDemo, and CustomerDemographics table

customizing
reports

2nd

CustomTask object
methods
properties
CustomValidator control

2nd

CustOrdersHist stored procedure, T-SQL code

[ Team LiB ]

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D ] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
DAO
(Data Access Objects)

2nd

data
adding
in DataGrid control

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

20th

21st

22nd

23rd

24th

25th

26th
to Items collection
adding to lookup tables

2nd

binding
to ComboBox control

2nd

to DataGrid control

2nd

canceling changes (code)

3rd

4th

3rd

5th

6th

7th

8th

9th

4th

5th

6th

7th

8th

9th

8th

9th

10th

8th

9th

10th

2nd

command buttons
to edit, save, or cancel

2nd

connected
constraints

2nd

controls
DataSet
DataView
in Windows forms

2nd

3rd

OleDbCommand
OleDbConnection
OleDbDataAdapter
SqlCommand
SqlConnection
SqlDataAdapter

deleting
in DataGrid control

2nd

3rd

4th

5th

6th

7th

in bound list boxes

2nd

3rd

4th

5th

6th

7th

in DataGrid control

2nd

3rd

4th

5th

6th

7th

26th
details, retrieving
disconnected

displaying

templates

2nd

3rd

URLs (uniform resource locators), creating


with Repeater control

2nd

3rd

4th

2nd

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

with SELECT statement (code)


with Table control

2nd

3rd

displaying in DataGrid control


drilling down to
editing

2nd

2nd

3rd

3rd

4th

5th

6th

7th

8th

9th

2nd

4th

5th

6th

7th

8th

9th

10th

11th

12th

4th

5th

6th

7th

8th

9th

10th

in DataGrid control

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

in DataSet objects

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

9th

10th

11th

12th

26th

notifying users

2nd

entering into columns


errors, communicating to developers

exporting
with SQL-DTS
filling DataGrid control

generating
code

2nd

loading into list boxes


manipulating

2nd

2nd

3rd

2nd

3rd

4th

5th

6th

7th

8th

methods

2nd

objects

2nd

3rd
3rd

paging through
in DataGrid control
properties

2nd

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

3rd

regenerating (code)
retrieving from SQL Server 2000 database
retrieving with DataReader object

2nd

2nd

3rd

3rd

4th

4th

5th

5th

6th

6th

7th

7th

8th

9th

10th

11th

saving
code

2nd

to servers (code)

2nd

sorting
in DataGrid control

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

tables
binding (code)
updating

2nd

validating

3rd

2nd

4th

3rd

validating on Web Forms


validation code, writing
26th

5th

4th

6th

5th
2nd

2nd

7th

6th
3rd

3rd

8th

7th
4th

4th

9th

8th
5th

5th

10th

9th
6th

6th

10th
7th

7th

8th
8th

9th
9th

10th
10th

27th

views

[See DAO]2nd [See DAO]

Data Access Objects.

Data Adapter Configuration Wizard

2nd

data adapters
creating

2nd

Table Mappings collection


data bound forms

2nd

2nd

3rd

4th

5th

6th

7th

8th

5th

6th

7th

9th

data driven
definition

data grids
binding to DataView control (code)
button events

2nd

data tables
binding (code)

2nd

3rd

data tables, binding (code)

4th

2nd

3rd

EditItemIndex

edits
cancelling (code)
HyperLink columns
hyperlinking from rows to detail pages
information, retrieving

2nd

3rd

2nd

page indexes
updating (code)

paging through
code

records
deleting (code)
posting (code)

2nd
2nd

3rd
3rd

rows
deleting (code)

2nd

3rd

setting to edit mode (code)

sorting
code
sorting, setting

2nd

3rd

templates

updating
code
Data Link Properties dialog box
data providers
classes

2nd

4th

4th
4th

5th
5th

6th
6th

4th

5th

6th

7th

11th
11th

12th
12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

OLE DB (ADO)
choosing

2nd

data sets
filling with frmHowTo1_1 (code)

data sources
choosing

Data tab
Standard Report Wizard

data tables
binding
2nd

3rd

4th

binding (code)

code

2nd

3rd

creating

2nd

data
adding (code)

2nd

3rd

data, adding (code)


loading (code)

2nd

3rd

records
adding (code)

2nd

locating (code)

2nd

records, adding, code


schemas, loading

2nd

3rd

2nd

text boxes
assigning values (code)

2nd

[See DTS]

Data Transformation Services.

data types
bigint (SQL Server)
binary (SQL Server)
bit (SQL Server)
char (SQL Server)
cursor (SQL Server)
datetime (SQL Server)
decimal (SQL Server)
float (SQL Server)
image (SQL Server)
int (SQL Server)
money (SQL Server)
nchar (SQL Server)
ntext (SQL Server)
numeric (SQL Server)
nvarchar (SQL Server)
real (SQL Server)
smalldatetime (SQL Server)
smallint (SQL Server)
smallmoney (SQL Server)
SQL Server

2nd

3rd

4th

5th

sql_variant (SQL Server)


Table
creating and returning
table (SQL Server)
text (SQL Server)
timestamp (SQL Server)
tinyint (SQL Server)
uniqueidentifier (SQL Server)
varbinary (SQL Server)
varchar (SQL Server)

data-bound multi-select list boxes


data-driven techniques
26th

27th

28th

29th

data-driven techniques

2nd

30th
2nd

3rd
31st

3rd

4th

4th
32nd
5th

5th
33rd

6th

7th

34th

8th

35th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

36th

37th

38th

39th

40th

41st

42nd

43rd

44th

45th

46th

20th

21st

22nd

23rd

24th

25th

data-bound multi-select list boxes


25th

26th

27th

28th

29th

30th

2nd

3rd

31st

32nd

4th

5th

33rd

6th
34th

7th
35th

8th

9th

36th

10th

37th

11th

38th

12th

39th

13th

40th

14th

41st

15th

42nd

16th

43rd

17th

44th

18th

45th

19th

20th

21st

22nd

23rd

24th

46th

generic search forms


creating
28th

29th

2nd

30th

3rd

31st

4th

32nd

5th
33rd

6th
34th

7th

8th

9th

35th

36th

7th

8th

35th

36th

10th

37th

11th

38th

12th

39th

13th

40th

14th

41st

15th

42nd

16th

43rd

17th

44th

18th

45th

19th

46th

20th

47th

21st

48th

22nd

49th

23rd

24th

25th

26th

27th

50th

lookup tables
updating
28th

29th

2nd

30th

3rd

31st

4th

32nd

5th

33rd

6th
34th

9th

10th

37th

11th

38th

12th

39th

13th

40th

14th

41st

15th

42nd

16th

43rd

17th

44th

18th

45th

19th

46th

point-and-click query tool


creating

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

10th

11th

12th

13th

14th

15th

16th

12th

13th

14th

15th

16th

17th

point-and-click SQL Server query tool


creating

2nd

3rd

4th

5th

6th

7th

8th

9th

DataAdapter
frmHowTo1_2.vb
code

2nd

DataAdapter Configuration Wizard


Choose a Query Type page
Choose Your Data Connection page
code

2nd

2nd

3rd

Generate the SQL Statements page

2nd

DataAdapter object
methods

2nd

properties

3rd

2nd

3rd

DataAdapter Update method

database list boxes


loading
code

2nd

Database property
Database Role Properties dialog box
Permissions tab

2nd

2nd

Database User Properties dialog box

2nd

databases
1stDatabases list box
populating (code)

2nd

3rd

4th

application roles (SQL Server security)

5th
2nd

3rd

4th

5th

2nd

3rd

4th

backing up
with SQL-DMO

btnDetach button
toggling (code)

classes
code

2nd

3rd

columns
listing (code)

2nd

3rd

4th

connecting to
with SQL-DMO objects

2nd

custom database roles (SQL Server security)

5th

6th

db_accessadmin (fixed database role)


db_backupoperator (fixed database role)
db_datareader (fixed database role)
db_datawriter (fixed database role)
db_ddladmin (fixed database role)
db_denydatareader (fixed database role)
db_denydatawriter (fixed database role)
db_owner (fixed database role)
db_securityadmin (fixed database role)

Detach/Attach SQL Server Database dialog box


creating
detaching

2nd

2nd

diagrams

dialog boxes

3rd

4th

5th

6th

7th

8th

9th

10th

11th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

27th

creating and connecting to

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

fixed database roles (SQL Server security)

2nd

3rd

4th

5th

6th

7th

8th

11th

12th

13th

14th

15th

16th

17th

18th

files
attaching

2nd

finding and attaching (code)

how-to, design view

2nd

2nd

JET
opening (code)

2nd

JET database engine

list boxes
loading (code)
lists, refreshing

2nd

2nd

local
ADO (ActiveX Data Objects)

2nd

names
retrieving (code)

2nd

3rd

Northwind
customers, tracking demographics

Northwind SQL Server


Customers table list
null values, interpreting
public (fixed database role)
reattaching

records
inserting

relationships
Create Relationships dialog box

2nd

rights, sharing
Server Explorer

2nd

SQL
backing up
verifying

SQL Server
attaching (code)

2nd

backing up

3rd

2nd

constraints, defining
creating

2nd

3rd

3rd
4th

5th

2nd
4th

default values, defining

6th

3rd

7th

4th

5th

8th

5th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

11th

12th

13th

14th

15th

16th

17th

18th

6th

6th

2nd

3rd

4th

5th

6th

detaching (code)
fields, defining

2nd

indexes, defining

3rd

2nd

primary keys, defining


restoring

2nd

3rd

4th

2nd

views, creating

8th
8th

9th

2nd

3rd

4th

5th

6th

7th

8th

4th

5th

6th

2nd

2nd

3rd

7th

3rd

3rd

4th

4th

4th

5th

2nd

8th

6th

7th

4th

5th

8th

9th

2nd

3rd

4th

5th

6th

7th

8th

3rd

4th

5th

6th

7th

2nd

3rd

2nd

creating
2nd

table properties
tables

2nd

3rd

2nd

SQL-DMO
(SQL-Distributed Management Objects)

2nd

3rd

8th

9th

6th

7th

SQL Server database objects

rows

7th

6th

SQL Server 2000

columns

9th

10th

8th

5th

column properties

11th

5th

5th

6th

3rd

9th

4th

2nd

10th

7th

3rd

data, retrieving

9th

6th

tables, defining relationships


verifying

7th

5th

tables, copying between


2nd

6th

4th

stored procedures, creating

tables, defining

5th

3rd

7th

10th

9th

10th
8th
11th

10th
11th
9th
12th

13th

14th

15th

16th

17th

18th

19th

20th

19th

objects

states
supporting

tables
listing (code)

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

2nd

3rd

4th

5th

6th

7th

8th

3rd

4th

5th

6th

11th

12th

many-to-many relationship
one-to-many relationship

2nd

one-to-one relationship
referential integrity
searching

2nd

2nd

3rd

3rd

4th

updating
methods, implementing
user accounts, creating

2nd

9th

10th

11th

12th

13th

14th

15th

16th

17th

user options

users
logging in
databases.

[See also data-driven techniques]

DataBind method
Databind method
DataColumn object
methods
properties
DataDefinition object
DataFormatString property

DataGrid control
button events on data grids

2nd

buttons, adding

2nd

4th

buttons, events

2nd

columns, setting

3rd

2nd

data
adding

2nd

binding to
deleting

3rd

2nd

2nd

displaying

3rd

2nd

drilling down to
editing

2nd

4th
3rd

4th

6th
5th

5th

7th
6th

6th

8th
7th

7th

9th
8th

8th

10th

11th

9th

10th

11th

4th

5th

6th

7th

8th

9th

10th

2nd

3rd

4th

5th

6th

7th

8th

9th

4th

5th

6th

7th

8th

12th

9th

10th

10th
11th

12th

11th
12th

2nd

new items
paging through
sorting

2nd

2nd

3rd

3rd

4th

4th

5th

5th

6th

6th

7th

7th

8th

8th

9th

9th

10th

10th

data grids
hyperlinking from rows to detail pages
data, displaying

2nd

2nd

datasets
filling (code)

2nd

events, wiring (code)


General properties, setting

2nd

loading
code
loading (code)

2nd

3rd

lookup tables
data, adding

2nd

objects
handling

2nd

3rd

Paging properties, setting


populating (code)
properties

2nd

2nd
3rd

property settings

2nd

refreshing (code)

2nd

schemas

2nd
3rd

4th

5th

4th
3rd

4th

5th

6th

3rd

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

9th

3rd

3rd

manipulating

5th
4th

4th

5th

6th

7th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

12th
13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

loading into data tables

2nd

DataGrid object
loading
code

2nd

products
filling and binding (code)
properties

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

5th

6th

7th

8th

9th

binding with ListBox control properties

2nd

11th

12th

13th

14th

15th

16th

17th

18th

19th

records, canceling editing/adding (code)


special instructions, code

DataGrid Property Builder


Columns tab
General tab
Paging tab
DataGrid Web server control

DataGrids
displaying

2nd

DataItem.RegionURL column
DataList Web server control
DataReader object
data, retrieving

2nd

3rd

4th

10th

11th

list boxes
loading (code)

2nd

3rd

methods
properties
datareader.Read( ) method
DataRepeater control
DataRow object
methods

2nd

properties

2nd

DataRowView object

DataSet
(data control)
controls

2nd

frmHowTo1_2.vb
code

2nd

DataSet control
DataSet controls
for frmHowTo1_9b forms

2nd

3rd

DataSet object
methods

passing
code
properties

2nd

3rd

4th

5th

6th

7th

8th

DataSet objects
data
editing

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

rows
adding
deleting

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

datasets
dsCustomerIndividual
records, adding (code)
loading (code)

2nd

2nd

passing from XML Web Services

2nd

3rd

refreshing
code

2nd

retrieving from XML Web Services (code)


returning as XML (code)

strongly typed

2nd

3rd

4th

5th

6th

7th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

VB .NET tools, writing ADO.NET code

2nd

3rd

tables

text boxes
binding

2nd

XML documents

2nd

3rd

creating (code)

2nd

reading (code)

2nd

4th

5th

6th

7th

8th

9th

8th

9th

DataSource property
DataSource property (list boxes)

DataTable
object
creating (code)

2nd

DataTable object

ComboBox control
loading (code)

2nd

3rd

creating
code

2nd

creating (code)

2nd

list boxes
loading (code)
methods
objects

2nd
2nd

properties

2nd

3rd

4th

5th

3rd

2nd

3rd

4th

5th

2nd

3rd

4th

5th

6th

7th

results, retrieving

2nd

3rd

4th

5th

6th

records, locating

SQL Server
tracking
code

2nd

3rd

4th

5th

DataTable.Columns object
methods
properties

DataTable.Rows object
methods

2nd

properties

2nd

DataTextField property
DataType property
DataValueField property

DataView
(data control)
DataView control
data grids, binding (code)
DataView object
records, filtering
records, sorting

2nd
2nd

3rd
3rd

4th
4th

RowFilter property, setting (code)

5th
5th

6th
6th

7th
7th

8th
8th

2nd

DateDiff function
Days_to_Ship, displaying
DateDiff( ) function
Days_to_Ship, displaying
datetime data type (SQL Server)

DateTimePicker object
properties
Days_To_Ship (SQL statements, displaying in labels)
db_accessadmin (fixed database role)
db_backupoperator (fixed database role)
db_datareader (fixed database role)
db_datawriter (fixed database role)
db_ddladmin (fixed database role)
db_denydatareader (fixed database role)
db_denydatawriter (fixed database role)

2nd

9th
9th

10th
10th

11th
11th

12th
12th

13th
13th

db_owner (fixed database role)


db_securityadmin (fixed database role)
dbcreator (fixed server role)

ddSortby control
SelectedIndexChanged event
routines, attaching
decimal data type (SQL Server)

declarations
CCustomer class properties (code)

2nd

class variables (code)


MaximumStringLengthExceededException class, code

2nd

Private
property (VB .NET code)
property (VB 6 code)
DECLARE statement

declaring
default properties
code

DefineValidChars method
code

delete method
code
InvalidCustomerIDException (code)

IsValid method
code

local variables
in T-SQL

2nd

public variables
code

save method
code

ThrowException method
code

2nd

Default keyword

default properties
classes, defining

declaring
code

in VB 6
code

default values
defining

2nd

3rd

4th

5th

6th

DefineValidChars method
declaring
code

defining
classes

2nd

3rd

4th

5th

default properties
in interfaces
methods, adding to interfaces

2nd

3rd

parameterized properties
properties

2nd

3rd

4th

properties, adding to interfaces


read-only properties

2nd

write-only properties

2nd

constraints

2nd

default values
fields
indexes

2nd

2nd

3rd

2nd

primary keys

3rd

4th
3rd

4th

5th
4th

5th

6th
5th

6th

6th

7th

8th

9th

3rd

4th

5th

6th

7th

8th

9th

2nd

3rd

4th

5th

6th

7th

8th

10th

9th

11th

table relationships
tables

2nd

3rd

2nd
4th

3rd

4th

5th

5th

6th

7th

8th

9th

6th

7th

8th

9th

10th

11th

3rd

4th

5th

6th

7th

8th

9th

10th

8th

9th

10th

definitions
class
Control.ViewState property
cookies
data driven
HTML hidden fields
inheritance
instances of classes
loosely coupled messages
members of classes
method signature
objects
query strings

definitios
XSD
(XML Schema Definition)
Delete button

Delete command
code

Delete key
clicking, effects
Delete method

2nd

delete method
declaring
code

Delete method
implementing
code

testing
code
delete object permission
Delete Record, creating
deletes, cascade

deleting
data
in DataGrid control

2nd

26th
lookup table information

2nd

records

5th

2nd

3rd

4th

6th

7th

8th

9th

5th

6th

10th

buttons
code

2nd

3rd

exceptions, catching (code)


in data grids (code)

2nd

2nd
3rd

3rd
4th

rows
in data grids (code)
in DataSet objects

2nd

3rd

2nd

3rd

4th

5th

6th

7th

5th

6th

7th

8th

9th

10th

Web Services, creating with parameters

2nd

tables

2nd

3rd

4th

DROP TABLE statement

2nd

DeliveryDate
values

2nd

demographics of customers, tracking


deny access level (object permissions)
deny state (statement permissions)
DESC (Sort property)
Description text box

2nd

descriptions
of methods

2nd

11th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

XML Web Services


viewing

2nd

2nd

3rd

4th

5th

XML Web Services Description

[See also WSDL]


design time
DataGrid control
descriptive languages.

columns, setting

2nd

Paging properties, setting

2nd

design view
database how-to

2nd

Design view
reports, displaying

2nd

designers
Report Designer
View Designer
Diagram pane
Grid pane properties
Results pane
SQL pane
tables

2nd

designes
Taable Designer

desktop applications (VB .NET)


generic search forms, creating
25th

26th

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

27th

DestinationOptions (ExportOptions object)


Detach button

Detach/Attach SQL Server Database dialog box


2nd

3rd

DetachDB method

creating

2nd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

detaching
databases

2nd

SQL Server database, code


detail data, retrieving

detail information of Product ID


loading (code)

2nd

detail pages
hyperlinks

2nd

rows, hyperlinking to

2nd

3rd

4th

5th

6th

7th

Details section of reports


fields

2nd

developing
Windows forms

2nd

Devices property
dgRegion_CancelCommand routine
dgRegion_DeleteCommand routine
dgRegion_EditCommand routine
dgRegion_UpdateCommand routine
Diagram pane (View Designer)

diagrams
databases
of classes

2nd

dialog boxes
Add Members

2nd

Add New User

2nd

3rd

Add Reference
Add Role Members

2nd

Add Table
connecting to databases
Create Database

2nd

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

19th

20th

21st

22nd

23rd

24th

Create Relationships
creating

2nd

2nd

3rd

4th

Crystal Report Gallery

3rd
5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

2nd

Data Link Properties


Database Role Properties
Permissions tab

2nd

2nd

Database User Properties

2nd

Detach/Attach SQL Server Database


creating

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

11th

12th

13th

14th

15th

Format
Generate Dataset
New Group

2nd

New Project
New Role

2nd

3rd

4th

OLE DB (ADO) dialog box


Properties
Windows Only option

2nd

Property Builder

Property Pages
indexes, creating

2nd

opening

Report Options
reports, customizing
Server Role Properties

2nd

2nd

SQL Server Login Properties

2nd

Standard Report Expert


tabs

2nd

Table Properties

2nd

Permissions tab

2nd

DialogResult, setting (code)

2nd

directories
XML Web Services Directories

disabled
Windows NT/2000 accounts

2nd

disabling
btnConnection button
code

2nd

text boxes
code

2nd

text boxes, code

2nd

disconnected data

discoveries
XML Web Services
Discovery
diskadmin (fixed server role)

disks
recordsets, persisting to (code)

2nd

3rd

displaying
ADO connections
code

2nd

connection string text

2nd

customer order header information

2nd

data
in bound list boxes

2nd

3rd

4th

5th

6th

7th

in DataGrid control

2nd

3rd

4th

5th

6th

7th

templates

2nd

URLs (uniform resource locators), creating


with Repeater control

2nd

3rd

with SELECT statement (code)


with Table control

8th

9th

10th

3rd

2nd

3rd

4th

2nd

5th

6th

7th

8th

2nd
4th

5th

6th

7th

8th

9th

9th

10th

16th

data in DataGrid control


DataGrids

2nd

2nd

Formula Editor
displaying

2nd

lists
look of in ValidationSummary control
message boxes

messages
in validation controls

order information
controls
records

2nd

2nd

3rd

in DISTINCT clause

2nd

regional descriptions
regional territories

2nd

3rd

4th

5th

RegionID
reports

2nd

3rd

in Design view

6th

7th

2nd

ReportSource property

2nd

with Crystal Report Viewer

3rd
2nd

3rd

4th

5th

6th

7th

8th

9th

SQL statements
in lables

2nd

table results

tables
with Table control

2nd

Windows NT/2000 domain users

2nd

DisplayMember property
DisplayMember property (list boxes)
Dispose method
DISTINCT clause
records, displaying

2nd

[See DMF]

Distributed Management Framework.

DMF
(Distributed Management Framework)

2nd

APIs (Application Programming Interfaces)

2nd

3rd

DOCUMENT object
properties

2nd

3rd

Document Object Model.

4th

5th

[See also XML, DOM]

DocumentElement (XMLDocument class)

documents
Report

2nd

Name property, setting


reports, exporting
reports, printing

2nd

3rd

4th

2nd

XML
creating (code)

2nd

3rd

4th

creating with XMLTextWriter


creating with XMLWriter
data tables, creating

5th

2nd

2nd

3rd

3rd

4th

4th

5th

6th

data, retrieving from SQL Server 2000 database

2nd

datasets

9th

2nd

reading (code)

3rd

7th

8th

4th

5th

6th

7th

8th

3rd

4th

2nd

reading into datasets (code)


reading with XMLReader

3rd

4th

5th

reading with XMLTextReader (code)

2nd

3rd

XMLTextWriter

2nd

2nd

3rd

XML Web Services Discovery

DoesCustomerIDExist function
code
DOM.

9th

10th

11th

2nd

[See also XML, DOM]

6th

7th

8th

5th

6th

7th

12th

13th

domains
Windows NT/2000
users, displaying

2nd

double quotation marks ("")


Drill Down reports

drilling
down to customer orders details
2nd

3rd

DROP TABLE statement

down to data

2nd

4th

5th

6th

7th

8th

9th

10th

11th

DropDown control
loading
code
populating

2nd
2nd

3rd

4th

3rd

5th

4th

5th

6th

7th

properties

DropDown object
properties

2nd

3rd

dsCustomerIndividual dataset
records, adding (code)

2nd

dsCustomers.xsd
Customers XSD, code

2nd

DTD
SQL-DTD objects
tables, copying

2nd

DTS
(Data Transformation Services)

packages
tasks and workflows
DTSPackage Object Library
Dynamic type cursor

[ Team LiB ]

2nd

2nd

8th

9th

10th

11th

12th

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
Edit button

Edit command
code

edit mode
data grids, setting to (code)
edit, remove SQL Server as subheading from servers main entry]

editing
data

2nd

3rd

4th

5th

command buttons

6th

7th

8th

9th

10th

2nd

in DataGrid control

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

in DataSet objects

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

26th

notifying users

2nd

data grids
cancelling (code)

2nd

lookup table information


records

2nd

3rd

2nd

4th

canceling in DataGrid object (code)


EditItemIndex

editors
Formula Editor
displaying

2nd

String Collection Editor

2nd

edits
canceling
code

2nd

element tags
Ellipsis button

Enabled properties
on Windows forms

2nd

enabled properties of buttons, toggling (code)

2nd

3rd

4th

5th

6th

7th

8th

9th

EnableTightHorizontal property

enabling
btnConnection button
code

2nd

Connect button

encoding
Unicode
encrypted security ID (SID)
END statement
EndCurrentEdit method
EndEdit method

engines
JET database engine

Enterprise Manager
Backup/Restore Wizard
SQL Server authentication mode

error handling
with bound controls

2nd

3rd

4th

5th

10th

error notices
ListAvailableSQLServer method
ErrorMessage property

errors
3621
of data, communicating to developers

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

20th

21st

22nd

23rd

24th

25th

throwing

2nd

unhandled
Errors collection/Error object

event handlers
code

for mFax variable


code

events
buttons in DataGrid control
buttons on data grids

2nd

2nd

CheckChanged
code

2nd

ItemCommand
Repeater control events, programming

Load
code

2nd

of Repeater control
programming

SelectedIndexChanged
code

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

code, adding
routines, attaching

TextChanged
code

2nd

3rd

4th

wiring for DataGrid control (code)

[See also click events]2nd [See also click events]3rd [See also click events]
exception handling
events.

code

exceptions
3621 error
application
ApplicationException
catching (code)

2nd

3rd

errors, unhandled

InvalidCustomerIDException
declaring (code)

InvalidPhoneNumberException
code

MaximumStringLengthExceededException
class declaration, code
passing (code)

2nd

save
trapping (code)

2nd

system
trapping with Try[ellipsis dots]Catch[ellipsis dots]End Try block
Try[ellipsis dots]Catch[ellipsis dots]Finally block
Try[ellipsis dots]Catch[ellipsis dots]Finally blocks
Execute button

2nd

2nd

Execute method
execute object permission
ExecuteABatchCommand routine
ExecuteReader method

executing
actions on forms
batch updates

2nd
2nd

3rd

4th

5th

bulk queries
code

2nd

on-the-fly batch updates in ADO.NET

2nd

3rd

parameterized stored procedures in ADO.NET

4th

2nd

5th

3rd

6th

4th

SQL Server stored procedures with ADO (ActiveX Data Objects)

5th
2nd

6th
3rd

7th
4th

8th

SQL statements
code

2nd

3rd

4th

stored procedures
code

6th

7th

objects, properties and methods

2nd

3rd

4th

5th

2nd

8th

9th

update statement
EXIST statement
expanding routines

Experts
(Crystal Reports)

experts
Formula Expert
Mailing Labels Expert
Standard Report Expert dialog box tabs

2nd

[See also Report Expert]

Experts.

EXPLICIT mode
Export button
Export method

Export tab page


objects
properties
ExportDestinationType (ExportOptions object)
ExportFormatType (ExportOptions object)

exporting
data
with SQL-DTS
files

2nd

reports

2nd

code

3rd

4th

5th

6th

7th

8th

2nd

ExportOptions object

2nd

DestinationOptions
ExportDestinationType
ExportFormatType
FormatOptions

expressions
tables

2nd

Extensible Markup Language.

extensions of files
.vb
.xsd

[ Team LiB ]

[See XML]

9th

10th

11th

12th

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
fields
adding in Report design
as hyperlinks

2nd

3rd

calculated, adding to reports


defining

2nd

3rd

2nd

4th

5th

Details section of reports

2nd

3rd

6th

4th

7th

5th

8th

9th

4th

5th

10th

11th

Formula

HTML hidden
definition
in recordsets, updating
in tables
of mailing labels

2nd

primary
index functions
primary key

2nd

2nd

properties

Fields tab
Standard Report Wizard

[See also columns]


file extensions
fields.

*.asmx

2nd

File menu commands


New

files
*.asmx extension

2nd

.vb extension
.xsd extension
attaching to databases
exporting

2nd

2nd

in databases
finding and attaching (code)

2nd

filling
datasets
code

2nd

3rd

4th

Letter Button result sets, code

2nd

3rd

filling and binding products to DataGrid object (code)


FillSchema method

2nd

Filter tab
Standard Report Wizard

filtering
records
with DataView object

2nd

3rd

4th

5th

6th

7th

finding
customers
in combo boxes
records

2nd

3rd

4th

5th

6th

inner joins
joins
left outer joins
right outer joins

2nd
2nd

rows
code

finding and attaching


database files, code
finding.

2nd

[See also locating]

7th

8th

9th

10th

8th

9th

10th

11th

12th

13th

fixed database roles


db_accessadmin
db_backupoperator
db_datareader
db_datawriter
db_ddladmin
db_denydatareader
db_denydatawriter
db_owner
db_securityadmin
public
fixed database roles (SQL Server security)

2nd

3rd

4th

5th

6th

7th

8th

fixed server roles


bulkadmin
dbcreator
diskadmin
processadmin
securityadmin
serveradmin
setupadmin
sysadmin
fixed server roles (SQL Server security)

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

float data type (SQL Server)


Flush (XMLTextWriter)

foreign keys
CustomerID column (Orders table)

2nd

Form Letter reports


Form reports
Format dialog box
FormatOptions (ExportOptions object)

formats
XML Web Services Wire Formats

formatting
code
UDFs (user-defined functions)

2nd

Formatting (XMLTextWriter)

forms
actions, executing

2nd

closing
code

2nd

closing (code)

3rd
2nd

command buttons, accessing

2nd

controls
arranging

2nd

3rd

bound at runtime

2nd

properties, setting
controls, arranging

2nd

creating
data bound

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

frmHowTo1_9b
DataSet controls

2nd

OleDataAdapter controls

3rd
2nd

3rd

frmMain

generic searches
creating, data-driven techniques

2nd

3rd

9th

10th

11th

12th

13th

14th

32nd

33rd

34th

35th

36th

37th

38th

39th

40th

41st

59th

60th

61st

62nd

63rd

64th

65th

66th

67th

68th

86th

87th

88th

89th

90th

91st

92nd

93rd

94th

95th

24th

25th

26th

27th

28th

29th

30th

31st

52nd

53rd

54th

55th

56th

57th

58th

79th

80th

81st

82nd

83rd

84th

85th

How-To 1.1, opening

2nd

loading

4th

5th

6th

7th

8th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

42nd

43rd

44th

45th

46th

47th

48th

49th

50th

69th

70th

71st

72nd

73rd

74th

75th

76th

77th

78th

96th

97th

98th

99th

100th

101st

51st

code

2nd

loading (code)

login
XML Web Services authentication

objects
changes to

2nd

opening
code

2nd

3rd

4th

order information, displaying (code)


public variables, declaring (code)

2nd

3rd

4th

2nd

record sources, searching

records
loading into text boxes (code)

2nd

3rd

search
calling

2nd

custom properties, creating (code)

2nd

3rd

4th

5th

6th

7th

SQL databases
backing up and verifying

tables
searching

2nd

3rd

4th

TextBox control, populating

2nd

unbound controls
user options

Windows objects
using in Web forms

[See also Windows forms]2nd [See also Windows Forms]3rd [See also Web Forms]

forms.

Formula Editor
displaying

2nd

Formula Expert
Formula fields

formulas
adding
tables

2nd

Forward Only type cursor


frameworks.

[See also DMF]

Friend object

frmHowTo.vb
constructors, testing, code

frmHowTo1_1
data sets, filling (code)

frmHowTo1_2.vb
code

2nd

frmHowTo1_3
Label control
property settings

2nd

3rd

4th

3rd

4th

TextBox control
property settings

2nd

frmHowTo1_3.vb
datasets
refreshing (code)

2nd

RefreshIndividual routine
calling (code)

2nd

RefreshIndividual subroutine
calls, code to add

2nd

frmHowTo1_4.vb
ActiveEditing subroutine
calling (code)

2nd

data
canceling changes (code)
saving to servers (code)

2nd
2nd

SaveRecord routine
calling (code)

2nd

text boxes
disabling (code)

2nd

toggling (code)

3rd

4th

2nd

frmHowTo1_5.vb
data
saving (code)

2nd

dsCustomerIndividual dataset
records, adding (code)

2nd

edits
canceling (code)

2nd

list boxes
reloading (code)

2nd

3rd

4th

5th

mbAddNew variable
resetting (code)

2nd

records
deleting (code)

2nd

3rd

2nd

3rd

text boxes
toggling (code)

4th

frmHowTo1_6.vb
btnSave click event
trapping save exceptions (code)

2nd

exceptions
catching (code)
passing (code)

2nd

3rd

2nd

frmHowTo1_7.vb
enabled properties of buttons, toggling (code)
forms, closing (code)

2nd

2nd

records, saving before closing (code)

2nd

frmHowTo1_8.vb
DataGrid control
refreshing (code)

2nd

datasets
filling (code)

2nd

3rd

4th

frmHowTo1_9
forms
opening (code)

2nd

frmHowTo1_9b
datasets
loading (code)

2nd

forms
DataSet controls

2nd

3rd

OleDataAdapter controls

2nd

3rd

frmHowTo10_4.vb
reports
exporting (code)

2nd

printing (code)

frmHowTo10_5.vb
Selection combo box
populating (code)

2nd

3rd

SelectionFormula property
setting (code)

frmHowTo13_3.vb
passwords, validating (code)
usernames, validating (code)

frmHowTo13_4.vb
datasets, retrieving from XML Web Services (code)

frmHowTo3_1.vb
list boxes, loading (code)

2nd

3rd

3rd

4th

5th

frmHowTo3_2.vb
list boxes, loading (code)

2nd

frmHowTo3_3.vb
ComboBox control, loading (code)
records, locating (code)

2nd

3rd

2nd

text boxes, assigning values (code)

2nd

frmHowTo3_4.vb
button controls, adding click events (code)
columns, sorting (code)

2nd

ComboBox control, adding column names (code)


data tables, loading (code)

2nd

2nd

3rd

3rd

DataView object, setting RowFilter property (code)

2nd

frmHowTo6_1.vb
data, regenerating (code)
forms, loading (code)
SQL strings, building (code)

frmHowTo6_2.vb
SQL statements
storing and executing (code)

2nd

frmHowTo6_3.vb
data, generating (code)

2nd

GenerateData routine, calling (code)

2nd

3rd

frmHowTo6_4.vb
left outer joins, code
frmHowTo6_5.vb

SQL statements
loading and executing

frmHowTo6_6.vb
PerformTask( ) function, calling (code)

2nd

3rd

SQL statements, executing (code)


SQL statements, loading (code)

2nd

frmHowTo6_7.vb
forms, loading (code)

2nd

SQL statements, storing (code)

2nd

frmHowTo6_8.vb
UDFs (user-defined functions)
assigning (code)
creating (code)

2nd
2nd

3rd

4th

5th

data, displaying (code)


SELECT statement, updating (code)

2nd

frmHowTo7_1.vb
btnConnection button
disabling (code)

2nd

enabling (code)

2nd

database list boxes, loading (code)

2nd

3rd

4th

list boxes, repopulating (code)

routines
calling (code)

2nd

3rd

list boxes, reloading (code)

2nd

SQL Servers
loading (code)

2nd

frmHowTo7_2.vb
1stBackupDevices list box, populating (code)
1stDatabases list box, populating (code)
backups, performing (code)

2nd

3rd

4th

routines
SQL Servers, loading (code)

frmHowTo7_3.vb
1stBackupDevices list box, populating (code)
1stDatabases list box, populating (code)

5th

6th

7th

backups, performing (code)

2nd

3rd

routines, calling (code)


SQL Servers, loading (code)
frmHowTo7_4.vb
1stBackupDevices list box, populating (code)
1stDatabases list box, populating (code)

2nd

2nd

forms, closing (code)


routines, calling (code)
SQL Servers, loading (code)
tables, copying (code)

2nd

3rd

4th

5th

frmHowTo7_5.vb
1stDatabases list box, populating (code)
btnDetach button, toggling (code)
database files, finding and attaching (code)
forms, closing (code)

2nd

2nd

routines, calling (code)


SQL Server database, detaching (code)
SQL Server databases, attaching (code)

2nd

SQL Servers, loading (code)

frmHowTo8_1.vb
1stUnSelected list box
reloading (code)

list boxes
populating (code)

2nd

3rd

4th

list boxes, loading categories (code)

5th

6th

2nd

routines
calling (code)

servers
updating (code)

2nd

3rd

4th

5th

6th

frmHowTo8_2.vb
1stLookupTables
pointing to items (code)

2nd

connection strings
establishing (code)

2nd

DataGrid control
populating (code)

2nd

3rd

4th

5th

frmHowTo8_3.vb
columns
listing in databases (code)

2nd

DataGrid control, loading (code)


SQL strings, creating (code)

2nd

stored procedures, executing (code)

2nd

3rd

4th

5th

tables
listing in databases (code)

2nd

3rd

frmHowTo8_4.vb
search forms, creating custom properties (code)

2nd

stored procedures, executing (code)

tables
listing in databases (code)
frmHowTo8_4a.vb
Letter Button result sets, filling (code)

2nd

records
loading into text boxes (code)

2nd

3rd

frmHowTo8_4b.vb
DialogResult, setting (code)
key values, storing (code)

2nd
2nd

Letter Button controls, click events (code)


search forms, creating custom properties (code)
stored procedures, executing (code)

tables

2nd

3rd

listing in databases (code)

frmHowTo8_6.aspx
data grids
page indexes, updating (code)
records, deleting (code)

2nd

3rd

4th

5th

6th

records, posting (code)

2nd

3rd

4th

5th

6th

2nd

3rd

data tables
records, adding (code)

DataGrid object
records, canceling editing/adding (code)
special instructions (code)

DataTable object
tracking (code)

2nd

3rd

4th

listing in databases (code)

2nd

5th

frmHowTo8_7.vb
columns
DataGrid control
loading (code)

2nd

3rd

SQL strings
creating (code)

2nd

3rd

4th

stored procedures, executing (code)

2nd

3rd

tables
listing in databases (code)

2nd

frmHowTo8_8a.vb
Letter Button result sets, filling (code)

2nd

records
loading (code)

2nd

3rd

Session object
values, storing (code)

frmHowTo8_8b.vb
calling pages, returning to (code)

2nd

data grids
updating (code)
key values, storing (code)

2nd

Letter Button controls, click events (code)


stored procedures, executing (code)

tables
listing in databases (code)

frmHowTo9_2.vb
CCustomer class
code

2nd

property declarations (code)


class variable declarations (code)

Customer class
code
property declarations (VB .NET code)
property declarations (VB 6 code)

TextBoxChange method
text boxes, writing values (code)

2nd

TextChanged event (code)

ToString method
property information output (code)

2nd

frmHowTo9_4.vb
CCustomer class, constructors (code)

2nd

ReadValuesFromDataRow method, code

frmHowTo9_5.vb
Clear method, code

2nd

constructors
rows, inserting into tables (code)
constructors, testing (code)

2nd

4th

Delete method, implementing (code)


Delete method, testing (code)
Save method, implementing (code)

2nd

3rd

4th

5th

Save method, testing (code)


WriteChangesToDB method, code

2nd

frmHowTo9_6
ContactName property, code
InvalidPhoneNumberException, code

MaximumStringLengthExceededException
class declaration, code
phone numbers, validating (code)

2nd

3rd

frmHowTo9_6.vb
DoesCustomerIDExist function (code)
If[ellipsis dots] Then block (code)
InvalidCustomerIDException, declaring (code)
ValidateCustomerID method (code)

frmHowTo9_7.vb
event handlers, code
frmMain form

frmMain.vb
ADO connections
opening and displaying (code)

forms
opening (code)

2nd

routines
calling (code)

functions
BuildCnnStr

2nd

BuildCnnStr( )

2nd

built-in
T-SQL

2nd

CType( )

DateDiff
Days_to_Ship, displaying
DateDiff( )
Days_to_Ship, displaying

DoesCustomerIDExist
code

2nd

In-Line
of indexes

2nd

PerformTask( )
calling (code)

2nd

3rd

2nd

3rd

scaler
T-SQL commands

4th

5th

6th

Table
Trim

UDFs
(user-defined functions)
calling

2nd

3rd

code, formatting
creating

2nd

4th

5th

3rd

4th

5th

parameters, passing
scalar types, returning
table types, returning

2nd
2nd

userk-defined
functions.

[See also UDFs]

[ Team LiB ]

6th

7th

8th

9th

10th

11th

12th

2nd
6th

7th

8th

9th

10th

11th

12th

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
General properties
in DataGrid control, setting

2nd

General tab (DataGrid Property Builder)


Generate Dataset command (pop-up menu)

2nd

Generate Dataset dialog box


Generate the SQL Statements page (DataAdapter Configuration Wizard)

2nd

GenerateData routine
calling
code

2nd

3rd

creating

generating
data
code

2nd

generic search forms


creating, data-driven techniques
25th

26th

27th

28th

29th

2nd

30th

3rd

31st

4th

32nd

5th
33rd

6th

7th

34th

35th

GetBackupDevices routine
creating
GetPageNum( ) routine
GetPageRows( ) routine
GetSQLDatabases routine

2nd

creating

GetString Display
records
displaying
GetString method

2nd

3rd

2nd

GetUserInfo method

2nd

grant access level (object permissions)


grant state (statement permissions)

Grid pane (View Designer)


properties

grids
data
sorting, setting
grids.

2nd

[See also data grids]

Group By property
Group Name text box

2nd

Group tab
Standard Report Wizard

Groupbox object
properties

GroupBox object
properties

groupings
reports

2nd

groups
creating for Windows NT/2000
Names list

2nd

[ Team LiB ]

2nd

3rd

4th

5th

6th

8th

9th

36th

10th

37th

11th

38th

12th

39th

13th

40th

14th

41st

15th

42nd

16th

43rd

17th

44th

18th

45th

19th

46th

20th

47th

21st

48th

22nd

49th

23rd

50th

24th

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
[See event handlers]
handling exceptions
handlers.

code

headers
customer orders
information, displaying
Hello World method, testing

2nd

2nd

help
.NET Framework Developer's Guide

hidden fields (HTML)


definition
How-To 1.1 form, opening

2nd

how-to's
database, design view

2nd

HTML
(Hypertext Markup Language)

code
repRegions Repeater control
repTerritories Repeater control

controls
and Web server controls, comparing

2nd

hidden fields
definition

http[colon, no spaces]//localhost/WebService1/Service1.asmx?WSDL
XML Web Services, SOAP definition (code)

2nd

3rd

4th

5th

HyperLink columns

HyperLink control
properties

Hyperlink control
properties

Hyperlink object
properties

HyperLink object
properties

2nd

Hyperlink object
properties

2nd

HyperLink object
properties

Hyperlink object
properties

2nd

3rd

4th

5th

6th

7th

8th

hyperlinking
in data grids, from rows to detail pages

2nd

hyperlinks
action options

2nd

BorderColor property
BottomLineStyle property
detail pages

2nd

EnableTightHorizontal property
on-screen reports

2nd

fields as hyperlinks

3rd

4th

2nd

3rd

Web site fields, adding


real-time

5th

6th

2nd

2nd

regional descriptions, displaying


Hypertext Markup Language.

[See HTML]

3rd

4th

5th

6th

7th

[ Team LiB ]

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
icons
Server Explorer
Server Roles

2nd

toolbox

ICustomer interface
code

properties
code
Identity property

IDs
CustomerID column (Customers table)
CustomerID column (Orders table)

2nd

2nd

RegionID, displaying
SID (encrypted security ID)
IF statement

If[ellipsis dots] Then block


code
image data type (SQL Server)

implementing
intertaces

2nd

3rd

4th

5th

6th

7th

8th

Implements ICustomer.[Property/Method Name]

9th

2nd

In-Line function

indexes
compound
constraints

creating
with Property Pages dialog box

2nd

data grid pages


updating (code)
defining
functions

2nd

2nd

3rd

4th

5th

6th

7th

8th

2nd

performance

2nd

Indexes/Keys command (View menu)

2nd

indices
Items (index) collection
SelectedIndices (index) collection

information
retrieving from data grids

2nd

infrastructures
XML Web Services

2nd

3rd

inheritance
definition
inheritance permission keywords of classes

initializing
local variables
in T-SQL

2nd

Web pages
code

2nd

3rd

inner joins
records

2nd

3rd

records, finding
inner joins (tables)
insert object permission

inserting
records

4th

5th

6th

2nd

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

instances
of classes
definition
int data type (SQL Server)

integrity
referential (database tables)

2nd

3rd

interfaces
classes
defining
creating

CustomerInterface9_1.vb
code

2nd

delete method, declaring (code)


ReadOnly keyword, code
save method, declaring (code)

ICustomer
code
properties (code)
implementing

2nd

methods, adding

3rd

2nd

4th

5th

6th

7th

8th

9th

3rd

properties, adding
XML Web Services

international phone number extensions


code

2nd

interpreting
null valuees

InvalidCustomerIDException
declaring (code)

InvalidPhoneNumberException
code
Invoke button
IsPostBack property

2nd

IsValid method
declaring
code
IsValid property

ItemCommand event
Repeater control events, programming
Items (index) collection

Items collection
data, adding

[ Team LiB ]

10th

11th

12th

13th

14th

15th

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
JET
databases
opening (code)

2nd

JET database engine

joining
taables

2nd

joins
inner
records

2nd

3rd

4th

records, finding
reords
inner (tables)

left outer
code
records, finding

2nd

records, finding

right outer
records, finding

[ Team LiB ]

2nd

5th

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
key values, storing (code)

2nd

3rd

4th

keys
Delete
clicking, effects

primary
defining

2nd

3rd

4th

5th

6th

7th

8th

9th

[See primary keys]2nd [See also foreign keys]


keywords
keys.

class inheritance permissions


Default
member override
MustInherit

2nd

2nd

abstract classes
MustOverride
Nothing
NotInheritable
NotOverrideable
Overrideable
Overrides

2nd

ReadOnly
code

[ Team LiB ]

2nd

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
Label control
properties

2nd

settings

3rd

2nd

4th

3rd

5th

settings for frmHowto_3


property settings

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

9th

10th

11th

12th

13th

14th

15th

16th

4th

2nd

3rd

2nd

3rd

4th

4th

5th

6th

7th

8th

Label object
properties
28th

29th

2nd

30th

3rd

31st

4th

5th

32nd

6th

33rd

7th

34th

8th

35th

9th
36th

10th
37th

11th
38th

12th
39th

13th
40th

14th
41st

15th
42nd

property
property settings

2nd

Text property

Label, TextBox, and Command button


control property settings

2nd

Label, TextBox, ListBox, and Command button


property settings

2nd

labels
lblSQLString
SQL statements, storing (code)

2nd

mailing
appearance control
creating
fields

2nd

3rd

2nd

sorting
printing

2nd

2nd

2nd

3rd

4th

5th

6th

7th

8th

SQL statements
displaying

2nd

loading (code)

2nd

UDFs (user-defined functions)


assigning (code)
languages.

2nd

3rd

[See also XML]2nd [See also HTML]3rd [See also WSDL]

LANs
(local area networks)

lblSQLString label
SQL statements
storing (code)

2nd

left (<) left arrow button

left outer joins


code
records, finding

2nd

Length property

Letter Button controls


click events
code

2nd

Letter Button result sets, filling (code)

2nd

letters
data, displaying in DataGrid control

levels of access
deny (object permissions)
grant (object permissions)
revoke (object permissions)

librariees
DTSPackage Object Library

libraries
accessing
COM type, registering

2nd

3rd

4th

16th

17th

18th

43rd

44th

45th

19th

20th

21st

22nd

23rd

24th

25th

26th

27th

SQLDMO Object Library

type
referencing with ADO (ActiveX Data Objects)

2nd

3rd

licensing
Crystal Reports

LIKE statement
data, generating (code)

2nd

lines
wavy blue (code)

linking
Web pages

list boxes
1stBackupDevices
populating (code)

2nd

3rd

4th

2nd

3rd

4th

1stDatabases
populating (code)

5th

1stUnSelected
reloading (code)

2nd

1stUnselected
reloading (code)

2nd

categories
loading (code)

2nd

3rd

4th

5th

data
loading

2nd

data-bound multi-select
data-driven techniques
26th

27th

28th

29th

30th

2nd

3rd

31st

32nd

4th

5th

33rd

6th

7th

8th

9th

34th

35th

36th

37th

9th

10th

11th

12th

10th

databases
loading (code)

2nd

DataSource property
DisplayMember property
loading (code)

2nd

3rd

4th

5th

5th

6th

lstSQLServers
code

multi-select
on Web Forms

2nd

populating
code

2nd

3rd

4th

7th

8th

reloading
code

2nd

3rd

reloading (code)

2nd

repopulating (code)
Selected Products ListBox control

SelectedIndexChanged event
code

2nd

3rd

4th

5th

SQL Servers
loading (code)

2nd

3rd

4th

5th

6th

7th

8th

9th

binding and viewing

2nd

3rd

4th

5th

6th

7th

8th

text boxes
binding

2nd
9th

Unselected Products ListBox control


ValueMember property
ListAvailableSQLServer method
error notice

ListBox control
loading
populating

2nd

3rd

4th

5th

properties

2nd

3rd

4th

5th

setting to bind DataSet control


settings

2nd

3rd

4th

6th

7th

2nd

8th

9th

10th

11th

10th

38th

11th
39th

12th
40th

13th
41st

14th
42nd

15th

16th

43rd

17th

44th

18th

45th

19th

46th

20th

21st

22nd

23rd

24th

25th

property settings

2nd

3rd

4th

5th

6th

7th

ListBox controls
properties

2nd

ListBox object
properties

2nd

3rd

4th

5th

6th

7th

8th

property
ListBox Web server control
ListBox Windows Forms control

ListBox1
frmHowTo1_1
data sets, filling (code)

listing
columns in databases
code

2nd

3rd

4th

customer information
code

SQL Servers
in SQL-DMO

2nd

tables in databases
code

2nd

3rd

4th

5th

6th

7th

8th

lists
based on Customers table
database, refreshing

2nd

look of in ValidationSummary control

Names
groups and users

2nd

region descriptions, displaying


RegionID, displaying
Tables in Report list

2nd

territories, displaying

2nd

3rd

[See also bound list boxes]


Load event
lists.

code

2nd

Load List button

2nd

3rd

LoadIndividual routine
creating

2nd

creating (code)

2nd

3rd

loading
categories
into list boxes (code)

2nd

ComboBox control
code

2nd

3rd

data
into list boxes

2nd

data tables
code

2nd

3rd

database list boxes


code

2nd

3rd

4th

DataGrid control
code

2nd

3rd

loading
DataGrid object (code)

2nd

datasets
code

2nd

DropDown control
code

2nd

3rd

4th

2nd

3rd

4th

3rd

4th

5th

forms
code

list boxes
code

2nd

5th

3rd

4th

5th

9th

10th

11th

12th

13th

14th

ListBox control
ProductID detail information (code)

2nd

records
into text boxes (code)

2nd

3rd

4th

5th

6th

repRegion Repeater control


code

repTerritories Repeater control


code

schemas
into data tables

2nd

SQL Servers
code

2nd

3rd

4th

5th

6th

7th

8th

9th

SQL statements
code

2nd

Table control
code

2nd

3rd

4th

5th

Web pages
code

2nd

LoadList routine
creating (code)

2nd

LoadProducts routine
calling
code

LoadSelectedProducts routine
creating

2nd

LoadSQLServers routine

2nd

LoadSQLString routine
LoadTables( ) subroutine

LoadUnSelectedProducts routine
creating
LoadUnSelectedProducts subroutine
LoadUnUnselectedProducts subroutine
LoadXML (XMLDocument class)

[See LANs]
local databases
local area networks.

ADO (ActiveX Data Objects)

2nd

local variables
declaring
in T-SQL

2nd

initializing
in T-SQL

2nd

Locate File button

locating
records
code

2nd

with DataTable object

2nd

3rd

4th

5th

6th

7th

8th

9th

login forms
XML Web Services authentication

logins
databases
using Windows NT Integrated Security
standard, creating

2nd

3rd

4th

5th

Windows NT/2000

2nd

3rd

4th

5th

6th

lookup tables
data, adding

2nd

information, managing
updating

2nd

2nd

data-driven techniques
26th

27th

28th

29th

30th

2nd

3rd

31st

32nd

loosely coupled messages, definition

4th

5th

33rd

6th

34th

7th
35th

8th
36th

9th
37th

10th
38th

11th
39th

12th
40th

13th
41st

14th
42nd

15th

16th

43rd

17th

44th

18th

45th

19th

46th

20th

21st

22nd

23rd

24th

25th

lstSQLServers list box


code

[ Team LiB ]

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M ] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
m (adding to code)
Mail Labels reports

mailing labels
appearance control
creating
fields

2nd

2nd

3rd

2nd

sorting

2nd

Mailing Labels Expert

Main forms
frmMain

management
state
on clients
server-side solutions

2nd

3rd

Session object

3rd

4th

2nd

4th

5th

managers
Enterprise Manager
Backup/Restore Wizard
SQL Server authentication mode
manipulating data

2nd

many-to-many relationship (tables)

mapping
Table Mappings collection

2nd

[See also XML]2nd [See also HTML]


MaximumStringLengthExceededException
markup languages.

class declaration, code

mbAddNew variable
resetting
code

2nd

mdtProducts DataTable object


rows
finding (code)

members
of classes
definition

message boxes
displaying

messages
displaying
in validation controls
loosely coupled, definition

methodds
AddNew
BeginEdit
MustOverride keyword
RejectChanges

methods
AcceptChanges
AcceptChanges method
Add
adding to interfaces

2nd

3rd

AddNew
AddObjectForTransfer
Application object
AttachDBWithSingleFile

2nd

AttributeCount
Backup object
BackupDevice object
BackupDevices object
BeginLoadData

Clear
code

2nd

Close
Command object
CommandBuilder object
Connection object
CustomTask object
data, handling

2nd

DataAdapter object

3rd
2nd

3rd

DataAdapter Update
database updates

2nd

3rd

4th

5th

6th

7th

8th

DataBind
Databind
DataColumn object
DataReader object
datareader.Read( )
DataRow object

2nd

DataSet object
DataTable object

2nd

3rd

4th

5th

DataTable.Columns object
DataTable.Rows object

2nd

DefineValidChars
declaring (code)
Delete

2nd

delete
declaring (code)

Delete
implementing
implementing (code)

2nd

testing (code)
descriptions
DetachDB

2nd
2nd

Dispose
EndCurrentEdit
EndEdit
Execute
ExecuteReader
Export
FillSchema
GetString

2nd
2nd

GetUserInfo

2nd

Hello World, testing

2nd

Implements ICustomer.[Property/Method Name]

IsValid
declaring (code)
ListAvailableSQLServer
error notice
NodeType

of objects
executing stored procedures

overloaded
code
overloading

2nd

Option Strict
overrideable

2nd

2nd

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

Package object
Page.Validate
PrinterToPrinter
QueryResults object
Read

2nd

ReadBackupHeader
ReadValuesFromDataRow
code

2nd

2nd

RefreshReport
RejectChanges
RemoveAt
Restore object

2nd

Save

save
declaring (code)

Save
implementing
implementing (code)

2nd

3rd

4th

testing (code)

signature
definition
SQLBackup
SQLRestore
SQLServer object
Step object
Task object
TestUserPassword

2nd

3rd

TextBoxChange
text boxes, writing values (code)

ThrowException
declaring (code)
ToString
property information output (code)
update
Update

2nd

3rd

UpdateCommand
Validate

ValidateCustomerID
code

2nd

Value

WebMethod
passwords, validating (code)

2nd

usernames, validating (code)

2nd

WriteChangesToDB
code

2nd

XML Web Services


calling

2nd

XML Web Services Description


XMLDocument
XMLDocument class
XMLNode
XMLNode class
XMLTextWriter

2nd

mFax variable
event handlers
code
Microsoft Office 200x
minus sign (-)
mixed-mode authentication

models

2nd

5th

6th

7th

XML DOM (Document Object Model)

2nd

3rd

4th

5th

6th

[See also object models]

models.

modes
authentication
Windows NT/2000

2nd

3rd

4th

5th

6th

7th

8th

AUTO

edit
data grids, setting to (code)
EXPLICIT
mixed-mode authentication

2nd

3rd

4th

RAW
modGeneralRoutines.vb

connection strings
creating (code)

2nd

3rd

4th

connection strings, creating (code)

2nd

3rd

4th

5th

7th

8th

9th

10th

modifiers
Public access

2nd

VB .NET properties

modifying
tables

5th

6th

ALTER TABLE statement

2nd

3rd

4th

2nd

modPublicVariables.vb
public variables, declaring (code)

modSQLDMORoutines.vb
backup device names, retrieving (code)
connection strings, creating (code)

2nd

database names, retrieving (code)

2nd

SQL Servers
loading (code)

2nd

money data type (SQL Server)

multi-select list boxes


on Web Forms

2nd

MustInherit keyword

2nd

abstract classes
MustOverride keyword

[ Team LiB ]

3rd

11th

7th

8th

9th

10th

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
Name property

names
of backup devices, retrieving (code)
of databases, retrieving (code)

2nd

3rd

2nd

3rd

Names list
groups and users

2nd

namespaces
.NET
bound list boxes, creating

XML (Extensible Markup Language)

4th
2nd

System.XML
System.XML.Schema
System.XML.Xpath
System.XML.XSL
Visual Basic
XML Web Services
namespaces.

2nd

[See also SQL-NS]

naming
objects

reports
with rptReportName
Web Forms
nchar data type (SQL Server)

networks
LANs
(local area networks)
New command (File menu)
New Connection button
New Database Role command (shortcut menu)
New Database User command (shortcut menu)
New Group command (Action menu)
New Group dialog box

2nd

New Login command (Actions menu)


New Login command (context menu)
New Login command (shortcut menu)
New Prject dialog box
New Role dialog box

2nd

3rd

4th

NodeType method

Northwind
connections
creating

2nd

Unassigned Products Only check box

Northwind database
customers
demographics, tracking
Orders table
Web site fields, adding

2nd

Northwind SQL Server database


Customers table list
CustOrdersHist stored procedure, T-SQL code
Nothing keyword
NotInheritable keyword
NotOverrideable keyword
ntext data type (SQL Server)

NULL

5th

columns
records

null values
Allow Nulls
null values, interpreting
number sign (#)

numbers
phone
validating (code)

2nd

numeric data type (SQL Server)


nvarchar data type (SQL Server)

[ Team LiB ]

3rd

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
object
ComboBox
properties

DataGrid
properties

Label
properties
Object Browser

object models
ADO
(ActiveX Data Objects)

2nd

ADODB
(ActiveX Data Objects 2.7 object model)

2nd

3rd

object permissions
delete
deny access level
execute
grant access level
insert
revoke access level
select
update
object permissions (SQL Server security)

2nd

3rd

4th

5th

6th

object-based constructors
for CCustomerID class (code)

2nd

objects
ADO.NET

2nd

3rd

4th

5th

6th

7th

8th

9th

4th

5th

6th

7th

8th

Application
methods
properties
Backup

2nd

methods
properties

BackupDevice
methods
properties

BackupDevices
methods
properties
BindingContext

2nd

BindingManagerBase

2nd

Button
properties

2nd

3rd

2nd

3rd

2nd

3rd

9th

28th

CheckBox
properties

ComboBox
properties

property settings
Command

2nd

4th

2nd

3rd

actions, executing with SQL statements

2nd

methods
properties
TextBox control, populating with stored procedures

Command Button

2nd

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

27th

properties

2nd

3rd

4th

5th

6th

CommandBuilder
methods
properties
CommandBuilider

CompareValidator
properties
Connection

2nd

ADO (ActiveX Data Objects)

2nd

3rd

4th

5th

methods
properties
constructors
creating

2nd

with SQL syntax

CrystalReportViewer
property settings

2nd

CustomTask
methods
properties
data, handling

2nd

3rd

DataAdapter
methods

2nd

properties

3rd

2nd

3rd

DataColumn
methods
properties
DataDefinition

DataGrid
loading (code)

2nd

products, filling and binding (code)


properties

2nd

3rd

4th

5th

2nd
6th

7th

8th

9th

10th

6th

7th

8th

9th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

records, canceling editing/adding (code)


setting to edit mode (code)
special instructions (code)
DataReader
data, retrieving

2nd

3rd

list boxes, loading (code)

4th
2nd

5th

10th

11th

3rd

methods
properties
DataRow
methods

2nd

properties

2nd

DataRowView
DataSet
data, editing

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

9th

10th

methods
passing (code)
properties

2nd

rows, adding

3rd

2nd

rows, deleting

4th

3rd

2nd

5th

4th

3rd

6th

5th

4th

7th

6th

5th

8th

7th

6th

8th

7th

8th

9th

DataTable
ComboBox control, loading (code)
creating (code)

2nd

3rd

4th

list boxes, loading (code)

2nd

methods

5th

objects

2nd
2nd

properties

3rd

4th

3rd
6th

3rd

2nd

records, locating

3rd

4th

5th

2nd

3rd

4th

SQL Server, retrieving results


tracking (code)

2nd
5th

2nd

3rd

5th

2nd

4th

3rd

5th

6th
4th

7th
5th

8th
6th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

21st

DataTable object

2nd

3rd

DataTable.Columns
methods
properties

DataTable.Rows
methods

2nd

properties

2nd

DataView
records, filtering
records, sorting

2nd
2nd

3rd
3rd

4th

5th

4th

5th

RowFilter property, setting (code)

6th

7th

6th

7th

7th

8th

8th

9th

8th

9th

9th

10th

10th
10th

11th
11th

12th
12th

13th
13th

2nd

DateTimePicker
properties
definition

DOCUMENT
properties

2nd

3rd

4th

5th

2nd

3rd

3rd

4th

5th

6th

2nd

3rd

4th

5th

6th

31st

32nd

DropDown
properties

DTSPackage Object Library


Errors collection/Error
ExportOptions

2nd

DestinationOptions
ExportDestinationType
ExportFormatType
FormatOptions
Friend

Groupbox
properties

GroupBox
properties

Hyperlink
properties

HyperLink
properties

2nd

Hyperlink
properties

2nd

HyperLink
properties

Hyperlink
properties

2nd

Label
properties
28th

29th

30th

property settings

33rd

34th

7th
35th

8th
36th

37th

38th

11th
39th

12th
40th

13th
41st

14th
42nd

15th
43rd

2nd

Text property

ListBox
properties

2nd

3rd

4th

5th

6th

mdtProducts DataTable
rows, finding (code)

methods
for executing stored procedures

OldDBCommand
CommandText property

OleDataAdapter
properties

2nd

3rd

4th

OleDb and SQLClient, comparing


OleDbCommandBuilder
OleDbDataAdapter
properties

2nd

on forms, changes to

3rd
2nd

5th

2nd

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th
44th

17th
45th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

27th

Package
methods
properties

Panel
properties

2nd

3rd

Parameter
PrimaryKey

PrintOptions
properties
Private

properties
for executing stored procedures

2nd

Protected
Protected Friend
Public
QueryResults
methods
properties

Radio Button
properties

RadioButton
properties

RangeValidator
properties
ReadOnly
Recordset

2nd

ADO (ActiveX Data Objects)

2nd

3rd

4th

5th

6th

7th

RegularExpressionValidator
properties

Repeater
properties

RequiredFieldValidator
properties
Restore

2nd

methods

2nd

properties

2nd

Session
.NET Framework Developer's Guide
Nothing keyword
values, storing (code)

SQL Server
creating (code)
creating with ADO (ActiveX Data Objects)
SQL-DMO

2nd

3rd

4th

5th

6th

7th

(SQL-Distributed Management Objects)


databases, backing up and verifying

SQL-DTD
tables, copying

2nd

SQL-DTS
data, exporting
SQLDMO Object Library

SQLServer
methods
properties

state management
values, storing
Step
methods
properties
Stream

2nd

8th

2nd

3rd

4th

8th

9th

10th

11th

12th

Table
properties

TableCell
properties

TableRow
properties
Task
methods
properties

TextArea
properties
TextBox

2nd

2nd

properties
28th

29th

30th

2nd

3rd

31st

32nd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

ValidationSummary
properties

values
storing

Windows forms
using in Web forms
WriteOnly
XML DOM (Document Object Model)

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

[See also SQL Server database objects]2nd [See also ADO]3rd [See also RDO]4th [See also DAO]
objectscommand buttons
objects.

properties

objets
naming

OldDBCommand object
CommandText property

OLE DB (ADO) data provider


choosing

2nd

3rd

4th

OLE DB (ADO) dialog box

OleDataAdapter controls
for frmHowTo1_9b forms

2nd

3rd

OleDataAdapter object
properties

2nd

3rd

4th

5th

OleDb classes
performance improvement

2nd

OleDb objects
and SQLClient objects, comparing

OleDbCommand
(data control)
OleDbCommandBuilder object

OleDbConnection
(data control)

OleDbDataAdapter
(data control)
? (question mark)
controls

2nd

OleDbDataAdapter object
properties

2nd

3rd

on-screen reports
fields as hyperlinks

2nd

Web site fields, adding

3rd

2nd

on-screen reports, creating with hyperlinks

2nd

3rd

on-the-fly batch updates


creating in ADO.NET
executing in ADO.NET

2nd

3rd

2nd

one-to-many relationship (tables)


one-to-one relationship (tables)

4th

3rd
2nd

4th

5th
5th

6th
6th

4th

5th

6th

24th

25th

26th

27th

Open a Recordset button


OpenAndDisplayADOConnection routine
OpenFileDialog control

opening
ADO connections
code

2nd

forms
code

2nd

3rd

How-To 1.1 form

4th

2nd

JET databases
code

2nd

Property Pages dialog box

recordsets
code

2nd

3rd

4th

OpenNorthwindADOConnection subroutine

operators
BETWEEN
syntax

2nd

Option Strict (overloaded methods)


Or property

order information
controls to display

2nd

OrderID
values

2nd

orders
details, drilling down to

information
displaying (code)

2nd

3rd

Orders table
CustomerID column

2nd

organizing
reports
groupings

2nd

Output property

overloaded methods
code

overloading
methods

2nd

Option Strict
Overrideable keyword
overrideable methods
Overrides keyword

2nd

overriding
member overriding keywords

[ Team LiB ]

2nd

4th

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
Package object
methods
properties

packages
DTS
tasks and workflows

2nd

page indexes
updating
in data grids (code)
Page.IsValid property
Page.Validate method

PageIndexChanged command
code

pages
calling
returning to (code)

2nd

3rd

detail
hyperlinks

2nd

rows, hyperlinking to

2nd

3rd

4th

5th

6th

7th

tab
controls

2nd

Paging properties
setting for DataGrid control

2nd

Paging tab (DataGrid Property Builder)

paging through
data grids
code

paging through data


in DataGrid control

2nd

3rd

4th

5th

6th

7th

8th

9th

Panel object
properties

2nd

3rd

panes
Diagram (View Designer)

Grid (View Designer)


properties
Results (View Designer)
SQL (View Designer)
Parameter object

parameterized properties
classes, defining
code

parameterized stored procedures


executing in ADO.NET

2nd

3rd

4th

5th

parameters
at symbol (@)

2nd

frmHowTo1_2.vb
code

2nd

passing
UDFs (user-defined functions)
XML Web Services, creating

2nd

stored procedures
calling (code)

2nd

3rd

XML Web Services


creating

2nd

descriptions

3rd
2nd

4th

5th

6th

7th

6th

7th

8th

10th

methods, descriptions
parameters, passing
security tables

2nd
2nd

2nd

parent records
cascade deletes

passing
DataSet object
code

datasets
from XML Web Services
exceptions (code)

2nd

3rd

4th

5th

6th

7th

8th

2nd

parameters
UDFs (user-defined functions)
XML Web Services, creating

2nd

passwords
validating
code

2nd

Paste command (pop-up menu)


percent sign (%)
Perform Backup button

performance
of indexes

2nd

performance improvement
OleDb classes

2nd

SQLClient classes

2nd

performing
backups, code

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

PerformTask( ) function
calling
code

2nd

3rd

permissions
class inheritance permission keywords

2nd

object
delete
deny access level
execute
grant access level
insert
revoke access level
select
update
object (SQL Server security)

2nd

3rd

4th

5th

6th

statement
deny state
grant state
revoke state
statement (SQL Server security)

4th

5th

Permissions tab (Database Role Properties dialog box)

2nd

Permissions button

2nd

3rd

2nd

Permissions tab (Table Properties dialog box)

persistence
Table control

persisting
recordsets

2nd

3rd

4th

recordsets to disks
code

2nd

persisting records

phone number extensions


code

international

2nd

6th

7th

code

phone numbers
validating
code

2nd

3rd

PhoneDatatypes.vb
CNumberString class
code

2nd

CNumberString class, code


DefineValidChars method, declaring (code)

event handlers
code
IsValid method, declaring (code)

phone number extensions


code
international (code)

StringValue property
code
ThrowException method, declaring (code)
plus sign (+)

2nd

point-and-click query tool


creating, data-driven techniques

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

point-and-click SQL Server query tool


creating, data-driven techniques

2nd

3rd

4th

3rd

4th

5th

point-and-click Web form


property settings

pointers
classes

points
break
setting in stored procedures

2nd

pop-up menu commands


Copy
Generate Dataset

2nd

Paste
Run as Server Control

populating
1stBackupDevices list box, code
1stDatabases list box, code

2nd

2nd

3rd

4th

5th

DataGrid control
code

2nd

3rd

DropDown controls

4th

5th

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

list boxes
code

2nd

3rd

4th

repopulating (code)
ListBox controls

2nd

5th

6th

7th

8th

9th

10th

11th

12th

9th

10th

11th

2nd

3rd

4th

5th

6th

7th

8th

Selection combo box


code

2nd

3rd

TextBox control

2nd

posting
records
in data grids (code)

2nd

3rd

4th

5th

6th

pound sign (#)

primary key field


index functions

2nd

primary keys
CustomerID column (Customers table)
defining

2nd

3rd

field in tables

2nd

PrimaryKey object
Print button

4th

5th

6th

2nd
7th

8th

9th

7th

8th

13th

17th

18th

19th

Print tab page


objects
properties

printing
labels

2nd

3rd

4th

5th

6th

7th

8th

records

2nd

3rd

4th

5th

6th

7th

8th

reports

2nd

3rd

4th

5th

6th

7th

8th

at run-time

2nd

3rd

4th

5th

6th

9th

7th

8th

9th

10th

11th

12th

code
options, setting

2nd

SelectionFormula syntax

2nd

PrintOptions object
properties
PrintToPrinter method
Private declarations
Private object

procedures
stored
creating

[See also stored procedures]2nd [See stored procedures]

procedures.

processadmin (fixed server role)


ProdAndCatTab table

2nd

ProductID
detail information, loading (code)

2nd

products
filling and binding to DataGrid object (code)
LoadUnSelectedProducts subroutine
LoadUnUnselectedProducts subroutine
Selected Products ListBox control

Table control
loading (code)
loading;code

2nd
2nd

3rd

4th

5th

Unassigned Products Only check box

6th

2nd

Unselected Products ListBox control

programming
Repeater control events
stateless (Web pages)
clients, state management

2nd

server-side solutions, state management


Session object

2nd

3rd

4th

Project menu commands


Add Reference

2nd

Add Web Reference

projects
XML Web Services

propertes
controls
properties

2nd

3rd

4th

Action
adding to interfaces
Allow Nulls
Anchor
Application object
AutoIncrement
AutoPostBack
Backup object
BackupDevice object
BackupDevices object
BorderColor
BottomLineStyle

5th

2nd

3rd

4th

5th

Button control

2nd

3rd

4th

5th

6th

Button object

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

28th
buttons

2nd

CheckBox object

2nd

3rd

Column Name
columns, binding to controls
Combo control

2nd

2nd

ComboBox control

2nd

3rd

4th

5th

6th

7th

ComboBox object

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

Command button
Command Button control

2nd

3rd

4th

5th

6th

Command Button object

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

command buttons
Command Object
Command object
CommandBuilder object
CommandText
CommandText, accessing

2nd

CompareValidator object
Connection object

ContactName
code

control settings
Label, TextBox, and Command button

2nd

Control.ViewState
definition
controls

2nd

3rd

4th

5th

6th

CrystalReportViewer control

2nd

CrystalReportViewer object

2nd

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

CustomerID
code
CustomTask object
data, handling

2nd

DataAdapter object

3rd
2nd

3rd

Database
DataColumn object
DataformatString
DataGrid control

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

DataGrid object

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

DataGrid Property Builder


General tab
DataReader object
DataRow object
DataSet object

2nd
2nd

3rd

4th

5th

DataSource
DataSource (list boxes)
DataTable object

2nd

3rd

DataTable.Columns object
DataTable.Rows object
DataTextField
DataType
DataValueField
DateTimePicker object

default
classes, defining
declaring (code)
in VB 6 (code)
Devices
DisplayMember

2nd

2nd

4th

5th

6th

7th

8th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

27th

DisplayMember (list boxes)


DOCUMENT object

2nd

3rd

4th

5th

DropDown control
DropDown object

2nd

3rd

enabled
of buttons, toggling (code)

2nd

3rd

4th

5th

Enabled
on Windows forms

2nd

EnableTightHorizontal
ErrorMessage

General
setting in DataGrid control

2nd

Grid pane (View Designer)


Groupbox object
GroupBox object
HyperLink control
Hyperlink control
Hyperlink object
HyperLink object

2nd

Hyperlink object

2nd

HyperLink object
Hyperlink object

2nd

3rd

4th

5th

6th

7th

8th

ICustomer interface
code
Identity
Implements ICustomer.[Property/Method Name]
IsPostBack

2nd

2nd

IsValid
Label control
28th

29th

2nd

30th

3rd

31st

4th

32nd

settings for frmHowto_3


Label object

2nd

3rd

2nd

3rd

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

27th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

27th

33rd
2nd

3rd

4th

6th

7th

34th

35th

Label Object
Label object
28th

29th

30th

4th

5th

31st

32nd

33rd

36th

37th

38th

39th

40th

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

41st

Length
ListBox control
ListBox controls

2nd

ListBox object
ListBox Object
ListBox object

2nd

Name
of buttons, setting
of columns

2nd

3rd

of Command button
of controls

4th
2nd

5th
3rd

6th

7th

8th

9th

4th

2nd

setting
of controls, setting
of fields
of Label control

2nd

3rd

4th

of Label, TextBox, ListBox, and Command button


of ListBox control

2nd

3rd

4th

setting to bind DataSet control

2nd

of objects
executing stored procedures
of tables

2nd

of TextBox control

2nd

OleDataAdapter object

3rd
2nd

OleDbDataAdapter object
Package object

2nd

3rd
4th
3rd

2nd

4th

3rd

5th

2nd

13th

42nd

43rd

44th

Page.IsValid
Panel object

2nd

3rd

parameterized
classes, defining
code
point-and-click Web Forms
PrintOptions object
QueryResults object
Radio Button object
RadioButton object
RangeValidator object

read-only
classes, defining

2nd

ReadOnly
RegularExpressionValidator object
Repeater control
Repeater object
ReplaceDatabase
ReportSource

2nd

3rd

4th

RequiredFieldValidator object
Restore object

2nd

RowFilter
setting for DataView object (code)

2nd

RowStateFilter
SelectedIndices.Count

SelectionFormula
setting (code)

2nd

SelectionMode
settings for controls
Sort
DESC
SQLServer object
Step object

StringValue
code
Table control
Table object
TableCell object
TableRow object
TabPages
Task object

Text
of Label object
setting
validation controls
TextArea object

2nd

TextBox control

2nd

3rd

4th

settings for frmHowto_3


TextBox object

5th

2nd

6th

3rd

2nd

3rd

4th

5th

2nd

3rd

4th

5th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

4th

TextBox Object
TextBox object

6th

ValidationExpression
ValidationSummary object
ValueMember
ValueMember (list boxes)

VB .NET
modifiers
Web Form validation controls

write-only
classes, defining

2nd

2nd

3rd

4th

5th

6th

7th

8th

19th

20th

21st

22nd

23rd

24th

25th

26th

WriteOnly
XMLDocument
XMLDocument class
XMLNode
XMLNode class
XMLTextWriter

2nd

Properties command (Action menu)


Properties command (context menu)
Properties dialog box
Windows Only option

2nd

Properties window
CommandText property, accessing

2nd

propertis
Alias
Column
Criteria
Group By
Or
Output
Sort Order
Sort Type
Table
Property Builder dialog box

property declarations
of CCustomer class
code

property information output


of ToString method
code

2nd

Property Pages dialog box


indexes, creating

2nd

opening
Protected Friend object
Protected object
providers.

[See data providers]

public (fixed database role)


Public access modifiers
Public object

public variables
declaring (code)
PushButton button

2nd

[ Team LiB ]

2nd

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
queries
bulk
creating and executing (code)

2nd

3rd

point-and-click query tool


creating, data-driven techniques

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

point-and-click SQL Server query tool


creating, data-driven techniques

2nd

3rd

4th

5th

6th

6th

7th

8th

9th

10th

records
retrieving

2nd

3rd

4th

5th

SQL
value ranges
wildcards

2nd

2nd

3rd

3rd

4th

4th
5th

5th
6th

6th
7th

7th

8th

8th

9th

subqueries
SQL statements, loading and executing (code)
subqueries (T-SQL)
Query Builder

2nd

2nd

3rd

SQL statements, building

query strings
definition
QueryResults object
methods
properties
question mark (?)

quotation marks
double ("")

[ Team LiB ]

3rd

4th

5th

6th

2nd

9th

10th

10th

11th

11th

12th

12th

18th

19th

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
Radio Button object
properties

radio buttons
rbDistinct Radio Button control

RadioButton object
properties

ranges of values
SQL queries

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

RangeValidator control

RangeValidator object
properties
RAW mode
rbDistinct Radio Button control

RDO
(Remote Data Objects)
Read method

2nd

Read XML File button

read-only properties
classes, defining

2nd

ReadBackupHeader method

reading
XML documents
code

2nd

into datasets (code)


with XMLReader

3rd

4th

5th

with XMLTextReader (code)

2nd

2nd

3rd

6th

7th

8th

ReadOnly keyword
code
ReadOnly object
ReadOnly property
ReadValuesFromDataRow method
code

2nd

2nd

real data type (SQL Server)


real-time hyperlinks

2nd

reattaching
databases

record sets
ADODB.CursorTypeEnum.adOpenForwardOnly cursor type

records
adding

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

buttons
to data tables (code)

2nd

to dsCustomerIndividual dataset (code)


adding to data tables (code)

2nd

2nd

3rd

canceling editing/adding in DataGrid object (code)


Cascade Delete Related Records
cascade deletes
Delete Record, creating
deleting

2nd

3rd

4th

5th

6th

7th

8th

9th

5th

6th

buttons
code

2nd

3rd

exceptions, catching (code)


in data grids (code)
displaying

2nd

2nd

3rd

in DISTINCT clause

2nd

2nd
3rd

3rd
4th

10th

11th

12th

DISTINCT clause
editing

2nd

3rd

4th

filtering with DataView object

2nd

3rd

4th

5th

6th

finding

6th

7th

8th

9th

10th

2nd

inner joins

3rd

4th

2nd

3rd

5th
4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

7th

inserting
joins
left outer joins

2nd

loading
into text boxes (code)
locating (code)

2nd

3rd

4th

5th

6th

2nd

locating with DataTable object

2nd

3rd

4th

5th

2nd

3rd

4th

5th

6th

5th

6th

7th

8th

6th

7th

8th

9th

NULL
persisting

posting
in data grids (code)
printing

2nd

retrieving

3rd

2nd

4th

3rd

right outer joins

4th

5th

6th

7th

8th

9th

10th

2nd

saving before closing (code)

2nd

sorting with DataView object

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

sources
searching
stored procedures
updating

2nd

Recordset object

3rd

4th

2nd

ADO (ActiveX Data Objects)

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

records
displaying
editing

2nd

2nd

updating

3rd

3rd

2nd

4th

3rd

4th

recordsets
persisting

2nd

3rd

4th

recordsets
Dynamic type cursor

fields
updating
Forward Only type cursor

opening
code

2nd

3rd

4th

persisting

2nd

3rd

4th

persisting to disks
code

2nd

retrieving
code

2nd

3rd

4th

red X (Windows NT/2000 accounts)

2nd

Red/Blue border style


reports

2nd

references
for SQL APIs (Application Programming Interfaces)
setting

2nd

3rd

Web
viewing

2nd

XML Web Services

2nd

3rd

referencing
type libraries
with ADO (ActiveX Data Objects)
referential integrity (database tables)
RefreshIndividual routine

calling

2nd

2nd
3rd

3rd

11th

12th

13th

code

2nd

3rd

RefreshIndividual subroutine
calls, code to add

2nd

refreshing
database lists

2nd

DataGrid control
code

2nd

datasets
code

2nd

RefreshReport method

regenerating
data
code

2nd

RegionID, displaying

regions
DataItem.RegionURL column
descriptions, displaying

repRegion Repeater control


loading (code)

repRegions Repeater control


HTML code

2nd

repTerritories Repeater control


code

2nd

HTML code

2nd

loading (code)
territories, displaying

2nd

3rd

registering
COM type libraries
Regular ExpressionValidator control

RegularExpressionValidator object
properties
RejectChanges method

2nd

relationships
tables
many-to-many
one-to-many

2nd

one-to-one
relationships between tables, defining

2nd

3rd

4th

5th

6th

7th

8th

9th

relationships of databases
Create Relationships dialog box

2nd

reloading
1stUnSelected list box
code

1stUnselected list box


code

list boxes
code

2nd

3rd

4th

5th

reloading list boxes


code

2nd

Remote Data Objects.

[See RDO]

RemoveAt method

removing
temporary users
reoirtsLFormula Editor

Repeater control
data
displaying

2nd

events
programming

lists

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

buttons
hyperlinks
territories (regional), displaying

2nd

3rd

URLs (uniform resource locators), creating

2nd

properties

repRegion
loading (code)

repRegions
HTML code

repTerritories
code
HTML code
loading (code)
templates

2nd

3rd

Repeater object
properties
Repeater Web server control
ReplaceDatabase property

replacing
SaveRecord routine
code

2nd

3rd

repopulating
list boxes
code
repopulating list boxes (code)

Report design
fields, adding
Report Designer
Report document

2nd

Name property, setting

reports
printing

2nd

reports, exporting

2nd

3rd

4th

4th

5th

Report Expert
Crosstab reports
Drill Down reports
Form Letter reports
Form reports

Formula Editor
displaying

2nd

Mail Labels reports

reports
creating

2nd

3rd

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

Standard reports
SubReport reports
tables

2nd

tabs
viewing

Report menu commands


Report Options
Report Options command (Report menu)

Report Options dialog box


reports, customizing

2nd

ReportDocument
reports, assigning

2nd

reports
assigning with ReportDocument
calculated fields, adding

2nd

creating with Report Expert


Crosstab

2nd
3rd

2nd

4th
3rd

5th
4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

customizing

2nd

Details section
fields

2nd

displaying

2nd

3rd

4th

5th

displaying in Design view

6th

7th

2nd

displaying with Crystal Report Viewer

2nd

3rd

4th

7th

8th

9th

5th

6th

7th

8th

9th

10th

11th

9th

Drill Down
exporting

2nd

code

2nd

3rd

4th

ExportOptions object

2nd

5th

6th

DestinationOptions
ExportDestinationType
ExportFormatType
FormatOptions
Form
Form Letter

Formula Editor
displaying

2nd

Formula fields

formulas
adding
groupings

2nd

hyperlinks
action options

2nd

labels
printing

2nd

3rd

4th

5th

6th

7th

8th

Mail Labels

mailing labels
creating

2nd

3rd

modifying

naming
with rptReportName

OLE DB (ADO) data provider


choosing

2nd

on-screen
creating with hyperlinks
fields as hyperlinks

2nd

2nd

Web site fields, adding


options, setting
printing

2nd

3rd

4th

5th

6th

3rd

2nd

2nd
3rd

4th

5th

6th

7th

8th

9th

printing at run-time

2nd

3rd

4th

5th

6th

7th

real-time hyperlinks

2nd

code

records
printing

2nd

3rd

Red/Blue border style

4th

5th

6th

7th

2nd

ReportSource property

2nd

SelectionFormula syntax

3rd

2nd

sort order
controlling at run-time

2nd

Standard
stringly typed
strongly typed
ReportSource property

2nd

SubReport
Tables in Report list
user flexibility

2nd

2nd

users controlling data viewed


reports.

2nd

[See also Crystal Reports]

ReportSource property

2nd

3rd

4th

8th

8th

12th

repRegion Repeater control


loading
code

repRegions Repeater control


HTML code

repTerritories Repeater control


code
HTML code

loading
code

2nd

RequiredFieldValidator control

RequiredFieldValidator object
properties

resetting
mbAddNew variable
code

2nd

resources
.NET Framework Developer's Guide
Restore object
methods

2nd
2nd

properties

2nd

restoring
SQL Server databases

2nd

3rd

4th

5th

2nd

3rd

4th

6th

7th

8th

9th

10th

result sets
Letter Button, filling (code)

results of tables
displaying
Results pane (View Designer)

retrieving
backup device names (code)

data
from SQL Server 2000 database
with DataReader object

2nd

2nd

3rd

3rd

4th

4th

5th

5th

6th

database names
code

2nd

3rd

datasets
from XML Web Services (code)

information
from data grids
records

2nd

3rd

2nd
4th

5th

6th

7th

8th

9th

recordsets
code

2nd

3rd

4th

SQL Server results


with DataTable object

2nd

3rd

4th

return values
ProdAndCatTab table

2nd

returning
scalar types
UDFs (user-defined functions)

2nd

Table data type

table types
UDFs (user-defined functions)

to calling pages
code

2nd

revoke access level (object permissions)


revoke state (statement permissions)
right (>) arrow button

right outer joins


records, finding

rights

2nd

2nd

5th

6th

6th

7th

10th

7th

8th

9th

10th

11th

sharing on databases

roles
application roles (SQL Server security)

2nd

3rd

4th

5th

2nd

3rd

4th

bulkadmin (fixed server role)


custom database roles (SQL Server security)

5th

6th

db_accessadmin (fixed database role)


db_backupoperator (fixed database role)
db_datareader (fixed database role)
db_datawriter (fixed database role)
db_ddladmin (fixed database role)
db_denydatareader (fixed database role)
db_denydatawriter (fixed database role)
db_owner (fixed database role)
db_securityadmin (fixed database role)
dbcreator (fixed server role)
diskadmin (fixed server role)
fixed database roles (SQL Server security)
fixed server roles (SQL Server security)

2nd

2nd

3rd

3rd

processadmin (fixed server role)


public (fixed database role)
securityadmin (fixed server role)
serveradmin (fixed server role)
setupadmin (fixed server role)
sysadmin (fixed server role)

routinees
GetSQLDatabases

routines
ActivateEditing
creating (code)

2nd

ActiveEditing subroutine
calling (code)

2nd

attaching to SelectedIndexChanged event


BindTheGrid

2nd

BuildCnnStr

calling
code

2nd

3rd

4th

5th

code
collapsing
dgRegion_CancelCommand
dgRegion_DeleteCommand
dgRegion_EditCommand
dgRegion_UpdateCommand
ExecuteABatchCommand
expanding

GenerateData
calling
calling (code)

2nd

3rd

creating

GetBackupDevices
creating
GetPageNum( )
GetPageRows( )
GetSQLDatabases
creating

list boxes
reloading (code)

2nd

LoadIndividual
creating

2nd

creating (code)

LoadList

2nd

3rd

6th

7th

8th

4th

4th

9th

5th

5th

6th

6th

7th

7th

8th

8th

9th

10th

creating (code)

2nd

LoadProducts
calling (code)

2nd

LoadSelectedProducts
creating

2nd

LoadSQLServers

2nd

LoadSQLString
LoadTables( ) subroutine

LoadUnSelectedProducts
creating
LoadUnSelectedProducts subroutine
LoadUnUnselectedProducts subroutine

modGeneralRoutines.vb
connection strings, creating (code)

2nd

3rd

2nd

3rd

4th

modSQLDMORoutines.vb
database names, retrieving (code)
SQL Servers;loading (code)

2nd

OpenAndDisplayADOConnection
OpenNorthwindADOConnection subroutine
RefreshIndividual
calling (code)

2nd

3rd

4th

RefreshIndividual subroutine
calls, code to add

2nd

SaveRecord
calling (code)

2nd

creating (code)

2nd

replacing (code)

3rd

2nd

3rd

SaveRecord subroutine
code
SetData subroutine

SQL Servers
loading (code)

subroutines
errors, throwing

2nd

UseAStoredProcedureWithAParameter
code
RowFilter property
setting for DataView object (code)

2nd

rows
adding
in DataSet objects

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

4th

5th

6th

7th

8th

9th

10th

deleting
in data grids (code)
in DataSet objects

2nd
2nd

3rd
3rd

finding
code
in data grids, hyperlinking to detail pages
SQL Server database objects

2nd

RowStateFilter property

rptReportName
reports
naming

rules
business
data constraints

2nd

Run as Server Control command (pop-up menu)


Run Query button

Run Time check box


Create Columns Automatically, unchecking

run-time

2nd

3rd

4th

5th

6th

7th

reports
options, setting
printing

2nd

2nd
3rd

4th

SelectionFormula syntax

5th
2nd

sort order
controlling

2nd

runtime
bound controls on Web Forms

[ Team LiB ]

2nd

6th

7th

8th

9th

10th

11th

12th

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
Sams Web site
source code for chapters in book
Save (XMLDocument class)

Save button
clicking, effects

save exceptions
trapping (code)

2nd

Save method

save method
declaring
code

Save method
implementing
code

2nd

3rd

4th

5th

testing
code
SaveRecord routine
calling (code)

2nd

creating (code)

2nd

replacing (code)

3rd

2nd

3rd

SaveRecord subroutine
code

saving
data
code

2nd

command buttons
to servers

2nd

2nd

records
before closing (code)

2nd

tables
Web Forms

saving to servers
data
code

2nd

[See also persisting]


scalar types
returning
saving.

UDFs (user-defined functions)

2nd

Scaler function

schemas
loading into data tables

2nd

XSD
(XML Schema Definition)
Search button

search forms
calling

2nd

custom properties
creating (code)

2nd

3rd

4th

searching
record sources
tables

2nd

3rd

4th

security
SID (encrypted security ID)
Windows NT Integrated Security
users, logging into databases

5th

XML Web Services, testing

2nd

security tables
Web Services, creating with parameters

2nd

[See also SQL Server security]

security.

securityadmin (fixed server role)

SecurityServices.asmx.vb
DataSet object, passing (code)
passwords, validating (code)
usernames, validating (code)
SecurityWebServices Web site
SELECT command text, code
select object permission
SELECT statement
ALL clause

data
displaying (code)
DISTINCT clause

updating
code

2nd

SELECT string
columns
Selected Products ListBox control

SelectedIndexChanged event
code

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

code, adding
routines, attaching
SelectedIndices (index) collection
SelectedIndices.Count property

Selection combo box


populating
code

2nd

3rd

SelectionFormula
reports
printing options, setting

2nd

syntax
reports, printing

2nd

SelectionFormula property
setting
code
SelectionMode property

Server Explorer
Create Database dialog box

2nd

Customers table
databases

2nd

icon on screen
Visual Studio .NET

2nd

Server Role Properties dialog box


Server Roles icon

2nd

2nd

server-side solutions
state management

2nd

3rd

4th

Session object

2nd

3rd

4th

5th

serveradmin (fixed server role)

servers
bulkadmin (fixed server role)

changes
updating (code)

2nd

3rd

4th

data
saving to (code)

2nd

data grids
records, deleting (code)

2nd

3rd

4th

5th

6th

11th

12th

records, posting (code)

2nd

3rd

4th

5th

6th

DataGrid Web server control


DataList Web server control

DataTable object
tracking (code)

2nd

3rd

4th

5th

6th

dbcreator (fixed server role)


diskadmin (fixed server role)
fixed server roles (SQL Server security)

2nd

3rd

4th

5th

6th

7th

8th

Northwind SQL Server database


Customers table list
processadmin (fixed server role)
Repeater Web server control
securityadmin (fixed server role)
serveradmin (fixed server role)
setupadmin (fixed server role)

SQL Server
bigint data type
binary data type
bit data type
char data type
cursor data type
data types

2nd

3rd

4th

5th

datetime data type


decimal data type
fixed database roles

2nd

float data type


image data type
int data type
money data type
nchar data type
ntext data type
numeric data type
nvarchar data type
real data type
smalldatetime data type
smallint data type
smallmoney data type
sql_variant data type
table data type
text data type
timestamp data type
tinyint data type
uniqueidentifier data type
varbinary data type
varchar data type

SQL Server database


constraints, defining
creating

2nd

3rd

2nd
4th

default values, defining


fields, defining

2nd

indexes, defining

3rd
5th

2nd

3rd

2nd

3rd

4th

3rd

primary keys, defining

4th

4th

5th

4th

6th

5th

6th

5th

6th

7th

6th

9th

7th

8th

3rd

4th

5th

6th

3rd

4th

5th

tables, defining

4th

5th

6th

7th

3rd

views, creating

2nd

3rd

2nd

4th

3rd

5th

SQL Server database objects


column properties
columns
creating

2nd

2nd

3rd

4th

6th

9th

8th

2nd

tables, defining relationships

8th

7th

stored procedures, creating


2nd

2nd

5th

6th

8th

5th

7th

6th

8th

10th

9th
7th

11th

9th

10th
8th

11th
9th

9th

10th

rows

2nd

table properties
tables

2nd

3rd

2nd

SQL Servers
loading (code)
sysadmin (fixed server role)
Table Web server control
objects, creating

2nd

3rd

2nd

updating
code

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

Web pages
break points

[See also SQL Server]2nd [See also Web servers]3rd [See also SQL Server]
Session object
servers.

Nothing keyword
state management

2nd

3rd

4th

values
storing (code)

Session objects
.NET Framework Developer's Guide

session variables
tracking (code)

2nd

3rd

sessions
states
SetData subroutine

setting
DialogResult
code

2nd

setting to edit mode


data grids (code)
setupadmin (fixed server role)

sharing
rights on databases

shortcut menu commands


New Database Role
New Database User
New Login
SID (encrypted security ID)
signatures of methods, definition

single tier applications


ADO (ActiveX Data Objects)

2nd

smalldatetime data type (SQL Server)


smallint data type (SQL Server)
smallmoney data type (SQL Server)

SOAP
XML Web Services
definition (code)

2nd

3rd

Solution Explorer
Web references, viewing

2nd

solutions
XML Web Services

sort order
controlling at run-time
Sort Order property
Sort property
DESC
Sort Type property

sorting
columns

data

2nd

4th

5th

in DataGrid control

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

data grids
code
mailing labels

2nd

records
with DataView object
setting in data grids

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

11th

12th

13th

2nd

setting on data grids

2nd

source code for chapters in book, Web site

sources
of records, searching

sources of data
choosing

sp_tables stored procedure


testing

2nd

SQL
auto-generated
VB .NET tools, writing ADO.NET code

2nd

3rd

BACKUP DATABASE statement


BACKUP LOG statement
CREATE DATABASE statement
CREATE DEFAULT statement
CREATE FUNCTION statement
CREATE PROCEDURE statement
CREATE RULE statement
CREATE TABLE statement
CREATE VIEW statement

DTS
(Data Transformation Services)

strings
creating (code)

2nd

3rd

4th

syntax
objects, creating

SQL APIs
(Application Programming Interfaces)
references, setting

2nd

2nd

3rd

3rd

SQL databases
backing up
verifying

[See SQL-NS]

SQL Namespace.

SQL pane (View Designer)

SQL queries
value ranges
wildcards

2nd

2nd

3rd

3rd

4th

4th

5th

5th

6th

6th

7th

7th

8th

8th

9th

9th

10th

10th

11th

12th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

SQL Server
authentication mode
Enterprise Manager

batch updates
executing

2nd

3rd

4th

5th

bigint data type


binary data type
bit data type
bulkadmin (fixed server role)
char data type
cursor data type
data types

2nd

3rd

4th

5th

2nd

3rd

4th

databases
backing up
restoring

2nd

3rd

4th

tables, copying between

5th
5th

2nd

6th
6th

3rd

7th
7th

4th

8th
8th

5th

9th
6th

10th
7th

8th

verifying

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

datetime data type


db_accessadmin (fixed database role)
db_backupoperator (fixed database role)
db_datareader (fixed database role)
db_datawriter (fixed database role)
db_ddladmin (fixed database role)
db_denydatareader (fixed database role)
db_denydatawriter (fixed database role)
db_owner (fixed database role)
db_securityadmin (fixed database role)
dbcreator (fixed server role)
decimal data type

Detach/Attach SQL Server Database dialog box


creating

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

diskadmin (fixed server role)

DMF
(Distributed Management Framework)
APIs (Application Programming Interfaces)

2nd

3rd

float data type


image data type
int data type
money data type
nchar data type
ntext data type
numeric data type
nvarchar data type

objects
creating with ADO (ActiveX Data Objects)

2nd

3rd

4th

point-and-click SQL Server query tool


creating, data-driven techniques

2nd

3rd

4th

5th

6th

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

processadmin (fixed server role)


public (fixed database role)
real data type

results
retrieving with DataTable object
securityadmin (fixed server role)
serveradmin (fixed server role)
setupadmin (fixed server role)
smalldatetime data type
smallint data type
smallmoney data type
sql_variant data type

stored procedures
executing (code)

2nd

3rd

4th

5th

6th

executing with ADO (ActiveX Data Objects)

7th
2nd

8th
3rd

9th

10th

11th

4th

sysadmin (fixed server role)


T-SQL commands

2nd

table data type


text data type
timestamp data type
tinyint data type
trusted connections

UDFs (user-defined functions)


calling

2nd

3rd

code, formatting
creating

2nd

creating (code)

4th

5th

6th

7th

8th

9th

10th

11th

12th

2nd

3rd

4th

2nd

5th

3rd

parameters, passing
scalar types, returning

2nd

6th

7th

8th

9th

10th

11th

12th

12th

11th

12th

13th

14th

15th

16th

table types, returning

2nd

uniqueidentifier data type


varbinary data type
varchar data type

views
flexibility
Windows NT Integrated Security
users, logging into databases
SQL Server 2000

database
data, retrieving

2nd

fixed database roles

2nd

3rd

4th

5th

6th

7th

SQL Server 7.0


SQL Server Authentication button

SQL Server database


constraints
defining
creating

2nd

2nd

3rd

3rd

4th

4th

5th

5th

6th

6th

default values
defining

2nd

3rd

4th

5th

6th

2nd

3rd

4th

5th

6th

7th

8th

9th

2nd

3rd

4th

5th

6th

7th

8th

9th

detaching (code)

fields
defining

10th

11th

10th

11th

indexes
defining

Northwind
CustOrdersHist stored procedure, T-SQL code

primary keys
defining

2nd

3rd

4th

5th

6th

7th

8th

9th

6th

7th

8th

9th

stored procedures
creating

2nd

3rd

4th

5th

2nd

3rd

4th

5th

tables
defining

relationships, defining

2nd

3rd

4th

5th

6th

7th

8th

9th

views
creating

2nd

3rd

4th

5th

6th

7th

8th

SQL Server database objects


column properties
columns

2nd

3rd

2nd

creating
rows

2nd

table properties
tables

2nd

3rd

2nd

SQL Server databases


attaching
code

2nd

SQL Server Login Properties dialog box

2nd

SQL Server objects


creating
code
SQL Server security
application roles

2nd
2nd

3rd

4th

5th

authentication, mixed-mode

2nd

3rd

4th

custom database roles

3rd

4th

5th

2nd

database user accounts, creating


fixed database roles
fixed server roles
object permissions

2nd

2nd

3rd

2nd

4th

2nd

2nd

4th

4th

3rd

standard logins, creating


statement permissions

3rd

2nd

3rd

3rd

6th

3rd
5th

5th
5th
4th
4th

4th
6th

6th

5th
7th

6th
8th

7th

8th

6th

7th

6th
5th
5th

9th

10th

Windows NT/2000
authentication mode
groups, creating
logins, creating
users, adding

2nd

2nd
2nd

2nd

3rd

3rd
3rd

4th

4th
4th

3rd

4th

names, retrieving (code)

2nd

5th

5th
5th

6th

7th

8th

6th
6th

5th

6th

2nd

3rd

7th

8th

9th

10th

SQL Servers
backup devices
SQL servers
database names, retrieving (code)

SQL Servers
listing
in SQL-DMO

2nd

loading
code

2nd

3rd

4th

5th

6th

7th

loading (code)

SQL statements
actions, executing

2nd

building with Query Builder


creating
displaying in labels

2nd

executing
code

2nd

executing (code)

2nd

loading
code

2nd

3rd

storing
code

2nd

storing (code)

2nd

SQL strings
building (code)

creating
code

2nd

[See SQL-DMO]

SQL-Distributed Management Objects.

SQL-DMO
(SQL-Distributed Management Objects)
collections

2nd

3rd

2nd

databases
backing up
connecting to

2nd

Detach/Attach SQL Server Database dialog box, creating

2nd

3rd

4th

5th

6th

8th

9th

10th

7th

8th

9th

10th

11th

12th

13th

14th

15th

dialog boxes
connecting to databases

2nd

3rd

4th

5th

6th

7th

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

3rd

4th

5th

6th

7th

8th

9th

databases, backing up and verifying

2nd

creating
objects

2nd

11th

12th

11th

13th

12th

14th

13th

15th

14th

16th

15th

17th

16th

18th

17th

18th

19th

SQL APIs (Application Programming Interfaces)


references, setting

2nd

3rd

SQL Server
DMF APIs (Application Programming Interfaces)

2nd

3rd

SQL Server databases


backing up
restoring

2nd

2nd

3rd

3rd

4th
4th

tables, copying between


verifying

2nd

3rd

SQL Servers
listing

2nd

SQL-DTD
objects
tables, copying

2nd

4th

5th
5th

2nd
5th

6th
6th

3rd
6th

7th
7th

4th
7th

8th
8th

5th
8th

9th
9th

6th
9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

10th
7th

8th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

19th

16th

17th

SQL-DTS

2nd

(Data Transformation Services)

data
exporting

objects
data, exporting

SQL-NS
(SQL Namespace)
SQL.

[See also T-SQL]

sql_variant data type (SQL Server)


SQLBackup method

SQLClient classes
performance improvement

2nd

SQLClient objects
and OleDb objects, comparing

SqlCommand
(data control)

SqlConnection
(data control)

SqlDataAdapter
(data control)
? (question mark)
SQLDMO Object Library
SQLRestore method

SQLServer object
methods
properties
square brackets ([ ])
standard logins, creating

2nd

3rd

4th

5th

Standard Report Expert dialog box


tabs

2nd

Standard Report Wizard


Chart tab
Data tab
Fields tab
Filter tab
Group tab
Style tab
tabs

2nd

3rd

4th

Top N tab
Total tab
Standard reports

state management
on clients

2nd

server-side solutions
Session object

2nd
2nd

3rd
3rd

4th

5th

4th

state management objects


values, storing
stateless programming (Web pages)

clients
state management

2nd

server-side solutions
state management

4th

5th

statement permissions (SQL Server security)

2nd

Session object

2nd

2nd

3rd

3rd

4th

statement permissions
deny state
grant state
revoke state

statements

3rd

4th

5th

6th

7th

ALTER TABLE

2nd

BACKUP DATABASE
BACKUP LOG
BEGIN

BETWEEN
data, generating (code)

2nd

3rd

Catch
in Try[ellipsis dots]Catch[ellipsis dots]End Try block
CREATE DATABASE
CREATE DEFAULT
CREATE FUNCTION
CREATE PROCEDURE
CREATE RULE
CREATE TABLE

2nd

3rd

4th

CREATE VIEW
DECLARE
DROP TABLE

2nd

END
EXIST
IF

LIKE
data, generating (code)

2nd

SELECT
ALL clause
data, displaying (code)
DISTINCT clause
updating (code)

2nd

SQL
actions, executing

2nd

building with Query Builder


creating
displaying in labels
executing (code)

2nd
2nd

3rd

loading (code)

2nd

3rd

storing (code)

2nd

3rd

4th

4th

update
executing

states
application
database support
deny (statement permissions)
grant (statement permissions)
revoke (statement permissions)
session
Step object
methods
properties
Stored Procedure with Parameter button

stored procedures
break points, setting

2nd

calling
code
creating

2nd
2nd

3rd
3rd

4th

5th

6th

7th

8th

9th

10th

11th

CustOrdersHist
T-SQL code

executing
code

6th

7th

objects, properties and methods

2nd

3rd

4th

5th

2nd

parameterized, executing in ADO.NET


records

2nd

8th

9th

10th

11th

3rd

4th

5th

6th

12th

7th

8th

sp-tables
testing

2nd

T-SQL (Transact-SQL)
TextBox control, populating

2nd

stored procedures (SQL Server)


executing with ADO (ActiveX Data Objects)

2nd

3rd

4th

storing
key values
code

2nd

3rd

4th

SQL statements
code

2nd

3rd

4th

values
to Session object (code)

2nd

Stream object
String Collection Editor

2nd

strings
connection
BuildCnnStr( ) function
creating (code)

2nd

establishing (code)
text, displaying

3rd

4th

5th

6th

4th

5th

6th

7th

2nd

2nd

LoadSQLString routine

query
definition

SELECT
columns

SQL
building (code)

2nd

creating (code)

2nd

3rd

[See also connection strings]


StringValue property
strings.

code

strongly typed datasets


VB .NET tools
ADO.NET code, writing
strongly typed reports

2nd

2nd

ReportSource property

2nd

strSQL
SQL statements
executing (code)

2nd

Style tab
Standard Report Wizard

styles
Red/Blue border

2nd

subqueries
SQL statements
loading and executing (code)
T-SQL

2nd

3rd

4th

5th

SubReport reports

subroutines
ActiveEditing
calling (code)
errors, throwing

2nd
2nd

LoadTables( )
LoadUnSelectedProducts
LoadUnUnselectedProducts
OpenNorthwindADOConnection

RefreshIndividual
calls, code to add

SaveRecord

2nd

6th

3rd

8th

9th

10th

code
SetData

syntax
SelectionFormula
reports, printing

2nd

SQL
objects, creating

variables
assigning
sysadmin (fixed server role)
System.XML namespace
System.XML.Schema namespace
System.XML.Xpath namespace
System.XML.XSL namespace

systems
exceptions
sysxlogins table

[ Team LiB ]

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
T-SQL
(Transact-SQL)
CustOrdersHist stored procedure, code

Server objects
creating;code

2nd

T-SQL (Transact-SQL)
stored procedures, creating

T-SQL commands
BETWEEN operator
built-in functions
creating

2nd
2nd

2nd

functions

2nd

3rd

4th

5th

local variables
declaring

2nd

initializing

2nd

records
finding

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

inner joins
joins
left outer joins

2nd

right outer joins


records, retrieving

2nd
2nd

3rd

4th

5th

6th

7th

8th

9th

10th

SQL queries
value ranges
wildcards

2nd

2nd

3rd

3rd

4th

4th

5th

5th

6th

6th

7th

7th

8th

8th

9th

9th

10th

10th

11th

11th

12th

12th

SQL Server

SQL Server UDFs (user-defined functions)


calling

2nd

3rd

code, formatting
creating

2nd

4th

5th

6th

7th

8th

9th

10th

11th

12th

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

parameters, passing
scalar types, returning
table types, returning
subqueries

2nd

3rd

2nd
2nd

4th

5th

6th

tables
ALTER TABLE statement

2nd

CREATE TABLE statement

2nd

creating

5th

2nd

3rd

4th

creating, modifying, deleting


DROP TABLE statement
variables

2nd

3rd
6th

2nd

3rd

4th

5th

6th

2nd

3rd

4th

5th

2nd

3rd

4th

3rd

4th

5th

tab pages
controls

2nd

Table control
data
displaying

loading
code

2nd

persistence
properties

Table controls
tables, displaying

2nd

Table data type


creating and returning
table data type (SQL Server)

5th

6th

7th

8th

9th

7th

8th

9th

10th

11th

Table Designer
Table function
Table Mappings collection

2nd

Table object
properties
Table Properties dialog box
Permissions tab

2nd

2nd

Table property

table types
returning
UDFs (user-defined functions)
Table Web server control
objects, creating

2nd

2nd

3rd

2nd

TableCell object
properties

TableRow object
properties

tables
1stLookupTables
pointing to items (code)

2nd

Alias property
ALTER TABLE statement

2nd

Column property

columns
adding
data, entering
listing in databases (code)

2nd

3rd

sorting
sorting (code)

2nd

copying
between SQL Server databases
code

2nd

3rd

4th

CREATE TABLE statement

2nd

creating

5th

2nd

3rd

2nd

3rd

4th

5th

7th

8th

9th

10th

6th

7th

4th

11th

customer demographics, tracking

Customers
2nd

Server Explorer
Customers table list
Customers, CustomerCustomerDemo, and CustomerDemographics

data
adding data (code)
binding (code)

3rd

4th

data, adding (code)

2nd

3rd

loading (code)

3rd

creating

2nd

5th

6th

7th

8th

2nd

2nd

records, adding (code)

2nd

records, locating (code)


schemas, loading

3rd

2nd

2nd

text boxes, assigning values (code)

2nd

DataItem.RegionURL column
defining

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

deleting

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

displaying
with Table control
DROP TABLE statement
EXIST statement
expressions
fields

2nd

9th

10th

11th

12th

13th

13th

14th

15th

16th

17th

3rd
6th

Criteria property

CustomerID column

8th

5th

2nd
2nd

12th

14th

15th

16th

17th

18th

properties
formulas

2nd

Group By property
IF statement
in datasets
inner joins
joining

2nd

listing in databases
code

2nd

3rd

4th

5th

6th

7th

8th

LoadTables( ) subroutine

lookup
data, adding

2nd

information, managing
updating

2nd

2nd

updating, data-driven techniques


24th

25th

26th

27th

28th

29th

2nd

30th

3rd

31st

4th

5th

6th

7th

32nd

33rd

34th

9th

10th

11th

many-to-many relationship
modifying

2nd

3rd

4th

one-to-many relationship

5th

6th

7th

8th

2nd

one-to-one relationship
Or property
Orders
CustomerID column

2nd

Output property
primary key field

2nd

ProdAndCatTab
properties

2nd

2nd

3rd

records
adding to data tables (code)

2nd

cascade deletes
finding

2nd

inner joins

3rd

4th

5th

6th

7th

8th

9th

2nd

joins
left outer joins

2nd

right outer joins

2nd

referential integrity

2nd

3rd

relationships
defining

2nd

Report Expert

3rd

4th

5th

6th

7th

8th

2nd

results
displaying

rows
deleting in data grids (code)

2nd

3rd

saving
searching

2nd

3rd

4th

security
Web Services, creating with parameters

2nd

SELECT statement
Sort Order property
Sort Type property
SQL Server database objects
sysxlogins
Table property
tblCustomerPhones, creating
tblCustomers

2nd

tblPhones
View Designer

2nd

wrapping
Tables in Report list
TabPages property

2nd

2nd

3rd

4th

5th

9th

10th

8th

35th

9th

36th

10th
37th

11th
38th

12th
39th

13th
40th

14th
41st

15th
42nd

16th
43rd

17th
44th

18th
45th

19th
46th

20th

21st

22nd

23rd

tabs
of Report Expert, viewing
Standard Report Expert dialog box
Standard Report Wizard

2nd

2nd

3rd

4th

tags
element
Task object
methods
properties

tasks
DTS packages

2nd

tblCustomerPhones table, creating


tblCustomers table

2nd

tblPhones table

[See also data-driven techniques]


[See phone]
templates
techniques.
telephone.

ASP.NET Web Service

2nd

3rd

data grids

Repeater control
data, displaying

2nd

3rd

temporary users, removing

territories
repTerritories Repeater control
code
HTML code
loading (code)
territories (regional), displaying

2nd

3rd

testing
constructors
code

2nd

3rd

4th

Delete method
code
Hello World method

2nd

Save method
code
sp_tables stored procedure
XML Web Services

2nd

2nd

3rd

XML Web Services security


TestUserPassword method

4th

5th

6th

2nd

2nd

3rd

text
of connection strings, displaying

2nd

text boxes
binding
to datasets

2nd

to list boxes

2nd

binding and viewing


Confirm Password
Description

2nd

3rd

4th

5th

6th

7th

8th

2nd

2nd

disabling (code)

2nd

3rd

4th

frmHowTo1_3.vb
datasets, refreshing (code)
Group Name

2nd

2nd

records
loading (code)

2nd

3rd

4th

5th

Text property, setting


toggling (code)

2nd

3rd

assigning (code)

2nd

values
values, writing (code)

2nd

4th

5th

6th

6th

7th

8th

9th

10th

text data type (SQL Server)

Text property
of Label object
setting
validation controls
TextArea control

TextArea object
properties

2nd

TextBox control
populating

2nd

properties

2nd

settings

3rd

2nd

4th

3rd

5th

settings for frmHowto_3


property settings
TextBox object

2nd

properties

2nd

28th

29th

30th

2nd

3rd

31st

6th

7th

8th

9th

10th

11th

12th

13th

14th

8th

9th

10th

11th

12th

13th

14th

4th
2nd

3rd

3rd

4th

4th

4th

5th

6th

7th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

32nd

TextBoxChange method
text boxes, writing values (code)

2nd

TextChanged event
code

2nd

3rd

ThrowException method
declaring
code
throwing errors

2nd

timestamp data type (SQL Server)


tinyint data type (SQL Server)

toggling
btnDetach button, code
enabled properties of buttons (code)

2nd

3rd

4th

5th

text boxes
code

2nd

3rd

4th

5th

6th

toolbox
icon on screen

tools
Administrative Tools
groups and users, adding

VB .NET
ADO.NET code, writing

2nd

3rd

4th

ADO.NET code, writing;XSDs

2nd

3rd

5th

6th

7th

Top N tab
Standard Report Wizard
ToString method
property information output (code)

2nd

Total tab
Standard Report Wizard

tracking
customer demographics

DataTable object
code

2nd

3rd

4th

session variables (code)

5th

2nd

3rd

Transact-SQL (T-SQL)
stored procedures, creating

[See T-SQL]
[See copy]
trapping
exceptions
Transact-SQL.
transfer.

with Try[ellipsis dots]Catch[ellipsis dots]End Try block


save exceptions (code)
Trim function

2nd

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

24th

25th

26th

27th

trusted connections (SQL Server)

Try[ellipsis dots]Catch[ellipsis dots]End Try block


Catch statements
controls, ignoring
exceptions, trapping
Try[ellipsis dots]Catch[ellipsis dots]Finally block

Try[ellipsis dots]Catch[ellipsis dots]Finally blocks


exceptions

2nd

type libraries
COM
registering
referencing with ADO (ActiveX Data Objects)

2nd

3rd

types
scalar
returning for UDFs (user-defined functions)

2nd

table
returning for UDFs (user-defined functions)
types.

[See also data types]

[ Team LiB ]

2nd

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
UDFs
(user-defined functions)

assigning
code
calling

2nd

2nd

3rd

4th

code, formatting
creating

2nd

code

3rd

2nd

5th

6th

7th

8th

9th

10th

11th

12th

2nd
4th

3rd

5th

4th

6th

7th

8th

9th

10th

2nd

3rd

4th

11th

12th

5th

data
displaying (code)
parameters, passing
scalar types, returning

2nd

SELECT statement, updating


code

2nd

table types, returning

2nd

Unassigned Products Only check box

2nd

3rd

unbound controls
on forms
underscore (_)
Unicode

[See URLs]

uniform resource locators.

uniqueidentifier data type (SQL Server)


Unselected Products ListBox control
Update command
code
Update command text, code
Update method

2nd

2nd

3rd

3rd

4th

update object permission

update statements
executing
UpdateCommand method

updates
batch
executing

2nd

3rd

4th

5th

batch (on-the-fly), executing in ADO.NET

5th

6th

updating
changes to servers (code)
data

2nd

3rd

4th

2nd

5th

3rd

6th

7th

4th

5th

8th

9th

10th

data grids
code

databases
methods, implementing
lookup tables

data-driven techniques
26th

27th

28th

2nd

3rd

4th

5th

6th

2nd

3rd

4th

5th

6th

31st

32nd

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

2nd

29th

30th

33rd

7th

34th

35th

36th

37th

9th

10th

11th

12th

page indexes
in data grids (code)
records

2nd

3rd

4th

recordset fields

SELECT statement
code

2nd

servers
code

2nd

URLs
creating

2nd

3rd

4th

5th

6th

7th

8th

38th

39th

40th

41st

42nd

43rd

44th

18th

45th

19th

46th

20th

21st

22nd

23rd

24th

25th

DataItem.RegionURL column
Use Distinct button
Use UDF button

UseAStoredProcedureWithAParameter routine
code
user accounts for databases, creating

2nd

3rd

4th

5th

6th

user-defined functions
user-defined functions.

[See UDFs]

usernames
validating
code

2nd

users
adding to Windows NT/2000

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

data
notifying to edit

2nd

files
exporting

2nd

logging into databases


Names list

2nd

of reports, flexibility

2nd

records
saving before closing (code)

2nd

reports
controlling data viewed

2nd

temporary, removing
Windows NT/2000 domains, displaying

2nd

Users Must Enter a User Name and Password to Use This Computer check box

[ Team LiB ]

2nd

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
Validate method

ValidateCustomerID method
code

validating
data

2nd

3rd

4th

data on Web Forms

5th

6th

2nd

3rd

7th
4th

8th
5th

9th

10th

11th

12th

6th

7th

8th

9th

10th

11th

12th

6th

7th

8th

9th

10th

11th

12th

passwords
code

2nd

phone numbers (code)

2nd

3rd

4th

usernames
code

2nd

validation code, writing

2nd

3rd

4th

5th

27th

validation controls
code
CompareValidator
CustomValidator

2nd

for Web Forms


property settings

2nd

3rd

messages, displaying
on Web Forms

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

RangeValidator
Regular ExpressionValidator
RequiredFieldValidator
ValidationSummary

validation controls (Web Forms)


property settings

2nd

3rd

validation errors for controls

4th

5th

2nd

Validation Web server controls

2nd

3rd

ValidationExpression property
ValidationSummary control
lists, look of

ValidationSummary object
properties

validator controls
reaction inconsistencies
Value method

value ranges
SQL queries

2nd

3rd

4th

5th

6th

7th

ValueMember property
ValueMember property (list boxes)

values
default, defining
DeliveryDate

2nd

3rd

4th

in text boxes, writing (code)


key, storing (code)

2nd

2nd

3rd

lookup tables
updating

2nd

null
Allow Nulls
null, interpreting
OrderID

2nd

return
ProdAndCatTab table
storing

5th

2nd

2nd

4th

6th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

to Session object (code)


varbinary data type (SQL Server)
varchar data type (SQL Server)

variables
ampersand ( & )
variables, declaring

assigning
syntax
class, declarations (code)
DECLARE statement

local
declaring in T-SQL

2nd

initializing in T-SQL

2nd

mbAddNew
resetting;code

2nd

mFax
event handlers (code)

2nd

placing under forms code

public
declaring (code)

session
tracking (code)
T-SQL commands

2nd
2nd

3rd

3rd

4th
4th

5th
5th

VB .NET
ADO.NET code, writing
SQL (auto-generated)

2nd

strongly typed datasets


XSDs

2nd

3rd

2nd

3rd

3rd

classes
default properties
defining

2nd

3rd

4th

5th

methods;adding to interfaces

2nd

3rd

parameterized properties
properties

2nd

3rd

4th

properties;adding to interfaces
read-only properties

2nd

write-only properties

2nd

properties
modifiers
property declarations
Public access modifiers

2nd

reports
displaying

2nd

3rd

4th

5th

6th

7th

exporting

2nd

3rd

4th

5th

6th

7th

printing

2nd

3rd

4th

5th

6th

7th

8th
8th

9th
9th

tools
ADO.NET code, writing

2nd

3rd

4th

5th

6th

7th

8th

9th

3rd

4th

5th

6th

7th

8th

9th

10th

10th

11th

12th

13th

14th

15th

16th

17th

18th

VB 6
default properties
code
property declarations

verifications
Crystal Reports
licensing

verifying
SQL databases
SQL Server databases
View Code button
View Designer
Diagram pane

2nd

2nd

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

Grid pane
properties
Results pane
SQL pane
tables

2nd

View menu commands


Code
Indexes/Keys
Indexes/Keys command

View tab page


objects
properties

[See Crystal Report Viewer]2nd [See Crystal Report Viewer]


viewing
Viewer.

Report Expert tabs


text boxes

2nd

Web references

3rd

4th

5th

6th

7th

8th

9th

10th

2nd

Web Services descriptions

2nd

3rd

4th

5th

6th

views
creating

2nd

3rd

4th

5th

6th

7th

8th

data

design
database how-to

2nd

Design
reports, displaying

2nd

in SQL Server

Visual Basic
namespaces

Visual Basic .NET


class inheritance permission keywords

2nd

desktop applications
generic search forms, creating
25th

26th

2nd

3rd

4th

5th

6th

7th

8th

9th

27th

member override keywords

2nd

[See VB .NET]
Visual Basic 6. [See VB 6]
Visual Studio
Visual Basic .NET.

.NET Framework Developer's Guide

Visual Studio .NET


Server Explorer

2nd

SQL Server database


constraints, defining
creating

2nd

3rd

2nd
4th

default values, defining


fields, defining

2nd

indexes, defining

3rd
5th

2nd

3rd

2nd

3rd

4th

3rd

primary keys, defining

4th

4th

5th

4th

6th

5th

6th

5th

6th

7th

6th

9th

7th

8th

3rd

4th

5th

6th

3rd

4th

5th

tables, defining

4th

5th

6th

7th

3rd

views, creating

2nd

3rd

2nd

4th

3rd

5th

SQL Server database objects


column properties
columns

2nd

3rd

2nd

creating
rows

2nd

table properties
tables

2nd

3rd

2nd

Visual Studio .NET's Server Explorer


Visual Studio 6.0

4th

6th

9th

8th

2nd

tables, defining relationships

8th

7th

stored procedures, creating


2nd

2nd

5th

6th

8th

5th

7th

6th

8th

10th

9th
7th

11th

9th

10th
8th

11th
9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

[ Team LiB ]

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
wavy blue lines (code)

Web applications (ASP.NET)


generic search forms, creating

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

Web Forms
ASP.NET
bound controls

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

AutoPostBack property
DataBind method
IsPostBack property

2nd

Web server and HTML controls, comparing

2nd

controls
bound at runtime

2nd

validation errors

2nd

creating
Crystal Reports

data
adding

2nd

deleting

displaying
editing

3rd

2nd

2nd

2nd

2nd

validating

3rd
4th

3rd

5th

4th

6th

5th

7th

6th

9th

8th

7th

5th

5th

8th

7th

6th

4th

4th

7th

6th

5th

3rd

3rd

6th

5th

4th

2nd

2nd

5th

4th

3rd

paging through
sorting

4th

3rd

8th

8th

6th

7th

10th

9th
9th

9th

7th

8th

11th

10th

13th

12th

14th

13th

15th

14th

16th

15th

17th

16th

18th

17th

19th

18th

20th

19th

21st

20th

22nd

21st

23rd

22nd

24th

23rd

25th

24th

26th

25th

26th

10th

10th

8th

9th

12th

11th

11th

9th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

10th

10th

6th

7th

8th

9th

10th

hyperlinking from rows to detail pages

2nd

3rd

4th

5th

11th

12th

data grids
6th

7th

data-bound multi-select list boxes


data-driven techniques

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

DropDown controls
populating

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

4th

5th

6th

7th

8th

9th

10th

11th

2nd

3rd

4th

5th

6th

7th

5th

6th

7th

8th

9th

10th

8th

9th

ListBox controls
populating

2nd

3rd

lookup tables
updating

2nd

multi-select list boxes

2nd

naming

point-and-click
property settings

point-and-click query tool


creating, data-driven techniques

8th

9th

10th

11th

12th

13th

14th

Repeater control
data, displaying

2nd

3rd

4th

11th

12th

events, programming
templates

2nd

3rd

URLs (uniform resource locators), creating

2nd

saving

Table control
data, displaying

2nd

3rd

4th

5th

6th

7th

Web forms
using Windows forms objects

Web Forms
validation controls

2nd

property settings

Web pages
break points

Customers

3rd

2nd

4th
3rd

5th
4th

6th
5th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

15th

16th

17th

18th

19th

22nd

23rd

search forms, calling

2nd

initializing
code

2nd

3rd

linking

loading
code

2nd

stateless programming
clients, state management

2nd

server-side solutions, state management


Session object

2nd

3rd

2nd

3rd

2nd

3rd

4th

Web references
viewing

2nd

XML Web Services

Web server controls


DataGrid
DataList
Repeater
Table
objects, creating

2nd

Web servers
controls
and HTML controls, comparing
Validation Web server controls

2nd

2nd

3rd

[See WSDL]

Web Services Description Language.

Web sites
fields, adding to Northwind database

2nd

Sams
source code for chapters in book
SecurityWebServices

WebMethod
passwords, validating (code)

2nd

usernames, validating (code)

2nd

wfrmHowTo12_1.aspx.vb
data tables, adding data (code)
data tables, binding (code)

2nd

3rd

DataTable object, creating (code)


XML documents, creating (code)

2nd
2nd

wfrmHowTo12_2.aspx.vb
XML documents, reading (code)

2nd

3rd

wfrmHowTo12_3.aspx.vb
data tables, adding data (code)
data tables, binding (code)

2nd

2nd

3rd

DataTable object, creating (code)


XML documents, creating (code)

2nd
2nd

wfrmHowTo12_4.aspx.vb
XML documents, reading (code)

2nd

wfrmHowTo12_5.aspx.vb
data tables, adding data (code)
data tables, binding (code)

2nd

DataTable object, creating (code)

2nd

XML documents, creating (code)


XML documents, reading into datasets (code)

wfrmHowTo5_1.aspx.vb
customer information, listing (code)
RefreshIndividual routine, calling (code)
Web pages, loading (code)

wfrmHowTo5_2.aspx.vb
validation controls (code)

wfrmHowTo5_3.aspx.vb
LoadProducts routine

4th

5th

calling (code)

rows
finding (code)

Web pages
initializing (code)

2nd

3rd

wfrmHowTo5_4.aspx.vb
DropDown control
loading (code)

2nd

3rd

4th

2nd

3rd

4th

Table control
loading (code)

wfrmHowTo5_5a.aspx
repRegions Repeater control
HTML code

repTerritories Repeater control


code

wfrmHowTo5_5a.aspx.vb
DropDown control
loading (code)

repRegion Repeater control


loading (code)

Table control
loading (code)

wfrmHowTo5_5b.aspx
repTerritories Repeater control
HTML code

wfrmHowTo5_5b.aspx.vb
repTerritories Repeater control
loading (code)

wfrmHowTo5_6.aspx.vb
data grids, binding to DataView control (code)
data grids, paging through (code)
data grids, sorting (code)
Web pages, loading (code)

wfrmHowTo5_7.aspx
events, wiring for DataGrid control (code)

wfrmHowTo5_7.aspx.vb
changes, updating servers (code)

2nd

3rd

4th

data grids, cancelling edits (code)


data grids, setting to edit mode (code)
DataGrid object, loading (code

2nd

records, adding to data tables (code)


rows, deleting in data grids (code)

2nd

2nd

session variables, tracking (code)

2nd

3rd
3rd

wfrmHowTo5_8a.aspx.vb
products
filling and binding to DataGrid object (code)

wfrmHowTo5_8b.aspx.vb
ProductID
loading detail information (code)

2nd

wfrmHowTo8_5.aspx
1stUnselected list box
reloading (code)

list boxes
categories, loading (code)
populating (code)

2nd

2nd
3rd

routines
calling (code)

servers
updating (code)

wildcards

2nd

3rd

3rd

4th

4th

5th

6th

caret (^)
percent sign (%)
SQL queries

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

9th

10th

square brackets ([ ])
underscore (_)

windows
Properties
CommandText property, accessing

2nd

Windows forms
bound list boxes
2nd

3rd

4th

5th

6th

7th

bound list boxes, creating

data, displaying

2nd

3rd

4th

5th

6th

7th

8th

11th

12th

13th

14th

Windows Forms
controls
properties, setting
Crystal Reports

Windows forms
data
binding to controls

2nd

editing and updating


data bound forms
data controls

2nd

2nd

3rd

2nd
3rd

4th

3rd
4th

5th

4th
5th

6th

5th
6th

7th

6th
7th

8th

7th
8th

9th

8th

9th

10th

9th

3rd

Windows Forms
data-bound multi-select list boxes
data-driven techniques

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

Windows forms
DataGrid control
data, drilling down to

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

DataSet
(data control)

DataView
(data control)
developing

2nd

Enabled properties

2nd

error handling
with bound controls

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

Windows Forms
lookup tables
updating, data-driven techniques
24th

25th

26th

27th

28th

29th

2nd

30th

3rd

31st

4th
32nd

5th

6th

33rd

7th

34th

8th

35th

9th

10th

11th

12th

13th

14th

36th

37th

38th

39th

40th

41st

9th

10th

11th

12th

13th

14th

15th

16th

42nd

43rd

15th

16th

Windows forms
objects
using in Web forms

OleDbCommand
(data control)

OleDbConnection
(data control)

OleDbDataAdapter
(data control)

Windows Forms
point-and-click SQL Server query tool
creating, data-driven techniques

2nd

3rd

4th

5th

6th

7th

8th

Windows forms
records
adding and deleting

SqlCommand
(data control)

SqlConnection
(data control)

SqlDataAdapter

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

17th
44th

18th
45th

19th
46th

20th

21st

22nd

23rd

(data control)

text boxes
binding and viewing

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

Windows NT Integrated Security


users, logging into databases

Windows NT/2000
accounts
disabled

2nd

authentication mode

2nd

3rd

4th

5th

6th

7th

8th

Control Panel applets


groups and users, adding

domains
users, displaying
groups, creating
logins, creating
users, adding

2nd

2nd

3rd

2nd
2nd

3rd
3rd

4th

5th

4th
4th

6th

5th
5th

6th
6th

Windows Only option (Properties dialog box)

7th

8th

9th

10th

2nd

wire formats
XML Web Services Wire Formats

wiring
events for DataGrid control (code)

wizardds
DataAdapter Configuration Wizard, code

2nd

3rd

wizards
Backup/Restore Wizard
Data Adapter Configuration Wizard

2nd

DataAdapter Configuration Wizard


Choose a Query Type page
Choose Your Data Connection page

2nd

Generate the SQL Statements page

2nd

Report Expert
reports, creating

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

10th

11th

12th

13th

14th

Standard Report Wizard


Chart tab
Data tab
Fields tab
Filter tab
Group tab
Style tab
tabs

2nd

3rd

4th

Top N tab
Total tab

workflows
DTS packages

2nd

wrapping
tables

write-only properties
classes, defining

2nd

WriteAttributeString (XMLTextWriter)

WriteChangesToDB method
code

2nd

WriteComment (XMLTextWriter)
WriteDocType (XMLTextWriter)
WriteEndElement (XMLTextWriter)
WriteOnly object
WriteOnly property
WriteStartDocument (XMLTextWriter)
WriteStartElement (XMLTextWriter)

writing
ADO.NET code

2nd

3rd

4th

5th

6th

7th

8th

9th

15th

16th

17th

18th

SQL (auto-generated)
strongly typed datasets
XSDs

2nd

validation code
28th

29th

30th

2nd
2nd

3rd
3rd

3rd
2nd

3rd

4th

5th

31st

values in text boxes (code)

2nd

3rd

WSDL
(Web Services Description Language)

[ Team LiB ]

6th

7th

8th

9th

10th

11th

12th

13th

14th

15th

16th

17th

18th

19th

20th

21st

22nd

23rd

24th

25th

26th

27th

[ Team LiB ]
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J ] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X]
X, red (Windows NT/2000 accounts)

2nd

XML
(Extensible Markup Language)
.NET

2nd

namespaces

2nd

System.XML namespace
System.XML.Schema namespace
System.XML.Xpath namespace
System.XML.XSL namespace

data
manipulating
datasets, returning as (code)

2nd

3rd

documents
creating (code)

2nd

3rd

creating with XMLTextWriter


creating with XMLWriter
data tables, creating

2nd

2nd

3rd

3rd

4th

4th

5th

6th

data, retrieving from SQL Server 2000 database

2nd

datasets

9th

2nd

3rd

reading (code)

7th

8th

9th

10th

11th

12th

13th

2nd

4th

5th

6th

7th

8th

3rd

4th

5th

6th

7th

2nd

reading into datasets (code)


reading with XMLReader

3rd

4th

5th

reading with XMLTextReader (code)

2nd

3rd

XMLTextWriter

2nd

2nd

2nd

3rd

4th

5th

5th

6th

records
persisting

XSD
(XML Schema Definition)

XML documents
creating
code

2nd

[See XSD]

XML Web Services


ASP.NET Web Service templates
consuming
creating

2nd

2nd

3rd

3rd

4th

2nd

5th

3rd

6th

7th

4th

5th

6th

7th

creating with parameters

2nd

3rd

4th

descriptions

2nd

methods, descriptions
parameters, passing
security tables

2nd
2nd

2nd

data grids
information, retrieving

2nd

datasets
retrieving (code)
datasets, passing

2nd

2nd

3rd

4th

5th

6th

Description
descriptions, adding
descriptions, viewing

2nd
2nd

Directories
Discovery
infrastructure

2nd

login forms
authentication

3rd

7th

8th

3rd

DOM (Document Object Model)

XML Schema Definition.

6th

3rd

4th

5th

7th

6th

7th

8th

9th

10th

11th

12th

messages
loosely coupled, definition
methods
methods, calling
namespaces

2nd

2nd

projects
security, testing

2nd

SOAP definition (code)

2nd

3rd

4th

5th

solutions
testing

2nd

3rd

Web references

4th

2nd

5th

6th

3rd

Wire Formats

XMLDocument
methods
properties

XMLDocument class
CreateComment
CreateNode
DocumentElement
LoadXML
methods
properties
Save

XMLElement class
AppendChild

XMLNode
methods
properties

XMLNode class
AppendChild
methods
properties
XMLNodeReader class

XMLReader
classes

2nd

XML documents, reading

2nd

3rd

4th

5th

6th

3rd

4th

5th

6th

7th

2nd

3rd

4th

XMLNodeReader class
XMLTextReader class
XMLValidatingReader class

XMLTextReader
XML documents
reading (code)

2nd

XMLTextReader class

XMLTextWriter
Close
Flush
Formatting
methods

2nd

properties

2nd

WriteAttributeString
WriteComment
WriteDocType
WriteEndElement
WriteStartDocument
WriteStartElement

XML
documents;creating
XML documents, creating
XMLValidatingReader class

XMLWriter

2nd

3rd

4th

7th

8th

XML documents
data tables, creating
XMLTextWriter

2nd

2nd

XML documents, creating

2nd

3rd

XSD
(XML Schema Definition)

XSDs
Customers XSD, code

2nd

3rd

ADO.NET code, writing

2nd

VB .NET tools
xyntax
BETWEEN operator

[ Team LiB ]

2nd

3rd

4th

5th

6th

7th

8th

9th

10th

11th

12th

13th

1
1.1 Create a Bound List Box

1.2 Limit the Data Displayed in a Bound List Box

1.3 Bind and View Individual Text Boxes Based Off a Selected List Box Item

13

1.4 Edit and Update Data Using Bound Controls

21

1.5 Add and Delete Records Using Bound Controls

27

1.6 Take Care of Error Handling with Bound Controls

32

1.7 Put the Finishing Touches on a Data Bound Form

37

1.8 Bind Data to ComboBox and DataGrid Controls

42

1.9 Drill Down to Data in the DataGrid Control

46

10.1 Create a Report Using Crystal Reports Report Expert

54

10.2 Display a Report That Was Created

64

10.3 Add Calculated Fields to the Crystal Reports Report

70

10.4 Select Whether the Report Will Be Displayed, Printed, or Exported Using Visual Basic .NET Code

73

10.5 Determine Which Records Will Be Printed at Runtime

80

10.6 Print Labels and Control the Order in Which Records Will Be Printed

85

10.7 Create an Onscreen Report That Contains Hyperlinks

90

11.1 Create Windows NT/2000 Users

94

11.10 Use Object Permissions

99

11.11 Use Fixed Database Roles

102

11.12 Create Custom Database Roles

106

11.13 Create Application Roles

109

11.2 Create Windows NT/2000 Groups

111

11.3 Establish a Windows NT/2000 Authentication Mode

114

11.4 Establish Mixed-Mode Authentication

118

11.5 Create a Standard Login

120

11.6 Create a Windows NT/2000 Login

123

11.7 Use a Fixed Server Role

125

11.8 Create a Database User Account

129

11.9 Use Statement Permissions

132

12.1 Use XMLWriter to Create an XML Document

135

12.2 Use XMLReader to Read an XML Document

143

12.3 Work with the XML Document Object Model

147

12.4 Retrieve XML from SQL Server 2000

154

12.5 Work with Datasets and XML

159

13.1 Get Started with XML Web Services

164

13.2 Create a Simple XML Web Service Using Parameters

172

13.3 Consume XML Web Services

177

13.4 Pass a Dataset Back from an XML Web Service

181

2.1 Create a New SQL Server Database from Within Visual Studio .NET

186

2.2 Define Tables and Fields

188

2.3 Define a Primary Key and Other Indexes

193

2.4 Define Relations Between Tables

197

2.5 Define Defaults and Constraints

202

2.6 Create Views

205

2.7 Create Stored Procedures

209

3.1 Retrieve Data by Using the DataReader Object

212

3.2 Retrieve Results from SQL Server by Using the DataTable Object

217

3.3 Locate Records with the DataTable Object

219

3.4 Filter and Sort Records Using the DataView Object

223

4.1 Edit Data and Update Changes That Are Made to an ADO.NET DataSet Object

229

4.2 Add and Delete Rows in a Dataset with ADO.NET

241

4.3 Execute Parameterized Stored Procedures in ADO.NET

246

4.4 Create and Execute On-the-Fly Batch Updates by Using ADO.NET

249

5.1 Use Bound Controls with Web Forms

253

5.2 Validate Data Using Validation Controls

263

5.3 Populate DropDown and ListBox Controls

269

5.4 Display Data Using the Table Control

276

5.5 Display Data Using the Repeater Control

281

5.6 Display, Sort, and Page Data in the DataGrid Control

289

5.7 Add, Edit, and Delete Data Using the DataGrid Control

295

5.8 Hyperlink from a Row in the Data Grid to a Detail Page

306

6.1 Retrieve Unique Records Using Only a Select Query

311

6.2 Use Variables and Functions in T-SQL

317

6.3 Use Wildcards and Ranges of Values in a SQL Query

321

6.4 Find Records in a Table Without Corresponding Entries in a Related Table

327

6.5 Take Advantage of Using Subqueries

331

6.6 Create, Modify, and Delete Tables

335

6.7 Create a New Table with Data from Existing Tables

342

6.8 Create and Call SQL Server 2000 User-Defined Functions

346

7.1 Create a Dialog Box to Connect to a New Database, Including Listing Available SQL Servers and
Databases

353

7.2 Back Up and Verify a SQL Server Database

362

7.3 Restore a SQL Server Database

371

7.4 Transfer Tables Between SQL Server Databases

376

7.5 Create a Detach/Attach SQL Server Database Dialog Box

386

8.1 Work with Data-Bound Multi-Select List Boxes Using Windows Forms

394

8.2 Use a Single Windows Form to Update Multiple Lookup Tables

403

8.3 Create a Point-and-Click SQL Server Query Tool for Users Using a Windows Form

409

8.4 Make a Generic Search Form in a Visual Basic .NET Desktop Application

417

8.5 Work with Data-Bound Multi-Select List Boxes Using Web Forms

428

8.6 Use a Single Web Form to Update Multiple Lookup Tables

438

8.7 Create a Point-and-Click Query Tool for Users Using a Web Form

454

8.8 Make a Generic Search Form in an ASP.NET Web Application

462

9.1 Define a Class in Visual Basic .NET

472

9.2 Create a Class That Implements the Interface You Defined

478

9.3 Use Visual Studio .NET Tools to Speed Up Writing ADO.NET Code

486

9.4 Control the Creation and Behavior of Classes

499

9.5 Implement the Methods That Update the Database

505

9.6 Validate Data Passed to Properties and Communicate Errors to Developers

514

9.7 Write Data Validation Code That Can Be Reused in Other Classes

520

About the Author

533

Acknowledgments

534

Appendix A. Desktop Development With ADO

535

Chapter 1. Developing Windows Forms Using Bound Controls

536

Chapter 10. Creating Reports Using Crystal Reports

537

Chapter 11. Managing SQL Server Security

538

Chapter 12. Utilizing XML Data In Your Visual Basic .NET Applications

539

Chapter 13. Creating XML Web Services

540

Chapter 2. Creating SQL Server Database Objects From Visual Studio .NET

542

Chapter 3. Viewing Data With ADO.NET

543

Chapter 4. Manipulating Data With ADO.NET

544

Chapter 5. Working With Data In Web Forms

545

Chapter 6. Creating Transact-SQL Commands

546

Chapter 7. Performing Common Database Tasks Using SQL-DMO

547

Chapter 8. Taking Advantage of Data-Driven Techniques

550

Chapter 9. Using Classes With Databases to Make Life Easier

553

Comments

554

Conclusion

555

Copyright

556

Creating SQL Server Objects with ActiveX Data Objects

558

Dealing with Stateless Programming

560

Differences Between ADO and ADO.NET

563

Executing a SQL Server Stored Procedure By Using ActiveX Data Objects

564

Executing Batch Updates with ADO and SQL Server

566

Index

568

Index A

569

Index B

573

Index C

581

Index D

608

Index E

622

Index F

625

Index G

633

Index H

634

Index I

636

Index J

638

Index K

639

Index L

640

Index M

645

Index N

649

Index O

651

Index P

657

Index Q

665

Index R

666

Index S

675

Index SYMBOL

687

Index T

688

Index U

694

Index V

696

Index W

700

Index X

706

Introduction

709

Looking At the ADO Object Models

710

Looking at the SQL Server DMF APIs

712

Main Page

714

Objects That Are Found in ADO.NET

715

Overview of the XML Web Services Infrastructure

719

Referencing the Type Libraries

721

Setting References in .NET for the SQL APIs

723

Table of content

724

Tell Us What You Think!

728

Using the 'Connection' Object

729

Utilizing Properties for Tables and Columns

732

Ways of Utilizing XML in .NET

734

What's Covered in 'Database Programming with Visual Basic .NET and ADO.NET: Tips, Tutorials, and
Code'?

735

When to Use ADO (Local Database/Single Tier Applications)

737

Who Is This Book For?

738

Working with Tables, Columns, and Rows

739

Working with the ADO 'Recordset' Object

740

X
XML Namespaces in .NET

744

Brought to You by

You might also like