Back ] Up ] Next ]

Chapter 5

Using Class Modules

Using Class Modules *

Certification Objectives *

Designing an Application *

Assessing the Potential Impact of the Logical Design *

Performance *

Maintainability *

Extensibility *

Availability *

Creating Class Modules *

Exercise 5-1:Creating a Class Module *

Using a Class Module *

Creating "Instances of Classes" or "Objects" *

COM Object Creation Services *

Designing and Adding Component Properties, Methods, and Events *

Exercise 5-2: Using Class Modules *

Designing Visual Basic Components to Access Data from a Database *

Creating Data-Bound Class Modules *

Creating a Data Source Class Module *

Exercise 5-3: Creating a Data Source *

Creating a Data Consumer *

Exercise 5-4: Creating a Data Consumer *

Certification Summary *

Two-Minute Drill *

Self-Test *

 

Certification Objectives

One of the biggest mistakes that some programmers make is thinking that they can create an application by just jumping into Visual Basic and coding away. This is far from reality. Imagine how reliable a car would be if the auto makers allow people on the line to slap together cars, giving no real thought about design. It’s the same with programming. A good chunk of the work you do on any program should be spent on designing the program and thinking about how to best make the program work for your user.

Beyond design issues, this chapter covers—as its title says—class modules. These allow you to create your own components, data sources and consumers, which you can add to your VB project. In doing so, you’re getting into the meaty part of programming.

Finally, we’ll also expand on some of the things you learned in the previous chapter, by delving into how to access data from databases. This includes using data consumers, and also using the Recordset object to add, delete, and modify records.

Designing an Application

Before any controls are added to a form, and before any code is written, an application must be designed. This doesn’t simply mean choosing the background color or font sizes and styles. It involves deciding and planning the architecture of your application.

In the old days (and today, with bad programming), creating an application was a matter of building one monolithic program. The preferred method these days, however, is breaking your application up into components.

Exam Watch: Microsoft’s new MCSD path doesn’t include the two Windows Architecture exams. Because these two exams will be retired in 2000, there is a stronger emphasis on design in other Microsoft exams, such as that for Visual Basic 6.0.The material covered in the Windows System Architecture exams still exist, but are now spread over the Desktop, Distributed, and Analysis Requirements exams.

When I mentioned components, you probably guessed that this has something to do with ActiveX. If you did, you’re right—but there’s more to it than that. As we saw in Chapter 3, ActiveX components allow us to split an application so that parts are spread across a network or a computer. We can also use things such as stored procedures, Class modules, and so on to break our application into manageable pieces. In doing so, programmers are able to focus on what they do best, and parts of the application are reusable and accessible to other programs. Using this method of design, you are able to split application design into two main tasks: creating reusable components (ActiveX, stored procedures, and so forth), and integrating those components into your application.

Designing an application in this manner is a matter of organizing or separating an application’s requirements, deciding what each component is to do, and deciding what each component requires. This is a conceptual process, and there are a number of conceptual models available to help you with application design. The one that Microsoft recommends is the three-tiered "services model." As its name suggests, the services model organizes your application’s requirements into specific services. Typically, such requirements fall into one of the three tiers or categories: user services, business services (and other middle-tier services), and data services.

The top tier of this model is user services. You can associate this category with the user interface, which presents the data to the end user . In other words, this is the compiled program that allows the user to actually view and manipulate data. While it is usually the executable program that the user starts at his or her workstation, it can also be a separate component. For example, if you needed to display financial data, you might use an ActiveX control that can display data in a grid.

The middle tier covers a number of services. For example, one such service might be offloading graphic-intensive processing to a server, rather than using a workstation’s CPU. However, this tier is associated primarily with business services, which applies business rules to tasks that a program performs . An example of this in action is a sales program. The user inputs an order into the program, which then checks the customer’s credit limit before adding the order to the customer’s account. Here, there is a "business rule" of a customer needing to have enough available credit before new items can be added to his or her account. If there isn’t enough credit, the item can’t be purchased. You’ve probably experienced such a program when you tried to charge something on a maxed-out credit card. When such rules are applied to a program, they are done so at the middle tier.

Another example of business services might involve the mark-up on a product. When a company sells a product, they add to the cost of the product (mark it up) so they make some profit on selling the item. In such cases, there must be a component of the program that looks at how much the company paid for the item, adds the mark-up, and then produces the selling price. With this business rule, the program is adding a percentage to the original amount. However, the percentage added often changes due to the law of supply and demand. This means the programmer will probably have to change this percentage on fairly regularly. Because business rules like this change more often than the actual tasks (in this case, the task of accepting input of an item and returning a price), business tasks are prime candidates for being a component of a program. By having such rules incorporated into components, the entire program doesn’t need to be changed, just a component of that application.

The final and bottom tier of this model is data services. What this tier does is define, maintain, access, and update data. When business services make a request for data, data services manages and satisfies the request. The ability to manage and satisfy the request can be implemented as part of the database management system (DBMS) or as components.

While the above explains the levels of the design model, it doesn’t explain the stages of the design process. Now that we’ve gone through how a program can be split into components and what each service is and does, we need to go through the steps of developing such an application. The design process is made up of four stages: conceptual design, logical design, physical design, and deployment.

Conceptual design is the process of identifying an application’s requirements . In other words, what is the thing supposed to do! This sounds simple in theory. However, in real life, it can be one of the most frustrating design stages. This stage involves interviewing business owners, managers, and end users to find out what they require from the application. Unfortunately, you will find that many times the people you interview will forget to mention certain things that they consider essential.

On the Job: It is common for new programmers to neglect design, and jump straight into coding. They talk with a manager or owner of a company, and then create the program on the fly. The problem is that many problems can be worked out through the design process, which saves you time and frustration in the long run. Also, outline in your contract what you originally agreed upon—that way you can charge for such things as adding spreadsheet functionality to a database.

Logical design is a process that’s made up of five activities. Like the other stages of design, each of these parts of logical design are refined and revised as later stages are being worked on. It is not a process that is completed in a single pass.

The first part of logical design is identifying business objects and services. Business objects are abstract representations of real-world things or concepts (such as orders, customers, and so on). Something should be considered a business object if one or both of these things are true:

Once the business objects and services are identified, interfaces need to be defined. An interface in this case is simply a statement of preconditions and conventions (syntax, input and output parameters) needed to call the service. It outlines what is needed to call on that particular component.

Business objects often call on services in other business objects. When this occurs, it is called "business object dependencies." One business object depends on the services of another business object. An example of this would be an order depending on the existence of a customer in a database. Such dependencies need to be identified at this stage. When this is done, validating, refining, and revising the logical design is all that is left.

Validation of the logical design requires a comparison of logical design to the requirements in the original conceptual view . This means assuring that the work done in the logical design stage matches what was originally outlined in the conceptual design. If the requirements are not met, the logical design is invalid and needs to be reworked.

As stated earlier, designing an application isn’t completed in a single pass. The logical design stage is no different, and it goes through multiple iterations. Revision and refining the logical design requires problems to be hammered out and the design improved upon. This is the final activity of logical design.

The final two stages of designing an application are physical design and deployment. The physical design takes the business objects and services identified in the logical design and maps them to software components . Once these components have been established, the deployment stage comes into effect . It is here that decisions are made as to how components are or aren’t distributed across the network.

It is important to remember that these stages aren’t usually completed in a single pass. In most cases, each stage may need to be refined, and revisions will need to be made. Each time improvements are made upon the previous incarnation. This is a normal occurrence in the four stages.

Assessing the Potential Impact of the Logical Design

No amount of planning and design is complete without considering the impact it will have on your application, your computer system, and (more often than not) a network. While testing after the application is complete is an important part of development, it is equally important to consider what the program will do before coding has even begun. Imagine spending 80 hours coding a project, only to find that it runs too slowly or could have been improved by spending an hour or two considering your initial design!

Basically the rule of thumb here is trying to get it right the first time. It is easier to improve an application in design than it is after coding. While you’ll never get it perfect, there are a number of proven tips and tricks that allow you to improve the performance, maintainability, extensibility and availability of your application.

On the Job: While there are no hard and fast rules regarding how much time should be spent on planning, there are some estimates on the time that should be spent on each phase of an application’s life cycle. On average, you should spend 25% in analysis, 38% in design, 13% coding, 19% testing, and 7% in integration. Again, these are estimates, not laws. There will be times when more or less time is spent in various phases.

Performance

The life of a programmer would be incredibly easier if every user had fast processors, speedy networks, and unlimited hard disk space. At the very least, it would be nice if every user had the same system that the programmer uses to create an application. Unfortunately, this is pure fantasy. Amazingly enough, many programmers do forget that many (if not most) users don’t have as good a computer system as the developer has. By not taking this into consideration, many users of an application wind up being hampered by processor, network, and/or disk space limitations.

As we’ll see in chapter 11, there are ways to optimize your VB application at the end of the development cycle. However, where performance really comes into effect is during the design stage.

One area where you can improve the design of your application is with variables. By putting some thought into the data types you’ll use for variables, you can increase the speed of an application and lower the amount of memory required by your application. Different data types use different amounts of memory. A variant uses more than an integer, for example. Also, you can use data types that provide a larger range of numbers than you could possibly need. In the case of a variable used to store a person’s age, you would want to use an integer rather than a long. Performance increases when consideration is given to the data types used in an application.

Because variables have to looked up each time they’re used, constants should be used where they can. Constants are resolved when an application is compiled. By using a constant for something like minimum wage, the application doesn’t have to look up what the value of minimum wage is. The value is always the same, so the program’s performance increases.

If a number of modules are loaded at startup, it can significantly slow the startup time of an application. Modules should only be loaded on a "as needed" basis. If there are a number of modules that must be loaded at startup, it may be an idea to incorporate a splash screen in your application. You’ve seen splash screens when you’ve started VB (it’s the form that first starts; it has no controls, min or max button). Splash screens let the user know that the application is actually starting, and masks the amount of time it takes to load the program. Such ways of improving the apparent speed of an application are covered in Chapter 11.Forms are a tricky matter with performance. If all forms are loaded at startup, the application will run faster as it switches from one form to another. However, if your application uses a lot of forms, loading them all can gobble up memory. A rule of thumb is determining which forms will be used commonly during the design stage, and then having the commonly used forms loaded into memory when the application starts. Forms used less often (such as one used for configuring preferences, or "About this Program" forms) should not be loaded at startup.

Reducing the number of controls on a form and carefully choosing what should appear on a form can also improve performance. More controls on a form means more memory will be used by an application. Brevity is the key here. The second part of this issue is choosing what should appear on the screen, and what should be accessed by clicking a "More Information" or "Advanced" command button. An example of this might be a personnel application that allows you to move through information on each employee in a company. While it would be nice to have a picture of each employee appear on the screen, graphics are a well-known memory gobbler. It might also be nice to have everything known about a person appear on the screen at once, but this makes for a slow application. You can increase performance dramatically by designing the application with a command button that brings up a form with additional information and adding a "View Picture" command button that brings up the employee’s picture.

A similar method involves having a drop-down style dialog box appear when a user clicks a button that says something like "Details>>". An example of this is seen when you use a dial-up connection in Windows 98. Once connected, the "Connected to" dialog box displays how long you’ve been connected, bytes received and sent, and your connection speed. By clicking the "Details>>" button, the dialog box expands (via another dialog box that drops down from the original) to show additional information. Because the additional information doesn’t need to be commonly accessed, the drop-down allows the information to be displayed on demand.

Other suggestions for improving performance are shown in Table 5-1. Each of these are proven methods suggested by Microsoft. For additional design stage tips, refer to VB help.

Coding Suggestion

Reason

Reduction of dialog boxes Dialog boxes require memory whether they’re visible or not. Reducing the number in your application will reduce the amount of memory your application uses.
Early binding of COM objects Late binding causes extra work for the processor because you don’t get compile-time type checking. This means that reference at runtime must be checked, slowing the performance.
Use variables instead of arrays It takes longer to access an array than it does to access a variable.
Remove "dead code" Remove all unused code from your application before compiling. This does not mean comments, but code that is no longer used by your application.

Table 1. Suggestions for improving performance at the design stage

Maintainability

When designing an application, be sure to consider maintainability. Without incorporating this into your design, problems can arise when changes need to be made. Throughout this book, we will cover various methods of making your application maintainable.

An important aspect of making your source code maintainable is documentation. This means writing down the objectives of your application, what aspects of your application does, creating flow charts, and so forth. Throughout the design process it is important to create such documentation, which can be utilized in later stages of design, and in the creation of Help documentation.

One area of maintainability that can’t be stressed enough is adding comments to source code. This means including some explanation as to what a procedure does, why a variable might be included, and so on. There are two ways of adding comments to your source code: with REM, or by putting an apostrophe before a statement. Anything typed on a line that starts with REM, or anything on a line that is typed after an apostrophe, is ignored when your application is designed. The following shows two examples of comments:

‘ This is a comment
REM This is a comment as well

The reason for adding such comments is simple . . . while you may remember what something does at the time of coding, you may forget later (that is, when you need to change the code days, weeks, months, or even years down the road). In addition, the person who creates the original code may not be the person who modifies the code later. Imagine the difficulty of trying to maintain an application’s source code when you have no idea what it was originally designed to do!

An example of the need for comments is apparent when considering the Y2K (Year 2000) issue. Many of the original computer programmers are no longer in the business or are not working for the company they worked for in the 1960s. As such, good comments are essential for understanding what the programmer was attempting to do in his code. It is up to new programmers to read the comments and rework the code. When considering the impact and time-sensitive nature of Y2K, it is becomes apparent how important comments are.

On The Job: Adding comments is an important part of real- world programming. Unfortunately, it’s also boring and monotonous, so many programmers can be sloppy with it. One way of being sloppy is supplying few or no comments. This makes it difficult to maintain the source code later. Another way of being sloppy is by putting in comments for nearly everything in the code. The following would be overkill with comments:

REM The next line creates a variable called "x", which is
REM an integer
Dim x as integer
X=1 ‘This line assigns the value of one to x

Lack of comments or poorly worded commenting can slow things down to the point where it may be faster to create a new application than to rework the badly commented one.

Another important aspect of making an application maintainable deals with standards and conventions. Appendix D covers this in great detail, and should be used in your programming.

Extensibility

Extensibility allows an application to go beyond its original design. In other words you can add stuff that wasn’t there before. A good example of extensibility is VB itself. With it you can add Add-ins, which extend the abilities of the development environment.

By incorporating extensibility into your design, you are able to benefit from other third party programmers developing added features to your application. In addition, you are able to allow an added method of upgrading your application. Rather than having to create a complete upgrade—with new features—for a product, the features can be added through something like an Add-in Manager that’s available as a menu item.

Availability

Availability has nothing to do with your plans for Saturday night, nor does it have anything to do with how easy it is to purchase your programs at a friendly neighborhood computer store. What it does deal with is how well your application handles failures.

Error handling is an important feature of any program, and is a necessary implementation. People won’t be too thrilled with a program that crashes when a file can’t be found or when another user is accessing the same file on a network. In later chapters we will deal with implementing error handling in greater depth. The important thing to remember is that it is a design issue. It is added during the design stage, and incorporated into the code. It is not something that’s added as an afterthought.

Creating Class Modules

Class modules allow you to add custom object variable types to your VB project. Classes are templates that are used to create objects. Using class modules, you can write code to specify methods, properties, and events for custom objects that you create in the module.

Adding a class module to a project is done through the Project menu in VB . From this menu, choose Add Class Module, and then choose Class Module from the dialog box that appears. Upon doing so, a Code window will appear for the new class module, as illustrated in Figure 5-1.

Figure 1: By selecting Project | Add Class Module, a class module is added to your project, and the module’s Code window appears

As with anything you add to your project, the first thing you should do when adding a class module is change its name. It is important to do so because controller code will use the Name property when a copy of your class is instantiated as an object. This is done through the Properties window in the right-hand side of VB. By clicking on the Name property, you can then change the default name to a meaningful, original name.

Exercise 5-1:Creating a Class Module

  1. Select Project | Add Class Module.
  2. From the dialog box that appears, select Class Module.
  3. When the class module is created, a Code window for it will appear. In the Properties window, change the Name property to a meaningful, original name.

Using a Class Module

A class module is different from a standard module. In a standard module, there is only one copy of the module’s data at any given time . Class modules have one copy of data for each instance of the class, meaning that there is a copy of the data for each object created from the class . The data is handled differently in each type of module.

The second difference between a standard and class module is its scope. A standard module’s data exists for the life of the program, while data in a class module exists only for the lifetime of the object . Data in a standard module is always there when the program runs, while the class module’s data is created when the object is created and removed when the object is removed.

The third and final difference between these two modules deals with Public variables. When you declare variables as Public in class modules, they are only accessible when objects variables contain a reference to an instance of a class. This is different from variables declared as Public in standard modules. In standard modules, Public variables are visible from anywhere in a project.

Class modules can be used to create custom object variable types. While VB comes with a wide variety of built-in controls—like text boxes, combo boxes, command buttons, and so forth—you can use class modules to create your own objects. Like these built-in controls, you can add properties, methods, and events to your own objects.

The big reason to use class modules is that—unless you’re creating ActiveX controls or documents—each custom object you create must have a class module behind it. You can think of class modules as the engine that gives your custom object power—no class module, and your custom object won’t run.

Creating "Instances of Classes" or "Objects"

Creating an instance of a class is done with the Dim statement. It is important to remember that you can have multiple instances of a class. Another term for an instance of a class is "object." While a class is a template for an object, an object is an instance of a class. In the following example, the objects Test1 and Test2 are objects of type Class1:

Dim Test1 As New Class1
Dim Test2 As New Class1

The other way to create an object is with the CreateObject function. This creates an instance of a server component, which your application can then call upon. The syntax for creating an object in this manner is:

Server.CreateObject

As we’ll see later, there are differences between using these two methods of creating objects.

COM Object Creation Services

VB always uses COM object creation services when objects are created from classes provided by other components. There is no difference between using the New operator, declaring a variable As New, and CreateObject when you are creating externally provided objects. This is not to say that there are not differences between these methods of creating objects.

The New operator and variables declared As New create objects that are part of your project. When New or As New are used, VB uses a private implementation of COM. However, when the object is provided by another component, it uses COM object creation services. It uses private object creation when a class is part of your project, and COM creation services when the object is provided externally by another component.

CreateObject always uses COM object creation services . It doesn’t matter if the objects created are externally provided or part of your project. As far as the CreateObject function is concerned, no differences exist between an object provided externally or internally.

Designing and Adding Component Properties, Methods, and Events

Creating components is great, but useless if they have no properties, methods, or events. It would be like buying a car that didn’t have an engine, wheels, or gas tank; in short, you’re not going to get very far without them.

When you create a new class module, it has two events associated with it: Initialize and Terminate. The Initialize event occurs when an instance of a class is created, before any properties are set. It is used to initialize any data used by the class and can be used to load forms used by the class. The Terminate event occurs when the object variable is set to nothing or goes out of scope. The way calling code destroys an object is with the syntax :

Set Object = Nothing

The Terminate event is used to save information, unload forms, and handle any other tasks required to be done when the class ends. As you’ve probably assumed, Initialize is the first event that occurs between the two, and Terminate comes last.

Events are handled in class modules much the same as they are in built-in objects. By referring to Chapter 3, you can see that the Initialize and Terminate events in a form are identical to what occurs when dealing with these same events in a class. By adding code to such events, you can manipulate how your class module will act, just as you would manipulate the actions of a form.

Adding events is first done by declaring it in the class module’s General Declaration section. To do this, use the following syntax:

Public Event Eventname (argument list)

In this, Eventname is the name you’ve chosen to call the event, and argument list is a list of arguments declared by name and type. The argument list is similar to a list of parameters that you’d use in a regular procedure. If you were going to use an event called LoginName, with an argument of the current user’s username, you might declare the following event in the General Declaration section of your class:

Public Event LoginName (UserName as String)

The code that raises the event (as we’ll see next) would then pass the value to the argument UserName.

To cause an event to run, you use the RaiseEvent keyword . Assigning a value to a variable then using the RaiseEvent keyword to call the event and pass the variable’s value does this. To use our previous example, the LoginName event we created would be raised with the following code:

StrName="My Name"
RaiseEvent LoginName (strName)

Once a class is created, you can create methods. The methods you create are called in the same manner that you call methods in a standard VB class. Adding methods is done by creating Public procedures in the class module. You can create either subprocedures or functions. Once these procedures are added, other parts of your application, or ActiveX controllers, can call the procedures as methods of the object class. These procedures will appear in the Object Browser, which you can view to see what objects and methods are available for a class.

In addition to creating methods, you can also create properties. Custom properties are implemented as either Public variables of the class, or by using the procedures Property Let, Property Set, and Property Get.

Public variables in a class become the properties of the object. Declaring Public variables is done the same way as declaring them in standard modules and forms. If you were going to add a property called UserName to the class, you would add the following code to your class module’s General Declaration section:

Public UserName as String

Once you declare the Public variable, it becomes a property of the object. When the object has been instantiated, your calling code can then manipulate the properties. You can then assign and manipulate values and get the values returned to your program. To expand on our previous UserName example, the following code assigns the value "Michael" to the UserName property of an object called Object1:

Object1.UserName = "Michael"

After doing this, we can create a message box that will display the UserName property of our Object:

MsgBox Object1.UserName

The other way to create properties is by using property procedures. These allow you to execute a procedure when a value is changed or read, have a property constrained to a small set of values, or expose properties that are read-only. Property procedures are created in two parts; one part assigns values to the property, while the other returns its value.

Property Set and Property Let both assign values to a property. The difference between them is that Property Set is used when we are setting the value of an object property. If the property is a reference to an object, Property Set is always used. Property Let on the other hand is used for nonobjects like variables.

You can assign object references to an object variable by using the Property Set command. In using the following syntax, Property Set is used to Set x as a New Class object:

Dim x as Class1
Set x = New Class1

In doing so, Property Set is used to set the value of an object property.

At this point, you’ve seen the Property Let statement used without even realizing it. In the early days of VB, you assigned a value to a variable by using the following syntax:

Let x=5

While this is still supported in VB, the common way to assign a value to a variable is with the following syntax:

x=5

There may be times where both Property Let and Property Set would be used. This is done in cases where you set a property that is a reference to an object, and then use Property Let to assign values to its properties. This is seen in the following example:

Private Amnt As Integer
Dim x As Class1
Set x =New Class1
x.Amnt=13

In this example, we create a class module, setting x as a new Class1 object. We also have a property (Amnt) that has an integer value. By using Property Let, the value of 13 is assigned to the Amnt.

Property Get, as it’s name suggests, executes when the calling code reads the property. Since Property Set and Property Let write to the property, you can create read-only properties by using Property Get without a matching Property Set or Property Let.

To fully understand property procedures, let’s work through the following example of code:

Private gstrUser As String

Public Property Let UserName (strInput As String)
gstrUser=Lcase(strInput)
End Property
Public Property Get UserName () As String
UserName = gstrUser
End Property

In the first line, we declare gstrUser as a String (a group of characters not longer than 255 characters in length). We then create our first property procedure. Notice that both the Property Let and Property Get procedures have the same name. You are able to do this because Property Let is writing to the property, while Property Get reads the property. They are not duplicates of one another; they perform different tasks. In the example, Property Let takes the value that our user has input and assigns the value to the variable gstrUser. In the example, it is also converting the characters to lowercase. In the next procedure, Property Get UserName is reading the value of gstrUser and returning it. While Property Let assigns a value of UserName, Property Get is reading and returning the value.

Exercise 5-2: Using Class Modules

  1. Start a new VB Standard EXE project.
  2. Select Project | Add Class Module
  3. In the class module’s General Declarations section, declare a Public variable called strUser as a String. This is done with the following code:
  4. Public strDate As Variant

  5. In your class module, create a public procedure that will return the days to the millennium. Use the code as follows:
  6. Public Function NewMil()
    NewMil = "The Millennium is in " & DateDiff("d", Now, strDate) & " days!"
    End Function

  7. Now add a command button to your form, and add the following code. Since some people consider the new millennium to start in 2000, while others consider it to be in 2001, we will create a separate class instance (that is, object) for each. Each object will then return a string that will print the result on your form:
  8. Dim Date1 As New Class1
    Dim Date2 As New Class1

    Date1.strDate = "January 1, 2000"
    Date2.strDate = "January 1, 2001"
    Print Date1.NewMil
    Print Date2.NewMil

  9. Run your program, click the command button, and test your program.

Designing Visual Basic Components to Access Data from a Database

One of the truly exceptional uses for VB is creating database applications. VB provides a number of controls that specifically deal with creating programs of this type. As we saw in the previous chapter, the Data control allows you to bind data to a control at Design time. However, this is not your only resort in accessing data from a database.

In addition to using the built-in controls available in VB, you can also create your own data consumers and data sources. As we’ll see in the next section, there is no Design time visual representation of data, as is the case with the Data control. By using the Binding Collection and class modules, data can be bound to a control or consumer at any time .

Because class modules offer no visual representation of data, you must create your own methods of navigating through data. Otherwise, a user is stuck looking at the same record, with no way of moving to the next. The answer to this dilemma starts with the Recordset object.

The record set is a representation of all records in a table, or the result of a query. You can create as many Recordset objects as you need. However, it can only refer to one record at a time as the current record . In other words, you can only manipulate a single record at a time. It is made up of records and fields, with records being the equivalent of rows, and fields being the equivalent of columns. When using ActiveX Data Objects (ADOs), almost all data is manipulated using Recordset objects. Therefore, knowing the methods and properties of the Recordset object is important to learn.

When a record set is opened, the current record is the first record in the record set. When this happens, two properties are set to False. These properties are the BOF (beginning of file) and EOF (end of file). BOF indicates that the beginning of the record set has been reached, while EOF indicates the end of the record set. If both the EOF and BOF are set to True, then there are no records in the record set .

The MoveFirst and MoveLast methods of the Recordset object allow you to move to the first and last records, respectively. MoveNext and MovePrevious enable you to move to the next and previous records in the Recordset, respectively. Finally, the Move method allows you to move forward or backward a specific number of records. An example of this is seen in the following code:

Recordset.Move +2
Recordset.Move –2

In this example, the first line moves the user ahead two records, while the second line moves the user back two.

Of course, in designing a way to access data, it will do a user little or no good if he or she can’t add new records and manipulate the data. The this end, the Recordset object has a number of methods for this purpose. Adding new records is done with the AddNew method, while editing an existing record is done with the Edit method . For changes through editing to take effect, you must follow it with the Update method. Without using Update, changes to the data will be lost. It is important to remember that the Update method always follows an Edit . If Update is used first in your code, you will receive a runtime error. Should you wish to cancel either an Edit or an AddNew, you can use the CancelUpdate method. This method will cancel an Edit or Addnew and refresh the record set.

Finally, the Delete method allows you to remove the current record. It is important to remember that this method gives no warning or second chance. When the Delete method is used, the current record is gone, period. If you want to give the user a chance to change his or her mind, you must add code that provides a chance to escape from the record deletion. It is also important to remember that deletion of the current record won’t move the user to the next record in the record set. To cause the next record to become the current record, you should execute the MoveNext method after using Delete .

Creating Data-Bound Class Modules

In the previous chapter we saw how to use the Data control to access a database. In this section, we’ll cover how to create class modules that are used as data sources. Data sources are classes that provide data from external databases. The data is then "consumed" or used by other objects. In this section, we’ll work through creating modules that act as data sources and data consumers.

Creating a Data Source Class Module

The first question many new programmers have is "Why bother creating such classes?" It’s a valid question. After all, the Data control is an object that acts as a data source and can be used to provide data from external sources. The difference between the Data control and data-aware classes is power.

Unlike the Data control, classes can act as data sources for any type of data . They can be data sources for ADOs, OLE (Object Linking and Embedding) providers, ODBC (Open Database Connectivity) sources, and so on. To do this, you must make a reference to an object library. For example, to have your class act as a data source for ADO, you need to select "Microsoft ActiveX Data Objects 2.0 Library" from the list that appears in References (accessed from the Project menu).

The first step in creating data-aware classes is creating a class module in your VB project. There are two properties that should be changed here: Name and DataSourceBehavior, which are shown in Figure 5-2. When changing the name of a class module, your should start the name with the prefix of "cls." DataSourceBehavior controls whether the class will act as a data source. To set the class as a data source, change DataSourceBehavior to "1 – vbDataSource".

Figure 2: By changing the DataSourceBehavior to vbDataSource, the GetMemberData event is added to the Class object. This allows your class to act as a source of data for other objects

Exam Watch: While name conventions aren’t directly covered on the exam, it is important to know them. Knowing such things as the Hungarian naming conventions (covered in Appendix D) will allow you to know if an object is a text box, class module, and so on. All examples of code that appear in the exam use this naming convention.

When the DataSourceBehavior is changed to vbDataSource, a new procedure is added to the class module. When a class module is created, the Class object initially has two events: Initialize and Terminate (covered earlier). Changing this setting adds a third event called GetDataMember. To view this new procedure, select Class from the left listbox of your code window, and GetDataMember from the right listbox.

GetDataMember is used to set the source of the data for this class . What you will do here is specify what record set will be used. A record set is what you will almost always interact with. If you wish to provide multiple sources of data through this new data source class, you could use a Select Case statement in this procedure. The source name would be passed to the Select Case as a DataMember argument. If you wish to use a single source of data—as you would in most cases—you would use the Set command. This would be done with the following syntax:

Set Data = RecordsetName

or, to use a more common example:

Set Data = rs

This assigns the variable rs (which would be your record set) to Data, which is an argument of GetDataMember.

For the above example to work, the variable rs would have to be declared as a Recordset object in your class module. If you were going to declare this object as an ADO Recordset object, you could use the following code in the General Declarations section of the module:

Private rs as ADODB.Recordset

After you’ve declared a Recordset object and used GetDataMember to specify the source of data you’ll be using, you’re ready to create a new instance of the Recordset object. This is done in the Initialize event of class with the following code:

Set rs = New ADODB.Recordset

In doing this, a new instance of the Recordset object we’ve called rs is created.

Following this, you would add code to the Initialize event that would add any necessary properties to your record set. Adding properties to a record set is the done in the same manner as adding properties to components. What is different is when dealing with ADO, you can use a shorthand method to append Field objects. A Field object represents a column of data, which has the same data type. The Fields collection is made up of Field objects and allows you to append Field objects that can be used to show, delete, and manipulate data. The following is an example of adding a two different Field objects to the Fields collection:

With rs
.Fields.Append "YesNoField", adBoolean
.Fields.Append "MyField", adBSTR
End With

In this example, we have added one field that accepts Boolean data, and a second that accepts data of the string type. We can later populate the fields by either assigning information to the field, or having it read the data from a variable. In the next example, the "MyField" field is assigned a value:

Dim x As String
x="Michael"
With rs
.AddNew
.Fields.Item("MyField") = x
.Update
End With

In the first and second line of this example, the variable x is declared as a string, and then assigned the value of Michael. The next section of code has a new field added to the Fields collection, assigned the value of x, and then updates the record set. In doing this, we have populated the record set with a single record.

Having stepped through how to create a data-bound class object, there is an easier way to create data source class modules. From the Project menu, select Add Class Module and then choose Data Source. This will create a class module that is preconfigured to work as a data source. This is illustrated in Figure 5-3. The Data Source Class Module has its DataSourceBehavior set to vbDataSource and includes a number of comments to help you create a data source class. Not all of the work is done for you, but it does make your work easier.

Figure 5-3: The data source class module

One of the best ways to learn something is by doing it. In the next exercise, we’ll create a data source class module. The data source will be used through all remaining exercises in this chapter. After creating the following exercise, you must wait until creating a data consumer to view the results.

Exercise 5-3: Creating a Data Source

  1. Open a new Project. Select File | New Project | Standard EXE.
  2. Select Project | Add Class Module | Class Module.
  3. Rename the Class to "clsSource," and set the DataSourceBehavior property to vbDataSource.
  4. Select Project | References. From the list that appears, select Microsoft ActiveX Data Objects 2.0 Library. Click OK to continue.
  5. In the class module’s General Declarations section, insert the following code:
  6. Option Explicit
    Private rs As ADODB.Recordset

  7. In the Class object’s GetMemberData event, assign the variable rs to the Data object:
  8. Set Data = rs
    Dim strPath, strFile As String
    Dim count As Integer

    strPath = "C:\"
    strFile = Dir(strPath, vbDirectory)

  9. The following code must be placed into the class Initialize event. In this procedure, we must first declare several variables that we’ll use shortly, and assign values to these variables:

  10. count = 0

  11. Before using the rs object as a record set, we must create an instance of it and set its properties. An instance of the rs object is created with the Set keyword. Since we’re setting more than one property, a With statement can be used to set multiple properties. Place the following code in the class Initialize event of your class module:
  12. Set rs = New ADODB.Recordset
    With rs
    .Fields.Append "intRecord", adInteger
    .Fields.Append "strDir", adBSTR
    .Open
    End With

  13. A database is useless without data. As such, we must populate the record set with data. In this exercise, data will be collected when the class initializes. Because of this, we’ll add code that will loop through our hard disk, reading the folders and directories on the disk. Place the following code in the class Initialize event:
  14. Do While strFile <> ""
    If strFile <> "." And strFile <> ".." Then
    If (GetAttr(strPath & strFile) And vbDirectory) = vbDirectory Then
    count = count + 1
    With rs
    .AddNew
    .Fields.Item("intRecord") = count
    .Fields.Item("strDir") = strFile
    .Update
    End With
    End If
    End If
    strFile = Dir
    Loop

  15. At the bottom of the Class Initialize procedure, place code that will set the record set to the first record in the record set:

rs.MoveFirst

Creating a Data Consumer

The first step to creating a data consumer is adding a class module to your project. The property you change to make the class a data consumer is DataBindingBehavior . By changing this property, you’re setting whether the class is a data consumer and how it will be bound to the data source. You have three options to choose from: vbNone, vbSimpleBound, and vbComplexBound . If DataBindingBehavior is set to vbNone, you won’t be able to set binding to a data source—which means the class won’t be a data consumer. If you set the property to vbSimpleBound, then the consumer can bind to single fields. If the property is set to vbComplexBound, binding to a rowset can occur.

To actually bind the data in the data source to the consumer, you use the Binding Collection . This is an object that allows you to bind a consumer to the DataSource supplied by the Binding Collection. In other words, the Binding Collection works as a link between the consumer and source, allowing information from the source to be passed to the consumer. Using the Binding Collection allows you to bind data during runtime.

Using the Binding Collection is relatively painless. To add an object to it, you must first create a new instance of the BindingCollection object. After this, the Add method is used, as shown in the following example:

Set objBinder = New BindingCollection
objBinder.Add objConsumer, "Name", "strDir"

In the first line, a new instance of the BindingCollection is created called objBinder. We then add an object to objBinder using the Add method. In this case, the Name property of a consumer called objConsumer was bound to a data source field called strDir. In doing so, any data in the strDir field will now appear in the consumer’s Name property.

Exercise 5-4: Creating a Data Consumer

  1. Using the project from the previous exercise, add a new class module.
  2. Change the name of the class module to clsConsumer, and the DataBindingBehavior property of the module to vbSimpleBound.
  3. Select Project | References | Add a reference to Microsoft Data Binding Collection. Click OK to continue
  4. In the General Declarations section of clsConsumer, declare a Private variable called NewDir as a String data type:
  5. Private NewDir As String

  6. Add a Get and a Let procedure to clsConsumer. This will allow us to retrieve a record and then display it.
  7. Public Property Get Name() As String
    DirName = NewDir
    End Property
    Public Property Let Name(strDir As String)
    NewDir = strDir
    Debug.Print NewDir
    End Property

  8. We must now create an instance of clsConsumer, the Binding Collection, and the data source. We then add clsConsumer to the Binding Collection. In doing so, the Name property of clsConsumer will be bound to the strDir field of the data source. To do so, we add the following code to the Load event of our Form:
  9. Set objSource = New clsSource
    Set objBindingCollection = New BindingCollection
    Set objConsumer = New clsConsumer
    Set objBindingCollection.DataSource = objSource
    objBindingCollection.Add objConsumer, "Name", "strDir"

  10. If you ran the program now, you would be able to view one record in the Immediate window. To view other records in the clsSource class module, we must add code that will allow us to navigate through the different records in the data source. To do this, add the following procedure:
  11. Public Sub Move()
    rs.MoveNext
    If rs.EOF = True Then
    rs.MoveFirst
    End If
    End Sub

  12. Add a command button to your Form. Name it cmdNext, give it the caption "Next," and then add the following code to the Click event:
  13. objSource.Move

    This will allow you to toggle through the different records in your data source.

  14. Press F5 to run your program. As you click the Next button, different records will appear in the Immediate window.

Certification Summary

There are four stages to designing an application. They are conceptual design, logical design, physical design, and deployment. Conceptual design identifies an application’s requirements. Logical design involves taking this information, and is where a system actually evolves. Physical design maps the business objects and services established in logical design and maps them to software components. Deployment determines how components are distributed across a network.

Class modules are used to create objects. When these objects are created, you can then create methods, properties, and events. To create a class module, you use the Add Class Module item from the Project menu. Once this is done, code can be added to the module through the class’s Code window. By changing the DataBindingBehavior or DataSourceBehavior properties, you can change a class module to act as either a data consumer or data source, respectively.

Begin Q&A

I’ve want to create a data source, but I’m missing the Data Source Class Module template. How can I configure a class module as a data source from scratch? Set the class module’s DataSourceBehavior property to vbDataSource. This will add an additional event to the class called GetMemberData. The Data Source Class Module already has this configured, and has some comments to help you create a data source. Aside from that, there is no difference between a standard class module and the Data Source one that comes with VB.
What actually binds a data source to a data consumer? The Binding Collection acts as a link between fields in the data source and the data consumer. This allows data to be passed to the consumer by binding one to the other.
I’ve added code to Edit my record set, but I keep getting a runtime error. Check to see that the record set’s Update method follows the Edit method in the code. Update must be executed after Edit, or a runtime error will occur.

Two-Minute Drill

Chapter 5 two minute drill

Self-Test

