Chapter 1. Differences Between Visual FoxPro and Visual Basic .NET

A Whole New World

When I first began to look at Visual Basic .NET a couple of years ago, it reminded me of the movie Alien. Nothing looked familiar!

Where's the command window? And when you find it, what is it for? In Visual FoxPro, you can practically dispense with the menu and just type commands in order to do your work. For example, instead of selecting File, Open, Project from the menu, you can type MODIFY PROJECT (or MODI PROJ as most FoxPro commands can be abbreviated to four characters) and the project name, or just let the list of matching files pop up and press the spacebar to pick the first one. In Visual Basic, although there is a command window, the list of commands that you can type is pretty short.

And while I'm at it, where have all the functions gone? Recent versions of FoxPro (and of Visual Basic, for that matter) have around two thousand commands and functions. Visual Basic .NET has only a couple of hundred. Where did they go?

In FoxPro, commands and functions are in many cases simply wrappers for calls to DLLs that are themselves components of Windows. The developers of FoxPro enclosed all such method calls in wrappers to produce the commands and functions that you see when you enter HELP. For example, the UPPER function converts a string to uppercase. In Visual Basic .NET, the Upper method belongs to string objects. So UPPER(mVar) in FoxPro is mVar.Upper in Visual Basic.NET.

Other methods belong to classes, such as Strings and Math. Thanks to IntelliSense, you can type “Strings.” Or “Math.”, and when you press the period, in each case several dozen familiar method names will appear. But you have to know in which class to look. In FoxPro, you're relieved of the responsibility of knowing which Dynamic Link Library (DLL) the method came from.

Most methods are only exposed when you instantiate an object based on a particular class. For example, TableUpdate() in FoxPro is like a dataset's AcceptChanges() method. But to get to the point where you can use it, you would have to create a dataset, which requires creating a new connection, opening it, creating a new dataadapter, using its Fill method to create a dataset, displaying the dataset in a table to allow the user to make changes, then executing the dataset's AcceptChanges() method to save the changes, like this:

Imports System.Data.SQLClient

Public ds as Dataset

