Scrigroup - Documente si articole

     

HomeDocumenteUploadResurseAlte limbi doc
AccessAdobe photoshopAlgoritmiAutocadBaze de dateCC sharp
CalculatoareCorel drawDot netExcelFox proFrontpageHardware
HtmlInternetJavaLinuxMatlabMs dosPascal
PhpPower pointRetele calculatoareSqlTutorialsWebdesignWindows
WordXml

AspAutocadCDot netExcelFox proHtmlJava
LinuxMathcadPhotoshopPhpSqlVisual studioWindowsXml

A Visual Basic Primer for Visual FoxPro Developers

Fox pro



+ Font mai mare | - Font mai mic



A Visual Basic Primer for Visual FoxPro Developers

Let's face it. As you saw from Chapter 28 on ADO, you're going to have to get acquainted with Visual Basic one of these days. In this chapter, I'm going tackle VB from a Fox developer's point of view-pointing out similarities and differences, what to look at, what to ignore. And, most importantly, I'm going to provide a very fast path to getting up to speed, because I know what your background is. Unlike those general-purpose books, I don't have to write for a wide range of developers in the audience.



Much of the material for this chapter came from articles originally published in FoxTalk. For more information, see www.pinpub.com/foxpro.

You picked up this book because it's about Visual FoxPro, not Visual Basic. There are a thousand VB books out there, and you're a Fox developer. So you might be taking issue with my hypothesis in the introduction to this chapter. So let me explain.

You're going to need to become familiar with VB for three reasons. But those reasons aren't the ones that you might be thinking of.

It's not because "they're going to kill Visual FoxPro." The next release of Visual FoxPro is already floating around in certain circles, and if you subscribe to FoxTalk, you'll be reading about it shortly.

It's not because Visual Basic is "better" than Visual FoxPro. It's a general-purpose programming language with its own strengths and weaknesses. Fox is a data-centric programming language, again with its strengths and weaknesses. Each does some things well, and other things, er, poorly. "Better" is truly in the eyes of the developer.

And it's not because I've gone bonkers. (Be charitable, please!)

The first of the three reasons is that you might think I'm lying about the future of VFP, and figure it's safe to cover your bases. Well, I'm not lying-just like any dutiful Microsoft shill, I'm simply repeating what I'm told to. Okay, seriously, not putting all your eggs in the same basket is probably a good idea, regardless. Ever really wonder why we're still saddled with a Report Writer and Menu Builder from 1992, and there are no plans for updates? Maybe it is time to look elsewhere for those components!

Second, "everybody" has it. And you want to be popular, don't you? How many of you preferred another word processor or spreadsheet, but gave up because every document you received was in Word 6.0 format and you got tired of converting-and having pieces lost in the process? It was just easier to go with the tide; besides, you probably got a copy of it free from somewhere or another.

As a result of everyone having VB, when you see sample source code, it's often in VB (or VBA). Wouldn't it be nice if you were more familiar with the language so you understood what DIMs and VARIANTs were, instead of having to guess?

The last reason is that, as alluded to earlier, there are some things that Visual FoxPro just doesn't do very well. For example, I've been wrestling with the integration of a high-end imaging ActiveX control in VFP. It's been an ugly road, and as I'm writing this, it looks like the road is a dead end. The customer isn't really very interested in hearing about "the differences in VFP's and VB's containership and hosting abilities." (There's the one guy in their IS department who's muttering, "I knew we should have written this in VB.") They just want it to work.

Why spend hours and days working around a weakness when another tool can be used that doesn't have that weakness? How about, as Dan Freeman suggested, "building an OCX in VB? Just wrap it around the recalcitrant control. The control will think it's hosted in VB and Fox seems to work better with VB controls."

Well, if you go that route, and I think it's one worth considering, you'll need to get up to speed with VB. While that's not a significant challenge (hey, there are only something like 19 keywords in the entire language), it could still be a longer process than you have time for. You could pick up one of those "Learn VB in 21 Days" books-you know, one that sits next to the 2000-page "Write your own NT kernel in 24 hours" book. (I swear-I saw one of these books!) But those are written to be as generic as possible, aimed at everyone from the experienced VB5 developer to the novice who still hasn't gotten the hang of his mouse. We can do this in a more expedient fashion.

The Visual Basic IDE

I'm assuming you've installed Visual Basic 6.0 that comes with the Visual Studio 6.0 package, and for the time being, it doesn't matter if you've got the Professional or Enterprise version. You can also get VB in some other versions-Training, Standard, and Mega-Galactica-but I'll ignore those. When you load VB, it will present you with a dialog that asks you what type of project you want to create. I checked the "Don't show this dialog ever, ever, ever, ever again" check box because when you create a new project from the File, New Project menu command, you'll get access to the same dialog.

MDI versus SDI

Once VB loads, you'll see an MDI window filled with windows, much like Visual InterDev. See Figure 30.1. This might startle some of you who have seen previous versions of VB- those that used an SDI interface, which was rather disconcerting to us Fox developers. The windows just sat on the desktop and you could see the desktop showing through the holes. (You can turn SDI on through the Tools, Options, Advanced tab.) See Figure 30.2.


What are all these windows?