The following Self Test questions will help you measure your understanding of the material presented in this chapter. Read all the choices carefully, as there may be more than one correct answer. Choose all correct answers for each question.

  1. Which of the following services represents the application’s user interface?
    1. User services
    2. Business services
    3. Middle-tier services
    4. Data services
      A. User services are identified with the user interface. It can be the actual executable file or a component (such as an ActiveX control).
  2. Which stage of design involves identifying an application’s requirements?
    1. Conceptual
    2. Logical
    3. Physical
    4. Deployment
      B. Conceptual design involves identifying the requirements of an application.
  3. Which stage of design determines how components are distributed across a network?
    1. Conceptual
    2. Logical
    3. Physical
    4. Deployment
      D. Deployment is the stage where it is determined how components are distributed across a network.
  4. Why is it important to change the name of a class to a name that isn’t in use?
    1. So that when the class calls on itself it doesn’t fall into an endless loop.
    2. If the name is already in use, the class module won’t be able to initiate itself.
    3. If the name is already in use, controller code won’t be able to instantiate objects.
    4. It doesn’t matter if the name is already in use.
      C. It is important that the Name property be original and not in use for a class module. This is because controller code uses the name to instantiate a copy of the class as an object.
  5. Validation is a necessary part of designing an application. Which of the following best describes what validation is in the design process?
    1. Validation is a comparison of deployment to the requirements of the logical design.
    2. Validation is a comparison of the physical design to the requirements of deployment.
    3. Validation is a comparison of logical design to the requirements in the original conceptual view.
    4. Validation is the concept of verifying that all variables meet the requirements of the logical design.
      C. Validation of the logical design requires a comparison of logical design to the requirements in the original conceptual view.
  6. In a class module, how many copies of the module’s data can exist at any one time?
    1. One
    2. Two
    3. A copy for each object created
    4. A copy for each instance of a standard module
      C. In a class module, one copy of the module’s data is created for each instance of the class. This means there is a copy of the data for each object created from the class module.
  7. How long is the lifetime of data in a standard module?
    1. The life of the object
    2. The life of the program
    3. The life of the programmer
    4. None of the above
      B. A standard module’s data exists for the life of the program.
  8. What is the syntax used to destroy an object?
    1. Set Object = Nothing
    2. Get Object = Nothing
    3. Let Object = Nothing
    4. Set Object = 0
      A. The syntax used to destroy an object is Set Object = Nothing.
  9. You use the following code in your application. When you test it, it appears that nothing happens the current record in the record set (which you’ve called rs). Why?
  10. Sub cmdDelete_Click()
    rs.Delete
    End Sub

    1. The method of the record set should read rs.Del.
    2. There should be a MoveNext method executed after the Delete method.
    3. There should be a Next method executed after the Delete method.
    4. There is no reason why it shouldn’t move to the next record.
      B. To have the next record become the current record after a deletion, you must execute the MoveNext method after the Delete method has been used.
  11. Julie inputs a sale into an application. The application then adds the necessary taxes, checks the customer’s credit rating, and determines whether the sale can be added to the customer’s account. Which tier of the services model do the rules applied to this procedure apply to?
    1. User services
    2. Business services
    3. Data services
    4. Graduated services
      B. Business services applies business rules (such as adding taxes or checking a customer’s credit limit) to tasks that the program performs.
  12. Which design stage takes business objects and services and maps them to software components?
    1. Conceptual
    2. Logical
    3. Physical
    4. Deployment
      C. Physical design takes maps business objects and services to software components.
  13. How is a class module added to a VB project?
    1. Select Format | Add Class Module.
    2. Select Project | Add Class Module.
    3. Select Project | Add Class Module, then choose Class Module from the dialog box that appears.
    4. Select Tools | Add Class Module, then choose Class Module from the dialog box that appears.
      C. To add a class module to a B project, select Project | Add Class Module. When the dialog box appears, choose Class Module.
  14. In a standard module, how many copies of the module’s data is there at any given time?
    1. One
    2. Two
    3. A copy for each object created
    4. A copy for each instance of a class module
      A. In a standard module, there is one copy of the module’s data at any given time.
  15. In a class module, what is the lifetime of the module’s data?
    1. The life of the object
    2. The life of the program
    3. The life of the programmer
    4. None of the above
      A. A class module’s data exists for the life of the object created. A copy of the data is created when the object is created, and then destroyed when the object is destroyed.
  16. What is wrong with the following code?
  17. Sub cmdEdit_Click
    rs.Update
    rs("strName")=txtName.Text
    rs.Edit
    End Sub

    1. The property of txtName should be Caption.
    2. The Edit method has to be implemented as part of an IF…THEN statement.
    3. The Edit method must come after the Update method.
    4. The Update method must come after the Edit method.
      D. The Update method must be used after the Edit method. If it isn’t used in this order, a runtime error will occur.
  18. You want to add code to move three records ahead in the record set. Which of the following will do this?
    1. Recordset.Move -3
    2. Recordset.Move +3
    3. Recordset.Move 3
    4. Recordset.Move =3
      B. To move three records ahead in the record set, use the syntax: Recordset.Move +3
  19. You want to add a new record to the record set. Which of the following examples of code would add a record to a record set named rs?
    1. rs.AddNew
    2. rs.New
    3. rs.Add
    4. rs.New +1
      A. The correct way to add a new record to the record set called rs is rs.AddNew
  20. Both the EOF and BOF of a record set are set to True. How many records are there in the record set?
    1. One
    2. Two
    3. Unknown
    4. None
      D. If both the EOF and BOF are set to True, then there are no records in the record set.
  21. Which of the following would you set to create a Data Source class module?
    1. DataSourceBehavior
    2. DataBindingBehavior
    3. vbSimpleBound
    4. Both DataSourceBehavior and DataBindingBehavior
      A. To create a Data Source class module, you would change the DataSourceBehavior to vbDataSource.
  22. When you create a standard class module, what two events does the Class object initially have? Choose all that apply.
    1. Initialize
    2. Terminate
    3. GetDataMember
    4. Load
      A and B. When you create a standard class module, it has two events, Initialize and Terminate.
  23. What actually binds a data source to a data consumer?
    1. Class module
    2. DataBinding property of class module
    3. Binding Collection
    4. DataBondage argument
      C. To bind a data source to a data consumer, you use the Binding Collection.
  24. You want to cause an event to run programmatically. Which keyword will you use to make this happen?
    1. CallEvent
    2. RaiseEvent
    3. RunEvent
    4. Event
      B. To cause an event to run, you use the RaiseEvent keyword.
  25. Which of the following always uses COM creation services?
    1. Property Let
    2. Property Get
    3. Dim
    4. CreateObject
      D. CreateObject always uses COM object creation services. It uses COM creation services regardless of whether objects created are externally provided or part of your project.
  26. When can data be bound to a control or consumer, when using the Binding Collection and a class module?
    1. Runtime
    2. Design time
    3. Anytime
    4. Never
      C. Using the Binding Collection and class modules, data can be bound to a control or consumer at any time.
  27. Which of the following is used to set the source of the data to be used for a class module?
    1. Initialize
    2. Terminate
    3. Load
    4. GetDataMember
      D. GetDataMember is used to set the source of the data for a class. The record set to be used is specified here.
  28. The record set can refer to how many records at one time as the current record?
    1. One
    2. Two
    3. Three
    4. Unlimited
      A. The record set can only refer to one record as the current record at one time.
  29. A class module can be used as a data source for which of the following types of data? Choose all that apply.
    1. ADO (ActiveX Data Objects)
    2. OLE (Object Linking and Embedding) providers
    3. ODBC (Open Database Connectivity)
    4. None of the above
      A, B and C. A class module can be used as a data source for any type of data.
  30. When you modify the DataBindingBehavior of a class module, what options are available to you? Choose all that apply.
    1. vbDataSource
    2. vbNone
    3. vbSimpleBound
    4. vbComplexBound
      B, C and D. The DataBindingBehavior of a class module has three options: vbNone, vbSimpleBound, and vbComplexBound.
  31. When you modify the DataSourceBehavior of a class module, what new event will be added to the Class object?
    1. GetData
    2. DataMember
    3. GetDataMember
    4. Load
      C. When the DataSourceBehavior is set to vbDataSource, the GetDataMember event is added to the Class object.
  32. Which of the following would you set to create a data consumer class module?
    1. DataSourceBehavior
    2. DataBindingBehavior
    3. VbDataSource

Both DataSourceBehavior and DataBindingBehavior
B. To create a data consumer class module, you would change the DataBindingBehavior to either vbSimpleBound or vbComplexBound.