Dim cn as New SQLConnection("server=localhost;database=North Wind;UID=sa;PWD=;"
Cn.open()
Dim da as New SQLDataAdapter("SELECT * FROM Customers",cn)
ds = New DataAdapter
Da.fill(ds)
DataGrid1.DataSource = ds.Tables(0)
...
' Save commandbutton Click event handler:
Ds.AcceptChanges()
... 
' Code to save changes back to data store

That would be two lines of code in FoxPro:

BROWSE
TableUpdate(.T.)

One of the characteristics of the .NET languages that looks strangest to Visual FoxPro (VFP) developers is that most of what were commands and functions in FoxPro are methods of some two hundred class libraries (called namespaces in .NET) containing over two thousand classes. If you want to use a method, you instantiate an object based on the class that contains it. Then when you type the object's name and a period, IntelliSense kicks in, and the entire list of properties, events, and methods is laid out before you like a banquet table. In order to get to this point, however, you have to know which of the approximately two thousand classes contains the method you want. In some sense, you have to know what you're looking for in order to find it.

Some classes are directly accessible, without requiring that you first instantiate an object based on them. For example, type “Strings.”, and as soon as you type the period, a list of dozens of familiar functions appears, again thanks to IntelliSense. Similarly, typing “Math.” Displays most of the math functions you know and love in FoxPro. So as long as you know to start by typing the name of the Namespace that contains the functions you need, they're only a word away.

FoxPro FUNCTION and PROCEDURE declarations are as simple as can be. In Visual Basic .NET, Sub and Function declarations seemed to include keywords of endless variety: Overrideable, Shadow, Protected, MustOverride—what did these mean? And did I really have to know about all of them and use all of them in my code? And variable declarations in Visual Basic .NET have a different but equally perplexing variety of options. Do I need to use some of these options? All of them?

And what's up with events? The handling of events in Visual Basic escaped me for years. My eyes glazed over every time I saw the word WithEvents. How was it possible that Visual Basic relied so completely on a feature that you don't even have in FoxPro? You can do everything that events are used for in Visual Basic .NET transparently in FoxPro, without any special effort. I spent hours trying to think of a reason to create an event that FoxPro didn't already have so that I'd have a reason to “raise” it.

I also discovered that data binding, one of FoxPro's greatest strengths, is Visual Basic .NET's greatest weakness. Data binding is so intrinsic in Visual FoxPro that you don't even think about it (at least not until you change to a different data store than the DBF). In .NET, data access is disconnected; there are always additional steps required—sometimes lots of them—to send edited data back where it came from.

After months of experimentation, I finally concluded that most of the things I do in FoxPro are done in very similar ways in Visual Basic .NET. Little by little I've found equivalences that make me feel more at home. And sometimes, I even find something in Visual Basic that I like more than the analog in FoxPro. Other than minor name changes, there are only a few things that are truly different.

In most cases, you can ignore many of the powerful but irrelevant features in Visual Basic .NET and use only a subset that's just right for database development. In saying this, I know that I run the risk of incurring the territorial wrath of some guy who's just written an article about how critically important it is to use overloading in all of your applications—because if I'm right, he's just wasted your time and a tree. (There's a lot of money to be made by saying that things are harder than they really are, because if you're a consultant, and if you know about it and they don't and if they really, really need it, they had better hire you.) But I'm not afraid of controversy, as you may know. Someone has to point out that the emperor's naked.

Before you start building applications, let's review the principal differences between Visual FoxPro and Visual Basic .NET. I've summarized these differences in this chapter under 12 major subject headings. After reviewing these differences, you should be able to read Visual Basic .NET code and have a general idea of what the code is trying to do.

The Integrated Development Environment (IDE)

Visual Basic .NET is similar to the latest version of FoxPro. Features like the Visual FoxPro 8 Toolbox and the Task Pane appear to have been ported directly from .NET. As you'll see, many of the differences stem from the fact that Visual Basic .NET can build many kinds of applications—Windows Forms, ASP.NET Web pages, Web Services, Smart Devices (Pocket PC and Tablet PC applications), console applications, services that run when your computer starts up, and others. Visual FoxPro only builds two (executables and DLLs).

FoxPro can create .app and .exe files as well as .dlls. However, the .app and .exe files contain tokenized FoxPro, not machine code. The .app files can only be called by other FoxPro programs. The .exe files are .app files with a loader that Windows can use to run the encapsulated .app. DLLs in FoxPro are pretty much used only in XML Web Services. So the generation of a DLL for each project in .NET looks strange to FoxPro developers. FoxPro apps don't have to be compiled into an executable unless they're going to be used with the FoxPro runtime.

Many of the differences between the two languages stem from the fact that so many more output options are available in Visual Basic .NET. In the following sections, we'll look at the main differences in the components of the respective IDEs one by one.

Typing Commands in the Command Window

FoxPro has a command window, which appears automatically when you start up. If the command window isn't visible, you can activate it using Ctrl+F2. The command window is crucial to FoxPro. In it, you can type commands to do virtually everything that you can by clicking on a menu bar. In fact, I use commands so extensively that I've occasionally watched another FoxPro developer do something using the menu and wondered “Why did I never see that before?” Some menu selections actually insert the text of the corresponding command into the command window and execute it there, in effect using the command window as the shell.

The closest equivalent in Visual Basic .NET is Immediate mode in the command window. Although the command window is central to Visual FoxPro, you can use Visual Basic for months without ever opening it. In fact, I haven't opened it up in months. I had to do so just to remember enough to write this paragraph. So, although it's not as central to your work as it is in FoxPro, it can be used in testing and debugging.

Immediate mode in Visual Basic .NET is only available during program execution. You can either set a breakpoint or add Debugger.Break, or press Ctrl+B and specify a breakpoint condition: function name and line number, a variable to monitor for changes, or some other condition.) You can then use the command window (or the Locals window, or one of the four Watch windows) to examine variables. You can use IntelliSense to expose their PEMs (Properties, Events, and Methods). You can execute their methods, run subroutines and functions from the currently executing program, and assign values to object properties.

FoxPro's command window saves prior commands. You can use the UpArrow or PgUp keys to find and execute previously entered commands, or use the keys to cut and paste code into your programs. Visual Basic .NET toggles between Mark and Immediate mode. In Visual Basic .NET's Immediate mode, the up/down arrow keys cycle through recent commands; in Mark mode (which is like the FoxPro variant), you can cut and paste. You can use the shortcuts Ctrl+Alt+I and Ctrl+Shift+M to switch between Mark mode and Immediate mode.

However, Visual Basic .NET doesn't have commands like MODIFY PROJECT, CREATE TABLE, or any of the dozens of commands that you use in FoxPro. Scroll back through your Visual FoxPro command window; every single command you see there will have to be done in Visual Basic .NET either through an IDE menu selection, a builder in one of Visual Basic .NET's windows, an external program (like the Query Analyzer or the SQL Server Enterprise Manager) or through your own program code. As a result, the Visual Basic .NET IDE exposes the equivalents of Visual FoxPro commands, where they exist, as menu selections. That's yet another reason to become very familiar with the .NET IDE before you go too far. (Appendix A, “A Comparison of FoxPro and Visual Basic .NET Commands and Functions,” provides a concordance of Visual FoxPro commands and their Visual Basic counterparts.)

The Toolbox

In FoxPro you add controls to your forms using the Form Controls toolbar, which can display either the FoxPro base control classes, your current selection of registered OCXs and DLLs, or any combination of your own class libraries and the excellent classes that ship with FoxPro. Starting with FoxPro 7 you can also drag and drop classes from the Component Gallery. Beginning with FoxPro 8, you have the Toolbox, with tabs for the Component Gallery and a customizable toolbox where you can store your own classes.

The Toolbox is the only way to drag and drop controls onto forms in Visual Basic .NET. In that sense, it replaces FoxPro's Form Controls toolbar. But it does much more. The FoxPro and Visual Basic .NET toolboxes are very similar. Visual FoxPro's Toolbox has bars for text scraps (it's initially seeded with a bug report template and a commented program header, but you can add entries by highlighting and dragging them to the Toolbox). The Visual Basic .NET equivalent is the Clipboard, to which code fragments are automatically added whenever you highlight code or text and press Ctrl+C. Figure 1.1 shows the Visual Basic .NET Toolbox.

The Visual Basic .NET Toolbox.

Figure 1.1. The Visual Basic .NET Toolbox.

The .NET toolbox contains collections of objects for every conceivable environment: Data, Device Controls, XML Schema, Dialog Editor, Mobile Web Forms, Web Forms, Components, Windows Forms, HTML, the Clipboard Ring, Crystal Reports, and any other toolboxes that you might want to add and customize.

When the Visual FoxPro Screen Designer is open, the Toolbox entries for the FoxPro Base Classes, the Foundation Classes, and My Base Classes permit you to drag and drop screen design elements from the toolbox instead of dragging them from the Form Controls toolbar. Any ActiveX controls that you've registered in the Tools, Options, Controls dialog page will appear here as well, so you don't have to switch between your class library toolbar and the ActiveX Controls toolbar. And you can also use the Form Controls toolbar and the Toolbox at the same time.

In Visual Basic .NET, the Toolbox is your one-stop-shopping source for screen controls and components. As you move from one design context to another, the toolbar elements change to reflect what's appropriate. For example, if you're building an ASP .NET form, you can select from either HTML controls or Web Forms controls. Visual Basic .NET provides a collection of components, such as FileSystemWatcher, MessageQueue, and others that you just don't have in FoxPro.

In Visual Basic .NET, you can build your own user controls, just as you can build your own classes in FoxPro and then add them to the User Controls toolbar. In Chapter 2, “Building Simple Applications in Visual FoxPro and Visual Basic .NET,” you'll learn how to add a Windows Control Library to your project, after which any user controls you add to it will automatically be displayed in the User Controls toolbar. As in FoxPro, you use these controls instead of the standard Visual Basic controls on your forms. And because all of your user controls show up here, regardless of which Windows Control Library they came from, you don't have to switch between VCXs as you do in FoxPro.

TIP

Unlike FoxPro, where each open class library is a selection on the Class Picker toolbar context menu, there's only one User Controls toolbar in Visual Basic .NET.

Tab Ordering

Tab ordering of controls is a little different in VB .NET as well. In FoxPro, you can use the Tools, Options, Forms dialog to select either interactive or list ordering of your form controls. If you use interactive ordering, you click on controls in the order in which you want the user to be able to tab through them, then click somewhere on the form surface to tell FoxPro you're done. I usually prefer tab ordering by list.

In Visual Basic .Net, interactive tab ordering is the only way that's available. To start the process, select Tab Ordering from the View menu pad. When you're done, select Tab Ordering from the menu pad again; clicking somewhere on the form won't do a thing.

Projects and Solutions

FoxPro's basic container for building an application is the project. FoxPro stores the pieces of a project in a project file, which is a table and associated memo file with the same structure as a DBF and the extensions PJX and PJT. When the Project Manager screen is open, a Project menu pad with the relevant options (including Set Main, Exclude from Project, and Build Executable/DLL) appears in the FoxPro menu. Although FoxPro has had the ability to build DLLs for several versions, in general FoxPro developers haven't warmed up to them. Files that are located in the current directory are not necessarily listed in the project; files can be listed but excluded, which documents the files' importance to the project. These files remain freestanding elements not included in the executable.

Visual Basic .NET adds one more layer on top of the projects layer: the solution. I think the marketing guys had a hand in this, because where I come from, it's not a solution until it solves something. But I suppose they had to call it something. And I like what it does.

Visual Basic .NET applications sometimes consist of a data server that sends and receives data in XML format, a Windows Forms, and/or an ASP.NET application that uses that data. Because each of these is a project, it's useful to group them in such a manner as to be able to associate the data source with the data consumer. Solutions do just that. If you have only one project, it's still enclosed in a solution. But if there are several related projects, the solution keeps them together for administrative purposes.

Figure 1.2 shows a Visual Basic. NET solution containing a Web service and a Windows form application that uses the Web service. The two FoxPro projects in Figure 1.3 do the same thing, but you just have to remember that they're related to one another. In both cases the Web service and the application that uses it are in different directories.

A Visual Basic. NET solution containing a Web service and a Windows form application that uses the Web service.

Figure 1.2. A Visual Basic. NET solution containing a Web service and a Windows form application that uses the Web service.

Two FoxPro projects.

Figure 1.3. Two FoxPro projects.

However, the solution doesn't produce an executable. Executables or DLLs are the result of compiling projects. Solutions are simply a bookkeeping mechanism. In a solution, there will always be one project designated as the Startup project. That's the one that will run if you press F5. It can't be a class library because they're not executable. But it can be a Web page, a Web service, or any kind of executable.

Compiler Output Formats

In FoxPro, although you can compile to APP, DLL, or EXE format, you generally build EXE files. APP files can only be called from another FoxPro application and DLLs require some additional work that generally isn't worth the bother. In Visual Basic .NET (and in earlier versions of Visual Basic), it's common practice to build components as Dynamic Link Libraries (DLLs). Typically, at least one of the projects in the solution compiles to EXE format. DLLs built from other projects in the solution—class libraries, user controls, and the like—are used as components of the executable.

When you build a FoxPro project, you can build an APP, an EXE, a single-threaded DLL, or a multithreaded DLL. APPs can be called by the FoxPro runtime, but I almost always use EXEs. I've only used DLLs for Web services and seldom even then. You can either click on the Build button, press Alt+D, or type BUILD EXE Name FROM Name (BUILD DLL Name FROM Name, or BUILD MTDLL Name FROM Name for multithreaded DLLs).

In Visual Basic .NET, DLLs are used as you might use class libraries in FoxPro. For example, you can build an inheritable form (a Form Template class in FoxPro) as a DLL and inherit from it in another form, after you include the DLL as a reference in your project. When you build a Class Library project in Visual Basic .NET (by right-clicking on the project name in the Solution Explorer, selecting Properties, and setting the Output option to Class Library), the project compiles to a DLL.

NOTE

The name Dynamic Link Library (DLL) derives from the fact that they are linked when called (dynamically) rather than when the executable is created. So, you can run them from within other programs. They are said to run in-process as opposed to out-of-process in the non-FoxPro world, where components are compiled as DLLs; for us, the distinction is meaningless.

When you build a project, the type of project you select determines which references to .NET class library components will be automatically included. You can then add any other references (DLLs or namespaces) that you need. For example, if you want to inherit from an inheritable form that you had built previously, you would add a reference to the inheritable form DLL in your project before trying to add the inherited form. In that way, the project knows where to look for and display any available inheritable forms.

The nearest FoxPro equivalent would be adding class libraries to a project. (The other case where you add references, when you add ActiveX controls to forms, involves using the Tools, Options, Controls page to add ActiveX controls to the Selected list. This causes them to be displayed in the Form Controls toolbar ActiveX menu. However, ActiveX components don't explicitly appear in the Project Manager's file list.)

In Visual Basic .NET you first designate the Startup project because a solution may contain several projects. You can see this if you build my favorite .NET walkthrough, Creating a Distributed Application. (This walkthrough can be found in the Visual Studio .NET, Samples and Walkthroughs Help selection. In it, you build a solution containing a Web service, then add a Windows client application, and finally add a Web client application.) You can designate any of the three as the startup project. If you choose the Web service, a test page is automatically built and presented to you to test those of your Web services that are testable with keyboard input (that is, those that don't have a diffgram [a dataset containing changes made to another dataset] or other type of dataset as the input parameter). Change the Startup Project selection in the Solution Explorer and you can test either of the other projects. It beats fumbling around trying to remember the name of your latest FoxPro Web Service directory.

The Code Window

The code window for a Windows Forms project is shown in Figure 1.4. Unlike FoxPro, which puts each code snippet in a separate window, they're concatenated in Visual Basic. The Text Editor toolbar contains elements for indenting text, setting and jumping to bookmarks, and displaying object and parameter information.

The code window for a Windows Forms project.Visual FoxProversus Visual Basic .NETcode windowVisual Basic .NETversus Visual FoxProcode windowIDE (Integrated Development Environments)Visual FoxPro versus Visual Basic .NETcode windowcode windowVisual FoxPro versus Visual Basic .NETwindowscode windowVisual FoxPro versus Visual Basic .NET

Figure 1.4. The code window for a Windows Forms project.

The Document View Window

FoxPro's Document View window automatically displays all of the events and methods in the current form or class where you've already written some code and lets you click to jump to the selected code snippet.

There is no Document View window in the Visual Basic IDE. However, the Class view does just about the same thing, and exposes all of the classes in your project, not just the currently open file. And if you enter comments preceded by a single quote and the word TODO at the beginning of each routine, the Task List (activated from the menu using Alt+V, K, A) will display an entry for each TODO item. Double-clicking on an item takes you straight to the comment line in the code. So with a tiny amount of effort you get the same capability, and in fact, a little more.

To jump to a code snippet that you haven't yet coded in a Visual Basic .NET form, you select the object (for example, the form or a button or list box) from the drop-down list at the upper left of the code window screen, and then select the corresponding event or method from the drop-down list at the upper right of the screen. Just as in FoxPro, the names of snippets containing code appear in boldface. In FoxPro you can open the Properties window and pick the Methods tab, and then double-click on an event or method name to open its code snippet; you can't do that in Visual Basic .NET because events and methods don't appear in the Properties window page. It makes more sense in Visual Basic .NET, but the convenience of FoxPro is nice, too.

Bookmarks and Task Lists

Bookmarks and a Task List are both available in both Visual FoxPro and Visual Basic, and perform very similar functions. To create a bookmark in FoxPro, Shift+Double-click or use Alt+Shift+F2. In Visual Basic .NET, use Ctrl+K, which is a toggle. You can also click on the Toggle Bookmark button (the little blue flag) in the Text Editor toolbar. In fact, there are four command buttons in the Text Editor toolbar for managing all aspects of bookmarks; in FoxPro, you have to use the context menu.

The Server Explorer

In FoxPro, using DBFs is extremely easy. The USE command opens a table. CURSORSETPROP("Buffering",5) turns on optimistic table buffering, so that APPEND BLANK or record changes are not finalized until and unless you issue a TableUpdate() call. TableRevert() undoes anything done since optimistic buffering was enabled. If you're using a DBF, TableUpdate() commits changes back to the file on disk, and you're truly done.

There is no Visual Basic .NET equivalent to FoxPro's DBF in terms of ease of use. You must create a connection to find the data source, a data adapter (or at least a command object) to retrieve the data, and a dataset (an XML schema and container) to hold the data while you work with it. Then you must explicitly send an INSERT or UPDATE command back to the data source. There's even a CommandBuilder object that will use your SELECT command to infer the other three commands for you.

Visual Basic .NET doesn't have a USE command. To connect to a table, you need to define a connection using a connection string such as this one:

Server=(local);Database=Northwind;UID=sa;PWD=;

Visual Basic's Server Explorer exposes some of the capabilities of FoxPro's BROWSE command. After you've defined a connection, you can open a table in the connected database, return all rows, and optionally type changes or additions, which will be sent back to the data source. It allows access not only to ODBC/OLEDB data sources (your FoxPro tables, for example), but also to SQL Server databases.

Thus you can open a database and then view, edit, or add data without opening up the SQL Server Enterprise Manager or the Query Analyzer. You can also create tables, indexes, and relations. Still, the connection to your tables seems a little removed compared to FoxPro. And there's a slight delay when you access any data table. However, you can do all of the things that you can do with FoxPro tables, albeit in more roundabout ways.

In FoxPro, you use CREATE TableName to open up the schema design tool, or MODIFY STRUCTURE to change an existing one. USE TableName opens the table and points to the first row, whereas BROWSE returns the number of rows that you see in the resulting grid. There is no perceptible delay in either a USE or a BROWSE in FoxPro because little or no data is moved from the data source to the workstation.

In FoxPro, we use local tables because they're cheap and because they're fast. In fact, as of this writing, Microsoft doesn't sell a faster data store for single-user workstation applications than FoxPro DBFs with CDX files. (Actually, they don't sell it; they give it away.)

In Visual Basic .NET, using local data sources (like DBFs or Access MDB files) implies exactly the same degree of difficulty as does the use of SQL Server. As a programmer, you save your client no programming costs if you use local tables, as would have been the case with FoxPro.

I would love to have FoxPro's ease of access to local tables in Visual Basic .NET. Still, using the Server Explorer to build connections and data adapters is much easier than it used to be in Visual Basic, so to Visual Basic 6 programmers it's a step up.

What Can You DO in the IDE?

In FoxPro you type DO AppName to run an APP, an EXE, or a PRG. You can type DO MAIN.PRG and run your entire application within the IDE, rather than compiling and running an executable with MAIN as the entry point. You can also DO FORM FormName to run a form standalone.

You can't do that in Visual Basic; you can only execute an executable. But it's easy. Press F5 and the designated Startup project runs. To test a form, you'll need to add a couple of lines of code in your main form menu to instantiate the form class as an object and call its Show() method, like this:

Dim Frm as new CustForm
Frm.Show()

That's two lines of code instead of one, but it's almost as easy as DO FORM CustForm.

The Class Browser and Visual Basic .NET's Class View

Both FoxPro and Visual Basic .NET have a class browser. If you need to become familiar with a class in a FoxPro VCX, you can use the Class Browser to examine its structure (although you can't edit the method code in the Class Browser). Also, you'll need to use the Open dialog to locate and open a class. Visual Basic .NET's Class view automatically shows all of the classes in the current solution in a tree view, and permit code editing. To jump to the code for a method, double click on its name.

The Object Browser

For your own classes, you use the Class Browser; for all other objects, there's the Object Browser. FoxPro's Object Browser lets you open a type library from any of the registered classes on your computer and browse through the class hierarchy, whereas Visual Basic's Object Browser uses the currently open solution to navigate (although you can browse objects that aren't in the project as well.). It lists the objects of whichever project you click on in the Solution Explorer. Visual Basic .NET's Object Browser not only knows what's in your project, but remembers selected components from session to session. FoxPro's Object Browser is not connected to the current project.

The Properties Window

FoxPro's Properties windowopens with Alt+W, P (Windows, Properties), whereas in Visual Studio, F4 brings up the Visual Studio Properties window. You can view properties on .NET's built-in controls, or properties on your own classes.

In both Visual FoxPro and Visual Basic .NET, properties can be listed either alphabetically or grouped by categories. These categories are fixed in FoxPro, whereas in Visual Basic .NET you can add your own by preceding property procedures with the <Category("XYZ")> attribute (provided that you've included Imports System.ComponentModel at the top of your class). Precede your custom categories with an asterisk (for example, *XYZ) and they'll appear right at the top of the Properties window.

The most important difference between the two languages in this regard is that in FoxPro, properties are like public variables of classes and have immediate visibility in the Class Designer as well as in classes that inherit from the class. In Visual Basic you need to write a property procedure (as follows) consisting of about eight lines of code for each and every property that you want to see in the Properties window.

To create a property procedure in a class, you type

Public Property (name)

As soon as you press Enter, the IDE adds six lines of code.

You're expected to do three things:

  1. Declare a private variable in the Declarations section of the class; the name should be an underscore followed by the name you used for the property procedure. A good time to do this would be just before creating the property procedure.

  2. Add a Return _PropName statement in the Getter.

  3. Add a _PropName = Value statement in the Setter.

Furthermore, unlike FoxPro, in Visual Basic .NET you only see your properties in classes that inherit from your class—not in the class itself. So if you define property procedures in a class, you won't see them in the Properties window. As soon as you inherit from that class, the inherited object's Properties window will expose the property.

Getters and Setters Versus ASSIGN and ACCESS Methods

Property procedures do more than simply expose the property in the Properties window; they also provide a trappable event at the moment of assigning or retrieving a property value. That's what the Get and Set methods (often called Getters and Setters) in a Visual Basic .NET property procedure do. (These will be discussed in more detail in the discussion of the Properties window because they're very important, very different, and you'll use them every day.)

FoxPro properties by default do nothing when you assign them a value, unless you indicate that you want to include Assign and/or Access methods when you add a property in the Class Designer. If you do, FoxPro adds procedures (methods) to the class with the name of the property followed by either _Assign or _Access. You can type code into these methods, and when you either assign a value to the property or request its value, the corresponding property procedure code will execute.

If you're writing classes in a .prg file and you want Assign and Access methods for your properties, simply use the name of the property with _Assign or _Access added to the end of the property name as the procedure name, and it will indeed fire when the property is assigned a value or when the value is read, respectively.

I've only had occasion to use Assign and Access methods twice in my professional career, so I don't think they're very important. Requiring eight lines of code for every single property in a class on the off chance that you might want to use Assign and Access methods seems to me to be a case of the tail wagging the dog. However, you absolutely have to use property procedures if you want to use properties as you do in FoxPro.

Shortcut Keys

The IDE is where you spend your day, so it's important to know where everything is. By this point in your career, you probably don't even need to look for things. When you want to open up the Properties window, your hands type Ctrl+W, P without any conscious effort on your part.

You need to develop the equivalent reflexes for Visual Basic .NET. The task is somewhat complicated by the fact that FoxPro is a command window-based IDE, so that all of the features that are accessible from the menu, or from toolbars, are generally accessible as well by typing a command string.

In both languages, the menus are context-sensitive. For example, the Project menu pad is only visible after you've opened a project. For example, the Form Designer shown in Figure 1.5 must be open in order to right-click on the designer surface and expose the context menu that lets you activate the Properties window.

The Form Designer.

Figure 1.5. The Form Designer.

Table 1.1 summarizes the menu shortcut or the command that activates the main functionalities of the respective Integrated Development Environments. (The entries in the table assume that the target context is available—for example, the hotkey for a Form Designer feature is only available if the Form Designer is open.)

Table 1.1. Keyboard Shortcuts in Visual Basic .NET and Visual FoxPro

Feature

Visual Basic .NET

Visual FoxPro

Properties sheet

F4

Alt+W,P

Solution Explorer

Ctrl+Alt+L

Alt+W,Project Mgr, or Alt+F, New Project to create

Server Explorer

Ctrl+Alt+S

Like the Database Container or Data Environment

Class view

Ctrl+Shift+C

Alt+T, C

Object Browser

Ctrl+Alt+J

Alt+T, J

Compile and Run

F5

Ctrl+D, then select

The Tools, Options Dialog

FoxPro can be managed from the Tools, Options dialog. The Tools, Options page contains a page frame with 14 pages, containing settings that have huge implications for the way the IDE, and your applications, work. Some of these pages have equivalents in Visual Basic .NET, but a few of them don't. For example, the Data and the Remote Data pages, which handle access to DBFs and to SQL using remote views respectively, are irrelevant in Visual Basic .NET; the Data page controls access to DBFs in FoxPro, and in Visual Basic .NET DBFs are just another OLEDB or ODBC data source. Also, the purpose of Remote views in FoxPro is to provide access to SQL Server table data in a way that's very similar to the way we use DBFs.

The Options page I wish Visual Basic .NET had is the Field Mapping page, which controls which of your classes are used to automatically populate a form. There is no equivalent feature in Visual Basic .NET. The Data Form Wizard does some things that are interesting, but I use inheritance for forms, and the wizard doesn't. It's the greatest shortcoming of the form design environment.

Visual Basic .NET also has a Tools, Options menu selection. The resulting dialog uses a tree view for navigation instead of a page frame. In addition, some of the settings found in FoxPro's Tools, Options dialog are to be found in the Solution and Project Properties sheets, respectively. Right-click on either the solution or a project and select Properties from the resulting context menu.

Table 1.2 shows the location of equivalents to the FoxPro Tools, Options Page elements in the .NET IDE.

Table 1.2. Equivalents to the FoxPro Tools, Options Page Elements in the .NET IDE

FoxPro

Visual Basic .NET

View

Tools, Options, Environment, General.

General

No equivalent (FoxPro-specific).

Data

Connection- and DataAdapter-specific.

Remote Data

Connection- and DataAdapter-specific.

File Locations

N/A; Visual Studio controls file locations.

Forms

Tools, Options, Windows Form Designer; for templates, use Add Inherited Form from Solution Explorer context menu.

Projects

Source Control, General tab for SourceSafe settings

Controls

Add References in the Solution Explorer exposes the Add Reference dialog; the COM page lists registered COM objects, and has a BROWSE button for unregistered COM objects.

Regional

Environment, International Settings controls most; use the locale property of the dataset to set CultureInfo; FormatCurrency function; System.Globalization Namespace components can be used to override the System and User language and currency specifications.

Debug

Environment, Fonts and Colors sets colors in various windows. VS .NET debug settings involve features that Visual FoxPro doesn't have.

Editor

Text Editor, Basic and Text Editor, General tabs; The Visual FoxPro equivalent of some Visual Basic Text editor settings are found in the Edit, Properties dialog available while editing a text file. The Visual FoxPro Tools, Options IDE tab is used to set many of the VS Text Editor tab settings.

Field Mapping

FoxPro specific; I wish there were a Visual Basic equivalent.

IDE

Text Editor, Basic; Environment, Fonts and Colors;

Reports

In a Crystal Report, right click on the design surface and select Designer, Default Settings

The Task Pane Manager

The FoxPro Task Pane is a command center that brings together resources for learning FoxPro, samples of code, and links to related Web sites. This is similar to the Online Resources panel of the .NET Start Page. Some elements of the Task Pane are to be found in the Tools, Options dialog, under Environment, General (for example, what to show when the IDE starts up). But the FoxPro Task Pane manager doesn't have an exact duplicate in the Visual Basic .NET IDE.

The Output Window and Your Application Menu

FoxPro's output goes by default to _Screen, the background screen for FoxPro and by default any FoxPro application that you build. You can use a combination of a form's DeskTop, MDIForm, and ShowWindow properties to change that, but many developers use FoxPro's _Screen as their background and as the container for their menu (which replaces FoxPro's _MSYSMENU), as well as for all of the forms in their application. The simple application we'll write in Chapter 2 does just that.

No similar default background exists in Visual Basic .NET. Your first form is the container for all other forms. You manually drop a MainMenu control on the form and use Visual Basic .NET's delicious menu designer, which I would have loved to have in FoxPro.

When an IDE action such as a compile produces output, it opens the Output window by default at the bottom of the screen. If you have compile errors, they'll appear here. You can double-click on an error to be taken directly to the offending code.

Customizing the IDE

In FoxPro, I usually put the form that I'm working on at the upper-left corner of the screen; the Project Manager, Properties window, and the command window over on the right. I reduce the screen resolution to the smallest size that doesn't give me a headache, then expand the Properties sheet to fill almost all of the vertical size of the screen; the Project Manager and the command window split what's left at the lower-right corner of the screen. The Toolbox goes somewhere on top of the properties window whenever I need it.

In Visual Basic .NET I have a more convenient option: I open the Properties window using F4, then open the Solution Explorer with Ctrl+Alt+L and drop it on top of the Properties window. It becomes a member of the same container. I do the same thing with the Toolbox. As I drop these elements, tabs with their names appear at the bottom of the container. All three can thus share in the same space, each coming to the foreground when its tab is clicked.

FoxPro IDE components can be right-clicked and characterized as dockable, which controls whether or not they stay put and behave as expected. I generally make them dockable. Visual Basic .NET components can be configured with more specificity, although I have a 21-inch monitor and it's still not big enough. If the truth be told, though, it's not big enough for FoxPro application development either.

Getting used to the IDE is important if you want to enjoy application development. And it's a never-ending task. I even learned a bunch of new IDE tweaks from Cathy Pountney at the DevTeach Conference in Montreal, and I've been doing this for 15 years. Productivity is the name of the game, and the fine folks in Redmond have given us some great tools. So take the time to learn where things are in the IDE and what they can do for you. It's a small investment that will return a big dividend.

The Visual Basic .NET Solution Explorer

The distinction between FoxPro's Project Manager and Visual Basic .NET's Solution Explorer was touched on earlier, but there are additional important differences.

FoxPro groups every project into five classes of elements, as indicated by the tabs at the head of the Project Manager:

  • Data lists excluded tables as well as files that define queries and views.

  • Documents contains screens (forms), reports, and labels.

  • Classes lists all Visual Class Libraries contained in your project.

  • Code includes PRG files for standalone programs, procedures, or class libraries in code.

  • Other holds menus, graphics, and miscellaneous text files.

Items can be listed in the project file but are marked as excluded, so that they are not part of the executable and can be modified. For example, by default DBFs are marked as excluded in the Project Manager. If a DBF (say a table of States) is included in the executable, it can be read but can't be modified. This is sometimes useful, but rarely. Items that are in the current directory but haven't been included in the project don't appear in the Project Manager files list.

TIP

I once saw a clever use of a DBF embedded in the executable. In a multiuser FoxPro application running on a Local Area Network (LAN), the programmer added a “licenses” table containing 10 records. Then, whenever a user logged on, he used LOCATE FOR NOT RLOCK(). If he found a record, he issued an RLOCK(). As users logged out, the records were unlocked. After 10 users, no more records could be found.

Finally, if you create an empty project and add your MAIN program, the BUILD PROJECT command will start by reading all components referenced in MAIN and then follow the rabbit trail until the project has been rebuilt.

There is no corresponding automatic discovery process in Visual Basic .NET. You have to add files to a project manually, one at a time. Click on the Show All Files button to see files that are located in the project directory. The icon beside each file's name indicates whether it's included in the project or not.

In Visual Basic .NET, when you create a project, a solution of the same name is also created, with a hierarchy consisting of a bin and a debug directory. In the Solution Explorer there's a Show All Files button; when you click it, all of the files in the application directory are displayed. You can right-click on them and include them in the build. Once the files are included, the Properties window gives you several options for the build action—none, content, compile, or embedded resource.

All of the code files in a project must be written in the same language. However, you can build a solution with one Visual Basic (.vb) project and one C# (.cs) project, and they'll make beautiful music together. This is, of course, because both have been translated to Intermediate Language by the time the solution is built, and neither one cares which boat the other's ancestors arrived on.

Intermediate Language

Intermediate language is the result of the compilation project in Visual Studio .NET, regardless of which language you use to write the code. It looks a bit like assembler. When you first run an executable on a workstation, the internal IL is compiled to machine codeas pieces of your application are invoked. This happens each time the application is run, and a noticeable delay occurs during compilation.

The FoxPro IDE does something similar. If you DO a PRG inside FoxPro and if an .fxp file exists with an earlier time stamp, FoxPro automatically compiles the PRG to produce an FXP. If the FXP is more recent, it's run, and the PRG is ignored. Of course, we don't send PRGs to our customers, and the FoxPro runtime can't compile them; but the analogy is useful.

In general, project elements are either code or data. If they contain code, their extension indicates which language you chose for your project (currently .vb, .cs, or .js for Visual Basic, C#, and J#). There are also a host of extensions that have been created for various components of a Visual Studio solution or project component. Generally, these are tables in XML format. In FoxPro we use DBFs with other extensions; the notion is precisely the same.

Some files have companion files under certain circumstances. For example, forms have a .resx (resource) file, which contains information about the form. Open it up, and you'll be able to see the contents either as XML or in table form.

Similarly, .xsd (schema) files have an associated .vb or .cs file if they represent a typed dataset. Typed datasets allow FoxPro developers to do something near and dear to our hearts—use the Table.Field syntax in Visual Basic .NET that we have always had in FoxPro. To do so in Visual Basic .NET, there must be a class with one property procedure for each field name in the table in question. These classes are automatically generated by the Generate Dataset phase of the DataAdapter Wizard dialog.

If you generate a typed dataset (an XML schema file with an .xsd extension), the .xsd file will have a .vb file behind it with one property procedure for each column in the table. You have to click the Show All Files button to see it. Typed datasets are the only way you'll be able to type employee.lastname in Visual Basic .NET. You don't even want to know what you have to type to refer to a field without them. I imagine that FoxPro does something similar internally, although not knowing about it has never bothered me.

TIP

You can generate typed datasets directly from the command line if you have an XML schema. You can get the XML Schema from SQL Server using the FOR XML AUTO&Root=Root option, or from any other source. Open a Visual Studio command window, use the command XSD (filename.xml) to create an XSD file, and then use XSD (filename.xsd) –c /l:VB to generate a Visual Basic typed dataset class. Or you can simply right-click on a data adapter at the bottom of a form and select Generate Dataset.

Other files in the project use XML files to store tables of information concerning how to manage and build the project. An XML file in its simplest form describes rows and columns, so it's exactly like a DBF without the ability to have an index and thus be searched quickly. You can think of them as dumbed-down DBFs. Generally, if you double-click on a file with a .vbproj, .sln, .xsd, or other extension, you'll get XML; and at the lower-left corner of the screen you'll see a Data tab. Click on it to see a tabular representation of the XML.

DLLs

A solution can consist of multiple projects. Typically, one of them is an executable, and the rest are class libraries, which are compiled into Dynamic Link Libraries (DLLs).

There is a fundamental belief in the non-FoxPro world that components should be bullet-proofed, then shared as DLLs and patched together. What would be a class library in FoxPro is generally built as a separate DLL in Visual Basic, and shared with other developers. FoxPro developers generally view their class libraries as an integral part of the forms that use them, and continue to modify forms and the class libraries that use them simultaneously. Because sharing DLLs is a normal practice in the non-FoxPro developer communities, they're often looked at as a final product, and subjected to quality assurance procedures of some sort.

That's not the world of the rapid prototyper, the world we as database developers live in. My clients' requirements shift daily, sometimes hourly. I simply can't send out a memo requesting changes in a DLL, and then wait a few weeks for them to code, test, and ship back the result for me to test.

In FoxPro, although you can do this, I've never felt the need to build components and stitch them together to build the executable. It just never occurred to me—and still doesn't, come to think of it. I don't see changing our thinking to accept black-box DLLs as a normal component of application development in FoxPro. But in Visual Basic .NET, classes compile to DLLs—there is no equivalent to FoxPro's VCX file.

Other Characteristics of a Visual FoxPro Project

FoxPro uses a current directory concept. At any time you can type CD in the command window to see “where you are”—that is, what the current directory is. You can also type ? SET("DEFAULT") or ? CURDIR() to display the current directory in the output window (_Screen). You can use CD (path) or SET DEFAULT TO (path) to change it. Before creating a project, you use CD and MD commands to create and navigate to the folder the project will occupy.

The mere fact that files are located in this directory doesn't make them part of the project, and in fact a project isn't strictly necessary. FoxPro is unique in that you can run programs without compiling them. If you type DO MAIN in the command window, the program will run exactly as if you had compiled it and run the executable. And even if you do build an executable, if FoxPro can't find a program, screen, menu, or class file in the executable, it searches first in the current directory, and then along any paths specified with a SET PATH TO command (for example, SET PATH TO Classes;Menus;Programs).

Using Tables to Store FoxPro Project Components

If you type CREATE Table1, a dialog opens to allow you to build a table schema and store it in a file with a .dbf extension (and optionally an additional file with the same name and the extension .fpt if your table schema contains variable-length fields called “memo fields”). Any DBF that has variable-length fields has an associated memo file with the extension "FPT". There are other extensions that are used for other types of files. I'll introduce each of them.

Projects are stored in one of these file pairs with the extensions .pjx and .pjt respectively. The "PJX" is the "DBF" file, and the "PJT" is the "FPT" file. It shouldn't surprise you that FoxPro uses a table to store the project details. Screens, classes, and reports use the same scheme of pairs of files with extensions that suggest their usage.

Visual Studio uses HTML files for similar purposes. For example, each page in an ASP.NET application is stored in an HTML file with the extension "aspx", which is essentially a table containing the descriptive elements of the page. The code is stored separately in a “code-behind” file. But if you change the page, you don't have to recompile, because it's just data—exactly as if it were coming from a DBF.

By default, any files you create for a project will go in the current directory. When you create a project using the MODIFY PROJECT Name command, the .pjx and .pjt files are created. Subsequently, as you add other files like MAIN.PRG, they are put by default into the current directory.

NOTE

Long ago, Windows performed poorly when more than 127 files were located in any single folder. As a result, it was customary to distribute the elements of Visual FoxPro projects into Screens, Reports, Data, and Other folders. That's no longer necessary.

If you build a menu, an MNX/MNT file pair is created. If it's included in a project, in the project record for the MNX file an MPR file with the same filename and the extension .mpr is designated as the outfile. The compiled code for the resulting compiled MPX file is stored in the Object memo field.

If you create a form (screen), a pair of files with .scx and .sct extensions are added. Again, if there's no project, FoxPro will compile and run the SCX file using the DO FORM FormName command. The SCX/SCT files don't have to be part of a project.

If you create a class library, a VCX/VCT pair is created. It has exactly the same column layout as an SCX/SCT file pair. (Think about that for a while. This is the reason that you can select Save As Class in the Form Designer and then instantiate a form object from the saved form class using frm = CREATEOBJECT("formclassname") followed by frm.Show()—almost exactly the same syntax used in Visual Basic .NET.)

TIP

If you want to ship someone your screens or class libraries without giving them source code, you can compile both VCX and SCX files, and erase the code in the Methods memo field. The compiled code in the ObjCode field is all the user's FoxPro compiler needs to build the executable.

If you use a FoxPro project file, when you build the application, any included PRG files are compiled, and the generated FXP code is added into the corresponding MAIN.PRG record in the object memo field. If you called an MPR (menu) program, it is compiled into an MPX file and the MPX is included in the executable. Any SCX (screen) and VCX (class) files are compiled and added to the executable.

After the source files have been compiled, you don't need to ship any of them to your users; the executable contains everything. However, if there's no project, FoxPro will look for and execute the MPX file if it exists; if not, it will look for and compile the MPR file, and then run the resulting MPX file. It won't automatically build an MPR file from an MNX file, though; you have to do that in the Menu Builder, or let the Project Manager do it for you at build time.

Similarly, any referenced screen files are called as needed. Class library files, on the other hand, whether in VCX or as DEFINE CLASS statements located in PRG files, must be referred to in the code with SET CLASSLIB TO VCXFileName ADDITIVE and SET PROCEDURE TO PrgFileName ADDITIVE in order for FoxPro to know how to use their contents.

Reports are stored in FRX/FRT pairs. It's common practice to exclude report files from the project and then simply build a list of available FRX files for the user to select. In this way, customized reporting is easy to support.

File Types Used in Visual Basic Projects

Unlike Visual FoxPro, Visual Basic puts your project in the directory you name when you create the project. By default the project directories you create are created under a default projects folder, which you can and should change when you start using Visual Studio.

For each project, .NET creates a project folder using the project name you supply, within your default projects folder. It also automatically creates a solution (with the extension .sln) with the same name as the project. A solution is a grouping of related items such as a form and the routine that returns data to it, or a Web page and a Web service. There can be any number of projects in a solution, but only one of them is the startup project that runs if you press F5.

FoxPro doesn't have this additional layer. I wish it did. I have to write down which Web service goes with which applications on little sticky notes. A little bookkeeping help would be nice.

Whereas FoxPro uses tables to store projects, screens, menus, and classes, Visual Basic uses either .vb files or XML text files to store solution details.

Project Information

If you're fond of customizing your projects in FoxPro using Ctrl+J, you'll be interested to know that you can right-click on the solution and select Properties to set a number of characteristics of the solution. Similarly, you can right-click on a project and select Properties from the resulting context menu to set project properties. However, if you want to set the program's name, the company's name, versioning information and so forth, double-click on the AssemblyInfo.vb file in the project and fill in the Assembly attributes you'll find there.

Command Syntax

When you first look at Visual Basic code, there are so many unrecognizable constructs that it's often difficult to tell what you're looking at. But they usually have equivalents in FoxPro.

For that reason, you should take a few minutes to go over Table 1.3 and familiarize yourself with some of the principal similarities and differences in command syntax.

Table 1.3. Command Syntax Differences

Item

Visual Basic

Visual FoxPro

Continuation character

_ (underscore)

; (semicolon)

Comments

' (single quote)

* (asterisk) at the beginning of a line, && (two ampersands) anywhere else

Data type indicators

Deprecated in Visual Basic

{} (curly braces) around dates

Boolean literals

True|False

.T.|.F. or T|F

String delimiters

""

"", '', []

Null

Nothing (the word)

.Null. or Null

Character literals

"abc"c

"abc"

Attributes of functions

<attr("value")>

No equivalent

Option Explicit On

Variables can't be used unless previously declared

No equivalent

Option Strict On

Narrowing conversions not allowed; default is Off

No equivalent

Option Compare Text

Case insensitivity

Must be explicit in FoxPro, for example, IF UPPER(a)=UPPER(b)...

Refer to class libraries

Imports (top of file)

SET CLASSLIB TO Name ADDITIVE or SET PROCEDURE TO Name ADDITIVE

Refer to base class

Inherits

ClassLibrary name shown in the Properties sheet, or declared with “as classname of sourcefilename” in code.

Namespace

Prefixes for classes

No equivalent

Array dimensioning

Dim X(2) is 3 long; X(0) is first one; all elements same type

Dim X(2) is 2 long; X(1) is first one; elements can be of different types

Array initialization

Can assign values, for example, Dim x(2) as new integer = {1,4}

DIMENSION X(2) or DECLARE X(2), then assign individual cells' values one line at a time

Parameters

Specified in Function or Sub statement in parentheses, with type declaration

Before Visual FoxPro7, specified in PARAMETERS or LPARAMETERS statement; beginning with Visual FoxPro7, same style as in Visual Basic .NET

Properties

Create property procedures with GET and SET methods, using a private variable as the get/set target; property name appears in Properties sheet of derived classes.

Enter the property name in the Class Designer, or assign a value to a variable name at the top of a class definition in code; the property appears immediately in the Properties sheet of the Class Designer

Default prefix block

WITH/ENDWITH

WITH/ENDWITH

Events

Used to call as yet unnamed functions in other modules

Don't have 'em, don't need 'em

Delegates

Var = AddressOf FunctionName; used to pass functions as parameters

Don't have 'em, don't need 'em

Determine variable type

TypeOf Var IS Typename or GetType()

TYPE() or VARTYPE()

Argument lists

Either positional or (Name:=Value)

Positional only

Variable or object creation

Dim VarName as Class; VarName = New Class; VarName = Value, or Dim VarName as New Class = Value

SET CLASSLIB TO XYZ; VarName = CREATEOBJECT("Class"), or VarName = NEWOBJECT("Class", "Lib.VCX")

Object creation from a COM object

CreateObject("Word.Application")

CreateObject("Word.Application") or GetObject("Word.Application")

Cast operators

CTYPE()

Individual cast functions like DTOC() or STR()

Reference to container

ME

THIS, THISFORM

If statement syntax

IF Expr Then; End If

IF Expr; ENDIF

Case statements

Select Case VarName; Case "abc"; Case else; End Select

DO CASE; CASE Name="abc"; OTHERWISE; ENDCASE

This is not meant to be an exhaustive list; we'll save that for Appendix A at the end of the book, where you can find a concordance of Visual FoxPro and Visual Basic equivalents.

Flow of Control

Branching, looping, and conditional execution are the fundamental building blocks of programming. Almost every one of the mechanisms listed in Table 1.4 is slightly syntactically different in the two languages, although clearly, you can accomplish the same things in either language.

Table 1.4. Flow of Control

Action

Visual Basic

Visual FoxPro

Call a procedure

Name() or CALL Name

Prog() or DO Prog

Exit a block

Exit Sub, Exit While, Exit <block>

Exit

End the program

End

Quit

Looping

DO/Loop, For/Next, For Each/Next, While/End While

DO/ENDDO, FOR [EACH]/ENDFOR|NEXT, SCAN/ENDSCAN

Conditional branching

Choose, IF/Then/Else, Select Case, Switch

IF/ELSE/ENDIF, DO CASE

Procedure definitions

Function, Sub, Property procedure

Function, Procedure

Immediate If

IIF()

IIF()

Following the table, I've made a few notes on how each construct works. IIF and SWITCH are included here as well because they replace what used to be four or five lines of IF/ENDIF or CASE/ENDCASE code.

Calling a Function, Procedure, or Method

To call a procedure called ProcName in FoxPro, you either type DO ProcName or simply ProcName(). The parentheses are required. The second variant is the way you call a subroutine or function in Visual Basic .NET.

If you instantiate an object named MyObj that has a method called ProcName, you call it using MyObj.ProcName. If it has parameters, you can add parentheses after the name and put the parameters within them. Otherwise, the parentheses are not required.

In Visual Basic .NET the syntax is exactly the same, except that DO ProcName() is not supported.

String handling

The well-known Canadian developer Steven Black has pointed out in several articles that string handling in FoxPro is thousands of times faster than it is in Visual Basic 6. In Visual Basic .NET, the String module contains dozens of methods that are more efficient. Type the word “String” and a period in a code window, and Intellisense will show you what's available.

In addition to the string functions found in FoxPro, String.Format can be used like TextMerge:

FoxPro:
 Name = "Fred" 
 Age = 32
 SET TEXTMERGE ON NOSHOW
 TEXT TO X
 <<Name>> is <<TRANSFORM(age)>> years old.
 ENDTEXT
 ? X

VB:
 Dim Name As String = "Fred"
 Dim Age As Integer = 32
 Label1.Text = String.Format("{0} is {1} years old", Name, Age)

Ending Your Program

In FoxPro, RETURN takes you out of the current procedure, THISFORM.Release closes the current form, and QUIT ends the program. RETURN from the MAIN program also ends the application, unless you're running MAIN from inside the FoxPro IDE. So a compiled application can use RETURN to end the program.

In Visual Basic .NET, CLOSE() closes a form; End ends the program. You can't end a program with Return in Visual Basic.

Do...ENDDO and Other Loops in Visual FoxPro

In FoxPro, DO...ENDDO is used to do something repetitively until a condition is satisfied. In the following FoxPro example, we test a printer object, and if its ErrorOccurred property is set to .T., or if its PrintedOk property is set to True, we exit the loop; otherwise, we keep trying.

DO WHILE .T.
   oPrinter.Test
   IF oPrinter.ErrorOccurred or oPrinter.PrintedOk
      EXIT
   ENDIF
ENDDO

DO WHILE is not as frequently used as its cousin FOR ... ENDFOR or FOR...NEXT. The syntax is either

FOR I = 1 TO N
   - do something -
ENDFOR | NEXT

or

FOR EACH obj IN Collection
   - do something -
ENDFOR | NEXT

Finally, in FoxPro, where we have tables and cursors, there is a special command pair to traverse the table or cursor from top to bottom:

SCAN
   - do something -
ENDSCAN

The Visual Basic equivalent is to use a FOR EACH row IN table loop:

Dim dr as datarow
For each dr in dataset.tables(0)
... 
End For

Loops in Visual Basic .NET

In Visual Basic .NET, the equivalent of FOR...NEXT is very similar:

FOR I = 1 TO N
   - do something -
NEXT I

However, DO...LOOP has four variants. The first two check the expression at the beginning of the loop, whereas the last two check the expression at the end of the first loop:

  • Do While:

    Do While condition
       statements
    Loop
    
  • Do Until:

    Do Until condition
       statements
    Loop
    
  • Do...Loop While:

    Do
       statements
    Loop While condition
    
  • Do...Loop Until:

    Do
       statements
    Loop Until condition
    

If that isn't enough, there's a While...Wend construct that's similar to DO...LOOP, except that it doesn't support Exit:

While condition
   statements
Wend

Conditional Execution in Visual FoxPro

In Visual FoxPro you can use either of these two mechanisms to execute only a certain block of code:

IF Expr
   - Execute if true -
ELSE
   - Execute if not true -
ENDIF

or

DO CASE
   CASE Expr1
   - Execute if Expr1 is true -
   CASE Expr2
   - Execute if Expr2 is true -
ENDCASE

It's a peculiarity of FoxPro's DO CASE construct that the expressions evaluated in the CASE statements don't have to refer to the same variable. It's very flexible.

Conditional Execution in Visual Basic .NET

As usual, Visual Basic .NET has more constructs. I don't know if that's good or bad: Sometimes although we're offered several ways of doing things, one of them is really the preferred method.

Choose returns the numbered entry from a list of choices, for example:

Return Choose(idx, "Lions", "Tigers", "Bears")

Choose is a primitive construct from years ago, and will seldom if ever be the best choice. It has been deprecated.

If Expr Then...Else...End If

If the evaluated expression is true, the first statement is executed; if Else is present and a second statement follows it, the second statement is executed, for example:

If Salary > 1000000.00 Then
   Bonus =  1000000.00
 Else
   Bonus = 25.00
End If

Note that a single line implementation is permitted. If it improves readability, use it. When I'm writing code for publication I use the single-line implementation. You can also include several statements separated by colons, but it gets hard to read pretty fast.

If Salary > 1000000 Then Bonus = 1000000
If Salary > 1000000 Then Bonus = 1000000 Else Bonus = 25
If Salary > 10000 Then Salary = Salary * 2 : Bonus = 10000 
Else Salary = Salary / 2: Bonus = 25

Select Case

This command evaluates a single variable's values, for example:

Select case StateName
       Case "CA"
            Governor = "Arnold"
       Case Else
            Governor = "Someone else"
End Select

Switch(VisualBasic Namespace)

This statement evaluates a series of pairs of expressions and their corresponding return values and returns the first value whose expression evaluates to True, for example:

Return Microsoft.VisualBasic.Switch( _
 CityName = "London", "English", _
 CityName = "Rome", "Italian", _
 CityName = "Paris", "French")

Note that the Switch function name is not unique in the .NET namespaces and so must be qualified. This version is from the Visual Basic compatibility namespace provided for people who are inordinately fond of Visual Basic6. I'd be sparing about using this function; it looks a little dated.

In summary, both languages offer very similar capabilities in the flow of control area. I like FoxPro's DO CASE statement, which allows all sorts of expressions, not just different values for the named variable or expression. But the differences are small.

Variables, Enums, Arrays, Collections, and Fields

Variable declaration in FoxPro is very straightforward because there are only a few options. But in Visual Basic .NET it's more complicated. Public, Private, Protected, Local, Hidden, Overrides, Overrideable, Shadow, and Friend are declarations that are used in Visual Basic .NET to define scope and accessibility not only of variables, but also of methods, functions, and procedures. Even in FoxPro, most developers have not fully considered their usage. We only have three options (Public, Protected, and Hidden) and we just make everything Public. In Visual Basic .NET, they are considerably more intimidating. But as usual, there's a simple answer.

Data Types

Data type declarations in FoxPro take two forms: fields in tables and memory variable creation. Scope declarations can be used to declare data types in FoxPro, for example:

LOCAL Salary as Double

In addition, function and procedure declarations can describe parameter types and return value types. However, it's an illusion; FoxPro data is stored internally as variants, and typing is not enforced. For example, the following Visual FoxPro function will cheerfully concatenate a pair of strings passed to it as parameters:

FUNCTION Test (One as Integer, two as String) as String
RETURN TRANSFORM( one, "@L ###") + Two

And the following code runs fine in FoxPro:

LOCAL MyVar AS Integer
MyVar = "Oops!"

In FoxPro, although there are 14 possible choices for a field type in a table, for in-memory data representation there are only six types: Character, Currency, Numeric, Date, DateTime, and Logical. Finer data resolution only happens when you store the data to a table. Visual Basic's insistence on converting to many more data types doesn't matter in FoxPro. The TYPE and VARTYPE functions report back objects as type “O”.

Table 1.5 compares the data types available in Visual Basic to the FoxPro data types and to FoxPro DBF Field types.

Table 1.5. Data Types in Visual Basic .NET and Visual FoxPro

Data Type

Visual Basic

Visual FoxPro memvar

Visual FoxPro Field

Logical

Boolean

Logical/Boolean

Logical(L)

Byte

Byte

Char

Char(C)

Money

-none-

Currency

Currency(Y)

Date

-none-

Date

Date(D)

DateTime

Date

DateTime

DateTime(T)

Decimal

Decimal

Numeric

Numeric(N)

Double

Double

Numeric

Double (B)

Integer

Integer

Integer

Integer(I)

Long

Long

Floating

Numeric(F)

Object

Object

Object,Variant

General(OLE objects)

Short

Short

Integer

Integer(I)

Single

Single

Numeric

Numeric(N)

String

Text

Memo

Memo(M)

Binary String

Binary Text

Memo Binary

Memo Binary(M)

Variable Scope Declaration in Visual FoxPro

In Visual FoxPro, some confusion results from the fact that PUBLIC, PRIVATE, and LOCAL refer to variable scope, whereas PROTECTED and HIDDEN refer to property and method visibility, and PROTECTED will always be part of a FUNCTION or PROCEDURE declaration in a class definition; no confusion there. But PUBLIC can refer to either a variable or to a property or method of a class.

PUBLIC, PRIVATE, and LOCAL are declarations that precede variables and determine their scope. If a variable is declared in a program, by default it's visible in that routine and in any routine below it. For example, if MAIN contains a line like Name=[Les] and uses DO FORM CUSTOMERS to show a form, within the form you can type THISFORM.Caption = [Les] in the form's LOAD event and the form's caption will appear as “Les”. If you move the line Name=[Les] to the CUSTOMERS form and change the Load code to read

Name=[Les]
THISFORM.Caption = Name

the form's caption will change, but as soon as the form closes, the variable Name disappears. Technically, it is said to go out of scope.

LOCAL means that a variable exists only within the procedure in which it's named, whereas PRIVATE means that it exists in the procedure in which it's created as well as in any procedure called by that procedure.

For example, variables can be scoped before a value is assigned using a PUBLIC, PRIVATE, or LOCAL declaration:

PUBLIC Address        && will be available after exiting this procedure
Address = FileToStr ( ClientAddressTextFile )

LOCAL I
FOR I = 1 TO 2        && does not conflict with any global instance of I
    ? "Hi"
ENDFOR

Within a program, we declare variables to store values. The declaration of and use of variables in Visual Basic looks confusing to FoxPro developers because it requires an extra step that's optional in FoxPro. And the reason for it is that in Visual Basic, all variables are actually objects.

In FoxPro, you declare a variable by assigning it a value:

X = 3

Similarly, you declare a class property by assigning it a value:

DEFINE CLASS Person AS Custom
FirstName = []
LastName = []
ENDDEFINE

FoxPro generally declares all variables as variants, which means you can assign any value to them. In fact, you can change their data type in the middle of the program—usually inadvertently and with confusing results. It's not a good idea, but it's possible. However, you can declare types using the PUBLIC, PRIVATE, and LOCAL declarations:

LOCAL LastName AS String
PUBLIC I AS Integer

You can also declare objects based on classes in your class libraries, or from the FoxPro base classes:

LOCAL oDoc as Word.Document
oDoc = CREATEOBJECT ( "Word.Document")

However, all variables declared with the LOCAL, PUBLIC, or PRIVATE declarations have an initial value of .F., and can be changed to any other data type any time you want. So why bother?

Type declarations in FoxPro are used to permit specification of parameters for creating Dynamic Link Libraries (DLLs), which require a type library (TLB) entry that specifies parameter data types in a universally accepted format. Because FoxPro didn't have anything but variants, type declarations were added to the language to permit building Web services, which must specify their parameters and output types. But internally, they're pretty much ignored except for supporting IntelliSense.

Similarly, classes can have PRIVATE and PUBLIC properties and methods. IntelliSense reveals the public ones, whereas private ones are not visible.

Essentially, PUBLIC variables are accessible any time after being declared, as if they had been initialized in the MAIN program. PRIVATE variables are declared in the procedure in which they are created and in procedures called by the creating procedure. LOCAL variables are available only within the creating procedure, so they're perfect for loop counters. In general, the idea is to avoid inadvertently overwriting a variable. That's the story on variable declarations in FoxPro. Pretty simple, isn't it?

Variable Declarations in Visual Basic .NET

Visual Basic .NET is rendered more confusing because there are scope declarations for functions and procedures as well as for variables. We'll look at these in great detail shortly.

In Visual Basic, variables are objects. In fact, you can't use variables unless you first declare them with a statement that specifies which object they're derived from. Best practices dictate that you use Option Strict On and Option Implicit Off, so that the compiler will stop you from trying to assign an integer to a string variable. This can catch a certain type of programmer error that can be hard to diagnose.

This is the reason that many functions in FoxPro are methods of objects in Visual Basic. For example, in Visual FoxPro you use the UPPER(StringVar) function to convert a string to uppercase. In Visual Basic .NET, strings are objects, with dozens of built-in methods. The string object's methods include a ToUpper method. So the Visual Basic .NET equivalent of UPPER(StringVar) is StringVar.ToUpper. In a way it's better: Type a string variable's name, press the period key, and IntelliSense shows you all of the object's methods. In FoxPro, you'd be searching Help about now.

Variables are created from the named type using the DIM statement, which has the following syntax:

[ <attrlist> ] [{ Public | Protected | Friend | Protected Friend |
                  Private | Static }] [ Shared ] [ Shadows ] [ ReadOnly ]
                  Dim [ WithEvents ] name[ (boundlist) ] 
                      [ As [ New ] type ] [ = initexpr ]

If any of the first nine declaratives are used, Dim can be omitted. An explanation of each of these options follows:

  • Public—. Variables declared with this keyword have no restrictions on their accessibility. (Public is used only at module, namespace, or file level; you can't declare public variables inside a procedure.)

  • Protected—. Variables declared with this keyword are accessible only from within their own class or from a derived class. Protected access is not a superset of friend access. Protected is used only at class level, to declare members of the class.

  • Friend—. Variables declared with this keyword are accessible from within their declaration context and from anywhere else in the same program. Friend can be used at module, namespace, or file level, but can't be declared inside a procedure.

  • Protected Friend—. Variables declared with these keywords can be used by code anywhere in the same program, by code in their own class, and by code in any derived classes. Protected Friend can be used only at class level; you can declare protected friend variables inside a class but not inside a procedure, and not at module, namespace, or file level, and only to declare members of the class.

  • Private—. Variables declared with this keyword are accessible only from within their declaration context, including from members of any nested types such as procedures. You can declare private variables inside a module, class, or structure, but not at namespace or file level and not inside a procedure.

  • Static—. Variables declared with this keyword remain in existence and retain their latest values after termination of the procedure in which they are declared. You can declare static variables inside a procedure or a block within a procedure, but not at class or module level.

  • Shared—. The variable is not associated with a specific instance of a class or structure. You can access a shared variable by qualifying it either with the class or structure name, or with the variable name of a specific instance of the class or structure. You can declare shared variables in a source file or inside a module, class, or structure, but not inside a procedure.

  • Shadows—. The variable shadows an identically named programming element, or set of overloaded elements, in a base class. You can shadow any kind of declared element with any other kind. A shadowed element is unavailable from within the derived class that shadows it, unless the shadowing element is inaccessible, for example, Private. You can declare shadowing variables in a source file or inside a module, class, or structure, but not inside a procedure.

  • ReadOnly—. Variables declared with this keyword can only be read and not written. Used to create constants of a particular type, such as an object variable with preset data members. ReadOnly can't be used inside a procedure.

  • WithEvents—. The variable is an object based on a class for which events have been created, so it needs to have the additional harness added to monitor for said events and respond accordingly using any added handlers, to be discussed later.

  • Type—. Any of the data types that Visual Basic supports. The list is slightly longer than that of FoxPro, but you'll always know what type to use. String is character, int is integer, double is numeric. Believe me; knowing the corresponding names of data types is the least of your problems.

Any of these attributes can be used with the DIM statement to allocate memory for variables. DIM comes from DIMENSION, which was the only way to allocate memory for variables in the early version of BASIC and was never replaced by ALLOC as it should have been. The DIM keyword is optional if any of these others are used, so it's often left out. If only DIM is used, it defaults to Private. That's part of the source of the confusion. Just think of Public, Protected, and so on as alternate ways of saying Dim. If you want to use variables in classes as we use them in FoxPro, you can generally use Public or Friend. Inside a procedure, just use Dim.

Enumerations, Arrays, and Collections

There are three variations on variables that demonstrate important differences between Visual FoxPro and Visual Basic .NET. We'll look at them here.

Enumerations

Visual Basic is rife with names of possible choices for everything. For example, if you type

MsgBox ("Hi",

Thanks to IntelliSense, you'll see a drop-down list of 19 MsgBoxStyle keywords from which you can choose the next parameter. These enumerations are surrogates for numbers, as those who use FoxPro know. In Visual Basic, it's customary to list them as words instead of numeric values. I must admit, it's easier to read

If MsgBox(
   "Print", _
     MsgBoxStyle.Question _
 and MsgBoxStyle.YesNo _
 and MsgBoxStyle.DefaultButton1) _
   = MsgBoxStyle.Yes

than it is to read

If MessageBox ( "Print", 4 + 32 + 256 ) = 6

as we do in FoxPro.

In FoxPro, we use include files full of #CONST declarations in place of enums. For example, if you open the foxpro.h header file in the home directory (the home() function returns the name of the directory where Visual FoxPro is installed), you can read the numeric equivalents for these enumerations. In FoxPro, we have a few dozen such enumerations, and they're all found in this one header file. In general, as you enter function or command parameters in Visual FoxPro, IntelliSense displays the selections that are available to you based on the context.

In Visual Basic, there are hundreds and hundreds of them. They are one of the reasons why IntelliSense is a part of Visual Basic. IntelliSense is nice to have in FoxPro; Visual Basic would be dead without IntelliSense.

Enumerations are ubiquitous in Visual Basic. Every time you're poised to set a property, you can expect to pick from an enumeration. The closest equivalent we have in FoxPro is in the Properties window, where available choices appear in a drop-down list box. Sometimes you simply have to know the name, for example, ControlChars.CrLf for CHR(13)+CHR(10). The real challenge of IntelliSense is knowing what to type before you press the period key—after that, it's easy.

If you want to provide names for your lists of numeric values that are assignable in code, you can use an enum to provide IntelliSense support. After you define your enumeration, the editor will pick it up and use it the way it uses the enumerations that are built into the language. Enums can be integers (one, two, or four bytes).

Here's what an Enum looks like:

Enum SecurityLevel
   IllegalEntry = -1
   MinimumSecurity = 0
   MaximumSecurity = 1
End Enum

Arrays

Arrays are one of the most powerful features available in programming. The good news is that arrays in both languages support FOR EACH <obj> IN ArrayName. However, there are important differences. And some of them are just silly.

To specify an array in Visual Basic, use parentheses after the name:

Dim x() as String  ' the array will be dimensioned by the mechanism that creates it

or

Dim x(5) as Integer ' the array will have six elements, to be filled in later

Visual Basic array elements start with 0. The first element is ArrayName(0), and the last element is one number smaller than the number of elements in the array. That is, when you dimension an array using ArrayName(3), you've created an array with four elements. Heaven knows why this ridiculous notion wasn't killed in Visual Basic .NET. Actually, they tried, but Visual Basic6 developers fought them off. If you're a FoxPro developer, you will never, ever get used to this.

Arrays in FoxPro can contain elements of different data types; that's why SCATTER TO ARRAY (name) works. You use DIMENSION or DECLARE to create an array and then assign values to its elements. A subsequent DIMENSION or DECLARE statement can increase or decrease the size of the array. Arrays, like variables, can be PUBLIC, PRIVATE, or LOCAL. Arrays in Visual FoxPro are either one- or two-dimensional. The size of an array named X is given by ALEN(X) or ALEN(X,0); the number of rows in a two-dimensional array is given by ALEN(X,1); and the number of columns by ALEN(X,2).

FoxPro arrays can be either one- or two-dimensional. Arrays in Visual Basic .NET can have any number of dimensions. I don't know how often you need to program in cubes, but you can do it in Visual Basic without going through the gyrations that were required in FoxPro.

In Visual Basic, all array elements must be of the same type. REDIM resizes an array, and REDIM PRESERVE does so without erasing the contents. We don't have PRESERVE in FoxPro because array contents are never removed if an array is redimensioned; if you want to do so, you RELEASE and DIMENSION the array anew.

As in FoxPro, you can either dimension an array initially, or simply declare the fact that it's an array and let some subsequent process determine its dimensionality. In FoxPro, the sequence

DIMENSION X(1)
X(1) = "Hi there"
DIMENSION X(4)

expands the array from one element to four. The contents of the first element of the array are not changed. The VB equivalent might be this:

DIM X() As String = { ”Hi there}

ReDim X(3) Preserve

Table 1.6 summarizes the handling of arrays in Visual FoxPro and Visual Basic.

Table 1.6. Array Handling

Action

Visual FoxPro

Visual Basic

Verify an array

No equivalent

IsArray()

Declare and initialize

DIMENSION, DECLARE

Dim, Private, Public, ReDim

Find limits

LBOUND(), UBOUND()

ALEN(name,1) for rows, ALEN(Name,2) for columns

Resize

DIM x(n)

Redim x(n) Preserve

You can add anything to a collection in FoxPro. For example,

X = CREATEOBJECT("Collection")
x.Add(_Screen,"one") 

adds the FoxPro background screen to a collection named x. You can subsequently display the FoxPro window caption using this:

WAIT WINDOW x("one").Caption

You can also set properties or call methods on the object as a collection member, for example:

X("one").WindowState = 1

So as you might suspect, in Visual Basic .NET, if you need an array of dissimilar data types (say, the values of the fields in a record—the columns in a row, as they say in Visual BasicSpeak), you can use a collection.

Table 1.7 summarizes the handling of collections in Visual FoxPro and Visual Basic.

Table 1.7. Collection Handling

Action

Visual FoxPro

Visual Basic .NET

Create a collection

X=CREATEOBJECT("Collection")

Dim x as collection

Add an item

x.Add("key","value")

x.Add("key","value")

Remove an item

x.Remove("key") or x.Remove(n)

x.Remove("key") or x.Remove(n)

Refer to an item

x("key") or X.item(n)

x("key") or X.item(n)

Fields

We can't leave the subject of variables without discussing table fields. In FoxPro, if you USE (TableName), or if you SELECT (TableName) subsequently, you expose the fields of the table as if they were memory variables. In fact, if you issue the SCATTER MEMVAR command, FoxPro creates duplicate memvars for all of your fields. How can you distinguish them?

Just remember that the field is the default. If both exist, the current table's field is assumed to be what you want, unless you preface the memvar with "m.". It's actually a common hard-to-find bug. The best fix for this is to preface the field name with its table name and a period, for example, Customers.Address, and to preface memvars with "m.". Alternatively, you can preface all memvars with an additional m. It seems like overkill, but FoxPro developers are used to it.

Visual Basic .NET doesn't have fields. Well, it does, but field in Visual Basic .NET means what we'd call a local variable in a class in FoxPro. But as you'll see, typed datasets act pretty much like FoxPro tables do in this regard. A typed dataset is a Visual Basic program, usually (make that always) generated by a program, that adds one property procedure for each field in a table. That is, when you create a dataset (an .xsd file, which is actually an XML file), if you ask the wizard to create a typed dataset, it will generate the code for you.

There are third-party tools available that will do this job for you, and I suspect that Whidbey (the next version of Visual Basic .NET) will do so as well. RapTier from SharpPower.com builds a robust data tier. Visible Developer and DeKlarit do even more. All of these are better than typing these things in yourself, or using the one that's automatically generated by the IDE.

Typed datasets allow you to do data binding using a syntax that looks just like FoxPro's; for example, you fill in tablename.fieldname in the DataBindings - Text property (the equivalent of the FoxPro controlsource property). However, data binding goes only as far as the dataset, which is a local cursor. It doesn't write the data back to the source table, wherever that is. For that you need a command object, as you'll see shortly.

Functions and Subroutines (Procedures)

In both FoxPro and Visual Basic .NET, code is divided into modules called functions and procedures (subroutines in Visual Basic .NET). These can be modules, or they can be methods in classes. You use Name() to call a function or procedure, and obj.Name() to call a method of a class.

When you define a class, whether in a PRG or in a VCX, you can add methods, which either return a value or don't. (In Visual FoxPro there is no distinction between function methods and procedure methods.)

When you add a method to either a Visual Class Library (VCX) or a class in a PRG, you designate it as Public, Protected, or Hidden. This determines the method's visibility when instantiated. If you define the class and its methods in a PRG, that is, a procedure library, you precede method names with PROTECTED or HIDDEN (the unstated default is PUBLIC):

  • PUBLIC methods can be called when you instantiate an object based on the class.

  • PROTECTED methods can be seen by other developers in the Class Browser and in the Properties sheet, but can't be invoked except by members of the class itself. You can even select them via IntelliSense, but any attempted call produces an error.

  • HIDDEN methods can't be seen or invoked except by members of the class itself.

When you create an object from a class definition stored in a VCX or PRG using CREATEOBJECT() or NEWOBJECT(), all of the base class properties and methods are exposed. There may be dozens of them. However, if you use a PRG that starts with the statement

DEFINE CLASS <name> AS CUSTOM OLEPUBLIC

and ends with ENDDEFINE and compile it as a single-threaded DLL (STDLL) or a multithreaded DLL (MTDLL), then when you instantiate an object from the resulting DLL, IntelliSense will only show you the PUBLIC properties and methods. It only takes two additional lines of code, and provides a cleaner way for others to use your class's methods.

Function Declarations

But even the many variations on declaring variables discussed in the preceding section don't cause as much confusion as subroutine and function declarations. For one thing, functions and subroutines in FoxPro have always been a bit amorphous; either one can return or not return a value.

Visual Basic .NET is very particular about this. Functions must return a value, and it must be of the type specified in the RETURN statement. To make matters worse, someone in Redmond (and I think I know who it was) once decided that assigning the function's value to its name was a good way to hide the RETURN statement, or save a line of code, or who knows what. So you see code like this:

FUNCTION DollarsToCharacters (inputval as double) AS String
DollarsToCharacters = CSTR(inputval,10,2)
END FUNCTION

Functions and Procedures in FoxPro

You can define functions and classes either in a PROCEDURE library or a program. Procedure libraries are simply collections of FUNCTION and PROCEDURE blocks stored inline in a text file ending with the extension .prg. They are opened for use as procedure libraries using the statement

SET PROCEDURE TO <filename> ADDITIVE

Typically this is done early in the MAIN program, after which the contained functions and procedures can be referred to as if they were FoxPro functions, that is, with no object name preceding them.

Functions are declared with the FUNCTION <name> ... ENDFUNC and PROCEDURE <name>... ENDPROC blocks. Methods in visual class libraries (VCX files) are not characterized as either functions or procedures, because fundamentally, FoxPro doesn't care whether you return a value or not.

FoxPro traditionally uses the PARAMETERS statement to collect passed values. More recent versions of FoxPro permit the use of the Visual Basic syntax. So you can now write either

FUNCTION DollarsToCharacters
PARAMETERS InputVal
RETURN STR(InputVal,10,2)
ENDFUNC

or

FUNCTION DollarsToCharacters ( InputVal as Numeric ) AS String
RETURN STR(InputVal,10,2)
ENDFUNC

In FoxPro, that's about all you have to know.

Functions and Subroutines in Visual Basic .NET

Brace yourself for the Visual Basic .NET equivalent. First, I'll show you the formal definitions. (Note that the only difference between functions and subroutines is the As type clause in the function declaration, which indicates what data type will be returned.)

The following is the SUB declaration in Visual Basic .NET:

[ <attrlist> ] [{ Overloads | Overrides | Overridable | 
NotOverridable | MustOverride | Shadows | Shared }] 
[{ Public | Protected | Friend | Protected Friend | Private }] 
Sub name [(arglist)] [ Implements interface.definedname ]
   [ statements ]
   [ Exit Sub ]
   [ statements ]
End Sub

The following is the FUNCTION declaration in Visual Basic .NET:

[ <attrlist> ] [{ Overloads | Overrides | Overridable | 
NotOverridable | MustOverride | Shadows | Shared }] 
[{ Public | Protected | Friend | Protected Friend | Private }] 
Function name[(arglist)] [ As type ] [ Implements interface.definedname ]
   [ statements ]
   [ Exit Function ]
   [ statements ]
End Function

The first group of options in a function or subroutine declaration determine how the routine relates to any identically named function in the base class. An explanation of each of these options follows. However, you probably don't need any of this. I'll explain at the end of this section:

  • Overloads—. This and several other functions with the same but different parameter lists (called signatures in .NET) will be defined in the class or module, and all of them will do the same thing using different parameters. This is good if you're building a compiler, but is not important for database applications.

  • Overrides—. This function procedure overrides an identically named procedure in a base class. The number and data types of the arguments, and the data type of the return value, must exactly match those of the base class procedure.

  • Overridable—. This function procedure can be overridden by an identically named procedure in a derived class. This is the default setting for a procedure that itself overrides a base class procedure.

  • NotOverridable—. This function procedure cannot be overridden in a derived class. This is the default setting for a procedure that does not itself override a base class procedure.

  • MustOverride—. This function procedure is not implemented in this class, and must be implemented in a derived class for that class to be creatable. Use this as a reminder to another member of the programming team to build his own function.

  • Shadows—. Like Overrides, except that parameters don't have to match. This means “Ignore the method of the same name in the base class and use this one instead.”

  • Shared—. This can be called either directly from the class (without instantiating an object from the class) or by instantiating an object based on the class.

The second qualifier determine visibility, that is, where the function or procedure can be seen.

  • Public—. No restrictions on the accessibility of public procedures.

  • Protected—. Accessible only from within their own class or from a derived class. Can be specified only on members of classes.

  • Friend—. Accessible from anywhere in the same program.

  • Protected Friend—. Union of protected and friend access. Can be specified only on members of classes.

  • Private—. Accessible only from within their declaration context.

Constructors: Init vs. New

In FoxPro, you can include a PARAMETERS or LPARAMETERS statement as the first line of code in a class's Init procedure, then pass parameters to it in the CreateObject() or NewObject() function call that instantiates an object based on the class. Init is called the class constructor.

In .NET, the New() function is the constructor, and it can also be used to pass parameters while creating an instance of a class. However, it's common in the .NET Namespaces to have several overloads of the New method, so that you can instantiate an object and pass it several types of parameters. The only difference between different overloads is their parameter lists; for example, when creating a DataAdapter object, you can either pass it a SQL select statement and a connection string, like this:

Dim da as New SQLDataAdapter("SELECT * FROM Customers", "server=(local);database=NorthWind, PWD=sa;")

or a SQL select statement and an object reference to an open connection, like this:

Dim cn as New SQLConnection("server=(local);database=NorthWind, PWD=sa;")
Cn.Open()
Dim da as New SQLDataAdapter("SELECT * FROM Customers", cn)

So even if you don't ever overload any of your own methods, you'll use overloaded .NET methods every day. When you do so, you'll need to add the statement

MyBase.New()

as the first line of your overloaded method, to call the object's original constructor.

Implementing Interfaces

Finally, you can declare a function signature (a collection of properties and methods) known as an interface, and then include an Implements clause in your function or subroutine declaration that checks to see that you did it right. It's a sort of project control feature.

Implements—. Indicates that this function procedure implements a function procedure defined by an interface.

If you're asked to add a specific functionality to your class that's been implemented elsewhere, you can build a list of the properties and methods you want to expose and include them in an Interface declaration. You can then include the statement Implements (interface name) in another class, and the compiler will report an error if the class doesn't reference all of the properties and methods in the interface.

For example, define a class called Class1 as shown in Listing 1.1.

Example 1.1. Declaring an Interface

Public Class Class1

    Public Interface Foo
        Property Banana() As String
        Sub One(ByVal a As String)
        Function Two(ByVal b As String) As String
    End Interface

End Class

Now, open another class named Class2, and on line 2, type Implements Class1.Foo. (The “Foo” will appear thanks to IntelliSense when you press the period key.) Prototyped declarations for the property, the subroutine, and the function will automatically be inserted into your code (see Listing 1.2).

Example 1.2. Implementing an Interface in a Class

Public Class Class2
    Implements Class1.Foo
    Public Property Banana() As String Implements Class1.Foo.Banana
        Get

        End Get
        Set(ByVal Value As String)

        End Set
    End Property

    Public Sub One(ByVal a As String) Implements Class1.Foo.One

    End Sub

    Public Function Two(ByVal b As String) As String Implements Class1.Foo.Two

    End Function

End Class

It's pretty hard to screw this up if you use these declarations. And as an added bonus, if the Interface definition in Class1 is changed, its implementation in Class2 produces a compiler error. On the other hand, you have to be pretty sure what you want Class1 to do before you define the Foo interface.

Defining interfaces and then using the Implements keyword to check your math is not a bad idea. You can do it in FoxPro as well (when working with Component Object Model [COM] objects), although I've never had occasion to do so. It's helpful, but it's not necessary. So unless someone tells you to use them, ignore Interface declarations and Implements clauses. In a team environment, they become more useful.

Summary of Visual Basic Declarations

The following summary of function scope declarations describes the available options. After I describe them, I'll suggest a simple approach.

  • Public and Friend can refer to any of the following elements: Class, Const, Declare, Delegate, Dim, Enum, Event, Function, Interface, Module, Property, Structure, or Sub; Protected and Private can be used with all of these except Module, which can only be Public or Friend (the default).

  • Static means that the variable continues to exist after the routine in which it was created is over. In that sense it's like FoxPro's PUBLIC declaration. Note that you can't specify Static with either Shared or Shadows.

  • Shared means that you can refer to the property without instantiating an object based on the class. It allows you to use the class itself as an object based on the class. We don't have that in FoxPro.

  • Shadows means that a variable completely replaces the identically named element in a base class. This is the default in FoxPro: If you write code in the method code window of a derived form class, it completely replaces the code that was written in the base form class. FoxPro doesn't check whether the signatures match or not. Ever.

So What Should I Use?

It's very, very simple. In FoxPro, put the initial assignment of variables in MAIN to default them to PUBLIC scope, and then declare variables as PRIVATE in all called routines that themselves might call other routines that might need the private values—for example, a screen that set up titles for a report. Use LOCAL for all variables that are definitely used only in a single routine (for example, loop counters). You can completely ignore method scope and make them all PUBLIC, the default. And remember that in general, variables that are used only by methods of a class should be public properties of that class, not PUBLIC variables in MAIN.

In Visual Basic, you can do the same thing. The greater variety of declarations in Visual Basic .NET implies that it's desirable to take advantage of every one of the options. However, as database developers, we don't need most of them.

Make all methods of your classes Shared Public, and ignore all of the nuances. Don't use Overrides and Overloads. And if you find yourself using Shadows a lot, consider redesigning your base class or writing two different classes.

A footnote on overloaded methods in .NET

The fact that you don't need to create multiple overloads of subs and functions doesn't mean, however, that you won't see them every day of your programming life. A great many of the .NET classes have overloaded methods. Let's say you want to create a connection to SQL Server. If you do this

Dim cn As SQLClient.SQLConnection(

when you press the left parenthesis, nothing happens. However, if you type this:

Dim cn As New SQLClient.SQLConnection(

you will be presented with a little tooltip box that informs you that you're about to use overload 2 (of 2 available overloads), which accepts a connection string. Multiple overloads of New() methods for .NET classes are ubiquitous, and rather helpful. I still don't think you need to overload your own methods, but you'll use overloaded methods every day.

Classes, Namespaces, Properties, and Forms

Many FoxPro developers began with version 1, which didn't have object orientation. We built forms that did what we wanted. If we needed a similar form, we cloned and modified the code. There was a template capability that allowed us to migrate features to the template and then stamp out similar forms like a cookie-cutter. But subsequent design changes could be painful.

When Visual FoxPro came out, for the first time we had the ability to write generic form class code and inherit forms from it. My own approach was to build a basic form and then slowly move code out of it into an underlying class, changing form references to a specific table name to the contents of a property, so if I changed

SELECT EMPLOYEES

to

SELECT ( THISFORM.MainTable )

I could base a form on this “form template,” assign a value to the MainTable property, and the form would run as expected. Gradually, most if not all of the code in the original form migrated to the class. I ended up with a form that did what I needed if I set a dozen or so properties; if I needed a similar form, I just used

Create form xxx as MyForm FROM PinterLIB

And set the same dozen properties, and the form was up and running in minutes. I was in heaven! My customers got more software for less money, and I could win every single bid I made.

Visual Basic .NET is similarly object-oriented.

However, classes don't work exactly the same way. For one thing, FoxPro form classes are usually stored in a table with the extension .vcx (and an associated memo file with the same name and a .vct extension). Visual Basic .NET forms are source code files that don't look any different from program files. They have the extension .vb.

However, the general idea is the same: Classes have properties and methods. You instantiate an object based on the class, assign values to its public properties, and call its public methods. It uses its private properties as variables, and calls private methods to do its work. In this regard, FoxPro and Visual Basic .NET are identical.

However, the devil is in the details. And there are lots of details that differ. For one thing, FoxPro classes are stored either as VCX files or PRG files. If they're stored as VCX files, you use the Class Designer to add properties and methods, which can be either Public, Private, or Protected. If they're defined in a PRG, the file starts with this line of code

DEFINE CLASS Foobar AS CUSTOM (OLEPUBLIC)

and ends with

ENDDEFINE

The OLEPUBLIC keyword is required if you want to compile to a DLL, which is required for Web Services applications, and is generally not used by FoxPro developers unless they use COM+.

Properties are created simply by assigning them values, one per line, after the DEFINE CLASS statement and before the first FUNCTION or PROCEDURE statement. A single PRG can have one or more class definitions in it. They're instantiated using either

SET CLASSLIB TO VCXNAME ADDITIVE or
SET PROCLIB TO CLSLIB.PRG ADDITIVE 

followed by

oName  = CREATEOBJECT ( "ClassName" )
oName2 = CREATEOBJECT ( "OtherClass" )

or you can skip the SET CLASSLIB and SET PROCLIB statements and use a shorter approach:

oName  = NEWOBJECT ( "ClassName", "VCXNAME.VCX" )
oName2 = NEWOBJECT ( "OtherClass", "CLSLIB.PRG" )

If you have parameters in a class's Init method, you can add the parameters at the end of the CREATEOBJECT() or NEWOBJECT() function call and pass them directly to the Init constructor. Inside the class, THIS refers to a property or method of the class itself. That's about the size of creating and using classes in FoxPro.

In Visual Basic .NET, classes are defined in code. There is no Visual Class Library. However, if the class is a form you can use the Form Designer to build it visually; and if it's a user control, you get a little container in which your control component or components appear.

The use of namespaces is confusing to FoxPro developers. Namespaces are just there to add an additional level of naming. For example, the class library shown in Listing 1.3 defines two classes in Visual Basic .NET. Note that the project name for this class library is NameSpaceDemo. This is important because the name of the project is the default name of the generated DLL, which becomes the first part of the class name.

Example 1.3. Namespace Declarations

Imports System.IO

Namespace PinterConsulting

    Public Class Functions
        Private _FileName As String
        Private sep As Char

        Public Property FileName()
            Get
                Return _FileName
            End Get
            Set(ByVal Value)
                _FileName = Value
            End Set
        End Property

    End Class

    Public Class Objects

        Public Class Person
            Private _FirstName As String
            Private _LastName As String
            Private sep As Char

            Public Property FirstName() As String
                Get
                    Return _FirstName
                End Get
                Set(ByVal Value As String)
                    _FirstName = Value
                    Select Case _FirstName
                        Case "Bob"
                        Case "Fred"
                        Case "Eddie"
                        Case Else
                    End Select
                End Set
            End Property

            Public Property LastName() As String
                Get
                    Return _LastName
                End Get
                Set(ByVal Value As String)
                    _LastName = Value
                End Set
            End Property

            Public ReadOnly Property FullName()
                Get
                    Return FirstName + " " + LastName
                End Get
            End Property

        End Class

    End Class

End Namespace

I've added a form in the same project that uses the classes. Notice that because the project name was NameSpaceDemo and the NameSpace was PinterConsulting, the name for the Imports statement (which is like SET CLASSLIB ... ADDITIVE) is the concatenation of the two. Listing 1.4 shows the form code:

Example 1.4. Using a Namespace in an Imports Statement

Imports NameSpaceDemo.PinterConsulting

Public Class Form1

    Inherits System.Windows.Forms.Form

    Dim NameMgr As New PinterConsulting.Objects.Person

    Private Sub Form1_Load( _
     ByVal sender As System.Object, ByVal e As System.EventArgs) _
     Handles MyBase.Load
    End Sub

    Private Sub txtFirst_TextChanged( _
     ByVal sender As System.Object, ByVal e As System.EventArgs) _
     Handles txtFirst.TextChanged
        NameMgr.FirstName = txtFirst.Text
        Label1.Text = NameMgr.FullName
    End Sub

    Private Sub txtLast_TextChanged( _
     ByVal sender As System.Object, ByVal e As System.EventArgs) _
     Handles txtLast.TextChanged
        NameMgr.LastName = txtLast.Text
        Label1.Text = NameMgr.FullName
    End Sub

    Private Sub txtFileName_TextChanged( _
     ByVal sender As System.Object, ByVal e As System.EventArgs) _
     Handles txtFileName.TextChanged
        Dim o As New PinterConsulting.Functions
    End Sub

End Class

Note that I could have left out the Objects class because it only contains one nested class. But I could have added more classes within Objects, and they would also have been exposed via IntelliSense.

This is why I say that IntelliSense is merely nice to have in FoxPro, but in Visual Basic .NET you'd be dead without it. But I can imagine complex projects that need namespaces to document functionality of class members and classes, and it's just a naming convention. I just can't escape concluding that FoxPro works just fine without namespaces and wouldn't be enhanced if we had them. I understand the theory, but isn't anyone else out there cognizant of the divergence between theory and reality, and gutsy enough to eschew the theory?

Instantiating objects in Visual Basic .NET

One thing that will come up constantly in .NET is the creation and use of objects based on .NET classes. For example, the following code opens a table and displays its contents in a datagrid (the .NET equivalent of FoxPro's BROWSE):

Dim cn As New SqlConnection("Server=(local);Database=Northwind;UID=sa;PWD=;")
cn.Open()
Dim da As New SqlDataAdapter("SELECT * FROM CUSTOMERS", cn)
Dim ds As New DataSet
da.Fill(ds)
DataGrid1.DataSource = ds.Tables(0)

The statement Dim cn As SQLConnection is like FoxPro

LOCAL cn As String

It doesn't create the variable. It just announces your intention to do so at some future date. Sounds goofy, but it's true. (It actually feeds IntelliSense - in both cases.) It's supposed to be followed by the statement that actually creates the variable. It can either be done with a New statement, or by referring to a member of an existing object. If you leave out the word New in the Dim cn statement above, you won't be able to pass the connection string in as a parameter. In the fourth line of the same code, you can leave out the word New without incurring a syntax error, but the following line will bomb because Fill only works with an existing Dataset, and the statement Dim ds As Dataset doesn't create one – it only declares one.

Property Procedures in Visual Basic .NET

For years, Visual Basic developers got used to the idea of trapping the assignment of a value to a property and doing something when that assignment occurred. For example, I used to use a communication package that used the following syntax to read data from a telephone line:

OModem.Setting = 14
OModem.Action = 4    ' Read

The way this worked was that the moment of assigning a value to a property was trapped in what is called the Property Set method. At that instant, other code can be called. So they used that when they couldn't declare a “Do-it” method. It was a workaround for Visual Basic's inability to give us any way to simply write oModem.Read. After a while, the workaround seemed normal. Goofy but true.

The tradition continues. If you type

PUBLIC PROPERTY FullName AS String

and press Enter, Visual Basic will add complete the routine by adding the following six lines of code:

Public Property FullName() As String
    Get

    End Get
    Set(ByVal Value As String)

    End Set
End Property

You have to add three more lines of code, one before the routine and two within it, to end up with this:

Private _FullName As String
Public Property FullName() As String
    Get
       Return _FullName
    End Get
    Set(ByVal Value As String)
       _FullName = Value
    End Set

If you have two properties, you'll have two of these. If you have 20 properties, you'll have 20. Notice that a separate private variable (by convention the name is the property procedure's name preceded by an underscore) is required in order to store the value manipulated by the property procedure, and it must be declared outside of the property procedure.

This is the equivalent of the Assign and Access methods that FoxPro allows you to create when you add a property to a class. As you may know, you can add code to the Assign method code to do other tasks when the property is assigned a value. That's the idea behind property procedures.

However, in Visual FoxPro property procedures (that is, Assign and Access methods) are completely optional; I've only had occasion to use them twice in years of client development.

In Visual Basic .NET, you can't see either class properties or public class variables in the Properties sheet for the class. However, you can't see public class variables in the Properties sheet. And you can only see them in subclasses that inherit from your classes. That's right; to get what you call a property in FoxPro, you must create a public property procedure in Visual Basic .NET. Strange but true. So even if you don't need the Assign and Access methods (called Getters and Setters in Visual Basic), you need property procedures. The fact that Visual Basic writes most of the code diminishes the pain, but I think it's goofy, and hope that it will be replaced by FoxPro's much simpler and clearly adequate counterpart some day.

Forms

Forms are classes in Visual Basic .NET. If you refer to one in a menu, it will take either two or three lines of code to display the form:

Dim frm as new CustomerForm    ' can be written as 2 lines of code
Frm.Show

In FoxPro you just write

Do form Customer

However, have you ever looked at the structure of an SCX file? It has exactly the same structure as a VCX file. And in fact, if you save a form as a class, the code to run the form changes to this:

Frm = CREATEOBJECT ( "FormClassName", "VCXFILE.VCX" )
Frm.Show()

So they're more similar than you might have thought.

Forms have a section of code called the “Windows Form Designer generated code.” When you're in the form designer, things that you do, like adding controls on the form, moving things around, and assigning values to properties, are actually stored in source code. The designer merely uses this code to create a visual display of the code's contents. If you manually change this code, it will be overwritten whenever a change is made in the designer and the file is saved.

However, sometimes it's useful to write code that is saved as part of the form's initialization sequence – for example, using property values to open tables and populate some standard controls. If you have such code, you can place it in the Public Sub New() method, after the word InitializeComponent(). It's the only place in the Form Designer generated code that you can include code that won't be overwritten by the designer.

Events

For a FoxPro developer, events constitute one of the strangest things in Visual Basic .NET. I looked for the equivalent in FoxPro for the longest time, and I just couldn't find it. It turns out that there are two kinds of events in Visual Basic .NET, and they bear little resemblance to one another. FoxPro only has one kind of event. Visual Basic actually uses events to call methods in other objects; hence the confusion. I hope this part of the chapter will clear it up for you.

Events in Visual FoxPro

FoxPro's classes have properties, events, and methods. Properties are variables, and methods are functions and procedures. But you can't add events. When you subclass a base class, you can add properties and methods at will. You can also add your own code to the existing methods. For example, when you refresh a form, you can add code to SEEK() records in all related tables so that bound fields displayed on the form will be coordinated.

Events are different. Events happen. When they do, any code that you add to the event's code window is executed, in addition to the default behavior of the event. But events happen when they happen—when you click, or double-click, or mouse over, or whatever. You don't add events in FoxPro.

Each of FoxPro's base classes comes with a generous but fixed list of events. You can't add to FoxPro's events; you're stuck with the list of events that each class was born with. In FoxPro, the name of an event implies what it handles. TextBox1.Click tells you which object the Click event responds to. There is no Handles clause at the end. It's not needed. You don't even see the mechanism that handles it. It's transparent.

To add code for an event, you pick the event from the Method Name/Members combo box of the code window navigation bar (at the upper-right side of the screen).

FoxPro's IDE supplies any necessary parameters. The code, in the form of an LPARAMETERS statement, is inserted when the code window opens. If you erase the LPARAMETERS line and leave the code window empty, the LPARAMETERS line will be put back in the next time you open the code window. For example, open a new MouseDown event code window and you get this:

LPARAMETERS nButton, nShift, nXCoord, nYCoord

NOTE

We'll look at the event argument parameter generated for this same event in Visual Basic by the .NET IDE in the next section. It does essentially the same thing.

There is no Event statement in FoxPro. (There are three new functions [BindEvent, RaiseEvent and ReleaseEvents] related to events in Visual FoxPro8, but it's difficult to come up with a reason to use them.) Looking at the list of events for a typical FoxPro control, I can't imagine why I'd want to add more events. That's why the Event statement in Visual Basic has always confused me. And why “raise” an event? Aren't events things that just happen, like “Click”?

NOTE

The closest things we have to events in FoxPro are the Assign and Access methods for form properties. They allow you to trap the instant when a value is assigned to or retrieved from a class property. But that's unrelated to the use of RaiseEvents in Visual Basic. It's interesting that in FoxPro we use the Visual Basic naming convention. If you add a MyPropName_Assign method to your class, the code in it will fire when the property is assigned a value.

This is at the heart of the reason that FoxPro developers can't easily understand Visual Basic events. The events that are declared for the purpose of calling them with RaiseEvent have nothing to do with the events that come with controls. That's not what RaiseEvent is used for in Visual Basic.

Events in Visual Basic .NET

One of the really huge differences between FoxPro and Visual Basic .NET is Visual Basic's use of events. FoxPro objects have events, and you can write code in the event snippets that will run when the event fires. It's transparent. In Visual Basic, event handling is not transparent. You get to watch.

If you create a Windows Form project and add a text box to the form, you'll find the following generated code in the form's codebehind:

    Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
...
    'TextBox1
    '
    Me.TextBox1.Location = New System.Drawing.Point(110, 54)
    Me.TextBox1.Name = "TextBox1"
    Me.TextBox1.TabIndex = 0
    Me.TextBox1.Text = "TextBox1"
    Me.Controls.Add(Me.TextBox1)

If the WithEvents clause were not included, the program would ignore any of the control's events. Presumably, doing so causes the program to run faster; otherwise, why would the WithEvents clause exist? This allows us to add a Handles clause to a subroutine, regardless of its name, that “reacts” when the named event occurs. So what is the Visual Basic default—without events? Oddly enough, it is.

The IDE writes the code for you if you open the code window and select an object from the Objects drop-down list at the upper-left corner of the window, and then select an event from the drop-down list at the upper-right corner of the same window.

For example, the following code changes the text box back to black text on a white background when the cursor leaves the control:

Private Sub TextBox1_LostFocus( _
       ByVal sender As Object, _
       ByVal e As System.EventArgs) _
      Handles TextBox1.LostFocus
    TextBox1.ForeColor = Color.Black
    TextBox1.BackColor = Color.White
End Sub

Although a subroutine name that references the event name is generated when you add the code, the name is in fact functionally unrelated to what the routine does. You can change the name, and the program will work exactly the same. The Handles clause at the end is what causes the subroutine to execute when the event fires. Or, as they like to say in Redmond, when the event is raised.

RANT

<rant>That's actually part of the problem as well. The guys who developed Visual Basic took considerable liberties with the language. You throw and catch an error. (Why? Because they had seen the Mariners the night before, and because they could. There were no language police. The first little guy high on Coca-Cola at three a.m. gets to pick a name.) Similarly, you raise an event. Don't even get me started on persisting, depersisting, and consuming XML. By capriciously assigning terms that don't particularly reflect what's being done, they made the language that much harder to understand. But I digress… </rant>

The two parameters in parentheses that follow the subroutine name are always ByVal sender As Object (a reference to the calling object) and an event argument whose type is based on what kind of parameters the called event needs. For example, if you add a MouseDown event routine, you get the following code:

Private Sub Form1_MouseDown( _
  ByVal sender As Object, _
  ByVal e As System.Windows.Forms.MouseEventArgs) _
 Handles MyBase.MouseDown
 Debugger.Break   ' like SET STEP ON
End Sub

I've included a breakpoint so that you can type “e” into the Watch window and see what it is. It's a structure containing the values needed to respond to a mousedown event: X and Y coordinates, among others. So it's pretty much the same thing that FoxPro is doing with its generated LPARAMETERS statements. One more mystery solved.

If this were the only use for events in Visual Basic .NET, you could simply ignore them and let the IDE generate event-related code whenever it felt like it. The developers of FoxPro apparently felt that the confusion resulting from optionally exposing events in this way was not worth the bother. Generally, they were correct.

RaiseEvent

Events in Visual Basic are also used as a workaround for a characteristic of the Visual Basic compiler that prevents it from calling functions and procedures that aren't resolved at compile time. In particular, although you can instantiate an object based on a class and call the object's methods at will from within a form, you can't do the opposite in Visual Basic. For example, in FoxPro, you can't call THISFORM.Refresh from inside a class, like this:

DEFINE CLASS Utilities AS CUSTOM

PROCEDURE CoordinateTables
THISFORM.Refresh        && in whatever form is active
ENDPROC

ENDDEFINE

You can, however, use _Screen.ActiveForm.CoordinateTables. You can even use PEMSTATUS ( Object, MethodName, 5 ) to determine whether the method exists before calling it. So we have workarounds in FoxPro. But in Visual Basic they don't. (Well, there is reflection, but it's new. RaiseEvents has been around since Visual Basic4 at least, so that's how they do it.)

In Visual Basic you can't call a method in a form from inside an object without resorting to some trick. RaiseEvent is that trick. That's why it's so confusing. Here we are trying to think up events that might go beyond clicking or double-clicking. The use of RaiseEvent is a trick with mirrors to overcome a limitation in the compile process for Visual Basic. You don't need to try to invent events in FoxPro. There's no reason to. You can do what you need to do without them. In Visual Basic, you can't.

This is why FoxPro developers have a hard time understanding events in Visual Basic. All of FoxPro's events are always trappable; there is no need to use WithEvents to declare that the events for a control are subject to handling; they just are. And the second reason for using events in Visual Basic is unnecessary in Visual FoxPro!

In Visual Basic, Public Sub XXX Handles ObjectName.EventName can be used to respond to the line RaiseEvent EventName in a class. So a form can contain a subroutine that is called when the event named in the Handles clause of the subroutine fires (or is raised, as they like to say).

How to Declare an Event in a Class

To declare an event in a class, include the statement

Public Event <Name> ( Parameters )

Instantiate your object:

Dim oObj as New <ClassName>

Then, within the code, invoke it using the RaiseEvent command:

RaiseEvent <Name> ( Parameters )

This “raises” the event and passes any parameters to it. Finally, trap the event using the Handles clause of a subroutine in the form:

Public sub xxx ( Parameters as string ) Handles oObj.EventName

Using this mechanism, I've created a “hook” that lets me call any method in the form when something happens in the class. Note that I could simply call a method in the class using oObj.MethodName ( Parameters ), as I do in the Update button code in Listing 1.5. But if I want to call form methods from the class, this is actually a pretty clean way to do it.

Here's an example, based on the “Visual Basic .NET How-To N-Tier Data Form App” sample included in the “101 Visual Basic .NET Samples” library available for free from the MSDN site. I can't say enough about this code library, which gives you good examples of how to do a huge variety of things in Visual Basic .NET. I've added a few properties so that it's more flexible and easier to configure.

Using Events in a Data Access Class

The benefits of separating the implementation of data access from the form are immediate. The data access class is external to the form, so you can change the data source without making any changes to the form. This allows (for example) using MSDE during development, then changing to SQL Server at a later date with minimal disruption. Listing 1.5 shows the example class.

Example 1.5. A Data Access Class

Option Strict On
Imports System.Data.SqlClient

Namespace DataAccessLayer

Public Class DataAccess

    Protected Const CONNECTION_ERROR_MSG As String = "Couldn't connect to SQL"
    Protected Const SQL_CONNECTION_STRING As String = _
                    "server=(local);database=Northwind;uid=sa;pwd=;"

    Public da As SqlDataAdapter
    Public ds As DataSet

    Public _MainTable As String
    Public _KeyField As String

    Protected DidPreviouslyConnect As Boolean = False
    Protected strConn As String = SQL_CONNECTION_STRING

    Public Event ConnectionStatusChange(ByVal status As String)
    Public Event ConnectionFailure(ByVal reason As String)
    Public Event ConnectionCompleted(ByVal success As Boolean)

    Public Property MainTable() As String
        Get
            Return _MainTable
        End Get
        Set(ByVal Value As String)
            _MainTable = Value
        End Set
    End Property

    Public Property KeyField() As String
        Get
            Return _KeyField
        End Get
        Set(ByVal Value As String)
            _KeyField = Value
        End Set
    End Property

    Public Function CreateDataSet() As DataSet

        Dim ds As DataSet
        If Not DidPreviouslyConnect Then
            RaiseEvent ConnectionStatusChange("Connecting to SQL Server")
        End If
        Dim IsConnecting As Boolean = True
        While IsConnecting
            Try
                Dim scnnNW As New SqlConnection(strConn)
                Dim strSQL As String = "SELECT * FROM " + MainTable
                Dim scmd As New SqlCommand(strSQL, scnnNW)
                da = New SqlDataAdapter(scmd)
                Dim cb As New SqlCommandBuilder(da)
                ds = New DataSet
                da.Fill(ds, MainTable)
                IsConnecting = False
                DidPreviouslyConnect = True
            Catch exp As Exception
                If strConn = SQL_CONNECTION_STRING Then
                    RaiseEvent ConnectionFailure(CONNECTION_ERROR_MSG)
                End If
            End Try
        End While
        RaiseEvent ConnectionCompleted(True)
        da.Fill(ds, MainTable)
        Return ds

    End Function

    Public Sub UpdateDataSet(ByVal inDS As DataSet)
        If inDS Is Nothing Then
            Exit Sub
        End If
        Try
            If (da Is Nothing) Then
                CreateDataSet()
            End If
            inDS.EnforceConstraints = False
            da.Update(inDS, MainTable)
        Catch exc As Exception
            RaiseEvent ConnectionFailure("Unable to update the data source.")
        End Try
    End Sub

End Class
End Namespace

There are four RaiseEvent calls in this code. Each call is made when the result of an attempt to connect to the database is known. Because you don't know which form is going to call these methods, or even what the form is going to do when this occurs, all you provide is the mechanism for notifying the form that it needs to do something.

This is called messaging. Internally, Windows threads send each other messages. Each thread listens for a message, and responds when the message is received.

RANT

<rant>There was once a book written about FoxPro that went on and on about “messaging.” I thought it was silly. When I start my car, it causes thousands of gasoline explosions per second. But I don't say “I'm going to explode some gasoline.” I say “I'm going to drive to the store.” The less I know about what's under the hood, the better. In fact, the one criticism I have of .NET at this point is that it gives me way too much information. I can't imagine why I need to see the code that instantiates text boxes and positions them on the form.</rant>

So in summary, the stage is set for the class code to “call” related methods in a form to be named later. In the form code shown in Listing 1.6, you'll see how this is done.

The Form

The form code answers the question “What do I do when these events occur?” The Data Access Layer class is instantiated as object m_DAL. The events are referred to with the prefix m_DAL because they belong to the object. See Listing 1.6.

Example 1.6. A Form That Uses a DAL Component with Events

Option Strict On
Imports System.Data.SqlClient
Imports DataAccessLayer

Public Class frmMain
    Inherits System.Windows.Forms.Form

...Windows Form Designer generated code goes here...

    Protected DidPreviouslyConnect As Boolean = False
    Private ds As DataSet
    Private dt As DataTable
    Private dv As DataView

    Public MainTable As String = "Customers"
    Public KeyField As String = "CustomerID"

    Protected WithEvents m_DAL As DataAccess

    Dim frmStatusMessage As New frmStatus

    Private Sub btnNext_Click( _
      ByVal sender As System.Object, _
      ByVal e As System.EventArgs) _
     Handles btnNext.Click
        NextRecord()
    End Sub

    Private Sub btnPrevious_Click( _
      ByVal sender As System.Object, _
      ByVal e As System.EventArgs) _
     Handles btnPrevious.Click
        PreviousRecord()
    End Sub

    Private Sub btnRefresh_Click( _
      ByVal sender As System.Object, _
      ByVal e As System.EventArgs) _
     Handles btnRefresh.Click
        frmMain_Load(Me, New System.EventArgs)
    End Sub

' Call the UpdateDataSet method:
    Private Sub btnUpdate_Click( _
      ByVal sender As System.Object, _
      ByVal e As System.EventArgs) _
     Handles btnUpdate.Click
     m_DAL.UpdateDataSet(ds.GetChanges())
        frmMain_Load(Me, New System.EventArgs)
    End Sub

    Protected Sub dt_PositionChanged( _
      ByVal sender As Object, _
      ByVal e As System.EventArgs)
        BindGrid()
    End Sub

    Private Sub frmMain_KeyDown( _
      ByVal sender As Object, ByVal e _
      As System.Windows.Forms.KeyEventArgs) _
     Handles MyBase.KeyDown
        If e.KeyCode = Keys.Right Then NextRecord()
        If e.KeyCode = Keys.Left Then PreviousRecord()
    End Sub

    Private Sub frmMain_Load( _
      ByVal sender As Object, _
      ByVal e As System.EventArgs) _
     Handles MyBase.Load
        frmStatusMessage = New frmStatus
        GetDataSet()
        BindGrid()
    End Sub

    Private Sub grd_CurrentCellChanged( _
      ByVal sender As Object, _
      ByVal e As System.EventArgs) _
     Handles grd.CurrentCellChanged
        grd.Select(grd.CurrentCell.RowNumber)
        If TypeOf (grd.Item(grd.CurrentRowIndex, 2)) Is DBNull Then
            grd.Item(grd.CurrentRowIndex, 2) = _
                grd.Item(grd.CurrentRowIndex, 0)
        End If
    End Sub

    Private Sub grdClick( _
      ByVal sender As System.Object, _
      ByVal e As System.EventArgs) _
     Handles grd.Click
        BindGrid()
    End Sub

* Handle the ConnectionCompleted event:
    Private Sub m_DAL_ConnectionCompleted( _
      ByVal success As Boolean) _
     Handles m_DAL.ConnectionCompleted
        frmStatusMessage.Close()
    End Sub

* Handle the ConnectionFailure event:
    Private Sub m_DAL_ConnectionFailure( _
      ByVal reason As String) _
     Handles m_DAL.ConnectionFailure
        MsgBox(reason, MsgBoxStyle.Critical, Me.Text)
        End
    End Sub

* Handle the ConnectionStatusChanged event:
    Private Sub m_DAL_ConnectionStatusChange( _
      ByVal status As String) _
     Handles m_DAL.ConnectionStatusChange
        frmStatusMessage.Show(status)
    End Sub

    Sub BindGrid()
        With grd
            .CaptionText = MainTable
            .DataSource = dv
        End With
    End Sub

    Sub GetDataSet()
        frmStatusMessage.Show("Retrieving Data From Data Access Layer")
        ds = m_DAL.CreateDataSet()
        dt = ds.Tables(MainTable)
        dv = dt.DefaultView
    End Sub

    Public Sub NextRecord()
        grd.UnSelect(grd.CurrentRowIndex)
        grd.CurrentRowIndex += 1
        grd.Select(grd.CurrentRowIndex)
    End Sub

    Public Sub PreviousRecord()
        grd.UnSelect(grd.CurrentRowIndex)
        If grd.CurrentRowIndex > 0 Then
            grd.CurrentRowIndex -= 1
        End If
        grd.Select(grd.CurrentRowIndex)
    End Sub

End Class

frmStatus is just a simple form with a label control in the center of the screen and an override of the form's Show method code:

Public Overloads Sub Show(ByVal Message As String)
    lblStatus.Text = Message
    Me.Show()
    System.Threading.Thread.CurrentThread.Sleep(500)
    Application.DoEvents()
End Sub

This displays the form with a message and resumes execution after a half-second delay.

To summarize: If you want to use events in your classes to call methods in forms when you don't yet know which form and which methods, do this:

  1. Create a solution containing a project named, say, “A”, output type “Class Library”, which contains a class named, say, “B”.

  2. In class B, define an event like this:

    PUBLIC Event ABC ( ByVal xxx As String )
    
  3. In the class, call using this:

    RaiseEvent ABC ( parms )
    
  4. Add a Windows Form project.

  5. In the form, include this:

    Imports A                      'so that it will know to use the class
    
  6. Add this:

    Protected Withevents C AS B    'instantiate an object based on the class
    
  7. In the form, add a subroutine or function with a Handles clause:

    Private Sub XYZ ( parms ) Handles C.ABC
    
  8. Use the sub/function's parameter list to pass the event's parameters.

Compiler Directives

In FoxPro, the #CONST directive can be used to specify a value that is substituted for occurrences of the named constant in the code at compile time. The #IF #ELSE #ENDIF construct can be used to deliver source code that will include only the code that will compile correctly given the compiler version. #DEF, #UNDEF, and #IFDEF can similarly be used to determine what code gets compiled. The #Include directive reads in a text file, and is like the #ExternalSource directive in Visual Basic.

In Visual Basic .NET, the #Const compiler directive also exists, and the #IF/#ELSE/#ENDIF construct works much like its counterpart in FoxPro, although not to support multiple language versions because there's only one at this time; besides, Visual Basic doesn't allow code that isn't syntactically correct to exist in the source code, as it compiles automatically when you close a code window.

The most interesting compiler directive in Visual Basic is the #Region Name/#End Region block, which allows you to group code elements and collapse them to a single label by clicking on the “+” beside the #Region directive. There's no equivalent in FoxPro, although the way that FoxPro groups code into snippets in the Form and Class Designers provides similar functionality, in the sense of letting you see only some of the code at one time.

Table 1.8 summarizes compiler directives in Visual FoxPro and Visual Basic.

Table 1.8. Compiler Directives

Action

Visual Basic

Visual FoxPro

Define a constant

#Const

#Const

Compile selected lines

#IF.. #ENDIF

#IF.. #ENDIF

Collapse part of code

#Region.. #End Region

No equivalent

Include a file

#ExternalSource

#Include

Data

Perhaps the greatest difference between Visual FoxPro and Visual Basic .NET is the handling of tables. It's a huge topic, and I'll just talk about general issues here. (It's covered in considerably more detail in Chapter 4, “A Visual Basic .NET Framework for SQL Server,” and Chapter 7, “XML.”) But I can give you a feel for the issues here.

FoxPro stores its data in either DBF files or in cursors. DBFs have a header of about 512 bytes that describes the nature of the table and its membership, if any, in a data base container (DBC). It then uses 32 bytes per field to describe all of the fields in the table. What follows are fixed-length records with a “delete byte” at the front, one per record. Cursors are in-memory representations of tables, having the same format except for the database container (DBC) information, which doesn't apply. When you USE a table or create a cursor using CREATE CURSOR or as the result of a SQL SELECT statement, FoxPro reads the header and uses its contents to interpret the fixed-length records that follow. BROWSE displays the table rows in a grid-like format.

Visual Basic .NET has no native data storage format. It always treats data as a foreign object. It usually reads it into a dataset, which is an XML representation of the data.

In its simplest form, XML consists of an XML header (a single line that says “I'm an xml string”), followed optionally by a Schema that describes the rows that follow, followed by a hierarchy of rows and fields that describe the table. A simple example follows:

<?xml version = "1.0" encoding="Windows-1252" standalone="yes"?>
<VFPData>
   <xsd:schema id="VFPData" xmlns:xsd="http://www.w3.org/2001/XMLSchema"  _
     xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
      <xsd:element name="VFPData" msdata:IsDataSet="true">
         <xsd:complexType>
            <xsd:choice maxOccurs="unbounded">
               <xsd:element name="customer" minOccurs="0" maxOccurs="unbounded">
                  <xsd:complexType>
                     <xsd:sequence>
                        <xsd:element name="name">
                           <xsd:simpleType>
                              <xsd:restriction base="xsd:string">
                                 <xsd:maxLength value="20"/>
                              </xsd:restriction>
                           </xsd:simpleType>
                        </xsd:element>
                        <xsd:element name="phone">
                           <xsd:simpleType>
                              <xsd:restriction base="xsd:string">
                                 <xsd:maxLength value="12"/>
                              </xsd:restriction>
                           </xsd:simpleType>
                        </xsd:element>
                     </xsd:sequence>
                  </xsd:complexType>
               </xsd:element>
            </xsd:choice>
            <xsd:anyAttribute namespace="http://www.w3.org/XML/1998/namespace" processContents="lax"/>
         </xsd:complexType>
      </xsd:element>
   </xsd:schema>
   <customer>
      <name>Les Pinter</name>
      <phone>650-344-3969</phone>
   </customer>
</VFProData>

It looks scary, but if you saw the FoxPro DBF header it would be equally scary. Oh, what the heck. Figure 1.6 shows what the data that was displayed in XML looks like as a FoxPro .DBF.

The same data as a FoxPro .DBF.

Figure 1.6. The same data as a FoxPro .DBF.

As you can see, neither one is really a “table.” The program reads the file and uses it for whatever display mechanism you choose. As you can see, the tags that bracket each data element occupy quite a bit of space, compared to the positional format that fixed-length records permit; on the other hand, trimmed strings mean that extra blanks aren't transmitted. On balance, DBFs are somewhat smaller than their XML counterpart. So the idea that XML is less of a table than a DBF is just silly.

But the idea that XML is less efficient as a data storage mechanism is absolutely accurate. In fact, DBFs are extraordinarily efficient, especially when combined with the structural .cdx index files supported by FoxPro. I've demonstrated a SELECT that returned 30 matching records from more than a million in less than a tenth of a second. You can't get performance like that from SQL Server, which is a much more expensive data store. In some sense, XML is a dumbed-down DBF. Still, it does the job.

In FoxPro, on-screen controls use the ControlSource to bind to the data. Binding is bidirectional; change the contents of a text box, and the changes are automatically written back to the DBF. (There are a few caveats to observe, especially in multiuser environments, but this is basically how it works.) Grids have a RecordSource property that binds data to the grid in the same way that ControlSource does for controls.

Visual Basic .NET doesn't have tables or cursors. It has datasets. You fill a dataset by creating a SQLCommand or an OLEDBCommand object, specifying its SelectCommand CommandText, executing the SELECT, and then calling the Fill method of the Command object or DataAdapter object to fill the dataset. If that looks more complicated to you than USE (Table), you're right.

But wait, there's more. A dataset can contain several tables; in that sense it's more like a form's Data Environment. In fact, it's a lot like a form's Data Environment. A dataset can also contain indexes and relations, just as the DE does.

Things stop being equally simple about here. You can bind a recordset to a DataGrid, but you have to tell it which of the tables in the recordset to bind to first. If you don't, you'll get a little plus sign that you click on to display all available tables and choose one. Or you can specify the table. Table(0) is the first table, so if there's only one table, it's always the same code. You can also select the DefaultView of the dataset, which is table(0), and assign that to the DataGrid's DataSource:

SqlDataAdapter1.Fill(DsEmployee1)
Dim dv As DataView
dv = DsEmployee1.Tables(0).DefaultView
DataGrid1.DataSource = dv

This assumes that you previously dropped a SQLDataAdapter on the form and specified the connection to use to the ensuing wizard, and then right-clicked on the DataAdapter and selected Generate DataSet from the context menu, providing the name dsEmployee as the dataset name. Visual Basic .NET adds a 1, because it always adds a 1, even to the first instance of the class.

However, binding is one-way. In order to update the data source from whence the data came, you'll have to add code to get the changes made to the dataset and put them into a diffgram. Then you'll have to pass the diffgram to the SQLDataAdapter's Update method, which was generated automatically when the DataAdapter was created. It's just a few more lines of code, but the fact that in FoxPro it's not necessary is notable.

But that's not entirely true. If you use DBFs it's not necessary. But if you use SQL or a Web service as the data store, you have to build a mechanism for executing the UPDATE statement that applies the changes that you made in the local cursor to the data store. So it's harder in Visual FoxPro as well.

The fact is that sending back new records or updates to a local table in Visual Basic .NET is exactly as difficult as sending them to SQL Server. So the programming cost saving of working with local tables in Visual Basic .NET is gone. You might as well print out the bill for some SQL Server licenses and hand them to your client.

Data Binding

In FoxPro, when you change a field's value on a screen and the corresponding data field in the underlying table is changed, you're taking a lot for granted. What's happening is called data binding.

Visual Basic .NET doesn't have data binding to the data store. Not really. What Visual Basic .NET does have is binding to the dataset, the XML string that holds the data you got from your data source. Assume you've built a form with a text box on it and then returned a typed dataset. (See Chapter 4, “A Visual Basic .NET Framework,” for more information on the Customers table containing Phone fields.) Select the text box on the form, press F4 to open the Properties window, and click on the plus sign beside the Databindings property to open the two fields under the heading. Select Text, and then select Customers.Phone from the list of available dataset classes that automatically appears.

This will cause the dataset's Phone value to be copied to the text box's Text property when you get values for the rows in the dataset, and conversely it will move any value that's typed into the text box into the dataset's Phone column. That's what Visual Basic .NET calls data binding.

The dataset's AcceptChanges and RejectChanges methods correspond to FoxPro's TableUpdate() and TableRevert() functions, respectively. But that still doesn't get your data back to the data store. Think of the dataset as a cursor into which you've copied a record. You've saved the changes to the cursor, but you haven't copied the changed record back to wherever it came from. How you do that will have to wait until Chapter 4.

Error Handling and Debugging

After years of primitive error handling, both Visual Basic and Visual FoxPro have finally acquired real error-handling capabilities with the TRY...CATCH block. And Visual Basic .NET actually has more debugging features than does FoxPro.

TRY...CATCH

The syntax for TRY...CATCH is slightly different for Visual Basic .NET than for Visual FoxPro.

Here's the syntax in Visual FoxPro:

TRY
      Code here
Catch To oEx WHEN ERROR() = 1234
      What to do for this error
Catch To oEx WHEN ERROR() = 4321
      What to do for this error
Catch To oEx
      What to do for all other errors
Finally
      Do this whether there was an error or not
ENDTRY

At a minimum, you can print out the error details:

Msg = oEx.Message + CHR(13)+oEx.Details + CHR(13)+oEx.LineContents
MessageBox ( Msg, 32, "Error" )

And this is the syntax in Visual Basic .NET:

Try
      Code here
Catch oEx as ExceptionType1  (of about 60 exception types)
      Code to handle ExceptionType1
Catch oEx as ExceptionType2  (of about 60 exception types)
      Code to handle ExceptionType2
Catch oEx as Exception
      Code to handle all other Exceptions
Finally
      Do this under all circumstances
END Try

Similarly, you can print out oEx.Message + CHR(13)+oEx.Source to see the error message and offending code.

Visual Basic does have additional language elements (Error, Raise, GetException, Err, On Error, and Resume), but Try...Catch is a lot better.

Error trapping is meant to allow graceful handling of unavoidable errors in the program, due to device unavailability (CD-ROM, printers, floppy drives), incorrect input that can't be prevented, and other errors that are the result of normal program operation. Error trapping is not meant to be used to catch errors in program logic. Still, it's better than letting users see messages from the FoxPro or the .NET IDE.

In the code that appears in this book, we have left out error trapping in most code samples. That doesn't mean that we recommend leaving it out of your code. In fact, Try...Catch blocks are an excellent idea, and should be used in every case where a user or system error is possible. (It can even be helpful in finding your own coding errors during debugging, although that's not the purpose of Try...Catch.) But we've decided to focus on the main purpose of each code fragment, and adding error trapping to a single line of code tends to make the code harder to read. So with regard to error trapping, do as I say, not as I do.

Debugging

Debugging support is awesome in both languages. In FoxPro, you can either toggle a breakpoint by clicking in the selection margin of the code at the point where you want the code to break, or you can insert a SET STEP ON command. In Visual Basic .NET, you can either toggle a breakpoint by clicking in the selection margin of the code at the point where you want the code to break, or you can insert a Debugger.Break command.

FoxPro Debugging Aids

At this point FoxPro provides you with five windows:

  • Trace (to step through the code)

  • Locals (all variables and objects)

  • Watch (type in the names of the variables to inspect)

  • Call Stack (A called B called C, and so on)

  • Debug Output (Debug.print output goes here)

In addition, you can add assertions to your code. Assertions are statements that only execute if they evaluate to False, and only if you SET ASSERTS ON. You'll get a little dialog window that asks if you want to debug. This permits bracketing of debugging code in a way that it can be turned on and off with a single command in the command window.

Visual Basic .NET Debugging Aids

In Visual Basic .NET, Debugger.Break (or a breakpoint entered in the Selection Margin of the code window) gives you a vast array of debugging windows:

  • Modules

  • Threads

  • Call Stack

  • Me

  • Command window

  • Locals

  • Autos

  • Watch (four of them!)

  • Running Documents

  • Breakpoints

Each of these windows gives you some of the information about your program's state at the breakpoint. You can either go to the command window in Immediate mode and print the contents of individual Variables and object properties, type the name of the object in a watch window, or look in the Locals window to see if it's there already.

It's clear that both languages offer excellent error trapping and debugging capabilities.

Summary

By now, I hope you've come to the same conclusion I have, which is that Visual Basic .NET is not nearly as different as it looks. The few things that appear truly strange are not really very important; the use of events, when it's necessary, can be reduced to a six-step cookie-cutter procedure; property procedures practically write themselves, and do the same thing as FoxPro properties do; and the additional effort get to your data can be relegated to a data access layer class that does the same thing FoxPro does, so that you don't ever have to think about it.

In Chapter 2 we'll build a FoxPro application using form classes and a data access class, permitting us to switch between DBFs and SQL Server as the data store. This application will serve as a model for developing your first Visual Basic .NET application in Chapter 3, using very similar coding. Then in Chapter 4 we'll compare data access to tables, SQL Server, and XML Web Services, in preparation for upgrading our applications to work with any of the three types of data stores.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.196.227