What are all of these windows? On the left side is a long, narrow window with about a dozen controls in it-that's your Controls toolbar, just like Fox. If you let your mouse icon hover over a control, you'll get a ToolTip describing the control, just like every other Microsoft product on the planet. (I was going to say "Windows product" but figured it's pretty close to the same thing.) The bar across the top that is captioned "General" is called a tab, even though you and I think it looks like a "bar." However, the toolbar can contain lots of controls-and I mean lots. The native controls that come with VBE (Visual Basic Enterprise) number close to 125.

However, instead of segregating controls into different toolbars like Fox does (Standard, ActiveX, and your own libraries), you can separate the Controls toolbar by adding tabs to the toolbar, each of which will contain a certain type. See Figure 30.3.

VB uses a project metaphor to build applications just like Fox, and is heavily dependent on forms, again, just like Fox. When you create a new project (either through the New Project startup dialog or the File, New Project menu command), you'll be faced with a plethora of options. Pick Standard EXE for the time being, and you'll get a project named Project1 in the upper right hand window that originally didn't have a caption in the title bar. This, then, is the Project Explorer.

Underneath the Project Explorer is the Properties window, which provides the same function as our own Properties window, although the functionality is a bit different. You'll already see that you can sort the properties alphabetically or by category-Appearance, Behavior, Font, Misc, and so on. I've kept the properties sorted alphabetically because it's hard enough keeping track of which properties belong to VFP and which to VB, and what the minute differences are. Trying to remember if "Visible" is an Appearance property or a Behavior property is just too hard. You'll see I've already changed the caption of Form1 to something more aesthetically pleasing-and I didn't even have to read the manual to find out how!

New to you is the Form Layout window in the lower right, which shows you a bird's-eye view of what the Form will look like on the desktop. As you resize a form, you'll see the corresponding view in the Form Layout window change as well.


Your first VB form

The form that is created when you create a project is named "Form1" by default. By itself, it isn't very interesting, but you can run it just as you can run an empty VFP form. To do so, you can click the Start button in the Standard VB toolbar (it's the button with the arrowhead pointing to the right, under the "m" in the Diagram menu if you've got both the menu and Standard toolbar docked and nudged up to the left). Alternatively, you can issue the Run, Start menu command or simply press F5. See Figure 30.4.

You know what's cool? If you task-switch with Alt+Tab, you'll see that the VB form is not running inside like VFP-it's an SDI form automatically.

You might be thinking, "Okay, how do I stop this thing?" You can click the form's close box, or press the ever-so-intuitive Alt+F4, or just click on the toolbar button with the square box (two buttons to the right of the Start toolbar button).


Adding code to a form

I know that example is less involved than the typical "Hello World" program you first write in C, but, hey, this is BASIC. Let's put some code in this form.

Code windows are one significant departure in behavior from "the Fox way." First, in order to get to them, you can't use the Properties window. It's for properties, after all. It doesn't say "Properties and Code Window," does it?

To open the Code window, you again have nine thousand ways to do so. You can highlight an object in the Project Explorer and click the View Code button in the project, as shown in Figure 30.5.

You can also issue the View, Code menu command, but the easiest way is to just double-click the form. After any of these operations, the Code window displays as shown in Figure

30.6. The left drop-down shows the various objects of the form, including "General" (I'll get to that later), "Form", and every control on the form. The right drop-down contains all of the events into which you can stuff code-and you're familiar with many of these, such as Activate, Load, Resize, and Unload.

In Figure 30.6, I got ahead of myself and started writing some code. Just when you got used to typing "MessageBox" in VFP, VB comes along and uses "MsgBox" to perform the same function. You'll notice in Figure 30.6 that VB has "Code Completion"-a ToolTip will appear, prompting you about the parameters that can be used with the function you're typing.

You'll also see that code for a particular procedure is bracketed by "Private Sub <name>" and "End Sub" statements, just like we use FUNC and ENDFUNC in Fox. "Sub" is a holdover from the olden days when programmers used to write "subroutines," and VB puts them in for you automatically. If you delete one of those lines, "unexpected results may occur."

I suppose I could have put a message box in the form's Click() method, but that would have been going from the excruciatingly trivial to the nearly excruciatingly trivial. Instead, I added a command button from the Controls toolbar, double-clicked it, and entered the MsgBox in there. Obviously, running the form will generate a form with a command button on it, and clicking on it will display a message box.


The VB Code window

But let's talk about the Code window for a minute because there are some new things showing up. In Figure 30.7, I've shown the Code window with pieces from several methods. As you can imagine, this is pretty handy-being able to see code for more than one procedure in the same window (as long as those procedures are small).

Notice two buttons to the left of the horizontal scrollbar in the bottom of the Code window. The right button is selected in Figure 30.7, and indicates that you want to see more than one procedure at a time. This is the Method View button. The left button, Procedure View, limits the display to one procedure at a time.


Saving and printing projects

A project is a file on disk, just like in Fox, but data in it isn't automatically saved like that in a .PJX, so you have to do it yourself. However, when you save a project, it will prompt you to save all of the unsaved components, and if they're unnamed, you'll be prompted to name them along the way.

You can also print the contents of a project by simply selecting the File, Print menu. The Print dialog contains a number of options that correspond directly to VB projects. See Figure 30.8.


Project components and files

I also should mention what the structure of a project looks like and what the files on disk are. The project is stored with a .VBP extension, and is simply a text file. A VB form is also just a text file, with an extension of .FRM. I've shown both of these opened in Notepad windows in Figure 30.9. A third type of component that I haven't discussed yet, but that you've probably seen here or there, is the Module. It's got an extension of .BAS, and contains code that isn't tied to a form-much like a separate FoxPro procedure file.

There you go. I've been through six or eight VB 6.0 books, and they've each taken 40 to 50 pages to cover this material. You now know more than what readers of those books do, and you're probably not even done with that can of Jolt. Not bad, eh? Repeat after me: Thank you, thank you, Sam-I-Am, I like VB6, Sam-I-Am!

I've been pretty flip about the use of some terminology-events, methods, procedures, code snippets, windows, and so on. It's time to get more rigorous.

You know how there are certain behaviors in VFP that just don't seem right? The

ones that make you ask yourself, "What were they thinking?" And when the explanation offered is "It's that way by design," you just roll your eyes. Take heart-VB has its share of incredulous behaviors.

Stupid VB trick #1: Add a command button to the form, double-click to open the code snippet for that button's Click event, and enter some code. Then delete the button. The code snippet stays put-it's not deleted.

Stupid VB trick #2: Add a command button to a form. Double-click to open the

code snippet for that button's Click event, and enter some code. Change the

name of the button from "Command1" to "MyButton." The code snippet for

"Command1" stays there-with the code you entered. Meanwhile, running the

form and clicking MyButton won't do anything, because the code is still attached

to Command1 (even though it's nonexistent), not MyButton.

Stupid VB trick #3: Repeat the steps in #2, but then add another command button. It will be named Command1-and the previously orphaned code will now be attached to the new button!



The basics of writing code

The Properties window, property pages, and toolbars

If you open the VB Properties window, you'll see a drop-down list box just like in VFP-it's called the "object box" and, like VFP, contains a list of all objects in the currently selected form. However, if you open it, you'll notice that all of the objects on the form, as well as the form itself, are listed in alphabetical order-and there's no apparent hierarchy involved. I'll discuss this in more detail in the next section.

The Properties window works like VFP's: Select an object, and the associated properties and their values appear in the list box below. The two tabs above the list box allow you to choose whether you want to see the properties in alphabetic order or by category.

If you select the Categorized tab, you'll see a third column appear on the left side of the list box-a series of plus and/or minus signs that allow you to expand or contract the list of properties for that category. I'll probably get around to describing a bunch of "tips and tricks" for various control properties later, but here's one that you'll likely want right away. In VFP, you use the "<" character string in a command button's caption to set the next character to act as a hotkey; in VB, you use the ampersand (&) character. Thus, the VB would use the text string "&Done" to accomplish the same task as the VFP caption "<Done".

Another difference between VB and VFP is that while right-clicking on a form displays a context menu with a Properties menu command, and selecting the Properties menu command will open the Properties window, right-clicking on a some controls in a form and selecting the Properties menu command will bring up a "property page." This property page contains a tabbed dialog that contains various physical or visible attributes of the control. More on this later.

Visual Basic's control toolbar is called the "Toolbox", and you can customize it by adding non-default Microsoft, or third-party controls to it. First, select (or create) the tab (the gray object that you and I would have called a "bar") under which you want to add the control. (Remember that you can add a new tab by right-clicking in the Toolbox and selecting the Add Tab menu command.) Next, right-click under the tab, select the Components menu command, and use the resulting dialog to select the control you want to show on the toolbox.

Finally, remember that the Properties window shows only properties-not methods or events. I'll discuss those shortly when I get to programming.


VB's version of containership

Earlier I mentioned that there is no hierarchical display of objects in the Properties window object box. This is because VB doesn't have the same type of containership as VFP-and while confusing, this actually makes life easier in same cases. For instance, if you put a check box on page 2 of a page frame in VFP, you use the following code to reference its caption:

thisform.pageframe1.page2.checkbox.caption

To reference the same check box's caption in VB, you simply use the code:

check2.caption

This tells us a couple things. First of all, you will wear down your keyboard less, since you have to do much less typing. Second, every control on a form has to have its own unique name-regardless of where it lives. Thus, while you can have a "checkbox1" on each page of a four-page tabbed dialog, you'd have "check1", "check2", "check3", and "check4" in VB. Third, the containership, or lack thereof, is going to have some serious ramifications on programming that I'll discuss later.


Slinging code

As I just mentioned, when you write code, you don't have to use a fully qualified name for a control-simply the name of the control and its property or method name. Second, in order to get to the code, you can either click the View Code button or just double-click on the form. But here's where it can get confusing. Remember that you can use the icon in the bottom left of the window to display all the code for the form, or just the code for the current object (either the form, or a control on the form). When you double-click a control that has no code attached to it, you'll get a new module for the Click() event of that control already built for you, as shown in Figure 30.10. Otherwise, double-clicking a control with code in one of its events will bring up the code for that event.

The Code window can contain code for one or more events, for one or more controls. Each "chunk of code" attached to an event has two parts. The first is the declaration of the module, and the second is the termination of the module, like so:

Private Sub Form_load()<your code goes here>End Sub

Each of these "chunks of code" is referred to as a subprocedure, or a function, or, generically, a code segment. The "Sub" keyword means that this is a subprocedure-much like procedures in Fox. (I'll discuss the difference between procedures and functions shortly.) The "Private" keyword means that this subprocedure scope is the form-it can be called by code elsewhere in this form, but not anywhere else in the project.

The subprocedure's name consists of the name of the control and the event to which the module is attached. But here's a warning: Putting code in a module and then changing the control's name will reflect in the module being orphaned.

The Code window also has a section called "General." (It's the first entry in the object drop-down at the top of the Code window.) You can use this area to create your own subprocedures and functions, much like form-level methods in Fox.

The parentheses following the name of the control enable you to pass parameters to the code segment. For example, suppose you have a form with a check box and a command button on it. You could use the following code to pass the caption of the control to a form-level function that would display the caption whenever the control was clicked:

Private Sub MySub(xmessage)

MsgBox (xmessage)End Sub

Private Sub Check1_Click()MySub (Check1.Caption)End Sub

Private Sub Command1_Click()MySub (Command1.Caption)End Sub

In this brief example, you see that the same basic programming principles apply-calling a subroutine with a parameter. And because MySub is declared as private, you can't go off and call that very valuable subprocedure from another component in the project.


Procedures vs. functions

The difference between subprocedures and functions in Visual Basic is the same as in VFP- subprocedures don't return values; functions do. You know the rest. Well, actually, you don't know all the rest. You define a function (a user-defined function, of course-VB also comes with its own set of functions that work just like in any other language) like so:

Private Function AlphabetizeName(FirstName, LastName)
AlphabetizeName = LastName & ' ' & FirstName
End Function

Private Sub Command1_Click()

MsgBox (AlphabetizeName(Check1.Caption, Check4.Caption))
End Sub

The AlphabetizeName function takes two parameters as input, reverses them, and concatenates them with a space between. The Command1 button takes the captions of two check boxes on the form and passes them to the AlphabetizeName function. The result of the AlphabetizeName function is then sent as a parm to a MsgBox function as the message to display.

The most interesting thing to note about VB functions is how to return values. The AlphabetizeName function has a variable with the same name as the function that receives the value to be returned to the caller. Once you've got that down, the rest is easy.


Option Explicit

Visual FoxPro is a "weakly typed" language, which means that you can assign any old value to any old variable, and then change the value of the variable any old time you want. This provides terrific flexibility, but at the same time exposes you to untold amounts of danger, which you are well aware of.

Visual Basic has this same capability-weakly typed variables-but you can also force VB to make you define what variables you are going to use, and what data type they will be. This is called "strong typing" and is done by checking the Require Variable Declaration check box in the Editor tab of the Tools, Options dialog.

After you do so, every new Code window you open will have the keywords "Option Explicit" automatically entered in the General code segment. All variables in the form will need to be declared before you can use them.

To declare a variable, it would be helpful to know what data types Visual Basic has, wouldn't it? See Table 30.1.

Table 30.1. Variable types in Visual Basic 6.0.

Boolean

Logical

Currency

Floating point number with four decimal places

Date

Date and/or Time

Double

Large floating point (decimal) number (in the gazillions)

Integer

Small integer

Long

Large integer

Single

Small floating point (decimal) number (in the billions)

String

We know these as character strings

To declare a variable, use the DIM keyword, like so:

Dim bIsAlive as Boolean Dim cNetPay as CurrencyDim dBirth as Date Dim fNationalDebt as Double Dim iCounter as IntegerDim lWorldCitizens as LongDim fTaxRate as SingleDim sName as String

You can also use the PRIVATE and PUBLIC keywords to control the lifetime of the variable. A Private variable has scope throughout the routine, form, or module in which it was declared, while Public variables are available to any code anywhere in the project in which they are declared.

Public sName as StringPrivate sName as String

I'll beat on this for a bit, because strongly typed variables are a significant difference between VB and VFP. Once you have an Option Explicit declaration in your code (and you can type it in manually in the General code segment if you didn't check the Require Variable Declaration check box), you must declare every variable you use.

This prevents two undesirable behaviors from occurring. First, you can't accidentally create a bug by mistyping the name of a variable. In the following code, we are trying to assemble a message, sMessage, from consecutive values of the string sNewValue. However, sMessage is never modified, because the second-to-last line assigns the concatenation of the existing string, sMessage, and the new value, sNewValue, to a mistyped variable, aMessage.

sMessage = ''

For iCounter = 1 to 10

<some code>

sNewValue = <some function>

aMessage = sMessage + sNewValueEnd For

If you included an Option Explicit declaration in your code, you would have to declare sMessage, iCounter, and sNewValue, and the compiler would detect an undeclared variable, aMessage. Bet you've done that once or twice in Fox, haven't you?

Second, by declaring the data type, you wouldn't accidentally change the data type of a variable. You've all done something like this:

dBirth = date()<some code> dBirth = 0

Strong typing of your variables will prevent this from happening again.


Adding your own methods to forms and applications

Earlier I showed how you can add your own methods-functions or subprocedures-to a form by simply adding them in the General section of the Code window. Even if declared Private, those functions and subprocedures are now available throughout the form.

However, you will also want to create functions and subprocedures that are available globally throughout your application, much like common code in an .APP procedure file in Fox. In VB, these types of files are called modules. They are text files with .BAS extensions, and live in the Project Explorer alongside forms and other components. To create a module, open the Project Explorer (View, Project Explorer), right-click in the window, and select Add, Module from the context menus that display. See Figure 30.11.

You'll be prompted for the module to add-either an existing one or a new one, as shown in Figure 30.12.

The module will be added to the Module node of the Project Explorer, as shown in Figure

30.13.

To create a subprocedure that's available in other parts of your project, simply create it like you did in a form:

Sub Herman()

MsgBox ('This is the Herman sub in the first module')End Sub

Then, to call the Herman subprocedure, use code like the following that's in the Click event of the Command2 command button on a form in the project:

Private Sub Command2_Click()
Call Herman
End Sub

Notice that the CALL keyword was required this time in order to run the subprocedure. This can vary, as you've seen earlier, depending on how you structure the syntax of the subprocedure and the way arguments, if any, are passed to it. Me? I like to use functions as much as possible, so that I can return a value that indicates whether the execution of a procedure was successful or not, so I don't worry about the CALL syntax at all.

You can also create a subprocedure in a module that serves as the startup program for your application. To do so, name it "Main." Then, if your project also has forms, you'll need to tell Visual Basic that it should run the Main subprocedure first, instead of the first form created in the project. To do so, right-click in the Project Explorer and select the ProjectN Properties menu command (where "ProjectN" is the name of your project.) Open the Startup Object drop-down in the General tab, and choose Sub Main as shown in Figure 30.14.

Stupid VB trick #4: If you double-click a control to open the Code window for

that control, the procedure for the Click event of that control will be displayed if there wasn't any code attached to that control to begin with. However, if you select that control, right-click to bring up the context menu, and select the View Code menu command, the same behavior does not occur. A command and function summary

So far, I've explored the Visual Basic IDE and explained how to create programs and forms. It's time to get to the fun part-learning the commands and functions that you can use to write programs and make forms actually do stuff.

What makes up a language? If you were to break down the basic elements that belong to every programming language, you'd end up with the following list: variables, operators, functions, expressions, and commands. I've already looked at a few examples of each of these; let's take a more in-depth look at them.


The Immediate window

It's time to mention another component of the Visual Basic IDE that many VB programmers ignore, but that you'll use on a daily basis. What's one of the coolest things about Fox? The Command window, right? Type in a command, an expression, whatever-and you can see the results immediately. The VB equivalent is called the Immediate window. (Stop your snickering; I think the name is pretty cute, too.)

Open the Immediate window by selecting the View, Immediate menu command, or simply pressing Ctrl+G. We'll use this to work with functions and expressions.


Variables

I've already gone into some detail about variables-you've learned about DIM and OPTION EXPLICIT and so on. You've also learned that there are a few data types in VB that are different than those in Fox. The trickiest part, to me, has been keeping the abbreviations for data types straight. See Table 30.2.

Table 30.2. Fox-to-VB data type abbreviations.

Fox

VB

c Character

s String

l Logical

b Boolean

y Currency

c Currency

d Date

d Date

t DateTime

n Numeric

f Double

l Long

f Single

i Integer

i Integer

m Memo

g General

Ever have one of those moments where you can't remember some obvious piece of data, like your mother-in-law's name, or your home phone number? The brain just does a total core dump all over the floor. With naming conventions between Fox and VB that seem to collide as often as they complement each other, I've given up trying to remember, and I just post a sticky note on my monitor.

Assigning values to variables is straightforward, with the possible exception of Booleans and Dates. I'll cover both of these in a couple of sections.

Operators

Once you've got more than one variable, you're going to want to do something with both of them. For this, you need operators (or functions, which I'll hit next). The VB operators work just slightly differently than in Fox. Using the Immediate window, try entering the following:

fAmtBase = 100 fAmtAddOns = 43.2 ? fAmtBase + fAmtAddOns

143.2 fAmtTotal = fAmtBase + fAmtAddOns ? fAmtTotal

143.2 fAmtDiscount = 12.4 ? fAmtTotal - fAmtDiscount

130.8

sNameFirst = 'Herman' sNameLast = 'Werke' ? sNameFirst + sNameLast HermanWerke ? sNameFirst - sNameLast

These operations all work as expected, until the last statement. In Fox, you can use the subtraction operator with two character strings, and not generate an error. Instead, you'll remove the trailing blanks from the first string, and then concatenate the two strings. In Visual Basic, however, doing so will generate an error, as shown in Figure 30.15.

The reason why is explained when you try the following lines of code:

s1 = '1111' s2 = '2222' ? s1 + s2 11112222 ? s1 - s2

As you see, Visual Basic will concatenate two text strings, but will then convert the strings to numbers, if possible, and then do arithmetic subtraction. If the strings can't be converted to numbers, you'll get an error message. To concatenate strings, then, you should use the "&" operator because, obviously, the + sign will mask mistakes if you don't have OPTION EXPLICIT declared.

Multiplication is just as straightforward, but division has a trick up its sleeve. A standard forward-leaning slash will divide two numbers; a backward-leaning slash will return the integral portion of the result. In this example, 1111/2222 results in a value of 0.5, and the integral portion is zero. Note that "" doesn't round-it truncates!

? s1*s2

2468642 ? s1/s2

0.5

? s1s2 0

Integer division runs considerably faster than decimal division, because there's less work to do. No messing around with lots of places after the decimal point. If you're doing division in a processor-intensive routine, such as in a loop, you should try to use integer division if possible.

Exponentiation is performed with a carat (^) just like in VFP.

Two other "special" characters, while not strictly operators, are the comment and code continuation characters. The "'" (single-quote) character denotes the beginning of a comment- probably easier to type than "*" except that you've had years of practice with the asterisk (unless, of course, you don't bother to use comments). The underscore denotes the continuation of a line of code, like so:

' This is a comment that precedes a two-line expression? fAmtBase = (fAmtRawMaterial + fAmtLabor + fAmtBurden) _* (1.0 + fRate * fRateMultiplier)

Booleans (logicals)

It seems that every language handles a logical True or False differently, and of course VB isn't any different. You simply assign "True" or "False" (without the quotes) to a Boolean variable, like so:

lIsAlive = True lIsTalented = False

Note that these are not character strings!

Dates

Dates are somewhat tricky until you grok the rules. First, the date delimiters are pound signs, not braces like in Fox. Second, VB assumes dates are entered as M/D/Y, with the current century. However, the display of dates is controlled by the Date tab in the Regional Settings applet in Control Panel. In other words, VB dates are always stored as m/d/yyyy but Windows controls how they are presented to the world.

d1 = #1/2/98#? d1 + 5 1/7/98' change the Short Date Style in the Date tab of Regional Settings to m/d/yyyyd1 = #1/2/2098#? d1 - 4 12/29/2097

Times

Times work similarly to dates, in that the pound signs are the delimiters. For example, to assign the time "10 past noon" to a variable, you would use the following code:

dLunchTime = #12:10:17 PM#

Doing arithmetic with time is a little trickier. For example, to add 12 minutes and 13 seconds to a time variable, you'd use an expression like so:

? dlunchtime + #0:12:13#

12:22:30 PM

Variants

Even if you use strongly typed variables throughout your Visual Basic career, you will need to have variables whose data type varies. For example, when you provide a text box on a form for a user to enter data into, you won't necessarily know what values are entered. You would store the value from this text box to a special type of variable called a variant, and then proceed from there, depending on what type of data the user entered.

You will run into two prefixes for a variant-var and vnt. When you want to declare a variable as a variant, use the statement:

Dim varMySchizoVariable as variant

VB has functions that determine what's in a variant variable, and these will be covered shortly. By the way, given a choice, you should strive to stay away from variants-they are processed slower than other data types because VB has to figure out what is in the variable before it can do anything else.

Constants

One practice to avoid in all programming practices is the use of "magic numbers." These are where you run into a line of code like this:

fWeightShip = fWeightOriginal + 3117

Obviously, the adder 3117 means something, and most likely, when the line of code was written, the meaning of the number 3117 was obvious to everyone. Six months later, to a rookie programmer who just joined the company, the number 3117 looks like Bob Gibson's lifetime strikeout record and nothing more.

You can avoid this by defining a "constant" that is then used throughout the program or application, like so:

' at beginning of module, for exampleConst CARDBOARD_CONTAINER_STANDARD_WEIGHT = 3117' much later in the code fWeightShip = fWeightOriginal + CARDBOARD_CONTAINER_STANDARD_WEIGHT

Then, when the weight of the cardboard container changes, you change it in only one place.

Static variables

A static variable is one that can be accessed only by the procedure or module in which it was created, but that retains its values for the life of the application. This might feel a bit funny- it's like a hybrid of a global and a local at the same time. For example, suppose you had a routine that was called from various places in your application, but the variables in the routine had to have a "memory" from one call to another. Thus, when this routine was called from Module A, static variable sPreferenceValue was assigned the value of "ASCENDING." This variable could be used throughout the routine, and, at some point, was changed to "DESCENDING." As soon as the routine went out of scope, the variable sPreferenceValue was no longer visible anywhere in the application. Then, later on, Module B called this routine. At this time, sPreferenceValue still had the value "DESCENDING."

So you can think of a static variable as being hidden, but not gone. You declare a static variable like so, using the "static" keyword instead of "dim" to define it:

Static iHowMany as Integer

Arrays

An array is a collection of variables, similar to VFP, except that each array element must contain the same type of data. Static arrays have a fixed length, while the size of Dynamic arrays can be changed. To create a static array, use this statement:

Dim asMyArrayOfStrings(20) as String

Obviously, this array has 20 elements. Whoa! Not so obviously! It actually contains 21 elements because VB starts referencing array elements with 0, not 1, as in VFP. You'll also notice I used parentheses, not square brackets, to surround the number of elements. Unfortunately for those of you who've practiced using brackets in array handling so as to keep arrays separate from function calls in VFP, you can't use square brackets in VB. That's the way it goes.

To define a Dynamic array, use the statement:

Dim asMyDynamicArrayOfStrings() as String

To define how many elements are in the array, use the ReDim statement:

ReDim asMyDynamicArrayOfStrings(13)

Here's a gotcha for those of you who are used to VFP's behavior of enlarging arrays, always keeping the existing contents: VB doesn't do the same thing by default. You need to include the Preserve keyword so that you don't blow away the contents of the existing array:

ReDim Preserve asMyDynamicArrayOfStrings(200)

You'll note that I used a two-character prefix for the array-one identifying it as an array and the other identifying the data type in the array. The first isn't technically necessary, since the parentheses ought to tell you you're working with an array, but I like to be explicit-it's just too easy to make a "dumb mistake" (as opposed to the intelligent kind of mistake). And the second one isn't often used in VFP because an array can contain many different types of data- but in VB, you're limited to a single type of data in an array (unless, of course, you define it as a variant array).


Functions

Functions are, like in VFP, "mini-programs" that may or may not take input values and then return values. Just like with operators, keeping functions in VB straight from VFP functions takes a bit of concentration (or luck). There are well over 100 functions in VB 6-considerably fewer than in VFP, but still more than could be covered in this chapter. Let's look at the different types of functions, and discuss some of the more useful ones.

Data conversion

There are a number of functions that convert one type of data to another. The first, strictly, doesn't convert, but it helps you handle variants. VarType(varArgument) returns a value according to what you feed it, as shown in Table 30.3.

Table 30.3. Data type conversion functions in Visual Basic.

VarType Value

Intrinsic Constant

Description

0

vbEmpty

There is nothing in the variant.

1

vbNull

There is not any valid data in the variant.

2

vbInteger

Contains a standard integer.

3

vbLong

Contains a long integer.

4

vbSingle

Contains a single precision floating point number.

5

vbDouble

Contains a double precision floating point number.

6

vbCurrency

Contains currency.

7

vbDate

Contains a date or time.

8

vbString

Contains a string.

9

vbObject

Contains an object.

10

vbError

Contains an error object.

11

vbBoolean

Contains a Boolean value.

12

vbVariant

Contains an array of variants.

13

vbDataObject

Contains a data access object.

14

vbDecimal

Contains a decimal value.

17

vbByte

Contains a byte value.

36

vbUserDefinedType

Contains a user-defined type.

8192

vbArray

Contains an array of values.

You can use the intrinsic constants in place of the numeric values to make it easier to read your code. The two following lines are identical:

If vartype(varMySchizoVariable) = 8 ThenIf vartype(varMySchizoVariable) = vbString Then

Table 30.4 lists a number of conversion functions and what they do.

Number handling

The Round() function works much like in VFP, but using a negative number as an argument in VFP will continue to round the number to the left of the decimal, while VB will generate an error.

Data manipulation

The Choose() function has several similar functions in VFP; the first argument is used as an index to return a later argument in the list of parameters, like so:

Choose(index, parm1, parm2, parm3, parm4)

Unlike arrays, the index starts with the number 1.

Table 30.4. Visual Basic conversion functions.

Conversion

Description

function

Cbool(argument)

Converts an argument to a Boolean. Best used to convert a numeric value to

True or False, such as non-zero to True and zero to False.

Cbyte(argument)

Converts an argument to a number between 0 and 255 if possible; otherwise

converts to 0.

Ccur(argument)

Converts an argument to a currency value if possible, using the Regional

Settings to determine the number of decimals in the currency value.

Cdate(argument)

Converts an argument to a date.

Cdbl(argument)

Converts an argument to a double precision number.

Cdec(argument)

Converts an argument to a decimal.

Cint(argument)

Converts an argument to an integer (truncating, not rounding the decimal).

Clong(argument)

Converts an argument to a long integer (truncating, not rounding the decimal).

Csng(argument)

Converts an argument to a single precision number.

Cstr(argument)

Converts an argument to a string.

Cvar(argument)

Converts an argument to a variant.

The Asc() and Chr() functions return an integer representing the ASCII character code of the first character in the argument, and the character associated by the ASCII character, respectively, just like in VFP. Join() will concatenate the elements in an array, using a specified delimiter. Split() returns an array containing a number of sub strings.

The Array() function converts a comma delimited list of values to an array, like so:

Dim varMyString As Variant
varMyString = Array('String 1', 'BBBBB', '3')
MsgBox (varMyString(0)) & Chr(13) _

& (varMyString(1)) & Chr(13) _

& (varMyString(2)) & Chr(13)

Note that the arguments inside the varMyString variable start with 0, not with 1! This will bite you sooner or later-you have been warned.

Machine and environment

CurDir() returns the current directory. Dir() returns the name of a file, directory or folder that matches a specified attribute.

Date and time

Date() returns the current system date. Now() returns the current date and time. DateAdd() returns a value to which a specific time interval has been added, while DateDiff() returns the difference between two dates. DatePart() returns a specific part of a date. Day(), Month(), Year(), Second(), Minute(), and Hour() all return components of a date argument. MonthName() and WeekdayName() do what they sound like-return a string indicating the month name or day name.

String manipulation

InStr() returns the position of the first occurrence of one string in another. InStrRev() does the same thing, but starting from the end of the string. Left() returns the leftmost characters from a string; Right() does the same thing from the right. Ltrim(), Rtrim(), and Trim() all rid spaces- leading or trailing-from a string. Len() returns the number of characters in a string. Space() returns a string full of spaces. StrComp() compares two strings to return the result, depending on a specified type of comparison. String() repeats a given string N times. StrReverse() returns a string in reverse order. Format() (and a number of variations) returns a string containing a formatted expression. Replace() will substitute a string for another, a specified number of times. Ucase() will convert a string to uppercase; lcase() converts it to lowercase.

Math functions

VB contains a standard set of math functions, including all of the geometry functions, Abs() (absolute value), Exp() (Exponentiation), Int() (Integer), Log(), Rnd() (random number), and Sqr() (square root).

Special functions

MsgBox() creates a message box, similarly to MessageBox() in VFP. InputBox() is similar to MsgBox, except that it allows the user to enter a single value in a text box. Switch() evaluates a list of expressions and returns an expression associated with the first expression in the list that is True. Error() returns the message that corresponds to an error number. VB, being tied much closer to Windows, also has a full complement of functions that talk to various components of the Windows environment, but I won't go into those here.


Commands

I know I claimed earlier that there were only 19 keywords in all of Visual Basic, but, well frankly, I was lying. There are actually over 70. And while that number pales to the 500+ commands and functions in VFP, it's still too many to simply list in an alphabetical manner and expect you to grok them in a single sitting. How to organize them?

I asked my family (a bunch of VB candidates if I ever saw them) for suggestions, and they came up with a number of interesting ones. One family member suggested listing them by length of word, another suggested listing them in the order in which they were introduced to VB, and a third suggestion was to sum up the ASCII codes that make up each word, and use the eventual values as a sort order. The fourth suggestion was "Winnie the Pooh" but the offerer of the suggestion is only 3 years old. Each of these suggestions was excellent (I have to say that or I'll end up sleeping on the couch until VB 43 is released), but it struck me that you use commands to do things when programming, and thus it makes sense to divide up the command into functional groups. So that's what I'll do here.

Logic structures

You can do the same things with Visual Basic as you can with VFP in terms of controlling flow within a program module. The syntax is just a little different.

Making a decision

The statement for making a decision is, as it is with VFP, IF. However, VB's IF is much more flexible, as shown in this code fragment:

IF <condition1> THEN
<stuff to do>
ELSEIF <condition2> THEN
<stuff to do>
ELSEIF <condition3> THEN
<stuff to do>
ELSE
<stuff to do>
END IF

You'll notice that a "THEN" keyword is required after the IF <condition> expression, and the closing expression is two words, not just one as in VFP. The coolest thing about this is that you can nest multiple IF conditions using the ELSEIF <condition> expression instead of having to build multiple IF/ELSE/ENDIF constructs as you do in VFP. Also take note that each condition can be different!


Choosing between multiple choices

The SELECT CASE construct is VB's version of DO CASE in VFP. It, too, is a bit more powerful, as shown in the following code fragment:

SELECT CASE sFileExtension

<stuff to do> CASE 'BAT' <stuff to do> CASE 'SYS' <stuff to do> CASE 'DBF', 'CDX', 'FPT'<stuff to do> CASE 'MAA' to 'MZZ' <stuff to do> CASE ELSE <stuff to do> END SELECT

This construct takes as input a string expression, sFileExtension, that contains the extension of a file, and processes it according to what extension it is. Thus, .BAT files get one treatment, .SYS files get a different treatment, and so on. This is different from VFP in that you can use multiple, inconsistent conditional expressions following each CASE statement in VFP, and, thus, you have to use the entire conditional expression. Note that you can include multiple values following VB's CASE statement, as in the third CASE statement. The code following that piece will be executed for .DBF, .CDX, and .FPT files. In VFP, you'd have to write an expression like so:

CASE inlist(cFileExtension, 'DBF', 'CDX', 'FPT')

Note that you can also have VB span a series of values, as in the fourth CASE where every extension between MAA and MZZ will be treated the same. CASE ELSE works the same as OTEHRWISE in VFP.


Looping

Somebody in Redmond went bonkers when it came time to assemble looping constructs in VB, because there are five of them. I'll describe the first four and then explain why you shouldn't use the fifth.

The FOR NEXT construct is the basic looping construct, as shown in this code fragment:

FOR nIndex = 1 to 10 STEP 2 <some code to run> IF <some bad condition occurred> THEN

EXIT FOR
END IF
<more code could be here>

NEXT

You'll see that NEXT is used in place of END FOR in VFP, although many of you are probably using the (sort of new) NEXT keyword in VFP instead of END FOR. (If you're not, you should, because it will cut down on the confusion generated when switching between languages.) You'll also see that the escape route out of a FOR loop is EXIT. Big surprise.

If your index expression is an element in a collection of some sort, you can use FOR EACH much easier:

FOR EACH <element> IN <group><some code to run> IF <some condition found the right element> THEN

EXIT FOR
END IF
<more code could be here>

NEXT

So far, so good. The next two logic structures are pretty similar: DO WHILE and DO UNTIL. In fact, you can probably noodle them out yourself, right? Not so fast!

DO WHILE <condition> <some code to run> LOOP

DO UNTIL <condition> <some code to run> LOOP

The trick to both of these is that, while the above syntax looks comfortable to you, you're most likely not going to see it in experienced VB-ers' code. That's because you can also put the WHILE and UNTIL expressions after the LOOP keyword, like so:

DO

<some code to run> LOOP WHILE <condition>

DO

<some code to run> LOOP UNTIL <condition>

Obviously, doing so changes how the loop is processed-much like incrementing a counter at the beginning or the end of a loop. You can use the EXIT DO statement to get out of DO JAIL early, like so:

DO WHILE <condition>

<some code to run>

IF <some bad condition occurred> THEN

EXIT DO END IF LOOP

The WHILE/WEND construct is a cheap, poorly imitated version of DO WHILE, because you can't use EXIT DO to escape early. If that's not important to you, then you can use WHILE/WEND instead. Keep in mind, however, that if your requirements change and you have to be able to terminate processing early, you'll have to recode your WHILE/WEND to DO WHILEs. Why not save yourself the inevitable hassle now?

Instead of trying to describe each VB statement in detail, I've just listed the rest of them, so that you can get a quick idea of what functionality is available to you natively, and, thus, what you'll have to write yourself or live without.

File handling

Close

Concludes IO to a file opened using OPEN.

Get

Reads data from an open disk file into a variable.

Input #

Reads data from an open sequential file and assigns the data to variables.

Let

Assigns the value of an expression to a variable or property.

Line Input #

Reads a single line from an open sequential file and assigns it to a string

variable.

Lock, Unlock

Controls access by other processes to all or part of a file opened using OPEN.

Lset

Left-aligns a string with a string variable, copies a variable of one user-defined

type to another variable of a different user-defined type.

Mid

Replaces a specified number of characters in a variant (string) variable with

characters from another string.

Open

Enables IO to a file.

Print #

Writes display-formatted data to a sequential file.

Put

Writes data from a variable to a disk file.

Reset

Closes all disk files opened using OPEN.

Rset

Right-aligns a string within a string variable.

Seek

Sets the position for the next read/write operation within a file opened using

OPEN.

Width #

Assigns an output line width to a file opened using OPEN.

Write #

Writes data to a sequential file.

Program control

Call

Transfers control to a Sub procedure, Function procedure or a .DLL.

Function

Declares the name, arguments, and code that form the body of a function

procedure.

GoSub

Branches to and returns from a subroutine within a single procedure.

GoTo

Branches unconditionally to a line within a procedure.

On GoSub, On GoTo

Branches to a line depending on the value of an expression.

Stop

Suspends execution of a program.

Sub

Declares the name, arguments, and code that form the body of a sub

procedure.

With

Executes a series of statements on a single object or user-defined type.

File system

ChDir

Changes the current directory or folder.

ChDrive

Changes the current drive.

FileCopy

Copies a file.

Kill

Deletes files from a disk.

MkDir

Creates a new directory.

Name

Renames a directory or file.

RmDir

Deletes a directory.

SavePicture

Saves a graphic from the picture or image property of a control to a file.

SetAttr

Sets attributes information for a file.

Variables

Const

Declares constants for use in place of literal values.

Declare

Declares references to external procedures in a .DLL-used at module level.

Deftype

Sets default data type for variables, arguments passed to procedures, and

return type for Function and Property Get procedures-used at module level.

Dim

Declares variables and allocates storage space.

Enum

Declares a type for an enumeration.

Erase

Reinitializes the elements of a fixed-size array and releases dynamic array

storage space.

Option Base

Declares the default lower bound for array subscripts-used at module level.

Option Compare

Declares the default comparison method to use when string data is

compared-used at module level.

Option Explicit

Forces explicit declaration of all variables in that module-used at module

level.

Option Private

Prevents a module's contents from being referenced outside its project.

Private

Declares private variables and allocates storage space-used at module

level.

Property Get

Declares the name, argument, and code that form the body of a property

procedure that gets the value of a property.

Property Let

Declares the name, argument, and code that form the body of a property

procedure that assigns the value to a property.

Property Set

Declares the name, argument, and code that form the body of a property

procedure that sets a reference to an object.

Public

Declares public variables and allocates storage space-used at module level.

Randomize

Initializes the random-number generator.

ReDim

Reallocates storage space for dynamic array variables.

Static

Declares variables and allocates storage space-used at the procedure level.

Type

Defines a user-defined data type containing one or more elements-used at

module level.

Section VI

Chapter 30: A Visual Basic Primer for Visual FoxPro Developers

743

Registry DeleteSetting

Deletes a section or key setting from an application's entry in the Registry.

SaveSetting

Saves or creates an application entry in the application's entry in the Registry.

Classes

Implements

Specifies an interface or class that will be implemented in the class module in

which it appears.

Load

Loads a form or control into memory.

Set

Assigns an object reference to a variable or property.

Unload

Unloads a form or control from memory.

N.E.C. (Not Elsewhere Classified)

Date

Sets the system date.

Error

Simulates an error.

Event

Declares a user-defined event.

On Error

Enables an error-handling routine and specifies the location of the routine within a procedure.

Resume

Resumes execution after an error-handling routine is finished.

Rem

Identifies a comment.

RaiseEvent

Fires an event.

SendKeys

Sends one or more keystrokes to the active window as if typed at the keyboard.

Time

Sets the system time.

If you look through these commands, you'll see that they seem to stop short of some of the functionality you're used to in VFP. That's because much of the language is a throwback to BASIC before it became "Visual Basic," and thus still contains legacy references to opening and closing files and such. Most of the work you'll do in VB now will be done visually- connecting to data will be done by dropping data-aware controls on a form, not through a series of commands like "USE" and "APPEND FROM" like VFP still does.

And one last thing: Remember that you can't abbreviate to the first four characters as you
can in VFP!

Stupid VB (Windows) trick #5: The DATE statement sets the current system

date. That seems reasonable. However, it appears that the emphasis that Microsoft puts on the term "cross platform" is well warranted, because there is a difference in permissible date specifications depending on whether the operating system is Windows 95/98 or Windows NT. Windows 95/98 can handle system dates between 1/1/1980 and 12/31/2099. However, Windows NT can only handle dates between 1/1/1980 and 12/31/2079. Hmmm, obviously a conspiracy. Do you think maybe this means that Microsoft is going to kill NT, and they're just stringing us along for 80 years? Hmmmm?




Creating the user interface: VB forms and controls

Now that we have covered the language, we've got a good foundation under our belt. It's time to look at the tools that we'll use to create the most interesting part of the application-from the user's perspective, that is. Much like an automobile and an 18-wheeler, some of the tools and techniques will be familiar to experienced VFP developers, but other techniques and components are either brand new, or just simply different. Let's explore.

It's difficult to do justice to the topic of "user interface" in a few pages, so I'm going to blast through this, and cover the big differences in two areas. First, I'll discuss the mechanisms you use to manipulate forms and controls in Visual Basic, and then I'll go through the more common controls and compare and contrast them with their VFP brethren.

Creating a form and dropping a control on it

Silly enough, it took me the better part of a day to get the hang of putting controls on a VB form. You don't click on the control in the toolbox and then on the form, and expect the control to be drawn for you like in VFP. Instead, either click on the control, and then click and drag the control on the form-just clicking on the form doesn't do anything. Alternatively, you can just double-click on the control in the toolbox-the control will be placed in the center of the form. If you double-click on a control in the toolbox multiple times, you'll end up with several controls on top of each other in the center of your form.

Moving a control is a little different-the cursor keys don't nudge a selected control or group of controls bit by bit. Instead you have to hold down the Ctrl key to nudge them one way or another. You'll also want to take note of the Format, Lock Controls menu option-selecting it will turn all of the sizing handles to white, and you'll not be allowed to use the mouse to move or resize the controls from then on. Evidently accidental moves by novice VBers was a problem at some point in time. You can, however, still use the keyboard to move and resize as you want.

Alignment is one of those "half-full, half-empty" issues. You can use the Tools, Options command to display or hide the grid on a form, define the granularity of grid lines, and to have controls snapped to grid lines or not. You can also use the Format, Align menu command to align a group of selected controls, much as you do in VFP. And if you're like me, you never use the menu command in VFP, preferring to use the Layout toolbar. Unfortunately, if you look for a Layout or an Alignment toolbar in VB, you won't find one. Instead, select the Form Editor toolbar. It looks sparse, but you'll see that each button opens up into a bunch of related choices, which I think is pretty cool. It just took a while to get used to it-it's still two mouse clicks instead of one.


VB's intrinsic controls

Visual Basic is considerably different from VFP in that hardly any VB developers just accept the controls that come "in the box" as all they'll use. The whole idea behind VB was to allow the developer to extend the environment by using additional controls as they desired-truly, very few VB developers' toolboxes look alike.

However, there are 20 controls that come with VB and appear automatically on the toolbox's toolbar. I'll talk about most of them here. If you dock the toolbox to the left of the VB IDE, you'll see them as shown in Figure 30.16.

The Label control is similar to VFP's label, and has many of the same properties.

The TextBox control is similar to its VFP counterpart, but also does double duty to serve as VFP's EditBox match. The Value property in VFP is called the Text property in VB for this control. You can set the MultiLine property to True and then enter more than one line of text-where you'd use an EditBox in VFP to do the same thing. If you have MultiLine set to True, you can use the ScrollBars property to display them as desired.

The CheckBox control is similar to VFP's CheckBox. It can contain a value of 0 (not checked), 1 (checked), or 2 (disabled). You have to be careful about using a value of 2, because once a check box's value has been set to 2, you can't tell whether it's checked or unchecked. 2 (Disabled) is also different from setting the Enabled property to False; the former keeps the check box's caption enabled (black) while the latter also dims the caption, turning to a fuzzy gray/white combination.

The CommandButton is VB's answer to VFP's command button-and a button is a button is a button.

The OptionButton sort of maps to an option button in VFP, but there are a couple of significant differences. If you've already placed an option button on a VB form, you might be wondering, "Where's the Option Group control?" And the answer is . there isn't any! All of the option buttons you place on a form-regardless of where on the form-are part of the same option group. Blech, you say? Me, too. It is a bit easier, of course, to be creative with placement-don't you hate having to enlarge an option group, and then edit the group, and then align the buttons where you want them? Kind of a pain. In VB, this is easier.

But the flip side is that if you want more than one option group on a form, you have to build your own container first, using a Frame control, and then add individual option buttons. Like I said, "Blech."

A group of option buttons (I didn't say "an Option Button Group," did I?) works just like you would expect-selecting one deselects another that had been selected. You need to write code to deselect all of them, like the following fragment (you could put this in the Click() method of a command button, for example):

Option1.Value = 0Option2.Value = 0Option3.Value = 0

The ComboBox and ListBox controls are similar to VFP's Combo Box and List Box controls, and I think they're actually somewhat easier to use "out of the box."

The ComboBox control is similar to VFP's combo box, complete with multiple modes and a variety of properties. You can set the Style property to one of three possibilities. 0 (DropDown Combo) means you can enter text or click on the arrow to open the combo. 1 (Simple Combo) means the combo box is always open but you can also enter text if you want. It's a little unusual-you need to resize the combo box to display more than one item, and the result ends up looking like a text box with a list box placed below it on the form. See Figure

30.17. 2 (DropDown List) means that you can't enter text, and you have to open the control in order to select a value.

The easiest way to add items to a ComboBox or ListBox is with the AddItem method:

List1.AddItem 'A' List1.AddItem 'B' List1.AddItem 'cccccc'

You can make a ListBox multi-selectable by setting the Mult-Select property to 1 (Simple) or 2 (Extended). Simple means that you can just keep clicking on items and they stay selected (clicking a second time deselects the item). Extended means you can use the Ctrl and Shift keys to select a range of items-such as the 3rd, 4th, 5th, 9th, and 12th.

You can determine which item has been selected in a ListBox with the Text property (just as you would use the DisplayValue property in VFP). If you have Multi-Select set to 1 or 2, the Text property displays the last item selected. In order to determine all those items selected, use the Selected property together with the ListCount property, much as you would in VFP:

Dim nItemNumber As IntegernItemNumber = 0 Do While nItemNumber < List1.ListCount

If List1.Selected(nItemNumber) = True Then

MsgBox 'Hey, I'm selected ' & nItemNumber + 1

End If

nItemNumber = nItemNumber + 1 Loop

Note that the index of the array populating the ListBox starts with zero, not 1 as in VFP.

The Shape and Line controls allow you to create a variety of lines, circles, and rectangles on your forms, much like the same controls in VFP do. The Shape property of the Shape control allows to you to choose between Rectangle, Square, Oval, Circle, Rounded Rectangle, and Rounded Square.

The Image and Picture controls allow you to place graphic files on your form. The Image control is lightweight, but doesn't allow you to overlap controls (and thus, images), and can't receive focus. The Picture control, on the other hand, can be overlapped and can receive focus-which makes it a good candidate to use when creating graphical controls.

The DirListBox control, in combination with the DriveListBox and the FileListBox controls, allow you to build file navigation dialogs quickly and easily.


More controls

There are, of course, more controls. Some ship with Visual Basic; others are produced by third parties and you have to beg, borrow, steal (or pay for!) them.

Those that ship with VB are also installed when you install VB; all you have to do is tell VB that you want access to them. To do so, right-click in the Toolbox and select the Components menu command. This opens the Components dialog as shown in Figure 30.18.

As with VFP, check off the controls you want to have displayed in the Toolbox, and then click OK. Note that this dialog is much nicer than the one we get in the Controls tab of the Tools, Options dialog in VFP. How much nicer? Oh, let me count the ways. First, you can see more of the name of the control than in VFP-and if the name is too long to fit in the list box, you can scroll the list box to the right and left. In VFP, you're just hosed. Second, you can see exactly which file on disk represents the control-in VFP, you're just supposed to guess, I guess. You can also choose between Controls, Designers and Insertable Objects in three different tabs. Finally, you can choose to see just those objects that you've selected-quite nice when you've selected three of 200 available. In VFP, get your pen and a piece of paper unless you've got a really good memory. (I've asked for these improvements, but you know the refrain-so many ERs, so little time.)

Once you've selected some controls, they'll show up in the Toolbox along with the original

20. You might not want your Toolbox littered with so many; right-click on the Toolbox, select the Add menu command, and add a tab to the Toolbox. Then drag your controls to the new tab. You can organize your controls any which way you want using this mechanism.



Incorporating data access into your VB app

What good is a programming language if you can't get to data somehow? Here is probably the biggest difference between Visual Basic and Visual FoxPro-VB doesn't have a native data engine. Thus, the way you talk to a data set is significantly different. However, you'll find that once you've made the connection to a data set, what you do next is more familiar than you would have thought. In this section I'll discuss the fundamental concept of how you go about accessing data in VB, and describe a couple of ways of making it happen. For those of you interested in the buzzwords of the month, I'll discuss the various TLAs involved in data access: ADO, DAO, RDS, RDO, UDS, ABC, XYZ and do-re-mi; and why you want to use ADO. Then I'll show you how to get started with ADO quickly and easily.

Visual Basic is a general-purpose programming language that has more and more been used as a front end to database applications. As such, it does not have a native data manipulation language-instead, data access is a feature that's been cobbled onto it, somewhat as an afterthought. Before you disparage this approach, you should know a little about how this state of affairs came about.

A brief history of data access in Visual Basic

If you look back a dozen years or so, you had three general choices when writing a database application. You could use one of those "Xbase" tools like dBASE, Clipper, or Fox. You could use a non-Xbase tool like Paradox or Rbase. Or you could use BASIC and manipulate text or binary files by hand. Many programmers chose the third route-not only hand-coding screens like Xbasers did in the early days, but also hand-coding data access. Instead of the Xbase "USE" command to open and gain access to a file, you had to go through a series of commands to identify the file, grab a file handle, open the file, write bytes to the file, reposition the pointer in the file, and so on-much like you can do with low-level file functions in Fox. Imagine if you were limited to LLFFs in Fox for all your data access!

When Visual Basic first arrived in the early '90s, hand-coding of screens went away-you simply drew objects with a Form Designer tool. Each subsequent version of VB added more power and capability, so no one noticed that they were still hard-coding the data access portion of the application.

In the mid-'90s, more powerful data access mechanisms were added to Visual Basic, to hide the hand-coding of data access much like the Form Designer hid the hand-coding of screens. These mechanisms have gone through a number of iterations, and Microsoft is not done yet.


DAO, ADO, 123, Hike!

It's not worth spending a lot of time going back to the beginning of data access in VB, but the last two mechanisms are worthy of discussion. Many VB applications in use today were written using a mechanism called Data Access Objects, or DAO. DAO provided access to relational databases like Microsoft Access and SQL Server. It's been superseded by ActiveX Data Objects, or ADO. ADO is actually a front end for a broader technology called OLE DB.

OLE DB has come about due to the explosion of non-relational data sources, such as e-mail, images, movies, sound files, HTML documents, and so on. Just as ODBC is a generic API for all sorts of relational databases, OLE DB is a generic API for all sorts of data. However, as has been said over and over again, writing to the OLE DB API is "just too hard" so ADO was put together to provide an easy-to-use interface to OLE DB.

While you can still use DAO, ADO is the current technology of choice, and unless you are faced with having to support old VB apps, I'd suggest you ignore DAO and learn ADO. That's what I'm going to do here.


What is ADO?

Just in case you skipped Chapter 28, I'll repeat myself briefly here. ADO is just a .DLL that sits in your WINDOWSSYSTEM or WINNTSYSTEM32 directory. It provides services just like any other .DLL provides functionality. You can drop an ADO control on a form, just like you can drop a Calendar .OCX or a .DLL that provides serial communications. No real magic there-but it's not terribly clear to those just getting started, or to those of us who are used to having data access built into the product like we are with Fox.

So, like JET, the data access engine used in Microsoft Access, ADO is just an engine-but it provides the ability to talk to a variety of data sources, as opposed to just an .MDB file like Access. This data engine, fortunately, was not written by the same people who wrote JET- instead, it was written by the folks who built and enhanced the Fox data engine. Yes, an ADO recordset is, in many ways, an abstraction of a Visual FoxPro cursor!


Connections

When you issue a USE command in Fox, an awful lot happens behind the scenes, but eventually you're attached to a .DBF file. Implicit in this scenario is that the file you're "USEing" is a .DBF with a specific file structure. Other than that, a .DBF is essentially a dumb file just sitting on disk. If you're using a .DBF that's part of a .DBC, there's a bit of additional complexity in that the .DBC inserts a layer of information in between you and your data, but the Fox engine handles all that transparently. Thus, you can USE any kind of database as long as it's got a .DBF file structure.

With ADO, you can connect to a variety of data sources that can be structured a whole bunch of different ways, and in fact, can be housed in an entirely different environment, such as a SQL Server database or an e-mail program. Not only that, but it is theoretically possible to write a data access layer in an application, and then attach to it from a different front end. For example, as the story goes, you could write a middle tier in Visual FoxPro using ADO to talk to any old back end, and then use a front end written in, oh, say, HTML or Visual Basic. I don't know of anyone who has done this successfully (in other words, had a legitimate business need for this architecture, successfully implemented it with a bare minimum of compromises, and finally, actually made money at it), but it's an interesting intellectual exercise, and somewhere down the road I imagine it will become useful.

First, I'm going to create a simple data-entry and navigation form using the ADO Data Control, and in doing so, create a connection so that you can see where it fits in with the larger scheme of things. The ADO Data Control acts much like a wizard-set a few properties and you're done. However, just like a wizard, it's easy to use it without understanding what's underneath, and thus your usage is superficial and limited. For this reason, I'll then take apart the connection, and build one programmatically so that you can see how each piece works.

Just like every introductory demonstration, I'll use Microsoft Access 2000 as the back end. Oh, get that gloomy look off your face. First of all, it's more likely that you've got a copy of Access lying around than SQL Server, and second, there's less overhead and infrastructure to worry about. Once you've got the basics down, you can upgrade your choice of back end and deal with the added complexities then.


Setting up a form with an ADO Data Control

This example uses the MG.MDB database provided in the downloads for this chapter. It contains about six tables having to do with an automobile car club,

including tblCars, which I'll use in this first example.

First, after opening Visual Basic, you'll need to add the ADO Data Control to your Toolbox. This control comes with Visual Basic, but is not part of the intrinsic Toolbox. Right-click on the Toolbox, select the Components menu option, and select the Microsoft ADO Data Control 6.0 (OLE DB) item in the list box in the Controls tab as shown in Figure 30.19. After you check the check box and select OK, you'll have another control on your Toolbox.

Next, open a new project (Standard EXE) and a blank form. Drop the ADO Data Control on the form. You'll get a navigation bar as the visible part of the control, as shown in Figure 30.20.

Next, it's time to connect this control to a data source. First, right-click on the ADO Data Control and select the ADODC Properties menu command. The Property Pages dialog appears. Note that this dialog is not the same as the Properties window that is also displayed, as shown in Figure 30.21. (You can also open the Property Pages dialog by double-clicking on the Custom property in the Properties window, or clicking the ellipsis button to the right of the Custom property.)

The Property Pages dialog acts like a "mini-wizard" where you are guided through each piece of building the connection. (I'll explain what all of these options are for later.) Select the Use Connection String option button, and then click the Build button. The Data Link Properties dialog opens, and a list of OLE DB providers is displayed (see Figure 30.22).

Remember that OLE DB is an API to a variety of data sources, just like ODBC is. The only difference is that ODBC basically attaches to relationally formatted data, while OLE DB is more robust. Pick the OLE DB provider you want-in this case, it will be Jet 4.0.

You might be wondering where these OLE DB providers come from. They just sort of come along for the ride. Some are installed with Windows, and others with a specific application. For example, when you install Office 2000 Pro (or, at least, Access 2000), you get the Jet 4.0 OLE DB Provider automatically.

Click the Next button to identify which Jet 4.0 database you want to work with.

The contents of the Connection tab of the Data Link Properties dialog will vary according to which kind of OLE DB provider you selected in the previous tab. Note that in Figure 30.23, I've already selected the MG.MDB Access 2000 database that's residing somewhere deep in the bowels of drive G. Also note that the OLE DB Provider for Jet 4.0 knew to ask for a user name, a password, and to allow a couple of password-related options.

Once you've entered or selected a database, you might want to make sure that ADO can talk to it by clicking the Test Connection button.

Finally, click OK, and you're all set-you'll be returned to the Property Pages dialog, and you'll see a string of text entered into the text box below the option button. The whole string will read something like this (all on one line in the text box, of course):

Provider=Microsoft.Jet.OLEDB.4.0;

Data Source=G:ft99vbsc06mg.mdb;

Persist Security Info=False

You'll see that the various items you selected are identified in the connection string, including a hard-coded data source. That's why I didn't provide a sample form with this application-you most likely wouldn't have a drive G with the same directories I have.

Finally, you'll need to identify a RecordSource. Select the third tab in the Property Pages dialog, change the Command Type to adCmdTable, and when you open the Table or Stored Procedure Name combo, you'll see all of the tables in the MG.MDB database displayed. Pick the table of interest-in Figure 30.24, I've picked tblCar.

That's it for setting up the connection. Now it's time to put something more interesting on the form than just a data control. How about a text box or two?


Connecting controls to an ADO recordset

Drop a couple of labels and text boxes on your form. In order to bind a text box to a field in a record source, you need to specify two properties. The first is to identify which ADO recordset contains the field you want. This might seem a bit odd at first, until you realize that an ADO recordset is much like a View in a VFP data environment. You can have several views in a DE, and you can have several ADO recordsets in a VB form. You'd just drop several ADO Data Controls on the form (and they'd be named, by default, ADODC1, ADODC2, and so on).

In Figure 30.25, I've sorted the Properties window with the Categorized tab, so that I can see the data properties all together and segregated from the other properties.

Clicking on the DataSource property will open a combo box that lists all of the available ADO recordsets-in this case, the combo box had only one entry, ADODC1. Then, click on Data Field. The combo box will be populated with all of the fields that are in the record source you specified. In this example, all of the fields in the tblCar table are available. If you created a different record source, say, by a SQL SELECT command, that resulted in a four-field result set, only those four fields would be displayed in the DataField property combo.

Do the same for the Year text box, and save your form.

To see the Data Control in action, run the form by clicking the square button in the toolbar. You'll see the form as shown in Figure 30.26.

Admittedly, this isn't very fancy, but it gives you two essential abilities. The first is to navigate through a recordset. You can click the four buttons on the data control to move to the first, previous, next, and last records in the recordset. The other is to edit a value. This isn't immediately obvious, because there is no Edit or Save button, but the text boxes are indeed live. Change a value, move to another record, and voila!, your change has been committed.


Revisiting the recordset idea

Okay, you're probably not going to pull this code out of this chapter and use it in an application. But it demonstrates the basic idea of an ADO recordset-the new way for Visual Basic applications to address data. A recordset, then, is just a cursor in Visual Basic clothing. It doesn't have to be the whole table, and it can contain data from more than one table as well.

But an ADO recordset is better than a cursor-it's actually an object, and as such has properties that you can manipulate and methods that you can call in order to work with the recordset. For example, the AbsolutePosition property is much like the Recno() function in Fox-it tells you which record in the recordset you're on. And the MoveFirst and MoveNext methods, for example, move the record pointer to the first record and the next record in the recordset, respectively. The syntax looks like this for a recordset named adodcNewCustomers (as opposed to the unhelpful nomenclature of "adodc1"):

adodcNewCustomers.Recordset.AbsolutePosition adodcNewCustomers.Recordset.MoveFirst adodcNewCustomers.Recordset.MoveNext

If you're thinking this all looks awful familiar, well, right you are. You already know all this, don't you?

In summary, data is data is data. Connecting to it in Visual Basic using ADO is more involved than the USE command you're used to in Fox, but once you've made the connection, you can manipulate the recordset just as you work with a view in Fox.



Real-world usage of ADO in VB

Now that you understand why you want to use ADO and how to get a connection started, I'll describe how to do a couple of real-world tasks with ADO.

Populating a combo box and a list box

In this example, I'm going to use a simple form that contains two command buttons, a combo box, and a list box. Initially, the combo and list boxes will be empty. The first command button will populate the combo box with a list of distinct values from a table, and the second command button will use the value selected in the combo box to populate the list box with associated values. See Figure 30.27.

Specifically, this form is digging data out of a "Tasks" table. This table is contained in a Microsoft Access 2000 .MDB named ATONCE.MDB. Each record in the table is a task that is assigned to an individual. The first command button, Fill Person, selects a list of distinct abbreviations for people to whom tasks are assigned as shown in Figure 30.28. (This way, tasks can be assigned to new people on the fly without having to first add the person elsewhere.)

Once the combo box is populated with people who have tasks assigned to them, the second command button, Fill Tasks, selects the value in the Task Description field for every record for that person, as shown in Figure 30.29. This actually is a lot of steps-you would probably automatically populate the list box upon selection of a new value in the combo box, but this is an example to show you how to perform different functions.

Using form-level methods

It's generally good practice to keep your method code sparse-one sign of poor programming practice is Click() methods with hundreds or thousands of lines of code. So, even for this example, I created form-level methods (I'm sorry, in VB, they're called SUBS) that would be called from each of the command buttons. One sub would fill the combo box, and the other would fill the list box. Thus, when I decided I didn't need a separate Fill Tasks command button, I could just call the FillList() sub from the combo:

Private Sub cmdFillPerson_Click()
fillcbo
End Sub

Private Sub cmdFillTasks_Click()
filllist
End Sub

Filling the combo box

The code to accomplish this task-pulling one instance of each task owner from the Tasks table-is fairly straightforward. First, you create a pair of variable references: one for a connection and one for a recordset.

Dim objcn As New ADODB.ConnectionDim objrs As New ADODB.Recordset

Next, assign values to the properties of the connection, like so:

With objcn.CommandTimeout = 15 .ConnectionTimeout = 15 .Provider = 'Microsoft.Jet.OLEDB.4.0' .Open 'dataatonce.mdb', 'admin'End With

The Open method assumes that there is a directory called DATA underneath the directory that holds the program-in real life, you'd use a property to hold this value so that users could modify the location of the data as they desired.

Next, it's time to populate the empty Recordset object, like so:

objrs.Open 'select distinct cOwner from TASKS', objcn

This Open method uses the connection that I set up just a few lines of code earlier.

Once you have a recordset (remember, it's just like a cursor in VFP), you can work with it as desired. For example, I scrolled through the recordset and added items from it to the combo box:

Do While Not objrs.EOFCombo1.AddItem objrs!cOwnerobjrs.MoveNext

Loop

Then I used the MoveFirst method of the Recordset object to position the record pointer back on the first record of the recordset, and used that value to populate what you and I would call the display value of the combo box:

objrs.MoveFirstCombo1.Text = objrs!cOwner

Finally, I closed all of the objects because I don't need them anymore:

objrs.CloseSet objrs = Nothingobjcn.CloseSet objcn = Nothing

The entire method looks like this:

Sub fillcbo()Dim objcn As New ADODB.ConnectionDim objrs As New ADODB.RecordsetWith objcn

.CommandTimeout = 15
.ConnectionTimeout = 15
.Provider = 'Microsoft.Jet.OLEDB.4.0'
.Open 'dataatonce.mdb', 'admin'

End With objrs.Open 'select distinct cOwner from TASKS', objcnDo While Not objrs.EOF

Combo1.AddItem objrs!cOwner

objrs.MoveNextLoopobjrs.MoveFirstCombo1.Text = objrs!cOwnerobjrs.CloseSet objrs = Nothingobjcn.CloseSet objcn = NothingEnd Sub

Filling the list box

Once the user selects an item from the combo box, I'm going to fill the list box with the appropriate tasks for the selected individual. First, create a couple of objects for the connection and the recordset, and initialize another variable to hold the SQL command that's going to actually work on the Tasks table:

Dim objcn As New ADODB.ConnectionDim objrs As New ADODB.RecordsetDim strSQL As String

Populate the Connection object just as was done for the combo box:

With objcn.CommandTimeout = 15 .ConnectionTimeout = 15 .Provider = 'Microsoft.Jet.OLEDB.4.0' .Open 'dataatonce.mdb', 'admin'End With

This part is new. Create a text string that represents the SQL statement to select the tasks (the cSubject field in the Tasks table) based on the value of the combo box. Then issue the Execute method of the connection using this string. You've now got a recordset populated just like in the combo box:

strSQL = 'select cSubject from TASKS where cOwner = ' _& ''' & Combo1.Text & ''' Set objrs = objcn.Execute(strSQL, adCmdText)

I've added a bit of fine-tuning here when filling the list box. Instead of just jamming the values from the recordset into the list box, I first cleared the items in the list box. Otherwise, the tasks from the new person would be added to the existing list of tasks:

List1.Clear

Do While Not objrs.EOFList1.AddItem objrs!cSubjectobjrs.MoveNext

Loop

Finally, close the objects as before:

objrs.CloseSet objrs = Nothingobjcn.CloseSet objcn = Nothing

Here's the code for the entire method:

Sub filllist()Dim objcn As New ADODB.ConnectionDim objrs As New ADODB.RecordsetDim strSQL As StringWith objcn

.CommandTimeout = 15
.ConnectionTimeout = 15
.Provider = 'Microsoft.Jet.OLEDB.4.0'
.Open 'dataatonce.mdb', 'admin'

End With strSQL = 'select cSubject from TASKS where cOwner = ' _

& ''' & Combo1.Text & ''' Set objrs = objcn.Execute(strSQL, adCmdText)List1.Clear Do While Not objrs.EOF

List1.AddItem objrs!cSubject

objrs.MoveNextLoopobjrs.CloseSet objrs = Nothingobjcn.CloseSet objcn = NothingEnd Sub

As you can see, the big difference is in the syntax of creating a connection and populating a recordset, and in working with controls that operate a little differently. But the other things you're used to doing-working with relational tables and SQL-hey, that's yesterday's news! Again, data is data is data. Connecting to it in Visual Basic using ADO, whether you use the controls or write the code yourself, is more involved than the USE command you're used to in Fox, but once you've got the connection made, you can manipulate the record set just as you work with a View in Fox.


Populating a grid

I'm using a page frame as my data-entry form. On the first tab, I've got the DataGrid control as shown in Figure 30.30.

Figure 30.31 shows the second tab of the form-a place where the user can add or edit records. I've added a couple of nice touches to this tab. First, if you have an existing record highlighted in the Task List tab, clicking the Add/Edit tab displays the record in "file card" view as shown in Figure 30.31. In this sample form, I've not put all of the fields on the tab- just enough so you get the idea.

If, on the other hand, you click the Add button, you're automatically moved to the Add/Edit tab, and have a blank record at your disposal for entering new data. When you tab out of the last field, focus is returned to the Task List tab.

The third tab, Filter/Sort/Columns, isn't really active yet, but eventually it will be used to allow the user to customize the view of the grid-which records (that would be a filter, right?), the order of the records (Sort), and which columns to display-and in which order to display them (Columns).

In addition, the four navigation buttons in the upper left corner allow you to move through the grid, a record at a time, or to the first or last records, while the This Record? button in the upper right simply displays a message box with the number of the current record. Okay, maybe you would find this on a mission-critical application that spans four continents, but it's useful for our discussion.

Setting up the data control

What you don't see on the form, of course, are the other two controls-the data controls I used to populate various parts of the form. Therefore, in Figure 30.32, you see the form in design mode, with the two data controls in the lower right corner of the form. I'll just focus on one of the data controls now-the one that fills the data grid on the first tab of the page frame. I also show the Property Pages for ADODC1. (Right-click on the data control and select the ADODC Property Pages menu to open the Property Pages dialog.)

In Figure 30.32, I show the General properties for the data control that will fill the grid. The connection string is a standard Access 2000 .MDB (it kills me every time I have to type that!), and the data source points to the ATONCE.MDB that's located in the data directory underneath the source code for this project. Note that Visual Basic will try to include the entire path for the data source-which, of course, plays hell for portability.

Pointing to an .MDB, of course, doesn't do any more good than pointing to a .DBC in Visual FoxPro. You need to describe what part of the .MDB you want to get at. In Figure 30.33, I show the RecordSource properties-the Command Type is a table, and the specific table inside the .MDB is the Tasks table.

Adding controls to your toolbox

Now, look away from the screen (or the paper, if you printed this out), and then come back to this chapter. The next screen shot is also a Property Pages dialog, but it's not of the ADODC1 data control-it's of the DataGrid control. Before I drill into each of the properties, however, I should explain how I got the DataGrid on the form at all. (Kind of like explaining where the Start button is before explaining how to shut Windows down, right?)

In the VB toolbox, you can right-click and select the Components menu option to open the Components dialog as shown in Figure 30.34. Scroll about four bazillion rows down in the list box on the Controls tab, and you'll find the Microsoft ADO Data Control 6.0 (SP3) and the Microsoft DataGrid Control (SP3). Note that they are both OLE DB controls. This is good- you'll want to stay away from data-oriented controls that aren't OLE DB because they're as my daughters would say, like, so five minutes ago (or, in our vernacular, "old fashioned"). These days, the two terms are basically the same, of course. Be sure to check the check box to the left of each control you want to appear on the toolbox; simply highlighting one and clicking Apply isn't good enough.

Once these controls are on the toolbox, of course, you can select one and drop it onto your form. (Be sure to select the page frame control and select the appropriate page so that you drop your control on the page you want it to appear on.) Finally, right-click the DataGrid control and select the Properties menu option.

Setting up the DataGrid control

I could spend three or four chapters just covering the various options in this control, and indeed, I'd like to. But it would get pretty boring because there's lots of other good stuff to cover as well. So how about just the high spots-you can dig in further if you want to yourself.

The only real customizing needed before you dive into this new set of property pages is to identify the DataSource property for the DataGrid control. Select the Properties window (not the pages!), order the properties by Category, and select DataSource property. You'll see a combo box populated with each of the recordsets that you've added to the form. Pick the appropriate one (if you name your recordsets with something more useful than "ADODC1" and "ADODC2", it will be easier to remember which one to use.)

The Columns tab, as shown in Figure 30.35, allows you to control which field is going to land in which column. The trick here is to remember it's not which column is going to show up-you get to filter the columns for display in a moment. Anyway, I've tied the Primary Key field, iidtasks, to the first column (and as with every Visual Basic index, the index starts with zero). Where do the contents of the DataField combo come from? Other than magic, that is? They come from the ADO DataControl, and because I specified "Table" in the RecordSource property, every field in the Tasks table will be available. I could have specified a cursor instead of the entire table, and then just the contents of the cursor would have been available.

The good stuff comes next-in the Layout tab. You can set a number of properties for each column in the DataGrid, including Locked, AllowSizing, Visible, and WrapText. I simply set Visible off, as shown in Figure 30.36, and now there's no confusing Primary Key field displaying in the DataGrid.

You can set about four bazillion more properties in the Splits tab (a split is similar to the Excel splitter) as shown in Figure 30.37. I use this tab to mention a beef about Visual Basic and MSDN Help. You see 11 controls on this tab, right? So, suppose you wonder what "ScrollGroup" means. You click on Help, thinking that there might, at the very least, be a small, helpful paragraph describing what "ScrollGroup" is, right?

Heck, no. You get a description (after following four hyperlinks) for the "split" property of an object, and it has nothing at all to do with the "ScrollGroup" control on this tab. Pardon my French, but what a crock! I'd rather they just rip the Help button off the dialog completely, rather than provide useless and misleading information like this. Okay, end of rant. But be prepared to experiment, because you won't find out how these things work any other way.

The last tab on this page, Format, provides you with the ability to control how the data in each column is formatted, as shown in Figure 30.38.

Okay, now you've got a quick run-down on the DataGrid control.

If you're a Visual Basic developer, the mind just reels with the flexibility that this control provides you. As a Fox developer, however, you've probably been very polite, quietly sitting in the back, but just aching to raise your hand: "How do you change the data source underlying the grid? In Fox, you just do a new SELECT and then refresh your grid." Or, "How can you color each row a different color?" Or, "How do you put check boxes in the third row?"

The answer will probably be unsatisfying, but just hold your horses for a minute. The answer is: "You buy another grid control that provides the functionality you want." That's how VB was designed, after all. We're spoiled because we've got so much functionality right in the box, but on the other hand, we've also had to be satisfied, since a lot of ActiveX controls don't work well with VFP. So the situation is kind of like a pair of golden handcuffs, right?

You can, of course, programmatically change all of these properties, and if you want, you can even repopulate the contents of the grid by creating your ADO recordset in code, and then refreshing the grid. It seems to me like it's more work than in VFP, but I venture that's due more so to familiarity than to fact.

As you can see, it's not a big deal to connect a rather complex control to an ADO data control. The bigger job is to decide which control you want to use, given the set of requirements that you have (and which, likely, continue to change as you develop the app).



A quick ADO command reference

So, Bunky, you don't want to experiment with learning ADO recordset syntax one command at a time? You'd like a quick reference of what's available? In this section, I'll show you what ADO keywords are available (at least in this week's version), how some of them compare to VFP, and describe what you'd use some of them for.

As I discussed earlier, a recordset (or, more accurately, a Recordset object, since it's got properties and methods attached to it) contains either the entire set of records from a table or the results of a command like a SQL SELECT. It's fundamentally just like a Visual FoxPro cursor except for those extra goodies-properties and methods. But you work with it the same way: navigate through the set of records and perform operations on the current record.

It's important to note that not all providers support the entire ADO syntax listed below. You can use the Supports() method to determine details.

Properties

AbsolutePage

Returns the page on which the current record resides. This may not always be

available, depending on the provider.

AbsolutePosition

Specifies the "ordinal position" of the current record. You and I know this as "the

record number."

ActiveConnection

Returns the name of the connection object to which the current recordset object

belongs.

BOF, EOF

Indicates that the current record position is before/after the first/last record in a

Recordset object. This is slightly different than Fox in that you can think of ADO as

having two phantom records-one at the beginning of the file and one at the end. Once you move onto one of these phantom records, BOF or EOF is set to True and

there is no "current record."

Bookmark

You know how you have to go through a bunch of code like the following in order to save the position of the record pointer so that you can return to it? if eof()m.liCurRec = reccount()else m.liCurRec = recno()endif <do some fancy stuff like moving around>go m.liCurRec With ADO, you can set a bookmark that serves the same purpose, as "m.liCurRec." This is not available in all recordsets.

CacheSize

Returns the number of records in a Recordset object that are locally cached.

CursorLocation

Sets or returns the location of the cursor engine: Client or Server.

CursorType

Sets or returns the type of cursor used. Options are ForwardOnly, Keyset, Dynamic, and Static. Each type of cursor trades off between performance and flexibility.

ForwardOnly: Same as static except allows only forward scrolling.

Keyset: Same as a dynamic cursor except Adds by other users aren't visible.

Dynamic: The most flexible of cursors-allows you to see modifications made by other users and all types of navigation.

Static: A static set of records that you would typically use for reporting. Allows forward and backward scrolling.

EditMode

Indicates the edit status of the current record-much like the buffer in VFP.

Filter

Allows you to set or determine a filter for a recordset.

LockType

Indicates the type of locks placed on records during editing. You can choose between ReadOnly, Pessimistic, Optimistic, and BatchOptimistic. Hmmm, sound familiar?

MarshalOptions

This is a bit advanced for a "quick-reference" so I'll quote directly from the help file:

When using a client-side (ADOR) Recordset, records that have been modified on the client are written back to the middle-tier or Web server through a technique called marshaling, the process of packaging and sending interface method parameters across thread or process boundaries. Setting the MarshalOptions property can improve performance when modified remote data is marshaled for updating back to the middle-tier or Web server.

Properties, continued

MaxRecords

Much like when you specify how many records should be retrieved in a view, this allows you to limit the number of records placed in a recordset. The default value is 0, which means all desired records are placed in the recordset.

PageCount, PageSize

Tells you how many pages of data are in the recordset and what the page size is. Much like AbsolutePage, this might not be supported by the recordset.

RecordCount

Yup, just like Reccount() in VFP. Well, not exactly. There are some performance issues here-see online help for the details.

Source

Indicates the underlying source for a Recordset object. Choices are Command object, SQL statement, table name or stored proc.

State

Indicates whether an object is open or closed.

Status

Indicates the status of the current record as far as batch updates and other group processes go. See online help for a long list of possible values.


Methods

AddNew

Creates a new record for an updateable Recordset object. rs.addnew fields, values

CancelBatch

Cancels a pending batch update.

CancelUpdate

Cancels any changes made to the current record or to a new record prior to calling the Update method. Also discards a newly added record. Can't undo changes after you call Update unless the changes are part of a transaction or a batch update.

Clone

Creates a duplicate Recordset object from an existing Recordset object. Returns a Recordset object reference. Set rsDupe = rsOriginal.clone()

Close

Closes a Connection object or a Recordset object. Closing a connection also closes active Recordset objects associated with the connection.

Delete

Deletes the current record or a group of records. rs.delete RecordsToDelete RecordsToDelete is a constant that is equal to adAffectcurrent (only delete the current record) or adAffectGroup (deletes the records that are in the current filter). Obviously, you must set the Filter property first.

GetRows

Places multiple records of a recordset into an array. Array = rs.getrows(rows, start, fields) After you call GetRows, the record pointer is placed at the first record that wasn't put into the array, or at EOF if there are no more records.

Move

The doc says "Moves the position of the current record in a recordset object" but this is awkwardly worded-to you and me, it means "move the record pointer to a new record." rs.move NumberOfRecords If you move before the first record, the current record is before the beginning of the file, and BOF is set to True. Same for EOF.

Methods, continued

MoveFirst, MoveLast, MoveNext, MovePrevious

Moves to the specified record

NextRecordset

This one requires a bit of explanation. You can create a series of recordsets with a compound command statement, such as: 'select * from TABLE1; select * from TABLE2; select * fromTABLE3' This method will close the TABLE1 recordset and open the TABLE2 recordset in turn.

Open

Opens a recordset.

Requery

Similar to requerying a combo box or a list box, this method will refresh a recordset by requerying the underlying source database.

Resync

Similar to requery but does not handle adds or deletes in the underlying source database. In other words, if a record was added to the underlying database, Resync will not see that record and will not include the record in the recordset.

Supports

Determines whether a specified Recordset object supports a particular type of functionality. The parameter passed to the Supports() method identifies a feature that may or may not be supported by the recordset in question.

Update

Saves changes made to the current record of a recordset.

UpdateBatch

Writes all pending batch updates to disk. Used when modifying a recordset in batch update mode. Instead of calling Update for each individual record in a recordset, you can cache all changes to a recordset and then send the entire set of changes back to disk with this command. Sorta like using table buffering.

The more things change, the more they stay the same. ADO's heritage-the development team is largely made up of people from the Fox data engine crew-is evident throughout. Concepts that are new and strange to VB developers are yesterday's news for Fox developers. But that's not the only place where you've got a leg up.



What's this business about Visual Basic classes?

I believe it was Abraham Lincoln who posited the question, "If you call a tail a leg, how many legs does a dog have?" The answer, then, was, "Four. Calling a tail a leg does not make it one." Unfortunately, some idiot in Redmond hadn't heard this one, and decided to use the term "classes" in order to name Visual Basic's implementation of templates. To close out this chapter, I'm going to discuss, at a high level, what Visual Basic classes are, and why you shouldn't bother with them.

VB classes are templates for creating visual interfaces or non-visual processes. They're by default created and stored on drive C, much like the VFP Component Gallery. (I'll get into why putting development tool data on drive C is such a stupid idea some other time, but it's nice to know that Fox isn't the only development team that does this.) VB classes don't support inheritance. Period.

You create a class for an object, and then you can create objects in your application using that class. If you change the class, however, the objects you created from the original version of that class do not change. Period.

While you and I are grimacing, evidently this is a big freaking deal to VB developers. To listen to a group of VB developers, you'd have thought they just learned how to turn lead into gold. Actually, if I get off my "Fox Rules" pedestal for a moment, it's easy to understand. Remember how we thought we were hot stuff when we got TEN work areas? When we could do light-bar menus? Or when we could use GENSCRNX to enhance the screen generation process? So we've got OOP now. I'm sure in 30 years we'll all be averting our eyes, digging our toes in the dirt, embarrassed to admit how exciting we were about "Object Oriented Programming" when it's become a minor stepping stone in the curriculum from fourth grade to fifth.

Given where VB programmers were with version 4.0, classes were a major step forward. Until classes were introduced, VB developers literally created every application from scratch. While we were building 2.x frameworks or using tools like FoxExpress or MaxFrame, they were hand-coding applications from line one, and their most advanced RAD technique was "Ctrl+C." Having the ability to create templates that can be reused was indeed a major step forward, not only in the coding process, but also in getting developers who formerly coded by the seat of their pants to start planning-yes, to start thinking about designing their apps ahead of time.

You might have detected a bit of anger here. And you're right. First of all, this is the second time I've written this section. Microsoft Word 2000 took a hike south on me, trashing my backup in the process, just as I was going through the final edits a week ago. I think it's criminal that version 9 of a silly word processor is still as unreliable as Word is. Sure, we've got fancy menus that change according to your use of them, and there are 17 different ways to create a Web page from your Word document, but you still can't depend 100% on being able to save a file. I guess this approach to developing software-shove it out the door before it's ready-will keep us employed for many years. Anyway, I digress.

I'm also angry-actually, much angrier-at two other aspects of this issue. The first is just personalities; it's been a bit tough to stomach folk who strut around like cock of the walk: "We've got CLASSES! How about us! Ain't we something!" while ignoring that the rest of the world has had the real thing for years. (Of course, as the saying goes, let those who are without sin throw the first stone. We Fox developers have had our nose up in the air for quite a while and other developers are pretty darn tired of it as well. But it's hard to be humble when you've got a tool as great as VFP, isn't it?)

But this has polarized the VB and VFP communities, and that's too darn bad. We've got a lot of knowledge about design, OOP, and databases that we could share with the VB community. And the VB guys could teach us a ton of stuff about working with Windows, ActiveX, and other Microsoft technologies that we've generally tried to stay away from. If you're not reading VBPJ right now, you're behind the times, for example. I'm afraid that the average developer in both camps has had their bridges burned by the animosity between the two groups.

The other thing that's really aggravating about this "class" business is that it has brought out some of the worst writers on the planet and plunked them into the VB community. I've yet to see a lucid description of what classes are and how they work in anything I've read, and that's a shame because there are a couple of geniuses in the VB community. I'll read anything that Dan Appleman writes, for example, whether I'm interested in the topic or not, just because he's fun and engaging.

C programmers have "Obfuscated Code Contests" where you show someone else your code, and dare them, "I bet you can't tell me what THIS does!" I get the same feeling when VB gurus write about classes. Here's my favorite quote:

"The classes we can create in VB support the concepts of polymorphism and

encapsulation, but not inheritance. That's just fine. While inheritance can be useful when

structuring collections of classes, it can be a major source of bugs and its implementation

requires the use of code that is difficult to understand."

-John Connell, Beginning VB6 Database Programming, WROX Press.

Yes, this is French for "You're too stupid to use inheritance correctly, so it's just as well that it's not in VB." And then-here's the corker-he dedicates the next 65 pages in the book to his first example on using classes. SIXTY FIVE pages for an introductory example? We've covered a complete introduction to the language in that amount of space. But this is kind of common. I've seen the same approach, albeit somewhat more moderate, in several other books.

So what's the deal? Is this topic just too hard? Is the mechanism too difficult to demonstrate in anything less than 25,000 words? Or is it that they don't understand it? Or is it that they're embarrassed that the implementation is half-baked, and they're trying to distract you from the fact? I dunno. If you've come across a VB book that does this justice, please let me know!

Well, I've tried to build classes in VB myself, and it is hard. There are a lot of steps-just like in VFP-to get your classes set up, and then to create objects based on those classes. And while I guess it's better than hand-coding, it's still a lot of work considering that the first time you change something, you have to start throwing out your previous work. Three steps forward, two steps back.

I can't believe that this is going to be as good as it gets in VB-I have to think that the object model will improve, and that some day, VB will get true inheritance as well. But until then, well, "it's too hard" and I think I'll wait.



Politica de confidentialitate | Termeni si conditii de utilizare



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 1344
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved