- •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
170 Chapter 4 WRITING AND USING PROCEDURES
The arguments to similar procedures are usually of equal stature, and their order doesn’t make any difference. A function that calculates the mean or other basic statistics of a set of numbers, or a subroutine that populates a ListBox or ComboBox control, are prime candidates for implementing using this technique. If the procedure accepts a variable number of arguments that aren’t equal in stature, then you should consider the technique described in the following section.
Named Arguments
You’ve learned how to write procedures with optional arguments and how to pass a variable number of arguments to the procedure. The main limitation of the argument-passing mechanism, though, is the order of the arguments. If the first argument is a string and the second is a date, you can’t change their order. By default, Visual Basic matches the values passed to a procedure to the declared arguments by their order. That’s why the arguments you’ve seen so far are called positional arguments.
This limitation is lifted by Visual Basic’s capability to specify named arguments. With named arguments, you can supply arguments in any order, because they are recognized by name and not by their order in the list of the procedure’s arguments. Suppose you’ve written a function that expects three arguments: a name, an address, and an e-mail address:
Function Contact(Name As String, Address As String, EMail As String)
When calling this function, you must supply three strings that correspond to the arguments Name, Address, and EMail, in that order. However, there’s a safer way to call this function: supply the arguments in any order by their names. Instead of calling the Contact function as follows:
Contact(“Peter Evans”, “2020 Palm Ave., Santa Barbara, CA 90000”, _
“PeterEvans@example.com”)
you can call it this way:
Contact(Address:=”2020 Palm Ave., Santa Barbara, CA 90000”, _
EMail:=”PeterEvans@example.com”, Name:=”Peter Evans”)
The := operator assigns values to the named arguments. Because the arguments are passed by name, you can supply them in any order.
To test this technique, enter the following function declaration in a form’s code:
Function Contact(ByVal Name As String, ByVal Address As String, _
ByVal EMail As String) As String
Console.WriteLine(Name)
Console.WriteLine(Address)
Console.WriteLine(EMail)
Return (“OK”)
End Function
Then, call the Contact() function from within a button’s Click event with the following statement:
Console.WriteLine(Contact(Address:=”2020 Palm Ave., Santa Barbara, CA 90000”, _ Name:=”Peter Evans”, EMail:=”PeterEvans@example.com”))
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
ARGUMENTS 171
You’ll see the following in the Immediate window:
Peter Evans
2020 Palm Ave., Santa Barbara, CA 90000
PeterEvans@example.com
OK
The function knows which value corresponds to which argument and can process them the same way that it processes positional arguments. Notice that the function’s definition is the same whether you call it with positional or named arguments. The difference is in how you call the function and how you declare it.
Named arguments make code safer and easier to read, but because they require a lot of typing, most programmers don’t use them. Besides, programmers are so used to positional arguments that the notion of naming arguments is like having to declare variables when variants will do. Named arguments are good for situations in which you have optional arguments that require many consecutive commas, which may complicate the code. The methods of the various objects exposed by the Office applications (discussed in Chapter 10) require a large number of arguments, and they’re frequently called with named arguments.
More Types of Function Return Values
Functions are not limited to returning simple data types like integers or strings. They may return custom data types and even arrays. The ability of functions to return all types of data makes them very flexible and can simplify coding, so we’ll explore it in detail in the following sections. Using complex data types, such as structures and arrays, allows you to write functions that return multiple values.
Functions Returning Structures
Suppose you need a function that returns a customer’s savings and checking balances. So far, you’ve learned that you can return two or more values from a function by supplying arguments with the ByRef keyword. A more elegant method is to create a custom data type (a structure) and write a function that returns a variable of this type. The structure for storing balances could be declared as follows:
Structure CustBalance
Dim BalSavings As Decimal
Dim BalChecking As Decimal
End Structure
Then, you can define a function that returns a CustBalance data type as:
Function GetCustBalance(ByVal custID As Integer) As CustBalance { statements }
End Function
The GetCustBalance() function must be defined in the same module as the declaration of the custom data type it returns. If not, you’ll get an error message.
When you call this function, you must assign its result to a variable of the same type. First declare a variable of the CustBalance type and then use it as shown here:
Private Balance As CustBalance
Dim custID As Integer
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
172 Chapter 4 WRITING AND USING PROCEDURES
custID = 13011
Balance = GetCustBalance(custID) Console.WriteLine(Balance.BalSavings) Console.WriteLine(Balance.BalChecking)
Here, custID is a customer’s ID (a number or string, depending on the application). Of course, the function’s body must assign the proper values to the CustBalance variable’s fields.
Here’s the simplest example of a function that returns a custom data type. This example outlines the steps you must repeat every time you want to create functions that return custom data types:
1.Create a new project and insert the declarations of a custom data type in the declarations section of the form:
Structure CustBalance
Dim BalSavings As Decimal
Dim BalChecking As Decimal
End Structure
2.Then implement the function that returns a value of the custom type. You must declare a variable of the type returned by the function and assign the proper values to its fields. The following function assigns random values to the fields BalChecking and BalSavings. Then, assign the variable to the function’s name, as shown next:
Function GetCustBalance(ID As Long) As CustBalance Dim tBalance As CustBalance tBalance.BalChecking = CDec(1000 + 4000 * rnd()) tBalance.BalSavings = CDec(1000 + 15000 * rnd()) GetCustBalance = tBalance
End Function
3.Then place a button on the form from which you want to call the function. Declare a variable of the same type and assign to it the function’s return value. The example that follows prints the savings and checking balances on the Output window:
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click Dim balance As CustBalance
balance = GetCustBalance(1) Console.WriteLine(balance.BalChecking) Console.WriteLine(balance.BalSavings)
End Sub
For this example, I created a project with a single form. The form contains a single Command button whose Click event handler is shown here. Create this project from scratch, perhaps using your own custom data type, to explore its structure and experiment with functions that return custom data types.
In the following section, I’ll describe a more complicated (and practical) example of a custom data type function.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
ARGUMENTS 173
VB.NET at Work: The Types Project
The Types project, which you’ll find in this chapter’s folder on the CD, demonstrates a function that returns a custom data type. The Types project consists of a form that displays record fields and is shown in Figure 4.2. Every time you click the View Next button, the fields of the next record are displayed. When all records are exhausted, the program wraps back to the first record.
Figure 4.2
The Types project demonstrates functions that return custom data types.
The project consists of a single form. The following custom data type appears in the form’s code, outside any procedure:
Structure Customer
Dim Company As String
Dim Manager As String
Dim Address As String
Dim City As String
Dim Country As String
Dim CustomerSince As Date
Dim Balance As Decimal
End Structure
Private Customers(8) As Customer
Private cust As Customer
Private currentIndex as Integer
The array Customers holds the data for nine customers, and the cust variable is used as a temporary variable for storing the current customer’s data. The currentIndex variable is the index of the current element of the array.
The Click event handler of the View Next button calls the GetCustomer() function with an index value (which is the order of the current customer), and displays its fields in the Label controls on the form. Then it increases the value of the currentIndex variable, so that it points to the next customer.
The GetCustomer() function returns a variable of Customer type (the variable aCustomer). The code behind the View Next button follows:
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click If currentIndex = CountCustomers() Then currentIndex = 0
Dim aCustomer As Customer
aCustomer = GetCustomer(currentIndex) ShowCustomer(currentIndex) currentIndex = currentIndex + 1
End Sub
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
174 Chapter 4 WRITING AND USING PROCEDURES
The CountCustomers() function returns the number of records stored in the Customers array. The event handler starts by comparing the value of the current index to the number of elements in the Customers array. If they’re equal, the currentIndex variable is reset to zero. The definitions of the CountCustomers() and GetCustomer() functions are shown next:
Function CountCustomers() As Integer
Return(Customers.Length)
End Function
Function GetCustomer(ByVal idx As Integer) As Customer
Return(Customers(idx))
End Function
Finally, the ShowCustomer() subroutine displays the fields of the current record on the Label controls on the form:
Sub ShowCustomer(ByVal idx As Integer) Dim aCustomer As Customer aCustomer = GetCustomer(idx) lblCompany.Text = aCustomer.Company
lblSince.Text = aCustomer.CustomerSince lblAddress.Text = aCustomer.Address lblCity.Text = aCustomer.City lblCountry.Text = aCustomer.Country lblBalance.Text = aCustomer.Balance
End Sub
The array Customers is populated when the program starts with a call to the InitData() subroutine (also in the project’s module). The program assigns data to Customers, one element at a time, with statements like the following:
Dim cust As Customer
cust.Company = “Bottom-Dollar Markets” cust.Manager = “Elizabeth Lincoln” cust.Address = “23 Tsawassen Blvd.” cust.City = “Tsawassen”
cust.Country = “Canada” cust.CustomerSince = #10/20/1996# cust.Balance = 33500 Customers(1) = cust
The code assigns values to the fields of the cust variable and then assigns the entire variable to an element of the Customers array. The data could originate in a file or even a database. This wouldn’t affect the operation of the application, which expects the GetCustomer() function to return a record of Customer type. If you decide to store the records in a file or a collection like the ones discussed in Chapter 11, the form’s code need not change; only the implementation of the GetCustomer() function will change. You should also change the CountCustomers() function, so that it detects when it has reached the last record.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
ARGUMENTS 175
The Types project uses a single button that allows users to view the next record. You can place another button that displays the previous record. This button’s code will be identical to the code of the existing button, with the exception that it will decrease the currentIndex variable.
Functions Returning Arrays
In addition to returning custom data types, VB.NET functions can also return arrays. This is an interesting possibility that allows you to write functions that return not only multiple values, but also an unknown number of values. Earlier in the chapter you saw how to return multiple values from a function as arguments, passed to the function by reference. You can also consider a custom structure as a collection of values. In this section, we’ll revise the Stats() function that was described earlier in this chapter, so that it returns the statistics in an array. The new Stats() function will return not only the average and the standard deviation, but the minimum and maximum values in the data set as well. One way to declare a function that calculates all the statistics is the following:
Function Stats(ByRef DataArray() As Double) As Double()
This function accepts an array with the data values and returns an array of doubles. This notation is more compact and helps you write easier-to-read code.
To implement a function that returns an array, you must do the following:
1.Specify a type for the function’s return value, and add a pair of parentheses after the type’s name. Don’t specify the dimensions of the array to be returned here; the array will be declared formally in the function.
2.In the function’s code, declare an array of the same type and specify its dimensions. If the function should return four values, use a declaration like this one:
Dim Results(3) As Double
The Results array will be used to store the results and must be of the same type as the func- tion—its name can be anything.
3. To return the Results array, simply use it as argument to the Return statement:
Return(Results)
4. In the calling procedure, you must declare an array of the same type without dimensions:
Dim Stats() As Double
5. Finally, you must call the function and assign its return value to this array:
Stats() = Stats(DataSet())
Here, DataSet is an array with the values whose basic statistics will be calculated by the Stats() function. Your code can then retrieve each element of the array with an index value as usual.
VB.NET at Work: The Statistics Project
The next project demonstrates how to design and call functions that return arrays. It’s the Statistics project, which you can find in this chapter’s folder on the CD. When you run it, the Statistics application creates a data set of random values and then calls the ArrayStats() function to calculate the
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
176 Chapter 4 WRITING AND USING PROCEDURES
data set’s basic statistics. The results are returned in an array, and the main program displays them in Label controls, as shown in Figure 4.3. Every time the Calculate Statistics button is clicked, a new data set is generated and its statistics are displayed.
Figure 4.3
The Statistics project calculates the basic statistics of a data set and returns them in an array.
Let’s start with the ArrayStats() function’s code, which is shown in Listing 4.2.
Listing 4.2: The ArrayStats() Function
Function ArrayStats(ByVal DataArray() As Double) As Double()
Dim Result(3) As Double
Dim Sum, SumSquares, DataMin, DataMax As Double
Dim DCount, i As Integer
Sum = 0
SumSquares = 0
DCount = 0
DataMin = System.Double.MaxValue
DataMax = System.Double.MinValue
For i = 0 To DataArray.GetUpperBound(0)
Sum = Sum + DataArray(i)
SumSquares = SumSquares + DataArray(i) ^ 2
If DataArray(i) > DataMax Then DataMax = DataArray(i)
If DataArray(i) < DataMin Then DataMin = DataArray(i)
DCount = DCount + 1
Next
Dim Avg, StdDev As Double
Avg = Sum / DCount
StdDev = Math.Sqrt(SumSquares / DCount - Avg ^ 2)
Result(0) = Avg
Result(1) = StdDev
Result(2) = DataMin
Result(3) = DataMax
ArrayStats = Result
End Function
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |