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
Table of Contents
Index
: Sams Publishing
Pub Date
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
: Sams Publishing
Pub Date
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
[ 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.
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 ]
[ 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 ]
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 ]
[ 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 ]
[ Team LiB ]
[ Team LiB ]
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.
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.
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:
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
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:
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 ]
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:
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
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
Note
SELECT CustomerID, CompanyName FROM Customers WHERE (CompanyName LIKE @parCustLimit + '%')
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
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.
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.
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
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
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 ]
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
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
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
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
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
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 ]
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
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
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
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
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 ]
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.
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
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
'
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
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 ]
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.
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
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
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:
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
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
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 ]
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.
Property
OleDataAdapter Name
Setting
odaCustomerList
SelectCommand OleDbSelectCommand1
DataSet
CommandText
Name
dsCustomerList
DataSetName
dsCustomerList
OleDataAdapter Name
odaOrdersForCustomer
SelectCommand OleDbSelectCommand2
DataSet
CommandText
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.
Property
Setting
Label
Name
Label1
Text
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
Listing 1.24 frmHowTo1_8.vb: Filling the Dataset Used by the DataGrid Control, Based
on the Item Selected in the ComboBox Control
'
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
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 ]
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.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
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
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
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 ]
[ Team LiB ]
[ Team LiB ]
[ 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:
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
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 ]
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.
Description
bigint
int
smallint
tinyint
bit
decimal
numeric
money
smallmoney
float
real
datetime
smalldatetime
char
varchar
text
nchar
nvarchar
ntext
binary
Varbinary
image
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
timestamp
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 ]
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 ]
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 ]
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.
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 ]
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
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
Output
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
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 ]
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:
2.
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 ]
[ Team LiB ]
[ Team LiB ]
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 ]
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
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 ]
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
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
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
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 ]
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.
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.
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
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
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 ]
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.
Description
Added
New rows
CurrentRows
Deleted
Deleted rows
None
OriginalRows
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.
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.
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.
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.
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
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
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
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
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 ]
[ 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 .
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
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.
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.
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 .
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
8.
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.
End If
oCurr.Enabled = bEnable
End If
Next
End Sub
10.
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.
Next
'-- Finish the editing of the data row
mdrCustIndiv.EndEdit()
Try
'-- Create an instance of the command builder
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 ]
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.
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.
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.
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.
6.
7. Enter the following code to the Click event btnDelete. Follow the comments to see what is
happening.
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 ]
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
Command
cmdText
Command
Connection
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
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.
Property
Setting
Label
Name
Label1
Caption
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.
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 ]
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
1.
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
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.
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 ]
[ Team LiB ]
[ Team LiB ]
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.
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.
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:
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:
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 ]
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.
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
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
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 ]
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:
Control
Description
RequiredFieldValidator
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.
CustomValidator
Allows you to create custom functions on both the client and server side
for validation.
ValidationSummary
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.
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
TextBox
txtRequiredExample
ID
Label
Text
TextBox
txtCompareFirst
ID
TextBox
txtCompareSecond
ID
Label
Text
TextBox
txtRangeExample
ID
Label
Text
TextBox
txtRegularExprExample
ID
Label
Text
TextBox
txtCustomExample
ID
Button
btnTestValidators
ID
Text
Test Validators
Label
Text
RequiredFieldValidator
Control
ToValidate
txtRequiredExample
ErrorMessage
ControlToCompare
txtCompareFirst
ControlToValidate
txtCompareSecond
ErrorMessage
ControlToValidate
txtRangeExample
CompareValidator
RangeValidator
MaximumValue
10
MinimumValue
ErrorMessage
RegularExpressionValidator ControlToValidate
txtRegularExprExample
ValidationExpression \d{3}-\d{2}-\d{4}
ErrorMessage
ValidationSummary
HeaderText
HyperLink
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
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 ]
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.
Description
Note
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.
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.
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
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
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
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
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 ]
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.
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
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
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
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
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 ]
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"
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.
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.
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
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
<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
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
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
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
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
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 ]
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
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
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
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
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.
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.
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
DataTable
NewRow
DataTable.Rows Add
DataTable
BeginLoadData
Turns off the schema checking that occurs when you're adding the
new row to the DataTable object.
DataTable
AcceptChanges
DataTable
RejectChanges
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
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.
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
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
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
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
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
intColCnt As Integer
intColCurr As Integer
drCurr As DataRow
cnn As New OleDb.OleDbConnection(BuildCnnStr("(local)", "Northwind"))
blnAdding As Boolean
strCurrValue As String
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
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 ]
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
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
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
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 ]
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 ]
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:
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
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
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
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
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
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 ]
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:
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:
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
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
&=
&=
&=
&=
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 ]
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:
Note
The literal values have been used here, rather than the text box values that will
be used in the How-To.
% (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:
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
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
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
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 ]
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:
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:
Note
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.
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.
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
&=
&=
&=
&=
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 ]
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:
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
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
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 ]
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:
3. Create fields that can't be NULL, and using different data types:
4. Create a field with the Default Value set, and with a Check Constraint specified.
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.
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:
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
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
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
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
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
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 ]
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:
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.
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
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
'
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 ]
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:
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:
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.
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.
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
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
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
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
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
'
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 ]
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 ]
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,
[ Team LiB ]
[ Team LiB ]
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
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
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
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.
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
'-- 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
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
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
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 ]
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
BackupSetDescription This property allows you to specify a description for the backup
set.
BackupSetName
Database
Devices
Initialize
TruncateLog
SQLBackup
BackupDevices
BackupDevice Name
ReadBackupHeader
QueryResults ColumnName
GetColumnString
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.
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
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
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
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
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
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
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
Next
'-- Disconnect and clean up
osvr.DisConnect()
osvr = Nothing
oDevice = Nothing
oResults = Nothing
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 ]
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.
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
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
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
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
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
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 ]
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
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
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
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
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
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
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
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 ]
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 .)
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.
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
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
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
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
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
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
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
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 ]
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
Items (index)
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
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
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
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
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
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
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
intItemsNum As Integer
intCurr As Integer
strItems As String
drv As DataRowView
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 ]
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 .
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
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
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.
'
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:
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:
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
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
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
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
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
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:
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
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
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
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
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
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
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
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
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:
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
Name
hplReturnToMain
Text
Return To Main
Command Button
HyperLink
NavigateURL wfrmMain.aspx
Note
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
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
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"
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
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
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
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
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 ]
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
Name
lstLookupTables
AutoPostback
True
Name
Label2
Text
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
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
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)
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
'-- 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
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
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
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
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
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 :
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
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
'
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
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
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
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 ]
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
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
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
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.
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
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
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
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
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
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:
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 ]
[ Team LiB ]
[ Team LiB ]
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. 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
Object
Purpose
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
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
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
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.
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
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,
you could simply declare a function that takes a variable of type IDbCommand, which would accept an
instance of either command type:
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 ]
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:
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
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.
Purpose
Private
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
When you are finished, your code should look like Listing 9.10.
Listing 9.10 frmHowTo9_2.vb: The Empty CCustomer Class
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
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
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
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
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
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
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
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
[ 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 = 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
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
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
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.
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 ]
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.
3.
1.
2.
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
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
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
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 ]
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
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
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
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
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
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
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
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 ]
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.
your code.
Listing 9.33 frmHowTo9_6.vb : Class Declaration for the
MaximumStringLengthExceededException
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
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
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
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
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
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.
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
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.
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
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
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
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
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
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
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.
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
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.
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
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
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
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
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
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
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
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 ]
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 ]
Technique
You can create a report and include it in your projects as needed.
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.
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.
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
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 ]
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.
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.
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).
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 ]
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:
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.
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
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.
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
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 ]
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:
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.
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).
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
With Me.cboRegions
.DataSource = dtRegions
.ValueMember = "Region"
End With
End Sub
Note
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
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.
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.
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
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
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
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 ]
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.
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:
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 ]
[ Team LiB ]
[ Team LiB ]
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 ]
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.
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 ]
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 ]
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 ]
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.
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 ]
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 ]
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.
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 ]
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.
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 ]
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.
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 ]
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.
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 ]
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.
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 ]
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.
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 ]
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.
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
[ Team LiB ]
[ Team LiB ]
[ Team LiB ]
[ Team LiB ]
[ Team LiB ]
[ Team LiB ]
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:
DataColumn
ColumnName
Specifies the Column name for the current data column that is being created.
DataColumn
Caption
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
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
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.
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
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
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
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
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 ]
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.
Purpose/Descriptions
XMLTextReader
XMLNodeReader
Provides a parser over an XML Document Object Model (DOM) API, similar to the
XMLNode tree.
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.
Property
Setting
Label
Text
File to Read
TextBox
txtFileToRead
ID
Button
btnReadFile
ID
Text
TextArea
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
xtrNames As System.Xml.XmlTextReader
strOut As String
intAtts As Integer
intCurrAtt As Integer
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 ]
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
XMLNode
AppendChild
Appends a node to another node as a child.
XMLDocument
Save
Saves the current XML document.
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
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.
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
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
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
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 ]
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:
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
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
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:
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 ]
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.
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
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.
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
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
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
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
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 ]
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 ]
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 ]
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.
Note
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:
That's it! You have created your first Web Service method. Now it's time to test it.
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:
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.
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
- <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>
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:
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 ]
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:
<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:
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.
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.
<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
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.
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 ]
Technique
To use a Web Service, you need to create a reference to it.
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.
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.
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 ]
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
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
- <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
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 ]
[ Team LiB ]
[ Team LiB ]
[ Team LiB ]
[ Team LiB ]
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.
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 ]
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:
After the library has been selected, you can see the ADODB reference entry in the Solution Explorer under
references.
[ Team LiB ]
[ Team LiB ]
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
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
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
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 ]
Tip
Notice that the cursor type returned will be
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.
cnn As New
rstCurr As
fldCurr As
strTemp As
ADODB.Connection()
New ADODB.Recordset()
ADODB.Field
String
OpenNorthwindADOConnection(cnn)
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
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 ]
CustOrdersHist
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
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 ]
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
cnn As
cmd As
prm As
rstOld
rstNew
New ADODB.Connection()
New ADODB.Command()
ADODB.Parameter
As New ADODB.Recordset()
As New ADODB.Recordset()
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 ]
cnn As
cmd As
prm As
rstOld
rstNew
New ADODB.Connection()
New ADODB.Command()
ADODB.Parameter
As New ADODB.Recordset()
As New ADODB.Recordset()
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 namespaces
bound list boxes, creating
2nd
3rd
4th
@ (at symbol)
parameters
2nd
[ ] (square brackets)
^ (caret)
_ (underscore)
2nd
3rd
4th
1stCustomers
Click event, code
2nd
3rd
1stLookUpTables
Items collection
data, adding
1stLookupTables
pointing to items (code)
2nd
[ 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
actions
execution on forms
2nd
2nd
ActiveEditing
frmHowTo1_4.vb
SaveRecord routing, calling (code)
text boxes, disabling (code)
2nd
2nd
ActiveEditing subroutine
calling (code)
2nd
2nd
3rd
clicking, effects
Add Class command (Add menu)
Add Members dialog box
2nd
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
2nd
3rd
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
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
2nd
2nd
2nd
2nd
3rd
2nd
3rd
3rd
3rd
4th
4th
5th
2nd
5th
connections
local databases
2nd
2nd
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
2nd
3rd
4th
2nd
2nd
3rd
4th
3rd
ADO 2.x
cursors
ADO Extensions 2.7 for DDL and Security (ADOX)
ADO.NET
2nd
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
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
ADODB
(ActiveX Data Objects 2.7)
ADODB (ActiveX Data Objects 2.7 object model)
2nd
3rd
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
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
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
2nd
variables
24th
syntax
assigning values
text boxes
code
2nd
at symbol (@)
parameters
2nd
2nd
attaching
database file, code
2nd
files
to databases
2nd
routines
to SelectedIndexChanged event
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 devices
1stBackupDevices list box
populating (code)
2nd
3rd
4th
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
2nd
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 tables
code
2nd
3rd
4th
5th
6th
7th
8th
DataSet control
with ListBox control properties
2nd
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
blocks
If[ellipsis dots] Then
code
2nd
2nd
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
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
2nd
records
adding and deleting
2nd
3rd
4th
5th
6th
7th
8th
9th
10th
2nd
3rd
4th
5th
6th
7th
8th
9th
Windows forms
bound list boxes, displaying data
data bound forms
2nd
3rd
4th
2nd
2nd
5th
3rd
4th
6th
4th
5th
7th
5th
6th
8th
6th
10th
11th
8th
6th
7th
8th
9th
10th
2nd
3rd
4th
5th
6th
7th
8th
9th
2nd
2nd
3rd
4th
5th
2nd
6th
3rd
2nd
7th
4th
3rd
8th
5th
4th
9th
6th
5th
14th
9th
5th
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
btnCreateXMLFile button
Click event, code
2nd
btnDelete
Click event, code
btnDelete button
2nd
3rd
5th
6th
7th
8th
9th
10th
9th
10th
2nd
3rd
4th
5th
4th
5th
2nd
btnDetach button
click event
code
toggling (code)
btnEdit
Click event, code
btnEdit button
enabled properties, toggling (code)
2nd
3rd
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
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
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
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
SQL strings
code
built-in functions
T-SQL
2nd
2nd
bulk queries
creating and executing
code
2nd
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
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
2nd
Command
properties
command
properties
Command
properties
properties, settings
2nd
3rd
4th
2nd
3rd
property settings
4th
2nd
5th
3rd
4th
6th
5th
7th
6th
8th
7th
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
2nd
2nd
2nd
2nd
3rd
Locate File
New Connection
Open a Recordset
Perform Backup
Permissions
2nd
properties
setting
property settings
PushButton
2nd
2nd
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
2nd
calls
RefreshIndividual subroutine
adding (code)
2nd
Cancel button
clicking, effects
Cancel command
code
canceling
edits
code
2nd
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
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
changes
updating to servers (code)
2nd
3rd
4th
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
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
CCustomerID
constructors (code)
2nd
3rd
CNumberString
2nd
12th
13th
3rd
BinderContext
code
11th
3rd
behavioral control
code
10th
3rd
3rd
4th
11th
12th
13th
14th
14th
15th
16th
17th
18th
code
2nd
3rd
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
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
2nd
instances
definition
interfaces
implementing
2nd
3rd
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.
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
2nd
3rd
2nd
2nd
3rd
2nd
2nd
2nd
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
closing
forms
code
2nd
forms (code)
3rd
2nd
CNumberString class
code
2nd
3rd
code
1stBackupDevices list box, populating
2nd
3rd
4th
5th
6th
7th
8th
2nd
3rd
3rd
4th
4th
5th
6th
1stLookupTables
pointing to items
2nd
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
2nd
3rd
4th
5th
basCommandExamples.vb
6th
7th
8th
9th
10th
11th
12th
13th
14th
16th
17th
18th
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
2nd
2nd
3rd
3rd
2nd
2nd
2nd
3rd
btnConnection button
disabling
2nd
enabling
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
2nd
bulk queries
creating and executing
2nd
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
2nd
3rd
4th
2nd
3rd
2nd
3rd
4th
5th
6th
7th
8th
9th
10th
11th
12th
13th
14th
default properties
defining
4th
5th
interfaces, implementing
2nd
3rd
2nd
3rd
4th
2nd
3rd
5th
6th
parameterized properties
properties
2nd
3rd
4th
2nd
write-only properties
2nd
Clear method
2nd
3rd
CNumberString class
2nd
3rd
4th
columns
listing in databases
columns, sorting
2nd
3rd
4th
2nd
2nd
2nd
3rd
3rd
connection strings
creating
2nd
establishing
3rd
4th
5th
6th
7th
8th
2nd
2nd
constructors
for CCustomerID class
2nd
3rd
4th
2nd
2nd
ContactName property
Customer class
2nd
2nd
customer information
listing
2nd
CustomerData.vb
DataAdapter Configuration Wizard
2nd
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
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
2nd
updating
2nd
data tables
binding
2nd
3rd
records, adding
4th
2nd
5th
3rd
2nd
2nd
3rd
2nd
3rd
2nd
3rd
4th
2nd
2nd
3rd
4th
2nd
3rd
DataGrid control
loading
2nd
3rd
4th
5th
6th
DataGrid controls
populating
2nd
3rd
4th
5th
DataGrid object
loading
2nd
2nd
DataSet object
passing
datasets
loading
2nd
2nd
3rd
DataTable object
creating
2nd
3rd
4th
tracking
2nd
3rd
4th
5th
2nd
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
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
2nd
2nd
frmHowTo1_4.vb
ActiveEditing subroutine, calling
data, canceling changes
data, saving to servers
2nd
2nd
3rd
2nd
2nd
2nd
4th
2nd
frmHowTo1_5.vb
data, saving (code)
2nd
2nd
2nd
2nd
3rd
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
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
2nd
frmHowTo3_4.vb
button controls, adding click events
columns, sorting
2nd
2nd
2nd
3rd
3rd
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
2nd
3rd
frmHowTo6_4.vb
left outer joins
frmHowTo6_6.vb
PerformTask( ) function, calling (code)
2nd
3rd
2nd
frmHowTo6_7.vb
forms, loading (code)
2nd
2nd
frmHowTo6_8.vb
UDFs (user-defined functions), assigning
UDFs (user-defined functions), creating
2nd
2nd
3rd
4th
5th
frmHowTo7_1.vb
btnConnection button;disabling
2nd
btnConnection button;enabling
2nd
2nd
3rd
4th
2nd
2nd
2nd
frmHowTo7_2.vb
1stBackupDevices list box, populating
1stDatabases list box, populating
backups, performing (code)
2nd
3rd
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
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
2nd
2nd
2nd
3rd
4th
5th
3rd
frmHowTo8_4.vb
search forms, creating custom properties
2nd
frmHowTo8_4a.vb
Letter Button result sets, filling
2nd
2nd
3rd
frmHowTo8_4b.vb
DialogResult, setting
key values, storing
2nd
2nd
2nd
3rd
frmHowTo8_6.aspx
data grids, deleting records
2nd
3rd
4th
5th
6th
2nd
3rd
4th
5th
6th
2nd
3rd
2nd
3rd
frmHowTo8_7.vb
columns, listing in databases
DataGrid control, loading
SQL strings;creating
2nd
2nd
2nd
3rd
3rd
4th
4th
5th
2nd
3rd
4th
2nd
frmHowTo8_8a.vb
Letter Button result sets, filling
records, loading
2nd
2nd
3rd
frmHowTo8_8b.vb
calling pages, returning to
2nd
data grids;updating
key values, storing
2nd
frmHowTo9_2.vb
CCustomer class
2nd
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
2nd
3rd
4th
5th
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
2nd
3rd
4th
2nd
2nd
3rd
4th
2nd
3rd
4th
list boxes
categories, loading
populating
reloading
2nd
2nd
2nd
3rd
3rd
2nd
3rd
4th
4th
3rd
4th
5th
5th
6th
7th
8th
9th
10th
11th
12th
5th
4th
5th
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
2nd
modGeneralRoutines.vb
connection strings, creating
2nd
3rd
4th
modPublicVariables.vb
public variables, declaring
modSQLDMORoutines.vb
backup device names, retrieving
connection strings, creating
2nd
2nd
3rd
2nd
2nd
passwords
validating
2nd
3rd
PerformTask( ) function
calling
2nd
3rd
2nd
2nd
3rd
PhoneDatatypes.vb
CNumberString class
2nd
3rd
5th
6th
7th
8th
9th
13th
StringValue property
ThrowException method, declaring
ProductID detail information, loading
property declarations (VB .NET)
2nd
2nd
2nd
ReadValuesFromDataRow method
2nd
records
adding to data tables
deleting
2nd
2nd
3rd
2nd
3rd
2nd
3rd
2nd
recordsets
opening and retrieving
persisting to disks
4th
5th
2nd
RefreshIndividual routine
calling
reports
exporting
printing
2nd
3rd
2nd
2nd
3rd
4th
5th
6th
7th
8th
9th
10th
2nd
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
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
2nd
2nd
SQL Servers
loading
2nd
3rd
4th
2nd
3rd
4th
SQL statements
executing
loading
2nd
2nd
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
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
2nd
TextBoxChange method
text boxes, writing values
TextChanged event
2nd
2nd
3rd
ThrowException method
declaring
ToString method
property information output
2nd
3rd
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
2nd
2nd
wfrmHowTo12_2.aspx.vb
XML documents, reading
2nd
3rd
wfrmHowTo12_3.aspx.vb
data tables, adding data
data tables, binding
2nd
2nd
3rd
2nd
2nd
wfrmHowTo12_4.aspx.vb
XML documents, reading
2nd
wfrmHowTo12_5.aspx.vb
data tables, adding data
data tables, binding
2nd
2nd
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
2nd
2nd
2nd
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
2nd
3rd
2nd
3rd
4th
5th
2nd
3rd
4th
5th
6th
7th
exporting
2nd
3rd
4th
5th
6th
7th
printing
2nd
3rd
4th
5th
6th
7th
8th
8th
9th
9th
collections
Items
data, adding
Items (index)
SelectedIndices (index) collection
SQL-DMO
2nd
2nd
2nd
columns
adding to tables
binding to controls
properties
2nd
2nd
2nd
data
entering
data grids
templates
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
2nd
3rd
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
2nd
3rd
property settings
4th
2nd
3rd
5th
6th
4th
7th
5th
6th
2nd
3rd
4th
5th
property
command buttons
accessing on forms
2nd
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
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
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
2nd
compound indexes
Confirm Password text box
2nd
Connect button
enabling
connected data
Connection object
2nd
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
conrtrols
bound at runtime
2nd
constraints
defining
2nd
for data
2nd
3rd
4th
5th
6th
of indexes
constructors
CCustomer class
code
2nd
2nd
object-based
for CCustomerID class (code)
objects
testing
code
2nd
consuming
XML Web Services
2nd
methods, calling
3rd
4th
5th
6th
7th
2nd
Web references
2nd
3rd
ContactName property
code
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
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
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
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
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
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
2nd
2nd
3rd
3rd
4th
2nd
2nd
2nd
3rd
2nd
4th
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
2nd
2nd
3rd
2nd
3rd
4th
5th
4th
property settings
2nd
refreshing (code)
2nd
3rd
4th
5th
6th
2nd
2nd
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
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
2nd
Letter Button
click events (code)
2nd
ListBox
loading
populating
2nd
3rd
4th
5th
6th
7th
properties
2nd
3rd
4th
5th
6th
7th
2nd
2nd
3rd
3rd
8th
9th
10th
11th
2nd
4th
4th
5th
6th
7th
8th
9th
OleDataAdapter
for frmHowTo1_9b forms
2nd
3rd
OleDbDataAdapter
controls
2nd
on Web Forms
bound at runtime
2nd
OpenFileDialog
order information, displaying
2nd
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
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
2nd
2nd
3rd
2nd
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
2nd
3rd
2nd
3rd
4th
5th
2nd
validator
reaction inconsistencies
2nd
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
2nd
2nd
3rd
3rd
4th
2nd
2nd
3rd
creatin
objectsd
2nd
creating
ActivateEditing routine
code
2nd
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
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
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
2nd
objects
with SQL syntax
on-screen reports with hyperlinks
fields as hyperlinks
2nd
2nd
3rd
4th
5th
6th
3rd
2nd
2nd
3rd
4th
5th
6th
2nd
3rd
4th
5th
6th
7th
8th
9th
10th
11th
12th
13th
14th
15th
16th
8th
9th
10th
11th
12th
13th
14th
15th
16th
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
2nd
constraints, defining
3rd
2nd
indexes, defining
4th
2nd
2nd
3rd
2nd
4th
3rd
4th
3rd
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
9th
7th
8th
5th
7th
9th
9th
6th
11th
10th
7th
8th
11th
9th
8th
2nd
3rd
2nd
2nd
table properties
tables
2nd
3rd
2nd
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
2nd
3rd
4th
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
2nd
2nd
3rd
4th
5th
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
2nd
with XMLTextWriter
2nd
3rd
with XMLWriter
2nd
3rd
XMLTextWriter
2nd
3rd
3rd
5th
6th
7th
4th
5th
6th
7th
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
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
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
2nd
3rd
4th
displaying
5th
6th
7th
2nd
3rd
4th
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
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
printing
6th
7th
options, setting
5th
6th
9th
3rd
3rd
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
3rd
2nd
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
customizing
reports
2nd
CustomTask object
methods
properties
CustomValidator control
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]
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
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
2nd
3rd
4th
5th
6th
7th
in DataGrid control
2nd
3rd
4th
5th
6th
7th
26th
details, retrieving
disconnected
displaying
templates
2nd
3rd
2nd
3rd
4th
2nd
5th
6th
7th
8th
9th
10th
11th
12th
13th
14th
15th
16th
2nd
3rd
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
exporting
with SQL-DTS
filling DataGrid control
generating
code
2nd
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
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
2nd
data adapters
creating
2nd
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
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
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
2nd
3rd
records
adding (code)
2nd
locating (code)
2nd
2nd
3rd
2nd
text boxes
assigning values (code)
2nd
[See DTS]
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
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
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
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
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
2nd
3rd
4th
5th
6th
7th
8th
9th
DataAdapter
frmHowTo1_2.vb
code
2nd
2nd
2nd
3rd
2nd
DataAdapter object
methods
2nd
properties
3rd
2nd
3rd
2nd
Database property
Database Role Properties dialog box
Permissions tab
2nd
2nd
2nd
databases
1stDatabases list box
populating (code)
2nd
3rd
4th
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
5th
6th
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
2nd
3rd
4th
5th
6th
7th
8th
9th
10th
2nd
3rd
4th
5th
6th
7th
8th
11th
12th
13th
14th
15th
16th
17th
18th
files
attaching
2nd
2nd
2nd
JET
opening (code)
2nd
list boxes
loading (code)
lists, refreshing
2nd
2nd
local
ADO (ActiveX Data Objects)
2nd
names
retrieving (code)
2nd
3rd
Northwind
customers, tracking demographics
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
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
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
rows
7th
6th
columns
9th
10th
8th
5th
column properties
11th
5th
5th
6th
3rd
9th
4th
2nd
10th
7th
3rd
data, retrieving
9th
6th
7th
5th
6th
4th
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.
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
2nd
loading
code
loading (code)
2nd
3rd
lookup tables
data, adding
2nd
objects
handling
2nd
3rd
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
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
2nd
11th
12th
13th
14th
15th
16th
17th
18th
19th
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
2nd
3rd
refreshing
code
2nd
strongly typed
2nd
3rd
4th
5th
6th
7th
12th
13th
14th
15th
16th
17th
18th
19th
20th
21st
22nd
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
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
ddSortby control
SelectedIndexChanged event
routines, attaching
decimal data type (SQL Server)
declarations
CCustomer class properties (code)
2nd
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
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
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
2nd
tables
2nd
3rd
4th
2nd
DeliveryDate
values
2nd
2nd
descriptions
of methods
2nd
11th
11th
12th
13th
14th
15th
16th
17th
18th
19th
20th
21st
22nd
23rd
24th
25th
2nd
2nd
3rd
4th
5th
columns, setting
2nd
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
26th
2nd
3rd
4th
5th
6th
7th
8th
9th
10th
11th
12th
13th
14th
15th
16th
17th
18th
27th
3rd
DetachDB method
creating
2nd
4th
5th
6th
7th
8th
9th
10th
11th
12th
13th
14th
15th
16th
17th
detaching
databases
2nd
2nd
detail pages
hyperlinks
2nd
rows, hyperlinking to
2nd
3rd
4th
5th
6th
7th
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
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
3rd
5th
6th
7th
8th
9th
10th
11th
12th
13th
14th
15th
16th
17th
18th
19th
2nd
2nd
2nd
2nd
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
2nd
Property Builder
Property Pages
indexes, creating
2nd
opening
Report Options
reports, customizing
Server Role Properties
2nd
2nd
2nd
2nd
Table Properties
2nd
Permissions tab
2nd
2nd
directories
XML Web Services Directories
disabled
Windows NT/2000 accounts
2nd
disabling
btnConnection button
code
2nd
text boxes
code
2nd
2nd
disconnected data
discoveries
XML Web Services
Discovery
diskadmin (fixed server role)
disks
recordsets, persisting to (code)
2nd
3rd
displaying
ADO connections
code
2nd
2nd
2nd
data
in bound list boxes
2nd
3rd
4th
5th
6th
7th
in DataGrid control
2nd
3rd
4th
5th
6th
7th
templates
2nd
2nd
3rd
8th
9th
10th
3rd
2nd
3rd
4th
2nd
5th
6th
7th
8th
2nd
4th
5th
6th
7th
8th
9th
9th
10th
16th
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
3rd
2nd
3rd
4th
5th
6th
7th
8th
9th
SQL statements
in lables
2nd
table results
tables
with Table control
2nd
2nd
DisplayMember property
DisplayMember property (list boxes)
Dispose method
DISTINCT clause
records, displaying
2nd
[See DMF]
DMF
(Distributed Management Framework)
2nd
2nd
3rd
DOCUMENT object
properties
2nd
3rd
4th
5th
documents
Report
2nd
2nd
3rd
4th
2nd
XML
creating (code)
2nd
3rd
4th
5th
2nd
2nd
3rd
3rd
4th
4th
5th
6th
2nd
datasets
9th
2nd
reading (code)
3rd
7th
8th
4th
5th
6th
7th
8th
3rd
4th
2nd
3rd
4th
5th
2nd
3rd
XMLTextWriter
2nd
2nd
3rd
DoesCustomerIDExist function
code
DOM.
9th
10th
11th
2nd
6th
7th
8th
5th
6th
7th
12th
13th
domains
Windows NT/2000
users, displaying
2nd
drilling
down to customer orders details
2nd
3rd
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
2nd
3rd
2nd
4th
editors
Formula Editor
displaying
2nd
2nd
edits
canceling
code
2nd
element tags
Ellipsis button
Enabled properties
on Windows forms
2nd
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
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
[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
2nd
3rd
4th
2nd
5th
3rd
6th
4th
5th
2nd
6th
3rd
7th
4th
8th
SQL statements
code
2nd
3rd
4th
stored procedures
code
6th
7th
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
Experts.
EXPLICIT mode
Export button
Export method
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
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
2nd
3rd
2nd
4th
5th
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
*.asmx
2nd
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
2nd
3rd
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
2nd
7th
8th
9th
10th
8th
9th
10th
11th
12th
13th
2nd
3rd
4th
5th
6th
7th
8th
2nd
3rd
4th
5th
6th
7th
8th
9th
10th
foreign keys
CustomerID column (Orders table)
2nd
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
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
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
2nd
3rd
4th
2nd
records
loading into text boxes (code)
2nd
3rd
search
calling
2nd
2nd
3rd
4th
5th
6th
7th
SQL databases
backing up and verifying
tables
searching
2nd
3rd
4th
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
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
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
2nd
frmHowTo3_4.vb
button controls, adding click events (code)
columns, sorting (code)
2nd
2nd
2nd
3rd
3rd
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
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
2nd
frmHowTo6_7.vb
forms, loading (code)
2nd
2nd
frmHowTo6_8.vb
UDFs (user-defined functions)
assigning (code)
creating (code)
2nd
2nd
3rd
4th
5th
2nd
frmHowTo7_1.vb
btnConnection button
disabling (code)
2nd
enabling (code)
2nd
2nd
3rd
4th
routines
calling (code)
2nd
3rd
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
2nd
3rd
2nd
2nd
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
2nd
frmHowTo8_1.vb
1stUnSelected list box
reloading (code)
list boxes
populating (code)
2nd
3rd
4th
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
2nd
2nd
3rd
4th
5th
tables
listing in databases (code)
2nd
3rd
frmHowTo8_4.vb
search forms, creating custom properties (code)
2nd
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
tables
2nd
3rd
frmHowTo8_6.aspx
data grids
page indexes, updating (code)
records, deleting (code)
2nd
3rd
4th
5th
6th
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
2nd
5th
frmHowTo8_7.vb
columns
DataGrid control
loading (code)
2nd
3rd
SQL strings
creating (code)
2nd
3rd
4th
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
tables
listing in databases (code)
frmHowTo9_2.vb
CCustomer class
code
2nd
Customer class
code
property declarations (VB .NET code)
property declarations (VB 6 code)
TextBoxChange method
text boxes, writing values (code)
2nd
ToString method
property information output (code)
2nd
frmHowTo9_4.vb
CCustomer class, constructors (code)
2nd
frmHowTo9_5.vb
Clear method, code
2nd
constructors
rows, inserting into tables (code)
constructors, testing (code)
2nd
4th
2nd
3rd
4th
5th
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.
[ 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
2nd
2nd
GenerateData routine
calling
code
2nd
3rd
creating
generating
data
code
2nd
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
grids
data
sorting, setting
grids.
2nd
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
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
5th
6th
2nd
2nd
[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
implementing
intertaces
2nd
3rd
4th
5th
6th
7th
8th
9th
2nd
In-Line function
indexes
compound
constraints
creating
with Property Pages dialog box
2nd
2nd
2nd
3rd
4th
5th
6th
7th
8th
2nd
performance
2nd
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
ICustomer
code
properties (code)
implementing
2nd
methods, adding
3rd
2nd
4th
5th
6th
7th
8th
9th
3rd
properties, adding
XML Web Services
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
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
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
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
2nd
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
2nd
3rd
LANs
(local area networks)
lblSQLString label
SQL statements
storing (code)
2nd
2nd
Length property
2nd
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
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
2nd
3rd
4th
5th
6th
7th
8th
text boxes
binding
2nd
9th
ListBox control
loading
populating
2nd
3rd
4th
5th
properties
2nd
3rd
4th
5th
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
Names
groups and users
2nd
2nd
territories, displaying
2nd
3rd
code
2nd
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
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
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.
2nd
local variables
declaring
in T-SQL
2nd
initializing
in T-SQL
2nd
locating
records
code
2nd
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
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
[ 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
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
mapping
Table Mappings collection
2nd
mbAddNew variable
resetting
code
2nd
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
2nd
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
2nd
WriteChangesToDB
code
2nd
2nd
2nd
mFax variable
event handlers
code
Microsoft Office 200x
minus sign (-)
mixed-mode authentication
models
2nd
5th
6th
7th
2nd
3rd
4th
5th
6th
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
2nd
3rd
4th
5th
7th
8th
9th
10th
modifiers
Public access
2nd
VB .NET properties
modifying
tables
5th
6th
2nd
3rd
4th
2nd
modPublicVariables.vb
public variables, declaring (code)
modSQLDMORoutines.vb
backup device names, retrieving (code)
connection strings, creating (code)
2nd
2nd
SQL Servers
loading (code)
2nd
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
4th
2nd
System.XML
System.XML.Schema
System.XML.Xpath
System.XML.XSL
Visual Basic
XML Web Services
namespaces.
2nd
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
2nd
3rd
4th
NodeType method
Northwind
connections
creating
2nd
Northwind database
customers
demographics, tracking
Orders table
Web site fields, adding
2nd
NULL
5th
columns
records
null values
Allow Nulls
null values, interpreting
number sign (#)
numbers
phone
validating (code)
2nd
[ 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
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
2nd
3rd
4th
5th
methods
properties
constructors
creating
2nd
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
2nd
3rd
4th
5th
2nd
6th
7th
8th
9th
10th
6th
7th
8th
9th
11th
12th
13th
14th
15th
16th
17th
18th
19th
20th
2nd
3rd
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
2nd
methods
5th
objects
2nd
2nd
properties
3rd
4th
3rd
6th
3rd
2nd
records, locating
3rd
4th
5th
2nd
3rd
4th
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
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
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
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
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-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
2nd
3rd
4th
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
3rd
2nd
2nd
3rd
2nd
3rd
2nd
4th
3rd
2nd
4th
5th
5th
6th
6th
4th
5th
6th
24th
25th
26th
27th
opening
ADO connections
code
2nd
forms
code
2nd
3rd
4th
2nd
JET databases
code
2nd
recordsets
code
2nd
3rd
4th
OpenNorthwindADOConnection subroutine
operators
BETWEEN
syntax
2nd
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 through
data grids
code
2nd
3rd
4th
5th
6th
7th
8th
9th
Panel object
properties
2nd
3rd
panes
Diagram (View Designer)
parameterized properties
classes, defining
code
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
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
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
2nd
Permissions button
2nd
3rd
2nd
persistence
Table control
persisting
recordsets
2nd
3rd
4th
recordsets to disks
code
2nd
persisting records
international
2nd
6th
7th
code
phone numbers
validating
code
2nd
3rd
PhoneDatatypes.vb
CNumberString class
code
2nd
event handlers
code
IsValid method, declaring (code)
StringValue property
code
ThrowException method, declaring (code)
plus sign (+)
2nd
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
2nd
3rd
4th
3rd
4th
5th
pointers
classes
points
break
setting in stored procedures
2nd
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
2nd
3rd
TextBox control
2nd
posting
records
in data grids (code)
2nd
3rd
4th
5th
6th
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
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
procedures.
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
6th
2nd
programming
Repeater control events
stateless (Web pages)
clients, state management
2nd
2nd
3rd
4th
2nd
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
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
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
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
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
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
2nd
3rd
4th
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
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
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
2nd
2nd
opening
Protected Friend object
Protected object
providers.
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
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
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
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-only properties
classes, defining
2nd
ReadBackupHeader method
reading
XML documents
code
2nd
3rd
4th
5th
2nd
2nd
3rd
6th
7th
8th
ReadOnly keyword
code
ReadOnly object
ReadOnly property
ReadValuesFromDataRow method
code
2nd
2nd
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
2nd
2nd
3rd
2nd
3rd
4th
5th
6th
7th
8th
9th
5th
6th
buttons
code
2nd
3rd
2nd
2nd
3rd
in DISTINCT clause
2nd
2nd
3rd
3rd
4th
10th
11th
12th
DISTINCT clause
editing
2nd
3rd
4th
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
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
4th
5th
6th
7th
8th
9th
10th
2nd
2nd
2nd
3rd
4th
5th
6th
7th
8th
9th
10th
11th
12th
sources
searching
stored procedures
updating
2nd
Recordset object
3rd
4th
2nd
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
2nd
2nd
references
for SQL APIs (Application Programming Interfaces)
setting
2nd
3rd
Web
viewing
2nd
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
2nd
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
list boxes
code
2nd
3rd
4th
5th
2nd
[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
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
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
reports
creating
2nd
3rd
6th
7th
8th
9th
10th
11th
12th
13th
14th
15th
Standard reports
SubReport reports
tables
2nd
tabs
viewing
2nd
ReportDocument
reports, assigning
2nd
reports
assigning with ReportDocument
calculated fields, adding
2nd
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
6th
7th
2nd
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
2nd
on-screen
creating with hyperlinks
fields as hyperlinks
2nd
2nd
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
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
2nd
ReportSource property
2nd
3rd
4th
8th
8th
12th
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
2nd
3rd
4th
return values
ProdAndCatTab table
2nd
returning
scalar types
UDFs (user-defined functions)
2nd
table types
UDFs (user-defined functions)
to calling pages
code
2nd
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
5th
6th
2nd
2nd
3rd
3rd
routinees
GetSQLDatabases
routines
ActivateEditing
creating (code)
2nd
ActiveEditing subroutine
calling (code)
2nd
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-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
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
2nd
security tables
Web Services, creating with parameters
2nd
security.
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
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
2nd
2nd
server-side solutions
state management
2nd
3rd
4th
Session object
2nd
3rd
4th
5th
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
2nd
3rd
4th
5th
6th
DataTable object
tracking (code)
2nd
3rd
4th
5th
6th
2nd
3rd
4th
5th
6th
7th
8th
SQL Server
bigint data type
binary data type
bit data type
char data type
cursor data type
data types
2nd
3rd
4th
5th
2nd
2nd
3rd
2nd
4th
2nd
indexes, defining
3rd
5th
2nd
3rd
2nd
3rd
4th
3rd
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
2nd
2nd
3rd
4th
6th
9th
8th
2nd
8th
7th
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
sharing
rights on databases
2nd
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
2nd
sources
of records, searching
sources of data
choosing
2nd
SQL
auto-generated
VB .NET tools, writing ADO.NET code
2nd
3rd
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 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
2nd
3rd
4th
5th
2nd
3rd
4th
databases
backing up
restoring
2nd
3rd
4th
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
2nd
3rd
4th
5th
6th
7th
8th
9th
10th
11th
12th
13th
14th
15th
16th
17th
DMF
(Distributed Management Framework)
APIs (Application Programming Interfaces)
2nd
3rd
objects
creating with ADO (ActiveX Data Objects)
2nd
3rd
4th
2nd
3rd
4th
5th
6th
2nd
3rd
4th
5th
6th
7th
8th
9th
10th
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
7th
2nd
8th
3rd
9th
10th
11th
4th
2nd
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
2nd
views
flexibility
Windows NT Integrated Security
users, logging into databases
SQL Server 2000
database
data, retrieving
2nd
2nd
3rd
4th
5th
6th
7th
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
2nd
3rd
2nd
creating
rows
2nd
table properties
tables
2nd
3rd
2nd
2nd
2nd
2nd
2nd
3rd
4th
5th
authentication, mixed-mode
2nd
3rd
4th
3rd
4th
5th
2nd
2nd
2nd
3rd
2nd
4th
2nd
2nd
4th
4th
3rd
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
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
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-DMO
(SQL-Distributed Management Objects)
collections
2nd
3rd
2nd
databases
backing up
connecting to
2nd
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
2nd
creating
objects
2nd
11th
12th
11th
13th
12th
14th
13th
15th
14th
16th
15th
17th
16th
18th
17th
18th
19th
2nd
3rd
SQL Server
DMF APIs (Application Programming Interfaces)
2nd
3rd
2nd
2nd
3rd
3rd
4th
4th
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
exporting
objects
data, exporting
SQL-NS
(SQL Namespace)
SQL.
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
2nd
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
clients
state management
2nd
server-side solutions
state management
4th
5th
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
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
2nd
3rd
4th
5th
2nd
2nd
8th
9th
10th
11th
3rd
4th
5th
6th
12th
7th
8th
sp-tables
testing
2nd
T-SQL (Transact-SQL)
TextBox control, populating
2nd
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
code
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
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
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
2nd
creating
5th
2nd
3rd
4th
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
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
2nd
creating
5th
2nd
3rd
2nd
3rd
4th
5th
7th
8th
9th
10th
6th
7th
4th
11th
Customers
2nd
Server Explorer
Customers table list
Customers, CustomerCustomerDemo, and CustomerDemographics
data
adding data (code)
binding (code)
3rd
4th
2nd
3rd
loading (code)
3rd
creating
2nd
5th
6th
7th
8th
2nd
2nd
2nd
3rd
2nd
2nd
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
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
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
2nd
tblPhones table
2nd
3rd
data grids
Repeater control
data, displaying
2nd
3rd
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
4th
5th
6th
2nd
2nd
3rd
text
of connection strings, displaying
2nd
text boxes
binding
to datasets
2nd
to list boxes
2nd
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
2nd
3rd
assigning (code)
2nd
values
values, writing (code)
2nd
4th
5th
6th
6th
7th
8th
9th
10th
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
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
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
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
5th
2nd
3rd
Transact-SQL (T-SQL)
stored procedures, creating
[See T-SQL]
[See copy]
trapping
exceptions
Transact-SQL.
transfer.
2nd
8th
9th
10th
11th
12th
13th
14th
15th
16th
17th
18th
19th
20th
24th
25th
26th
27th
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.
[ 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
2nd
2nd
2nd
3rd
unbound controls
on forms
underscore (_)
Unicode
[See URLs]
2nd
2nd
3rd
3rd
4th
update statements
executing
UpdateCommand method
updates
batch
executing
2nd
3rd
4th
5th
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
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
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
2nd
3rd
4th
usernames
code
2nd
2nd
3rd
4th
5th
27th
validation controls
code
CompareValidator
CustomValidator
2nd
2nd
3rd
messages, displaying
on Web Forms
2nd
3rd
4th
5th
6th
7th
8th
9th
10th
11th
12th
RangeValidator
Regular ExpressionValidator
RequiredFieldValidator
ValidationSummary
2nd
3rd
4th
5th
2nd
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
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
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
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
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
2nd
Web references
3rd
4th
5th
6th
7th
8th
9th
10th
2nd
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
2nd
desktop applications
generic search forms, creating
25th
26th
2nd
3rd
4th
5th
6th
7th
8th
9th
27th
2nd
[See VB .NET]
Visual Basic 6. [See VB 6]
Visual Studio
Visual Basic .NET.
2nd
2nd
3rd
2nd
4th
2nd
indexes, defining
3rd
5th
2nd
3rd
2nd
3rd
4th
3rd
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
2nd
3rd
2nd
creating
rows
2nd
table properties
tables
2nd
3rd
2nd
4th
6th
9th
8th
2nd
8th
7th
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)
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
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
2nd
3rd
4th
5th
11th
12th
data grids
6th
7th
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
2nd
naming
point-and-click
property settings
8th
9th
10th
11th
12th
13th
14th
Repeater control
data, displaying
2nd
3rd
4th
11th
12th
events, programming
templates
2nd
3rd
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
2nd
initializing
code
2nd
3rd
linking
loading
code
2nd
stateless programming
clients, state management
2nd
2nd
3rd
2nd
3rd
2nd
3rd
4th
Web references
viewing
2nd
2nd
Web servers
controls
and HTML controls, comparing
Validation Web server controls
2nd
2nd
3rd
[See WSDL]
Web sites
fields, adding to Northwind database
2nd
Sams
source code for chapters in book
SecurityWebServices
WebMethod
passwords, validating (code)
2nd
2nd
wfrmHowTo12_1.aspx.vb
data tables, adding data (code)
data tables, binding (code)
2nd
3rd
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
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
2nd
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
wfrmHowTo5_5a.aspx.vb
DropDown 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
2nd
2nd
2nd
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
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
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/2000
accounts
disabled
2nd
authentication mode
2nd
3rd
4th
5th
6th
7th
8th
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
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
2nd
2nd
Report Expert
reports, creating
2nd
3rd
4th
5th
6th
7th
8th
9th
10th
11th
12th
13th
10th
11th
12th
13th
14th
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
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
2nd
2nd
3rd
3rd
4th
4th
5th
6th
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
3rd
4th
5th
2nd
3rd
XMLTextWriter
2nd
2nd
2nd
3rd
4th
5th
5th
6th
records
persisting
XSD
(XML Schema Definition)
XML documents
creating
code
2nd
[See XSD]
2nd
2nd
3rd
3rd
4th
2nd
5th
3rd
6th
7th
4th
5th
6th
7th
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
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
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
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
2nd
3rd
XSD
(XML Schema Definition)
XSDs
Customers XSD, code
2nd
3rd
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.3 Bind and View Individual Text Boxes Based Off a Selected List Box Item
13
21
27
32
37
42
46
54
64
70
10.4 Select Whether the Report Will Be Displayed, Printed, or Exported Using Visual Basic .NET Code
73
80
10.6 Print Labels and Control the Order in Which Records Will Be Printed
85
90
94
99
102
106
109
111
114
118
120
123
125
129
132
135
143
147
154
159
164
172
177
181
2.1 Create a New SQL Server Database from Within Visual Studio .NET
186
188
193
197
202
205
209
212
3.2 Retrieve Results from SQL Server by Using the DataTable Object
217
219
223
4.1 Edit Data and Update Changes That Are Made to an ADO.NET DataSet Object
229
241
246
249
253
263
269
276
281
289
5.7 Add, Edit, and Delete Data Using the DataGrid Control
295
306
311
317
321
327
331
335
342
346
7.1 Create a Dialog Box to Connect to a New Database, Including Listing Available SQL Servers and
Databases
353
362
371
376
386
8.1 Work with Data-Bound Multi-Select List Boxes Using Windows Forms
394
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
438
8.7 Create a Point-and-Click Query Tool for Users Using a Web Form
454
462
472
478
9.3 Use Visual Studio .NET Tools to Speed Up Writing ADO.NET Code
486
499
505
514
9.7 Write Data Validation Code That Can Be Reused in Other Classes
520
533
Acknowledgments
534
535
536
537
538
Chapter 12. Utilizing XML Data In Your Visual Basic .NET Applications
539
540
Chapter 2. Creating SQL Server Database Objects From Visual Studio .NET
542
543
544
545
546
547
550
553
Comments
554
Conclusion
555
Copyright
556
558
560
563
564
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
710
712
Main Page
714
715
719
721
723
Table of content
724
728
729
732
734
What's Covered in 'Database Programming with Visual Basic .NET and ADO.NET: Tips, Tutorials, and
Code'?
735
737
738
739
740
X
XML Namespaces in .NET
744
Brought to You by