- •Using Your Sybex Electronic Book
- •Acknowledgments
- •Contents at a Glance
- •Introduction
- •Who Should Read This Book?
- •How About the Advanced Topics?
- •The Structure of the Book
- •How to Reach the Author
- •The Integrated Development Environment
- •The Start Page
- •Project Types
- •Your First VB Application
- •Making the Application More Robust
- •Making the Application More User-Friendly
- •The IDE Components
- •The IDE Menu
- •The Toolbox Window
- •The Solution Explorer
- •The Properties Window
- •The Output Window
- •The Command Window
- •The Task List Window
- •Environment Options
- •A Few Common Properties
- •A Few Common Events
- •A Few Common Methods
- •Building a Console Application
- •Summary
- •Building a Loan Calculator
- •How the Loan Application Works
- •Designing the User Interface
- •Programming the Loan Application
- •Validating the Data
- •Building a Math Calculator
- •Designing the User Interface
- •Programming the MathCalculator App
- •Adding More Features
- •Exception Handling
- •Taking the LoanCalculator to the Web
- •Working with Multiple Forms
- •Working with Multiple Projects
- •Executable Files
- •Distributing an Application
- •VB.NET at Work: Creating a Windows Installer
- •Finishing the Windows Installer
- •Running the Windows Installer
- •Verifying the Installation
- •Summary
- •Variables
- •Declaring Variables
- •Types of Variables
- •Converting Variable Types
- •User-Defined Data Types
- •Examining Variable Types
- •Why Declare Variables?
- •A Variable’s Scope
- •The Lifetime of a Variable
- •Constants
- •Arrays
- •Declaring Arrays
- •Initializing Arrays
- •Array Limits
- •Multidimensional Arrays
- •Dynamic Arrays
- •Arrays of Arrays
- •Variables as Objects
- •So, What’s an Object?
- •Formatting Numbers
- •Formatting Dates
- •Flow-Control Statements
- •Test Structures
- •Loop Structures
- •Nested Control Structures
- •The Exit Statement
- •Summary
- •Modular Coding
- •Subroutines
- •Functions
- •Arguments
- •Argument-Passing Mechanisms
- •Event-Handler Arguments
- •Passing an Unknown Number of Arguments
- •Named Arguments
- •More Types of Function Return Values
- •Overloading Functions
- •Summary
- •The Appearance of Forms
- •Properties of the Form Control
- •Placing Controls on Forms
- •Setting the TabOrder
- •VB.NET at Work: The Contacts Project
- •Anchoring and Docking
- •Loading and Showing Forms
- •The Startup Form
- •Controlling One Form from within Another
- •Forms vs. Dialog Boxes
- •VB.NET at Work: The MultipleForms Project
- •Designing Menus
- •The Menu Editor
- •Manipulating Menus at Runtime
- •Building Dynamic Forms at Runtime
- •The Form.Controls Collection
- •VB.NET at Work: The DynamicForm Project
- •Creating Event Handlers at Runtime
- •Summary
- •The TextBox Control
- •Basic Properties
- •Text-Manipulation Properties
- •Text-Selection Properties
- •Text-Selection Methods
- •Undoing Edits
- •VB.NET at Work: The TextPad Project
- •Capturing Keystrokes
- •The ListBox, CheckedListBox, and ComboBox Controls
- •Basic Properties
- •The Items Collection
- •VB.NET at Work: The ListDemo Project
- •Searching
- •The ComboBox Control
- •The ScrollBar and TrackBar Controls
- •The ScrollBar Control
- •The TrackBar Control
- •Summary
- •The Common Dialog Controls
- •Using the Common Dialog Controls
- •The Color Dialog Box
- •The Font Dialog Box
- •The Open and Save As Dialog Boxes
- •The Print Dialog Box
- •The RichTextBox Control
- •The RTF Language
- •Methods
- •Advanced Editing Features
- •Cutting and Pasting
- •Searching in a RichTextBox Control
- •Formatting URLs
- •VB.NET at Work: The RTFPad Project
- •Summary
- •What Is a Class?
- •Building the Minimal Class
- •Adding Code to the Minimal Class
- •Property Procedures
- •Customizing Default Members
- •Custom Enumerations
- •Using the SimpleClass in Other Projects
- •Firing Events
- •Shared Properties
- •Parsing a Filename String
- •Reusing the StringTools Class
- •Encapsulation and Abstraction
- •Inheritance
- •Inheriting Existing Classes
- •Polymorphism
- •The Shape Class
- •Object Constructors and Destructors
- •Instance and Shared Methods
- •Who Can Inherit What?
- •Parent Class Keywords
- •Derived Class Keyword
- •Parent Class Member Keywords
- •Derived Class Member Keyword
- •MyBase and MyClass
- •Summary
- •On Designing Windows Controls
- •Enhancing Existing Controls
- •Building the FocusedTextBox Control
- •Building Compound Controls
- •VB.NET at Work: The ColorEdit Control
- •VB.NET at Work: The Label3D Control
- •Raising Events
- •Using the Custom Control in Other Projects
- •VB.NET at Work: The Alarm Control
- •Designing Irregularly Shaped Controls
- •Designing Owner-Drawn Menus
- •Designing Owner-Drawn ListBox Controls
- •Using ActiveX Controls
- •Summary
- •Programming Word
- •Objects That Represent Text
- •The Documents Collection and the Document Object
- •Spell-Checking Documents
- •Programming Excel
- •The Worksheets Collection and the Worksheet Object
- •The Range Object
- •Using Excel as a Math Parser
- •Programming Outlook
- •Retrieving Information
- •Recursive Scanning of the Contacts Folder
- •Summary
- •Advanced Array Topics
- •Sorting Arrays
- •Searching Arrays
- •Other Array Operations
- •Array Limitations
- •The ArrayList Collection
- •Creating an ArrayList
- •Adding and Removing Items
- •The HashTable Collection
- •VB.NET at Work: The WordFrequencies Project
- •The SortedList Class
- •The IEnumerator and IComparer Interfaces
- •Enumerating Collections
- •Custom Sorting
- •Custom Sorting of a SortedList
- •The Serialization Class
- •Serializing Individual Objects
- •Serializing a Collection
- •Deserializing Objects
- •Summary
- •Handling Strings and Characters
- •The Char Class
- •The String Class
- •The StringBuilder Class
- •VB.NET at Work: The StringReversal Project
- •VB.NET at Work: The CountWords Project
- •Handling Dates
- •The DateTime Class
- •The TimeSpan Class
- •VB.NET at Work: Timing Operations
- •Summary
- •Accessing Folders and Files
- •The Directory Class
- •The File Class
- •The DirectoryInfo Class
- •The FileInfo Class
- •The Path Class
- •VB.NET at Work: The CustomExplorer Project
- •Accessing Files
- •The FileStream Object
- •The StreamWriter Object
- •The StreamReader Object
- •Sending Data to a File
- •The BinaryWriter Object
- •The BinaryReader Object
- •VB.NET at Work: The RecordSave Project
- •The FileSystemWatcher Component
- •Properties
- •Events
- •VB.NET at Work: The FileSystemWatcher Project
- •Summary
- •Displaying Images
- •The Image Object
- •Exchanging Images through the Clipboard
- •Drawing with GDI+
- •The Basic Drawing Objects
- •Drawing Shapes
- •Drawing Methods
- •Gradients
- •Coordinate Transformations
- •Specifying Transformations
- •VB.NET at Work: Plotting Functions
- •Bitmaps
- •Specifying Colors
- •Defining Colors
- •Processing Bitmaps
- •Summary
- •The Printing Objects
- •PrintDocument
- •PrintDialog
- •PageSetupDialog
- •PrintPreviewDialog
- •PrintPreviewControl
- •Printer and Page Properties
- •Page Geometry
- •Printing Examples
- •Printing Tabular Data
- •Printing Plain Text
- •Printing Bitmaps
- •Using the PrintPreviewControl
- •Summary
- •Examining the Advanced Controls
- •How Tree Structures Work
- •The ImageList Control
- •The TreeView Control
- •Adding New Items at Design Time
- •Adding New Items at Runtime
- •Assigning Images to Nodes
- •Scanning the TreeView Control
- •The ListView Control
- •The Columns Collection
- •The ListItem Object
- •The Items Collection
- •The SubItems Collection
- •Summary
- •Types of Errors
- •Design-Time Errors
- •Runtime Errors
- •Logic Errors
- •Exceptions and Structured Exception Handling
- •Studying an Exception
- •Getting a Handle on this Exception
- •Finally (!)
- •Customizing Exception Handling
- •Throwing Your Own Exceptions
- •Debugging
- •Breakpoints
- •Stepping Through
- •The Local and Watch Windows
- •Summary
- •Basic Concepts
- •Recursion in Real Life
- •A Simple Example
- •Recursion by Mistake
- •Scanning Folders Recursively
- •Describing a Recursive Procedure
- •Translating the Description to Code
- •The Stack Mechanism
- •Stack Defined
- •Recursive Programming and the Stack
- •Passing Arguments through the Stack
- •Special Issues in Recursive Programming
- •Knowing When to Use Recursive Programming
- •Summary
- •MDI Applications: The Basics
- •Building an MDI Application
- •Built-In Capabilities of MDI Applications
- •Accessing Child Forms
- •Ending an MDI Application
- •A Scrollable PictureBox
- •Summary
- •What Is a Database?
- •Relational Databases
- •Exploring the Northwind Database
- •Exploring the Pubs Database
- •Understanding Relations
- •The Server Explorer
- •Working with Tables
- •Relationships, Indices, and Constraints
- •Structured Query Language
- •Executing SQL Statements
- •Selection Queries
- •Calculated Fields
- •SQL Joins
- •Action Queries
- •The Query Builder
- •The Query Builder Interface
- •SQL at Work: Calculating Sums
- •SQL at Work: Counting Rows
- •Limiting the Selection
- •Parameterized Queries
- •Calculated Columns
- •Specifying Left, Right, and Inner Joins
- •Stored Procedures
- •Summary
- •How About XML?
- •Creating a DataSet
- •The DataGrid Control
- •Data Binding
- •VB.NET at Work: The ViewEditCustomers Project
- •Binding Complex Controls
- •Programming the DataAdapter Object
- •The Command Objects
- •The Command and DataReader Objects
- •VB.NET at Work: The DataReader Project
- •VB.NET at Work: The StoredProcedure Project
- •Summary
- •The Structure of a DataSet
- •Navigating the Tables of a DataSet
- •Updating DataSets
- •The DataForm Wizard
- •Handling Identity Fields
- •Transactions
- •Performing Update Operations
- •Updating Tables Manually
- •Building and Using Custom DataSets
- •Summary
- •An HTML Primer
- •HTML Code Elements
- •Server-Client Interaction
- •The Structure of HTML Documents
- •URLs and Hyperlinks
- •The Basic HTML Tags
- •Inserting Graphics
- •Tables
- •Forms and Controls
- •Processing Requests on the Server
- •Building a Web Application
- •Interacting with a Web Application
- •Maintaining State
- •The Web Controls
- •The ASP.NET Objects
- •The Page Object
- •The Response Object
- •The Request Object
- •The Server Object
- •Using Cookies
- •Handling Multiple Forms in Web Applications
- •Summary
- •The Data-Bound Web Controls
- •Simple Data Binding
- •Binding to DataSets
- •Is It a Grid, or a Table?
- •Getting Orders on the Web
- •The Forms of the ProductSearch Application
- •Paging Large DataSets
- •Customizing the Appearance of the DataGrid Control
- •Programming the Select Button
- •Summary
- •How to Serve the Web
- •Building a Web Service
- •Consuming the Web Service
- •Maintaining State in Web Services
- •A Data-Driven Web Service
- •Consuming the Products Web Service in VB
- •Summary
GETTING ORDERS ON THE WEB 1075
One drawback of the ProductSearch application is that it doesn’t allow the user to remove an item from the basket. You can add a second column with hyperlinks or buttons to the DataGrid of the first page and, every time the user clicks it, remove the corresponding product from the basket. To add a column with Remove buttons, open the Property Builder of the DataGrid control on the form. Add a new Button column and set its Text property to Remove and its CommandName property to RemoveItem.
When any of the buttons in this column are clicked, the ItemCommand event will be raised. You must add some code in this event’s handler to figure out what button was clicked (the Buy or Remove link) and act accordingly. Here’s the code you must insert in the ItemCommand event handler:
If e.CommandName = “RemoveItem” Then
If Not Session(sItemID) Is Nothing Then
Session.Remove(sItemID)
End If
Exit Sub
End If
The ProductSearch project on the CD contains the code for saving the new order to the database. It requires that the NewOrder and NewOrderLine stored procedures (from Chapter 22) be attached to the database.
Paging Large DataSets
Large DataSets have always been a problem in presenting data on Web pages. Displaying hundreds of rows on a single page isn’t very practical, and designers have come up with a technique for breaking the DataSet into pages. A page is a group of rows that are displayed on the same form. At the bottom of the form, there are usually links for the next and previous page, or links to all the pages that make up the DataSet. The user can click a link and jump to any other page of the DataSet. Paged DataSets are useful when they contain a relatively small number of pages. A DataSet with 300 pages isn’t very practical, even though you’re not displaying all the rows at once. How’s the user supposed to figure out which page contains the row he’s interested in? Very large DataSets aren’t appropriate for typical Web applications. If a search returns more than 100 rows, you should probably ask users to be more specific.
Among its many other features, the DataGrid control supports paging. It provides properties and recognizes events that simplify displaying the page numbers at the bottom of the control and moving to the appropriate page when a page hyperlink is clicked. To enable paging, you must set the control’s AllowPaging property to True and the PageSize property to the number of rows per page. The PagerStyle property determines the layout of the paging section of the control. This property is an object that exposes properties for setting the background/foreground color of the pager, the font, and so on. One of the properties is exposes is the Mode property, which can have one of the two values: NextPrev and NumericPages. These are the two pager modes that are supported automatically. You can also turn on AllowCustomPaging, in which case you must provide your own mechanism for paging through the DataSet (we will not discuss custom paging techniques in this book).
Once you’ve enabled paging, the control will receive the PageIndexChanged event every time one of the paging links is clicked. These links may be the previous/next arrows or page numbers. In this event’s handler, you must set the page to jump to, and refill the DataSet. Listing 24.15 is a typical PageIndexChanged event handler.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
1076 Chapter 24 ACCESSING DATA ON THE WEB
Listing 24.15: Handling Paging Events
Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, _
ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) _ Handles DataGrid1.PageIndexChanged
DataGrid1.CurrentPageIndex = e.NewPageIndex DACustomers.Fill(DSCustomerNames1, “Customers”) DataGrid1.DataBind()
End Sub
The property e.NewPageIndex is the page number selected by the user on the control. The transition to the new page isn’t automatic; you must explicitly set the CurrentPageIndex property. You can also set this property to –1 to effectively cancel the paging action.
Then you must refill the DataSet and bind the control to it again to force its contents to change. The DataSet doesn’t reside anywhere in the server’s memory, so you must recreate it.
There’s one last implication in programming DataSet paging events. Every time an item is clicked on the control, the ItemCommand event is also fired. We use this event to retrieve the selected item’s ID, in order to retrieve additional information from the database and update another control on the form. This event will also take place even when a paging link is clicked; you must ignore this type
of event in the ItemCommand event by inserting the following line at the beginning of the ItemCommand event handler:
If e.Item.ItemType = ListItemType.Pager Then Exit Sub
When to Use Paged DataSets
A word of caution on using the paging capabilities of the DataGrid control. The control doesn’t know how to retrieve the rows of the current page from the database. Instead, it retrieves all the qualifying rows and then displays the appropriate subset, taking into consideration the current paging settings. Every time you click the Next button, for example, you read all the qualifying rows from the database, display a small subset of the rows, and discard the rest. This isn’t the most efficient method of handling paging. If you want to add paging capabilities to your application, you must customize the default pager and provide your own code. However, we have already limited the number of qualifying rows to 100. We can use the default pager to create 10 pages of 10 rows each (at most) without placing a real burden on the database server. I have seen paging schemes on the Web with lists of page numbers that take up a dozen lines on the page. Use the DataGrid’s built-in paging features carefully, and make sure your pages will look good even if users select an enormous DataSet, like all history books. There’s nothing wrong with limiting the selection to 100 or 200 rows. Users will have to be more specific about the rows they’re searching for. Besides, what’s the average user going to do with 2,500 rows that meet their criteria?
Paging has always been a sore point in designing Web applications, and programmers have tried all kinds of paging schemes. The problem is that the rows in a table are not numbered, and there’s no SQL statement that can retrieve the N rows following a specific row. Even if the existing rows were numbered, how would you handle insertions and deletions? Paging is a technique for guiding the user close to the desired row. To use paging techniques efficiently, limit the number of rows returned by a query with the TOP keyword, so that you won’t have to display 300 pages of 20 rows each.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
A MASTER/DETAIL WEB PAGE 1077
A Master/Detail Web Page
This is one of the most common types of Web applications. Figure 24.17 shows a page with customers, orders, and order details. The top DataGrid control displays 10 customers at a time in paged mode. As explained already, paging doesn’t save you from downloading the entire Customers table every time the user switches to another page. If your Customers table contains thousands of customers, you should combine this application with the techniques discussed in the WebProducts or ProductSearch applications to limit the number of customers in the DataSet (you can force users to select customers by country or state, company, and so on).
Figure 24.17
A master/detail Web form with two nested tables
When the Orders button on a customer row is clicked, the selected customer’s orders are displayed on the DataGrid below. This DataGrid control isn’t paged (you can limit the orders to the current year, or even month, to make sure that the page doesn’t grow too long or you can add paging to this control as well). When the Details button on an order row is clicked, the order’s details are displayed on the third DataGrid control. MasterDetail is an interesting application that demonstrates data binding in a Web app, how to customize the DataGrid, and how to program the events of the DataGrid control.
Building the MasterDetail application is a lengthy process, but it’s also an overview of the material covered in Chapter 21 (how to populate DataSets with the visual tools). Start a new ASP.NET Web application and name it MasterDetail. Then drop the Customers, Orders, and Order Details
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
1078 Chapter 24 ACCESSING DATA ON THE WEB
tables from the Server Explorer onto the design surface. Rename the three DataAdapter object that will be created to DACustomers, DAOrders, and DADetails. Configure them as follows.
DACustomers This DataAdapter retrieves all the customers of the Northwind database with the following SELECT statement:
SELECT CustomerID, CompanyName, ContactName FROM dbo.Customers
DAOrders This DataAdapter retrieves the orders of a selected customer. In Figure 24.17, you’ll see that it includes a calculated field, the Order Total field. The total of an order isn’t stored anywhere in the database and must be calculated by the SELECT statement that retrieves the orders. The DAOrders object’s SELECT command is shown next (I’ve removed the table qualifiers to fit it nicely on the printed page):
SELECT |
Orders.OrderID, Orders.OrderDate, |
|
SUM ((UnitPrice * Quantity) * (1 - Discount)) AS Total |
FROM |
dbo.Orders INNER JOIN Order Details |
|
ON dbo.Orders.OrderID = dbo.[Order Details].OrderID |
WHERE |
(Orders.CustomerID = @CustID) |
GROUP BY |
Orders.OrderID, OrderDate |
DADetails This DataAdapter retrieves the details of a selected order and also calculates the subtotal of each line (quantity × price × (1 – discount)). Here’s the SELECT statement of the DataAdapter:
SELECT [Order Details].OrderID, [Order Details].ProductID, [Order Details].UnitPrice, [Order Details].Quantity, [Order Details].Discount, Products.ProductName,
[Order Details].UnitPrice * [Order Details].Quantity) * (1 - dbo.[Order Details].Discount) AS Total
FROM dbo.[Order Details] INNER JOIN dbo.Products
ON dbo.[Order Details].ProductID = dbo.Products.ProductID WHERE ([Order Details].OrderID = @orderID)
After configuring the three DataAdapters, create the corresponding DataSets. Create a separate DataSet for each DataAdapter, and name them DSCustomerNames, DSOrders, and DSDetails. Three instances of the DataSet class will appear on the Component tray, and they’ll be named after the corresponding DataSet suffixed by the digit 1.
Your next step is to bind the DataGrid controls to the corresponding DataSets. The data-binding properties of the three controls are shown next:
Property |
Setting |
DataGrid1 Control |
|
DataSource |
DSCustomerNames1 |
DataMember |
Customers |
DataKeyField |
CustomerID |
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |