Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows...

41
J uly 1997 Using VFP 5.0 Top-Level Forms Richard A. Schummer (1) Top-level forms open Visual FoxPro developers to a variety of opportunities. In this article, Rick gives you the low-down on this new type of form and describes some of its potential uses. One of the most requested interface capabilities in the past few years has been the ability to provide single document interface (SDI) forms, and they're here in Visual FoxPro 5.0. Also known as top-level forms, they run outside of the Visual FoxPro frame or main window. Each SDI form is independent and appears separately on the Windows desktop. This article will introduce you to the new top-level forms, explain what you need to do to create a top-level form, and describe how to use some of the new menu cap abilities associated with top-level forms. Finally, I'll discuss some implementations of this new capability. General overview A top-level form is a modeless form that runs without a parent form. It can appear in front of or behind other Windows applications, including Visual FoxPro. Each instance of a top-level form will appear on the Windows taskbar and can contain child frames as well. The VFP Debugger application is an example of an SDI Form. The main Debugger window can be configured to run outside the VFP main window by setting the Environment drop-down list box (in the Debug tab under Tools, Options) to Debug Frame . When set this way , several child forms are contained inside the Debugger "form." Another example of a top-level form is the Windows Help System. The help system also spawns other top-level forms for searching and the glossary. Form ShowWindow property The ShowWindow property is the only form property you need to set to make the form a top-level form. Three settings are available: Value Setting 0 In Screen 1 In Top Level Form 2 Top Level Form This property defaults to 0, which means all application forms will run inside the VFP frame. To make the form run outside the VFP frame, set this property to 2. Try running a form and you'll notice that you can move the form outside VFP. You could also do this in VFP 3.0b using the Desktop form property. The main difference between the ShowWindow and Desktop properties

Transcript of Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows...

Page 1: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

July 1997Using VFP 5.0 Top-Level Forms Richard A. Schummer (1)

Top-level forms open Visual FoxPro developers to a variety of opportunities. In this article, Rickgives you the low-down on this new type of form and describes some of its potential uses.

One of the most requested interface capabilities in the past few years has been the ability toprovide single document interface (SDI) forms, and they're here in Visual FoxPro 5.0. Alsoknown as top-level forms, they run outside of the Visual FoxPro frame or main window. EachSDI form is independent and appears separately on the Windows desktop. This article willintroduce you to the new top-level forms, explain what you need to do to create a top-level form,and describe how to use some of the new menu capabilities associated with top-level forms.Finally, I'll discuss some implementations of this new capability.

General overview

A top-level form is a modeless form that runs without a parent form. It can appear in front of orbehind other Windows applications, including Visual FoxPro. Each instance of a top-level formwill appear on the Windows taskbar and can contain child frames as well.

The VFP Debugger application is an example of an SDI Form. The main Debugger window canbe configured to run outside the VFP main window by setting the Environment drop-down listbox (in the Debug tab under Tools, Options) to Debug Frame. When set this way, several childforms are contained inside the Debugger "form." Another example of a top-level form is theWindows Help System. The help system also spawns other top-level forms for searching and theglossary.

Form ShowWindow property

The ShowWindow property is the only form property you need to set to make the form atop-level form. Three settings are available:

Value Setting 0 In Screen 1 In Top Level Form 2 Top Level Form

This property defaults to 0, which means all application forms will run inside the VFP frame. Tomake the form run outside the VFP frame, set this property to 2. Try running a form and you'llnotice that you can move the form outside VFP. You could also do this in VFP 3.0b using theDesktop form property. The main difference between the ShowWindow and Desktop properties

Page 2: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

appears when the VFP frame is minimized. If the Desktop property is used, the form disappearswhen VFP is minimized. If the ShowWindow setting is used to create a top-level form, the formwill remain visible even when the VFP frame isn't visible. With the ShowWindow property, eachSDI form also gets a separate entry in the Windows Taskbar. This doesn't happen with theDesktop property.

Hiding the main VFP window

So you just built your first SDI form after reading the first part of this article and now you'rethinking "What a powerful capability this is!" Several seconds later, you wonder, "How do I getrid of the VFP frame?" There are two ways to accomplish this. The first is a new VFPconfiguration setting called SCREEN in CONFIG.FPW. Just place the following line in theapplication's CONFIG.FPW:

SCREEN = OFF

This setting is critical if you want your applications to have a shrink-wrap look and feel. This isthe only way to remove the VFP frame during application startup.

It's important to note that this setting works differently with the development version of VFP.VFP is still displayed after the initial startup, but it isn't visible while Visual FoxPro boots orwhile a startup program called from CONFIG.FPW is running.

The second method is useful if you want to hide VFP when an SDI form is executed inside thedevelopment environment. To accomplish this, place any of the following lines of code in theForm.Init() method:

Application.Visible = .F.

or

_VFP.Visible = .F.

or

_SCREEN.Visible = .F.

To redisplay VFP when the form is shut down, place any of the following lines of code in theForm.Destroy() method:

Page 3: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

Application.Visible = .T.

or

_VFP.Visible = .T.

or

_SCREEN.Visible = .T.

If you don't make the main VFP frame visible with this second batch of code, you'll returncontrol to an invisible session of VFP when the form is closed. This is impossible to reinstate;you'll have to end the task or reboot. The application object (Application, or _VFP) is an objectreference created for each instance of Visual FoxPro. This exposes properties and methods foreach application. You can also use _SCREEN, as you did back in the days of VFP 3.0.

Where to use top-level forms

Now that you have all this new information to create really cool forms, how can you use thistechnology to better your customers' applications? So far, I've found five uses: application splashscreens, extended legacy applications, launcher forms, a replacement for _SCREEN, and VisualFoxPro developer utilities.

Splash screens

The splash screen is the most obvious use for top-level forms. For developers who aren't familiarwith splash screens, they're the forms that are displayed immediately when an application isstarted to keep the user occupied while the rest of the application is loading (see Figure 1). Theirpurpose is typically to distract users from noticing the lag time that occurs before applicationsare ready to use. By using SCREEN = OFF in the CONFIG.FPW and an SDI form you cancreate a start-up process that mimics many shrink-wrapped applications.

Download file 07SCHUMM.ZIP includes a form called SPLASH.SCX, which demonstrates howto build a splash screen. Here are a couple of quick notes about this form: Obviously I set theShowWindow Property to "2 -- As Top-Level Form," but several other form properties give thistype of form a style, like many shrink-wrapped packages we buy:

Page 4: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

ShowWindow = 2AutoCenter = .T.BorderStyle = 0Caption = ""ControlBox = .F.Closable = .F.MaxButton = .F.MinButton = .F.Movable = .F.AlwaysOnTop = .T.

Most of these properties are self-explanatory, such as no caption, no close button, no minimizeand maximize buttons, and no control menu. So why is the Movable property set to false? Thiswas set to remove the title bar at the top of the form. It took me awhile to figure this out; I hopemy discovery saves you some frustration.

Extended legacy applications

How many times has one of your customers asked for new functionality in a legacy application?He or she may want to update a FoxPro 2.x application that has been running in production forquite some time; and you may already be working on the customer's next killer application inVisual FoxPro. If you've been developing in VFP for a while you may find it difficult to useFoxPro 2.6 DOS for Windows, but the customer is willing to pay. To keep your customersatisfied, you agree to extend the functionality. What if you could add the functionality usingVisual FoxPro without rewriting the entire application?

One example of this is a postal code search routine I developed for my wife. She maintains acustomer list for a small company using a FoxPro application developed in FoxPro 2.6 forWindows. Because I didn't have time to deal with the intimate details of the application and shewanted to incorporate a postal code lookup routine, I quickly developed an SDI form that runs asa standalone app. She continues using the FPW 2.6 app and runs this quick SDI form to performher lookups. Now she can copy the postal code or city to the Windows clipboard and paste it intothe customer data entry form.

This is a unique way to incorporate Visual FoxPro technology into legacy applications withoutrewriting the entire system. In my case, I didn't have time to convert the old application anddidn't want to write version 2.6 code. One drawback to this type of extension is that the machinewill load both the FoxPro for Windows code and the Visual FoxPro code at the same time, whichcould be a problem on machines with only 8M to 12M of memory.

Launcher forms

One neat idea, which was presented during beta testing of VFP 5.0, related to SDI forms: A"launcher form" (see Figure 2) consists of an SDI form with CommandButtons or icons that canbe clicked to run other forms. This is one kind of menu interface that can be presented to a clientwithout using conventional menus. It's also a way to present a Windows 95 or Windows NT 4.0Explorer interface in your custom applications. An example of this type of interface is the

Page 5: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

Windows Control Panel.

I built a simplified example of this concept in an application called SDIART.EXE, which is alsoincluded with download file 07SCHUMM.ZIP. This application starts by presenting a launcherform. Clicking on the different forms in the ListView object will execute the other SDI forms.The ListView object is populated with all the forms that are in the default directory. You can addnew forms by simply dropping a new form file in the directory. This may not be the ideal way toextend a customer's application, but it shows the power of the "launcher" concept.

This launcher form contains a top-level menu (see the sidebar "Top-Level Menus") that's usedto control the form. Accordingly, the File . . . Exit menu option must be used to terminateSDIART.EXE. Simply closing the form will close the launcher form without terminating theEXE program. (If you close the form by mistake, use the Program . . . Cancel menu option in theVFP menu.)

_SCREEN replacement

Replacing the _SCREEN simply involves building a top-level form that houses the application.This top-level form contains a top-level menu and one or more child forms. These child formsare designed the same way as in VFP 3.0 except that they become child forms of the top-levelforms.

What are the advantages of building a replacement for the _SCREEN? The primary advantage isthat you can add custom properties and methods to a top-level form, while you can't modify the_SCREEN object. This is a very powerful capability. One use I've read about is adding code tothe "application" window Resize() or Click() methods. If the application window is resized youmight want to perform some action. None of these methods exists for the _SCREEN object.

Visual FoxPro utilities

I often use SDI forms for interactive development utilities that I want to run separately from thedevelopment version of VFP. It keeps the VFP frame clean and allows the application to bequickly accessed from the taskbar when I need it. I start the utility once and let it run in the"background." One example of these utilities is the Hex Editor that ships with Visual FoxPro.

I wrote a utility called Hack VCX/SCX. Have you ever renamed a Visual Class Library? I canalready hear your groans and see the looks of terror on your faces. What happens next? Tryopening a form that has objects you renamed in the class library and you'll get this message:"Error instantiating class. Cannot find <classname> in <class library>." Sure, you can locate therenamed classes and display the form in the Form Designer. Then if you close the form and openit up again, you'll see the locate dialog box again. Even though you pointed the object to thecorrect class, the Form Designer never noted that the form changed to kick in the Save logicwhen closed down. This sounds like a bug to me. You can change the form yet again to workaround this. Even more aggravating is that every object that was renamed (or if the class librarywas renamed) will spawn the locate dialog box. The same dialog box is displayed over and overeven if the new class or class library was located before. This can be quite frustrating after the

Page 6: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

first five changes.

So what happens when something goes wrong and you need to get inside the .VCX or .SCX?You can USE the file and browse it. Unfortunately, all the information you want to see is storedin memo fields. Then you have to look for all the fields you want to look into, resize them, movethem elsewhere so the information is viewable, and so on. The next time you open the file with aBROWSE LAST, the file will be okay, but a new class will cause you this grief all over again!

I built the Hack VCX/SCX form (HACKFORM.SCX in download file 07SCHUMM.ZIP) to getaround the configuration frustrations that accompanied the BROWSE technique. This top-levelform allows you to select a form or visual class library. It opens the file and displays theinformation inside it. I protected some of the fields I felt I had no business changing. The rest areopen to modify to my heart's content (or until I can disable the file altogether).

When you "do form HACKFORM," you'll be presented with the Open dialog, and if you'rerunning in the same location as the source code, you'll be tempted to open one of the sourcecode's class libraries (cBase or cCustom). Resist the temptation and instead open one of yourown class libraries.

One of the nicest things about this utility is that, when the table is open, you can manipulate thedata in ways you've been using for years with invoices, customers, and widget tables using theBROWSE command. You can even make global replacements with Hack VCX/SCX. Jump overto Visual FoxPro and bring up the DataSession dialog box. Change the DataSession to the HackVCX/SCX session (private datasession for the form). Type in the REPLACE logic in theCommand Window:

REPLACE ALL ClassLoc WITH ; "c:\devvfp5apps\common\cBaseClass" ; FOR UPPER(ClassLoc) = ; "C:\DEVVFP5APPS\COMMON\BASEOBJ"

Presto -- the changes are reflected back in the form. This form presents the viewing capability ina more usable format than BROWSE, without sacrificing flexibility. It doesn't help the customerdirectly, but if it helps the developer spend less time getting the application to market, itultimately helps the customer.

Issues with top-level forms

Here are some issues I've experienced with top-level forms that I want to pass along:

• Menu shortcut keys such as cut, copy, and paste aren't automatically available. You mustuse a top-level menu or construct an interface on the form to gain this functionality.While working on the beta version, I passed along a tip to gain the functionality of menusby running the menu and then deactivating it so the features were available, but the menuwasn't visible. Build a top-level menu using the Edit menu bars from Quick Menu. Thenplace the following code in the Form.Init() method:

Page 7: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

DO SdiEditMenu.mpr WITH THIS, .T.DEACTIVATE MENU (THIS.Name)

• Reports called from the form -- those that run in the print preview -- run in the VFP

Frame. This causes the VFP Frame to appear when the report is run, and disappear whenthe report preview is shut down. This might be disconcerting to the user. If this is aproblem for you, check out the Microsoft Knowledge Base for information on ways todeal with this problem.

• Top-level forms don't automatically include a status bar via the SET STATUSBAR ONcommand. You either need to make your own status bar or use an ActiveX Status Barcontrol.

• Make sure you don't have any Application.Visible = .T. settings in your code unless youwant the VFP Frame to become visible. If you have an SDI form that seems to bring upthe VFP frame as well, this might be the case unless you forgot the SCREEN = OFF inCONFIG.FPW.

• Don't forget a READ EVENT in calling a main program if you intend to make the form astandalone .EXE; otherwise the form starts and shuts down. This isn't specific totop-level forms, but it's more likely that you'll run into it while you're using top-levelforms.

• Form icons default to the "Windows" default icon instead of the FoxPro logo. This isacceptable, but it doesn't reflect the purpose of the form.

Conclusion

Ever since FoxPro was released on the Windows platform developers have asked for thecapability to build applications without displaying the main FoxPro window. They stated thattheir applications would be more professional and polished if they could start customizedsolutions with a splash screen, followed by the main application frame. Visual FoxPro 5.0 allowsyou to develop applications with this functionality. Enjoy!

Rick Schummer has been using FoxPro to develop computer-based solutions for a Fortune 1 company forthe last seven years. After hours, he enjoys writing developer tools to improve his team's productivity.Rick is a founding member and secretary of the Detroit Area Fox User Group (DAFUG) and president ofthe Sterling Heights Computer Club. He is also a regular presenter for these organizations and other usergroups, and is available for conferences.

Sidebar: Top-Level Menus Top-level menus allow VFP developers to add menus to top-level forms. The top-level menuruns in the top-level form just like a standard VFP menu runs in the VFP frame. There's no real

Page 8: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

magic to these menus, but they're slightly different from the standard menus we've been using foryears.

The first difference involves checking the top-level form on the menu's General Options dialogbox (see Figure 3).

GenMenu generates code in the .MPR file, which includes some new code that handles theneeded changes for the menu to run in a top-level form. It also has some excellent documentationon how to implement the menu.

For years in FoxPro we've been running menus by running code such as this:

DO MainMenu.mpr

Top-level menus are run in a similar fashion from the SDI Form.Init() method:

DO SDImenu.mpr WITH THIS,.T.

The two parameters in this call are specific to top-level menus. The first parameter is a referenceto the form. The second parameter can either be the name of the menu or a logical that tells themenu to rename the form to the name of the menu. This is used if you run multiple instances ofthe form. If you choose to change the form Name property via this parameter, make sure that allreferences to the form include THISFORM or THIS -- not a hard-coded reference to the Nameproperty. Otherwise your code may not work.

Remember to remove the menu from memory in the SDI Form.Destroy() method unless youwant to reactivate it later in a new form (this is helpfully stated in the generated code). If you runthe menu using the parameters in the previous example, you can place the following code in theForm.Destroy() method:

RELEASE MENU (THIS.Name)

Time to Test: The Search for an OperationsManager Whil HentzenOnce in a while, I get a good idea. To be more accurate, once in a while I steal a good idea.Shortly after I moved back to Milwaukee, I started running around with a bad crowd -- a groupof rebels who didn't tolerate the status quo, who did things their own way, who had to bedifferent. I'm talking, of course, about the gang from RDI: Grimsted, Kehoe, Hargreave, Leone,

Page 9: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

Florian, Lanzer, Opperthauser, Koenecke, and some others.

These were folk who thought that "software engineering" wasn't simply a catchy phrase thatlooked hot on a business card -- folk who, as much as possible, actually walked their talk. Andthe cool idea that I, er, borrowed was having a separate testing department.

Of course, at the time, I was working out of my den while RDI had 30 or 40 people. Theyactually had a department, while I had two cats and a couple of kindergarteners. But somewherethe idea of hiring local high school kids popped into my head, and things around the shop werenever the same.

First, I could no longer program in my skivvies. There was always a kid or two hanging around,waiting for the opportunity to bust some application that I thought was finished. Second, Istopped getting calls from customers asking "What does `Under Construction' in theOperations/Import Transactions menu mean?" And third, I started sleeping better.

There were a ton of ancillary benefits to this process as well, and I've yammered on about themelsewhere. I highly recommend the process -- I've never regretted any aspect.

But then growth set in. I moved into an office downtown (yes, where the lights are bright), andstarted hiring people. My first hire was a junior programmer -- a couple years of Fox experience-- and he helped out a lot. So it was just the two of us for a while, writing code, answering thephone, and shipping EXEs.

But the high school kids couldn't come downtown as easily as they could ride their bike over tomy house. Quality began to slip, at first imperceptibly, but then a noticeable incident occurredhere and there. At the same time, more people started showing up for work: an administrativeassistant, some more technical people. All of a sudden, I'm no longer writing code, I'm running acompany. And we're still not testing the way we had been.

Time for a full-time QA person.

However, we have several developers who have been working without the discipline of havingto send modules to test before shipping them, and it's easy to fall under the spell of customerswho say "Of course I want it today! If I wanted it tomorrow, I would have called you tomorrow!"

So I find myself playing the role of production scheduler, controller, and chief Test PlanWriter-Upper. In addition to all that other stuff, right? So writing code happens at 2 a.m. (someof you will argue that 2 a.m. is the only proper time to write code, but work with me on this, willya?). You can write an awful lot of code at 2 a.m. while Ozzy Osbourne wails in the background.And that's a very precise description: a lot of awful code.

So the next step, after I recover from the hate mail in response to last month's "Last ManStanding" editorial, is to find an operations manager -- the person with whom I can share duties.I do the sales and R&D while they run the factory. Some may call it the business manager; othersrefer to this partner as "the handler."

And here's where I'm supposed to come up with an ending for this column. But I don't have one.

Page 10: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

As of this writing, I'm looking for that person -- to be sure, there are several good candidatesbutthe chapter hasn't been closed. The addition of an operations manager marks yet another turn inthe road from one-man shop, to a couple of developers, to a "real" business, to a company withseparate "departments" for sales and manufacturing.

Details at 11.

FoxPro on the Internet William J. O'Connor

Let's face it: FoxPro and Visual FoxPro are difficult to use when you have no help. However, manyresources are available to you that cost essentially nothing -- and they're all on the Internet. Thisarticle discusses many of the resources that you can access from your office.

Well, your boss (or accountant) just told you that there's no money left to send you to FoxProDevCon or the latest training class that would let you accomplish your job twice as fast as youcould before. Well, take heart, bunky: the Information Superhighway is here for you.

FoxPro has long been represented on CompuServe through the FOXFORUM, FOXUSER, andVFOX forums. (The first two recently merged into one forum: FOXUSER.) These are veryactive places to find excellent information and assistance about FoxPro and Visual FoxPro. Of amore recent vintage is FoxPro on the Internet, and there's a lot of FoxPro on the Internet. Ifyou're tired of spending time on the Captain Kirk Sing-along page(http://www.loskene.com/singalong/kirk.html), I can show you how to get some work out of theWeb.

Fox on the Web

The World Wide Web is the hottest technology available right now, and many FoxPro andVFP-based pages are being updated and improved to take full advantage of it. Following is alisting of popular FoxPro sites and descriptions of what each has to offer the FoxPro or VFPdeveloper.

Microsoft's Visual FoxPro page () contains the latest in official information on FoxPro, casestudies, and downloadable software. Last November, Microsoft redesigned the Web pages andintroduced ActiveX controls. This is definitely one of the coolest FoxPro Web pages availablefor browsing.

The Visual FoxPro Yellow Pages , compiled by Michel Fournier(http://www.transformation.com/foxpro/index.html), is one of the best and most ambitiousFoxPro-oriented pages on the Web. The Visual FoxPro Yellow Pages are home to the firstvirtual user group accredited by Microsoft. Access to the Universal Thread is free, but limited.

Page 11: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

An additional level called the Premier Universal Thread allows unlimited access to the thread aswell as search capabilities. One interesting feature of the Universal Thread is the ability to attachphotos to mail messages. The Universal Thread is a Visual FoxPro-based system. Unlike mostUsenet newsgroups, it handles threading very nicely. Accessing the Premier Universal threadrequires payment of $35 U.S. or $50 Canadian.

Colin's FoxPage , by Colin Keeler (http://www.state.sd.us/people/colink/fox_page.htm) has theultimate list of Visual FoxPro links and resources. If Visual FoxPro is referenced somewhere ona Web page, Gopher database, Usenet newsgroup or mail list, chances are it's mentioned here.

At Ken Levy's Home Page () you can get the latest editions of his excellent tools, includingBuilderB, Superclass, and more. This site is fairly new, but it has a number of great utilities.

Steven Black Consulting (). Steve Black is FoxPro's internationalization guru, and offers a FAQrelating to internationalization issues. Black goes over nearly every command in Visual FoxPro3.0 and 5.0 and explains how each command can be used in a multiculturally friendly manner.He also has a links page and a downloads section for various tools and session notes fromconferences he has attended. Make certain you read his notes on patterns in Visual FoxProprogramming.

Neil's FoxPro Resource Page (http://adams.patriot.net/~johnson/neil/fox.html) focusesprimarily on giving access to a number of other FoxPro resources. In fact, his page wasinvaluable to me as I wrote this article. Through Johnson's site you can get instructions on howto subscribe to a mail list, find a list of FoxPro third-party vendors, access an excellent list ofFoxPro-related links, and much, much more. This is an excellent jumping-off page in yoursearch for Visual FoxPro information.

The FoxGang (). This page is run by Mike and Toni Feltman of Neon Software. It isn't acorporate page, but it's billed as a gathering point for anyone interested in FoxPro. They offer avariety of services, such as Web site hosting, domain hosting, and e-mail, among others. Theseservices are available for a fee, but you can tailor your plan according to your needs. One benefitof FoxGang that you may not receive from other Web hosts is the ability to run Microsoft'sFOXISAPI on their server. Several packages are available at discounted prices. A number of freeresources are available, including downloadable software, articles, FoxPro links, and informationon events and books. One of the site's more unusual features is that each of the resources ispresented to you in the form of a filterable, sortable list. Even if you don't join FoxGang, this siteis well worth bookmarking.

The FoxPro I/O Address (http://www.hop.man.ac.uk/staff/mpitcher/foxpro.html) was one ofthe first FoxPro-specific Web pages. It's a very interesting site that offers a lot of goodinformation (including Whil Hentzen's "17 Dreaded Questions for Job Seekers"). Its creator,Malcolm Pitcher, has collected a lot of information, including a searchable list of Internet maillists, which should be functional by the time you read this.

Willy De la Court's Visual FoxPro Add-Ons (http://www.ping.be/~ping0150/vfp.html) is anEnglish language FoxPro Web page from Belgium, which contains exactly what it advertises:add-ons. Willy has divided his add-ons into Development Tools, 32-bit ActiveX controls, VFP

Page 12: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

Loadable Libraries, and Visual Class Libraries. I even found a couple of programs at this sitethat I contributed to the public domain!

Jason Landry's Home Page For Garage Logicians (). Landry has created a home page forlovers of Ken Levy's GENSCRNX. He's written a number of GENSCRNX drivers and has madethem available at no cost to the general public. He also has several drivers written by others,including Steve Black's TABS driver. Articles on using GENSCRNX and creating your owndrivers are also available.

Creative Programming Unlimited (http://www.business1.com/cpu18/cpumod.htm) specializesin SBT programming. Their Web site is part commercial to advertise their services, and parttips-and-tricks for programming with SBT. They also provide links to other SBT resources.

SBT (http://www.sbt.com/sbtcs/index.html). This is SBT's presence on the Web. It includes tipsfor modifying SBT's programs for your own purposes.

The Perth FoxPro Page () contains Gordon Angus' Visual FoxPro FAQ page. The FAQ isprimarily based on the comp.databases.xbase.fox newsgroup. The site also has a number of filesavailable for download.

Visual FoxPro FAQ , by Bill O'Connor (). This is my personal Visual FoxPro page. It has verylittle in the way of pretty graphics, wondrous formatting, and so forth, but does contain a lot ofVisual FoxPro information, tips, tricks, and opinions. It's usually updated monthly frommessages that I find on CompuServe and other information that I receive via the Internet.

Developer's Studio Apartment () is the company page for FoxTalk's editor, Whil Hentzen.Hentzen's page features a searchable article index for most of the popular programmingmagazines. The index won't give you the text of the article (that would be a copyright violation),but will give you the article's title, author, magazine name, publication date, and the articledescription. You can search by author, key words, date of publication, programming language,and magazine title.

xCase Company Page () is the corporate page for xCase. It contains xCase demos, white papers,and other information.

Aditi FoxPro Page (http://www.aditi.com/docs/fox/foxpage.htm) features Visual FoxPro newsand tips. It offers shareware VFP FLLs, including an offer of free technical support (24-hourturnaround via e-mail) along with FoxPro 2.x and VFP FAQs for beginner, intermediate, andexpert programmers.

Visual FoxPro por Pablo Almunia(http://ourworld.compuserve.com/homepages/palmun/vfp.htm). This is an all Spanish languageFoxPro page with VFP articles, files, links, and conferences. Of particular interest are severalarticles on using the Windows API to write to the Windows Registry.

Donovan Louck's FoxPro Page (http://www.primenet.com/~dloucks/foxpage.html) doesn'thave much in the way of FoxPro information, but it does have links to a number of FoxProresources. More interestingly, it features a number of links to user-interface resource pages and

Page 13: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

database design resources.

FoxPro WWW Discussion Board(http://dataperipherals.com/html/wwwboard/wwwboard.html) is a Visual FoxPro discussiongroup. It looks very much like a newsgroup, with a topic and responses, but is entirely HTMLbased. You don't need a newsgroup reader to access it. This is a fairly active group and themessages don't disappear after you read them or after a set period of time as they might on anewsgroup or on CompuServe. The only downside is that the discussion group is kept in onevery large page and it can be a fairly slow process to find the topic you want. It might be betterserved by adding subject categories that would allow a visitor to more easily find a topic ofinterest.

Fox in the news

comp.databases.xbase.fox is the original FoxPro peer support newsgroup on the Web. It's a fairlyactive newsgroup containing generally good information. The signal-to-noise ratio is good, but itsuffers from the occasional spam, though seemingly less often than many other newsgroups. Itreceives around 600 messages per week.

Microsoft has its own news servers that aren't part of the general Usenet system. To access them,you have to set your newsgroup server to msnews.microsoft.com . These are the "official"sources for Microsoft technical support, though support is provided by other users. These newsservices are monitored by Microsoft, whereas the other resources are not. You still probablywon't get a response from Microsoft, but there's a chance they'll see your message and respond.

Fox by mail

If you have an Internet e-mail account, your best option for support might be an Internet maillist. These are run entirely through Internet e-mail. Colin's FoxPage has a number of FoxPro maillists to which you can subscribe via his Web page. E-mail lists have several advantages anddisadvantages. The biggest advantage is that mail lists are handled automatically. You don't needto do anything more than download your e-mail to get the messages. Because mail lists areusually moderated, they're remarkably free of Internet spam (advertising that gets spread to anumber of Usenet groups). Unfortunately, the incidence of thread drift is the same as within mostother forums.

Mail lists are normally available in two forms: interactive mode (you'll receive every individualmessage as it passes through the list server) and digest mode (messages are collected and onceper day you'll receive a message that contains all the collected messages from the list serve). Thebiggest disadvantage to a mail list is that thread handling is no better than what your mail readercan provide. It can be very hard to keep track of a topic as it gets spread across several e-mailmessages. Also, you may receive several responses with the same content from different people.A mail list is usually also available in a digest form, which gives you the daily messages to thelist in a compiled form. If the digest isn't properly formatted, it can be very difficult to follow atopic.

Page 14: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

Subscribing to a mail list

You can subscribe to a mail list by sending a message to the moderator or to an automatedsubscription service. Here are a couple good ones to try.

University of Kansas FoxPro Mail List . To subscribe, send a message tolistserv%[email protected] with "subscribe" and your first name and last namein the message body.

Hourglass FoxPro Mail List . Send a message to [email protected] and put thefollowing in the message body: subscribe hourglass [your email address]. Don't type bracketsaround your e-mail address. Then, using your favorite Internet browser, gotohttp://www.magmacom.com/~whyted/listserve/Index.htm and fill out the form. Once you havesubscribed, you can post messages that will be seen by all other subscribers by sending amessage to [email protected].

Gopher broke

Gopher is perhaps the most unusual of the Internet resources. It's similar in some ways to theWorld Wide Web because it's document based and searchable, but the resemblance ends there.You can access Gopher through one of several search tools: Veronica, Archie, or Jughead.Several FoxPro resources are available on the Internet, including the following:

• Microsoft's Searchable Knowledge Base --gopher://198.105.232.4:70/77%5ckb%5cdevelopr%5cfox%5cfox.src

• Rocky Mountain Mail List Gopher Search --gopher://halfdan.med.umn.edu:70/11/Computer%20Information/Foxpro%20List

Conclusion

The only guarantee in our business is that what you're doing now will change by the time youturn the page. The folks in our business who bury their heads in their cubicles and attempt tolearn by paging through the manual and occasionally pressing F1 are destined to be yesterday'snews. Dozens of rich resources are available, and with the Internet as the delivery vehicle they'rejust a mouse-click away. Avail yourself of these resources or end up in the crowds who'll bewondering why no one told them that the world was going to change.

William O'Connor, the senior programmer at ContrAcct Systems Corporation in Naperville, Illinois, hasbeen programming in xBase for 11 years and in Visual FoxPro for two years. He is the treasurer for theChicago FoxPro User/Developer's User Group. He is also a tenor. [email protected].

Using DBCx to Data-Drive YourApplications

Page 15: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

Whil Hentzen (2)

"Been there, done that" -- getting tired of this late `90s bromide? Nearly as tired as doingsomething for the 50th time, I'll bet. While the VFP 5.0 database container has a number ofimprovements over previous versions, it isn't complete. In this article, I'll examine a number ofplaces in your applications where data dictionary extensions are still needed, and show you how touse a publicly available data dictionary extender, DBCx, to provide that functionality.

The Visual FoxPro Database Container (DBC) is an excellent starting point for storing meta datafor applications, but as I've described in previous FoxTalk articles ("Extending the VisualFoxPro Data Dictionary," May 1995; "Extend the Visual FoxPro Database Container with EDC,"August 1995), it isn't complete. Two solutions were proposed during the VFP 3.0 beta cycle --Tom Rettig's EDC and DBCx, the result of a collaborative effort between Flash CreativeManagement, Micromega Systems, Neon Software, and Stonefield Systems Group.

Two years later, VFP has matured and the database container has received some enhancements,but the fundamental requirements for extending the data dictionary remain. Let's revisit what thedatabase container does in VFP 5.0, what it still won't do, what we would like it to do (or, atleast, what we need), and how we can get that functionality from DBCx.

What we can get from 5.0's DBC

Let's first review what we're starting out with. The DBC has a row for every table, field, index,view, and connection (as well as a few rows for other specialized objects). Each row containsfields for a unique ID, an ID that points to the parent object of that row's object (for example, theparent of a table is the database; the parent of a field is the table), a description of the object, anempty, unused memo field named User, and most importantly, not much else.

Well, okay, there is a memo field called Properties that looks promising. However, upon furtherexamination, it isn't all that helpful. It contains data that you can enter into various controls in theModify Structure dialog box, such as Display Format and Validation Rule. Why isn't thishelpful?

Four basic types of information can be entered in the Modify Structure dialog box. Fieldcharacteristics such as size and type are stored in the header of the DBF itself. Display, Fieldvalidation, and Map field information is all stored in the Properties field, but to see why this isn'tuseful, let's examine how this information is used.

Look at the Field Mappings tab in the Tools/Options dialog box (see Figure 1). At the bottom ofthe page, four check boxes specify what will happen when a field is dragged from the DataEnvironment to a form during design. If the Drag and Drop Field Caption check box is selected,the value in the Modify Structure Caption field will be used as the caption for the adjoining labelcreated when the field is dragged from the DE to a form.

Similarly, if the Copy Field Comment, Input Mask, or Format check boxes in the FieldMappings tab are selected, the values in those controls in the Modify Structure dialog box will

Page 16: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

be placed in the respective properties for that field when the field is dragged from the DE to theform.

However, try this: Make a change to the comment, and then drag the field from the DE onto theform again. The new comment will be reflected in the new field, but the first copy of the fieldwill still have the old field comment. Maybe comments aren't a big deal, but other properties are-- such as Format and Input Mask.

Next, let's look at the Validation rules. Although they're entered in the Modify Structure dialogbox and aren't stored in the Properties field, changes do propagate during the life of theapplication. The reason is that the VFP database engine actively reads from the DBC duringruntime to evaluate the values in the DBC that involve validation.

Finally, let's examine the Map Field Types to Classes controls. In the Field Mapping tab of theTools/Options dialog box (see Figure 2), you can specify which of your classes you want to useas the default class when dragging fields from a DE to a form, instead of using VFP's baseclasses. Each data type can be defined differently -- typically, you might specify a check boxclass for the logical data type and an edit box class for memo fields.

The default mappings that you selected can be overridden on a field-by-field basis for specificneeds with the Map Field Types to Classes controls in the Modify Structure dialog box. TheDisplay Library value points to the VCX while the Display Class is the actual class itself. Thesesettings override, for a specific field, which class will be used when dragging that field to anyform. This information is also stored in the Properties field in the DBC, and, like the Displayproperties, is read only once when the control is first created. Changes made to the Map FieldTypes controls aren't propagated through the application as changes are continually made tothem.

So, the bottom line is that much of the information entered into the Modify Structure dialog boxand stored in the Properties field in the DBC is used only once. We'd like that information topersist through the application through its lifetime -- not just upon initial application.

What's missing from the DBC?

One requirement of a complete data dictionary is that the developer be able to rebuild the datastructures from the information in the data dictionary. Because structural information, such asfield type and size and index tag expressions, isn't stored in the DBC, this is impossible. Thismeans that we can't build a reindex routine, a rebuild routine, or other similar maintenanceroutines that rely solely on information in the DBC.

But there's more missing than that. It doesn't take a lot of imagination to think of otherinformation about the data structures that we'd like to keep around. Starting with the tablesthemselves, useful information might include long descriptive names, a flag for whether the tableshould be opened upon application startup, an alias to use during opening, and perhaps a defaultor specialized location.

With fields, we need several types of long descriptive names. The first name would be used for apersistent caption for an associated label. The next could be used in grids and lists that use the

Page 17: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

field, and a third for report headings. Obviously, we need to store length, type, and whether nullsare allowed. Other useful things include persistent formats and input masks, as well as tool tips,help text, status bar text, and allowed data ranges.

Indexes can use a long descriptive name, a flag indicating whether they're selectable by the user,and a comment field.

A few more minutes of thought might reveal a variety of other information that you might like tokeep around, such as security-related settings that allow read/write access or even visibility ofselected fields. Or how about combined fields for output, such as the concatenation of first,middle, and last name fields -- there's no place for this type of meta data in the DBC.

How do we get there? The DBCx concept

Obviously, we need to store this additional meta data somewhere -- let's review quickly what theoptions are. The first choice of many developers might be that unused empty memo field in theDBC named User. Microsoft left it there for the express purpose of DBC extensions. Why notjust store information in this memo field?

For example, we could store the following data in the DBC record for the League Name field,cNaLeague:

Caption: NameList: Lg NameReport1: LeagueReport2: NameFormat: Help: The League Name identifies the specific group that plays on a . . .StatusBar: Enter the League Name

This approach has many problems. Two obvious issues involve performance and conflict.Parsing out data from memo fields is time-consuming compared to simple SELECTS from anoptimized table. And what happens when more than one type of meta data is being stored in theUser field? If you're the only person using the User field, you're safe, but your troubles begin theminute you decide to take advantage of a third-party tool that uses the User field to store moreinformation.

The originators of DBCx started out with the same concept as Tom Rettig did with EDC: store asingle key value in the User field that points to another table. But that's pretty much where thesimilarity ended. Because one requirement of DBCx was to allow multiple third-party vendors toplay in the same sandbox without fighting, a single table to store additional meta data was goingto become difficult to administer.

Instead, they used the concept of a registry -- anyone who wanted to share in the DBCx plancould include his or her own record without stomping on anyone else's turf. Each of theserecords would point to the data dictionary extensions for that third party. Because many

Page 18: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

developers had common needs, the collaborators agreed to use Flash's Codebook as the locationfor a set of basic extensions, and then each would write his or her own extensions as needed afterthat (see Listing 1).

Listing 1. The record in the registry, DBCXREG.DBF, that points to theCodebook extensions.

DBCXREG.DBFmDBCPathcDBCNamecProdName CodebookcVersion V3.2mDBCXPathcDBCXName CDBKMETA.DBFcDBCXAlias CODEBOOKmLibPath ..\..\..\COMMON50cLibName CDBKMGR.VCXcClassName CdbkMgriLstIDtLastUpdtlDefaultcObjName

It's important to remember that the records in the DBCXREG.DBF registry are simply pointersto a second set of tables. This second set of tables actually contains the extensions to the datadictionary. Typically, they each take the form of a table and each field in the table represents adifferent extension (such as long descriptive table name, field length, index tag, or tool tip text).Each record in the table is usually mapped to a corresponding record in the DBC via the IDstored in the DBC's User field. Each table is supplied by a different third party, such asCodebook, Micromega, Neon, or Stonefield.

Thus, if the DBC record for the League Name field had a User ID value of 422, there would be amatching record in the Codebook table that had a unique ID of 422, a matching record in theMicromega table that also had a unique ID of 422, and so on. See Figure 3 for an example of therelationships between the tables.

By now, you may think that we're done. We've seen how all the data hooks together, but there's amissing piece: the set of programs that gets all of these pieces to work together. It isn't evidentthat the piece is missing because there's no corresponding piece for DBC. Well, actually, there is,but it's invisible -- it's part of the VFP engine inside VFP.EXE. Because DBCx isn't part of VFP,it has to be driven by a set of external programs.

DBCx consists of a class library, DBCXMGR.VCX, that contains pointers to class libraries foreach extension, such as CDBKMGR.VCX, SDTMGR.VCX, and so on. In order to get thingsgoing, instantiate DBCXMGR like so:

Page 19: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

set classlib to DBCXMGR additiveoMeta=createobject('MetaMgr',.f.)

These two lines of code create a "meta-manager" object onto which are hung additional objectsfor each registry entrant in DBCXREG.DBF. Each of these objects has its own custom methodsand properties that allow access to its data dictionary extensions. Third-party products typicallyprovide a set of wrappers to shield you from having to delve deeply into the DBCX internals --after all, that's their job, right? For example, the reindex routine in the Stonefield DatabaseToolkit can be called like this:

oMeta.oSDTMgr.lQuiet = .f.m.llIndexWorked = oMeta.oSDTMgr.Reindex()if m.llIndexWorked wait window nowait "Reindex successful"else messagebox("Reindex was unsuccessful. ; Call for help.")endif

Let's look behind the scenes at what's happening, and what you would do to manually accessmeta data through DBCx. There are two basic steps. First, given a particular object, such as atable, field, or index, find its User ID. Next, given that ID, find the specific value for a datadictionary extension of interest, such as a long descriptive name for an index or a tool tip for afield.

For example, suppose you want to get the long descriptive name and the status bar text for afield. Here's how you'd get the ID:

m.liIDOfField = oMeta.DBGetDBCKey(dbc(), ; 'Field', 'Database.Field')

In this line, "Field" is the actual literal parameter but "Database.Field" represents a string such as"Customer.Address." Remember that m.liIDOfField is the value in the User field, such as 422 forLeague Name, as described earlier in this article.

Now that you have the ID, you can fetch one or more properties:

m.lcNaFieldLong = oMeta.DBCXGetProp('SDTmCaption', ; m.liIDOfField)m.lcToolTipText = oMeta.DBCXGetProp('CBmToolTip', ; m.liIDOfField)

Because you're getting these values from the DBC and the DBC extended tables on the fly, you

Page 20: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

can create data-driven applications that always use the very latest data. Let's look at a practicalimplementation.

How to extend through DBCx

One thing I always hate about generic "how-to" articles is that I still have to spend lots of timetrying to figure out how to get the process to work with my stuff. In an attempt to avoid that, I'lldescribe one possible real-life scenario, pointing out some of the issues you might run into aswell as a possible solution.

Here are the three things you have to know:

1. What files are needed.

2. Where they go.

3. Where the commands that run DBCx go in your application.

The files you need depend on which third-party products or tools you're using. Let's useCodebook as our starting point. You'll need to put DBCXMGR.VCX and CDBKMGR.VCX inyour path so that when you build your applications, they can be found. I put these in COMMONbecause only one copy is needed across all applications.

Each of your applications needs its own copy of DBCXREG.DBF and CDBKMETA.DBF. I putthese into the APPFILES directory of an application -- the place where application-specific dataresides. (My user file, error log, custom reports, and so on, also go here. These files aren't dataspecific, but rather common across all data sets, so I don't keep multiple sets of the same files ineach data directory.)

That's it for the files!

Now let's see how this stuff actually is handled in an application. You may have a startupprogram in which you instantiate your classes, such as oApp, oLibrary, and so on. I do, too, but Iquickly found that this isn't where oMeta should be created, because oMeta needs to know abouta database, and early in our startup process, we hadn't handled data yet. Thus, we simplyinitialize oMeta in the startup so that it's scoped properly, and then actually instantiate it in theInit() of oApp's instantiation:

Page 21: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

* IT.PRG* My startup program is always called IT.PRG.*<bunches of code here>

set classlib to HWAPP, HWLIB, HWCTRL, CUST addi

* Declare oMeta here even though we're going* to instantiate it later - so it's scoped properly.private oMetaoMeta=.f.wait window nowait "Setting up libraries..."private oLiboLib=createobject("LIB")

<more code here>

wait window nowait "Initializing..."oApp.it()

Then, in oApp.init():

<bunches of code>** data dictionary*set classlib to DBCXMGR additive* Set debugging on if we're in development mode.if this.cMethod = "DEV" oMeta=createobject('MetaMgr',.t.)else oMeta=createobject('MetaMgr',.f.)endifif type('oMeta') <> "O" or isnull(oMeta) messagebox("Unable to instantiate data dictionary. ; Please call your developer. Shutting down.") this.lWeAreDone = .t.endif

(The second parameter passed to createobject is a parameter that turns debugging messages on.)

Now we're ready to use DBCx to do some data driving. Let's use a simple form with a formatand a tool tip for a text box as our first example. The database has a table named League, and theLeague table contains a field named cNaLeague (League Name -- good thing we're going to puta tool tip on it, eh?).

In the Init of the form, we issue three commands. The first gets the ID for the League Namefield, and the second and third get the format and tool tip values for that field:

Page 22: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

m.liIDOfField = oMeta.DBGetDBCKey(dbc(),'Field', ; 'Database.Field')

Now that we have the ID, we can fetch one or more properties:

m.lcNaFieldFormat = oMeta.DBCXGetProp( ; 'CBmInFormat', m.liIDOfField)m.lcToolTip = oMeta.DBCXGetProp( ; 'CBmToolTip', m.liIDOfField)

Finally, we can set the properties for the field:

thisform.hwTxtNaField.Format = m.lcNaFieldFormatthisform.hwTxtNaField.ToolTip = m.lcToolTip

Let's examine a different way to do this. After all, it seems like a lot of code to write for aproperty that, once set, may not change a lot. It might be nice to have these properties setautomatically, right? You could combine both lines of code in a single expression and place it inthe property sheet. The following value in the Format property would automatically update theformat for the field regardless of how many times it was changed:

oMeta.DBCXGetProp('CBmInFormat', ; oMeta.DBGetDBCKey(dbc(),'Field', 'Database.Field'))

In this article I've described the basic mechanism for using DBCx to extend the VFP 5.0database container. Next month, I'll look at some of those routines that ought to be data driven,and explain how DBCx can help you.

Whil Hentzen is editor of FoxTalk. [email protected], [email protected].

Drill Down Into Your Data Doug Hennig (3)

Do your users want to drill down into their data to see increasing levels of detail? You might thinkthe TreeView control that Doug discussed last month is perfect for this, but it doesn't allow you toshow columns of information. This month's article describes a technique for drilling down into data

Page 23: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

using a grid, plus it tells you how to make your grids act more like a spreadsheet with frozen panes.

Last year, a client asked me to create an application to provide their marketing staff with productsales breakdowns in a more flexible way than the current accounting system could. Because theiraccounting system uses the Btrieve database manager, accessing the data is easy: I useDBFtrieve to read the Btrieve files and write the data to a VFP table. However, this applicationhas a complex requirement: the need to "drill down" into the data. Initially, users want to seesales by product. Then they want to "expand" a product to show the sales for that product brokendown by region. Next, they can drill down into a region to see sales by city. Finally, they candrill down into a city to see the sales by customer for the expanded product.

Sound familiar? It sure sounded like the Windows Explorer to me: opening a folder shows thefolders inside that folder, opening one of those folders shows its contents, and so on. So my firstthought was to use the Outline control that came with VFP (this was before VFP 5.0 -- with itsActiveX TreeView control -- was available). However, I quickly discarded that idea because ofthe need for many columns of sales data: Between various month-to-date and year-to-date valuesand quantities for this year and last year, users need to see more than 10 columns of figures. Inaddition, they want the first column "locked," so as they scroll horizontally to see the differentsales columns, the name of the product is always visible. While VFP's Grid control provides aPartition property to split the grid into two panels, the behavior of the two panels is far fromwhat Excel users are used to; for example, either panel can be scrolled horizontally to displayany set of fields. What I needed was a cross between a spreadsheet and an Outline or TreeViewcontrol. This article describes the technique I came up with.

To provide a concrete example to go along with this article, I'll present a similar sales applicationusing the test data that comes with VFP. The ORDERS table contains order information, whilethe ORDITEMS table contains the products sold for each order. PRODUCTS contains productinformation, while CUSTOMER and EMPLOYEE contain customer and employee information,respectively. Let's create a drill-down application that shows, at the top level, sales by product.These sales can then be drilled down to the employee level, then down to country, and finallydown to the specific customer. You can download the source code files for this application fromdownload file 07DHENSC.ZIP. Figure 1 shows the drill-down form with some records expandedso you can see how the application works.

How I spent my summer vacation

The secret to creating a control that can drill down into data while displaying multiple columns isto use a Grid with a filter so only certain records are visible at one time, and with certainproperties and methods that provide spreadsheet-like behavior.

First, drill-down tables are handled using a "smoke and mirrors" technique. Rather thandisplaying data directly from the source tables, a drill-down table is created that contains all theinformation you need in one place, with sales totals already calculated. This is a technique usedby data warehousing applications, where data from a relational database is pulled into adenormalized structure for the simplest and fastest access. There's a performance hit when thestructure is created, but after that the application doesn't have to worry about performing table

Page 24: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

joins or calculating totals, so performance is much better for the user.

The drill-down table is filtered so only the records that the user should see are in the currentfilter set. I control this using a field called VISIBLE and a filter on this field. Initially, this fieldis set to .T. only for product records. When the user drills down into a product, the VISIBLEfields for all employee records for that product are set to .T. Now, all product records and thoseemployee records for a specific product are visible. Similarly, when the user drills down into anemployee record, the VISIBLE field for all country records for that product and employee are setto .T. Now, all product records and those employee records for a specific product and thosecountry records for a specific product and employee are visible. Collapsing a row does just theopposite: the VISIBLE field of all applicable records is set to .F., making those records invisibleonce again. Another field, EXPANDED, saves the current state of a row. This allows us todisplay a plus sign (+) or a minus sign (-) to indicate whether a row can be expanded or hasalready been expanded, just like an Outline or TreeView control.

Getting a VFP Grid control to act like a spreadsheet with a frozen pane takes some doing. First,set the Partition property so the grid is split into two panels. The Partition property contains thenumber of pixels in the left panel, so you'll set it to display only the first two columns (one forthe "+" or "-" and one for the name). However, the first thing you'll notice when you do this isthat both panels have a vertical scroll bar, either of which can be used to scroll the grid. This isconfusing to the user and takes up precious real estate, so the next task is to eliminate the scrollbar for the left panel. The solution is to unlink the two panels so they act like two grids in one,eliminate the scroll bar for the left panel, then relink the two panels. Unfortunately, this can't bedone at design time, but it's relatively easy to do in code at runtime (once you know how). TheInit() method of the grid, which I'll explain later, does this.

Next, you have to prevent the user from scrolling unwanted columns into either panel (column 3or higher for the left panel, or columns 1 or 2 for the right panel). While you can't prevent theuser from scrolling, you can clean things up afterward. The Grid's Scrolled() event fires when theuser scrolls, so by checking the LeftColumn property of each panel, you can determine whetherunwanted columns appear in each panel, and use the DoScroll() method to put things backproperly. Unfortunately, this causes a brief flicker as unwanted columns move into view andthen disappear again, and using LockScreen doesn't help, but this is a minor glitch.

The drill-down table

Let's look at the drill-down table in more detail. It must contain the entire set of records the userwill see in the drill-down form. This means there will be several "levels" of records. One level ofrecords will be the topmost layer of records the user sees; call these level 1. Each level 1 recordwill have a set of records that represents the first layer of breakdown; these records will be level2. Each level 2 record will have a set of records called level 3, and so on.

The drill-down table can consist of any fields you wish, with the following minimum fields:

• LEVEL: This numeric field contains the level number.

• NAME: The text to display for each record. The source of the text can change from one

Page 25: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

level to the next. In the sales scenario, for example, for level 1 NAME records comesfrom PRODUCT.PROD_NAME while for level 2 it consists of the employee's first andlast names from the EMPLOYEE table.

• DESCRIP1: This field contains the sort value (such as a name) for level 1 records. Allrecords that belong to a given level 1 record will contain the same value. The name ofthis field would probably be the name of the field from the original data source; it's calledDESCRIP1 here as a place holder. In the case of the sales example, this field is calledPROD_NAME.

• DESCRIP<n>: Each level except the lowest one will have a sort value field as describedfor DESCRIP1.

• VALUE<n>: These fields will contain the rolled-up sales values at each level. The salesexample has the following fields: SALES93, SALES94, SALES95, SALES96 (salesvalues for 1993 to 1996), QTY93, QTY94, QTY95, and QTY96 (quantity sold for 1993to 1996). The values in these fields for each level's records represent the total values forthat level. For example, because level 1 in the sales scenario is product, these fieldscontain the total sales and quantity sold for each product by year. Level 2 is employee, sofor a given level 2 record, these fields contain the sales and quantity sold of theappropriate level 1 record (the product) by the employee.

The table needs to be indexed so all records for a given level appear together. Typically, theindex expression will be DESCRIP1 + DESCRIP2 + + STR(LEVEL, 1) + NAME. Take care toensure that the index expression doesn't exceed the maximum key size (usually 254 characters,but it's less if you use a collate sequence other than MACHINE). For the sales scenario, theindex expression is UPPER(PROD_NAME + EMP_NAME + COUNTRY + STR(LEVEL, 1) +NAME); UPPER() is used so the display order is case-insensitive.

Table 1 shows a brief set of the drill-down data for the sales example. Level 1 representsproducts, level 2 is employees for a given product, level 3 is the country a given product wassold to by a certain employee, and level 4 is the specific customer a product was sold to by anemployee in a country.

Table 1 . Example of a drill-down table.

LEV NAME PROD_NAME EMP_NAME COUNTRY SALES93 1 Alice Mutton Alice Mutton 70 2 Buchanan, Steven Alice Mutton Buchanan, Steven 25 3 Brazil Alice Mutton Buchanan, Steven Brazil 10 4 Hanari Carnes Alice Mutton Buchanan, Steven Brazil 10 3 USA Alice Mutton Buchanan, Steven USA 15 4 Save-a-lot Markets Alice Mutton Buchanan, Steven USA 15 2 Callahan, Laura Alice Mutton Callahan, Laura 45 3 UK Alice Mutton Callahan, Laura UK 10 4 Island Trading Alice Mutton Callahan, Laura UK 10

Page 26: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

3 USA Alice Mutton Callahan, Laura USA 35 4 Rattlesnake Canyon

Grocery Alice Mutton Callahan, Laura USA 20

4 White CloverMarkets

Alice Mutton Callahan, Laura USA 15

1 Aniseed Syrup Aniseed Syrup 44 2 Callahan, Laura Aniseed Syrup Callahan, Laura 10 3 Sweden Aniseed Syrup Callahan, Laura Sweden 10 4 Berglunds snabbköp Aniseed Syrup Callahan, Laura Sweden 10 2 Davolio, Nancy Aniseed Syrup Davolio, Nancy 34 3 Denmark Aniseed Syrup Davolio, Nancy Denmark 5 4 Vaffeljernet Aniseed Syrup Davolio, Nancy Denmark 5 3 USA Aniseed Syrup Davolio, Nancy USA 29 4 Rattlesnake Canyon

Grocery Aniseed Syrup Davolio, Nancy USA 12

4 Save-a-lot Markets Aniseed Syrup Davolio, Nancy USA 17

Notice the following:

• NAME contains the product name for level 1, the employee name for level 2, the countryfor level 3, and the customer for level 4.

• PROD_NAME, EMP_NAME, and COUNTRY contain the same values for all recordsfor a given product, employee, and country.

• EMP_NAME is empty for a level 1 record because that is the rollup record for a givenproduct. COUNTRY is empty for a level 2 record because that is the rollup record for agiven employee.

• For each level, SALES93 contains the sum of values for records at the next lower level.For example, the Alice Mutton product has 70 for SALES93, which is the sum of 25 and45 (from the two level 2 records for Alice Mutton), while the Steven Buchanan record forthe Alice Mutton product has 25 for SALES93, which is the sum of 10 and 15 (from thetwo level 3 records for this employee and this product).

Because both the original sources of the data and the format for the drill-down table can varygreatly from application to application, a different program must be used to create eachdrill-down table. However, the basic idea for such a program is as follows:

• Use a SQL SELECT statement to create a cursor of level 1 records, grouping on the level1 field.

• Use a SQL SELECT statement to create a cursor of level 2 records, grouping on the level1 and level 2 fields.

• Use a SQL SELECT statement to create a cursor of level 3 records, grouping on the level

Page 27: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

1, level 2, and level 3 fields.

• Create as many cursors as necessary, depending on how many drill-down levels aredesired.

• Concatenate all the cursors into a single table.

• Add a record for grand totals; use level 0.

• Index the table so all records for a given level appear together.

The sample MAKESALE.PRG creates a drill-down table called SALES.DBF from the VFPTESTDATA database for the sales scenario. This code isn't shown here due to its length, but youcan download it from download file 07DHENSC.ZIP.

The drill-down form

The drill-down form is a class called DrillDownForm in DRILLDWN.VCX. It consists of aform, a grid, and some custom properties and methods. This class was created to be as generic aspossible. This means it makes few assumptions about the structure of the drill-down table andmust have certain properties set after it's instantiated but before it's displayed to the user. I'llexplain the details of the class first, then examine how to use it.

The assumption this class makes about the structure of the drill-down table is that there's acolumn called LEVEL containing the level for each record, and a column called NAMEcontaining the display value the user should see for each level. If you use different names, you'llneed to change the SetupData() and Expand() methods of the class.

Form properties

BaseClass Form (use your own class here) Caption Sales DataSession 2 -- Private Data Session Height 360 Name frmSales Width 600

Form custom properties

aColorLevel[1] An array of colors to use for each level aLevelKey[1] An array containing the key field for each level cOrder The order to use so the data is properly sorted fordrill-downs cTable The name (including path if necessary) the data willcome from cTextBoxClass The class to create the text boxes in the grid from(default = DrillDownGridTextBox) nMaxLevels The number of levels in the cursor

Page 28: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

Form methods

Load() simply sets up some environmental elements:

set talk offset deleted offset notify offset bell off

Resize() resizes the grid when the form is resized so the grid always fills the form:

with This .LockScreen = .T. .grdSales.Width = .Width .grdSales.Height = .Height .LockScreen = .F.endwith

Show() sets the DynamicForeColor for each column (you'll see later how Thisform.aColorLevelis initialized) and displays the form:

LPARAMETERS nStyleThis.grdSales.SetAll('DynamicForeColor', ; 'Thisform.aColorLevel[LEVEL + 1]', 'Column')

Form custom methods

SetupData(), which is called before the form is displayed, creates a cursor from the sourcedrill-down table (to keep the class generic, the name of the drill-down table is stored in thecTable property of the class rather than hard-coded). You'll need to use a cursor rather than usingthe SALES table directly, because the values of two fields are changed as users expand andcollapse rows. In order for this to work in a multi-user environment, you can't modify theSALES table itself, but you can modify a cursor based on the SALES table. The cursor, calledLV_SALES, consists of all fields from the source table in addition to two new fields, VISIBLEand EXPANDED, which indicate, respectively, whether the record is visible to the user andwhether levels under this record are visible. Initially, VISIBLE is .T. only for level 0 (grandtotal) and level 1 (top level) records.

Page 29: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

local lcAlias, ; lcCDX, ; lnTag, ; lcKeywith This

* Ensure that the properties you need are set * (these could be ASSERT statements in VFP 5).

if empty(.cTable) wait window 'cTable not specified' return .F. endif empty(.cTable) if empty(.cOrder) wait window 'cOrder not specified' return .F. endif empty(.cOrder)

* Open the table the data will come from.

select 0 use (.cTable) lcAlias = alias()

* Ensure that the specified tag is a valid one * (this could be an ASSERT statement in VFP 5).

lcCDX = strtran(dbf(lcAlias), '.DBF', '.CDX') lnTag = tagno(.cOrder, lcCDX, lcAlias) if lnTag = 0 wait window 'cOrder not valid' return .F. endif lnTag = 0

* Create a cursor for sales with additional fields* you'll need: EXPANDED to indicate whether the * current record is expanded and VISIBLE to flag if* the current record can be seen.

select *, ; .F. as EXPANDED, ; LEVEL < 2 as VISIBLE ; from (lcAlias) ; into cursor TEMPSALES

* Now open the cursor again so it'll be read-write* and you can create indexes on it. You can then * close the original copy.

use dbf('TEMPSALES') alias LV_SALES again in 0 select LV_SALES use in TEMPSALES

* Create the index you'll need for the cursor * from the specified index in the SALES table.

l K k (l T l Ali )

Page 30: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

Expand() is called when the user expands or collapses the current row by clicking the + columnfor the row.

Page 31: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

local llExpanded, ; lnLevel, ; lcKey, ; lnI, ; luKey, ; lnRecno

* If you've drilled down below the maximum expandable* level or you're sitting on the "totals" record, don't* go on.

if LEVEL > This.nMaxLevels or LEVEL = 0 returnendif LEVEL > This.nMaxLevels ...

* Toggle the expanded status for the current record* and save the new status. Increment the level so you * know which one to work with.

This.LockScreen = .T.replace EXPANDED with not EXPANDEDllExpanded = EXPANDEDlnLevel = LEVEL + 1

* Create the key expression you'll use to know which* records to toggle the "visible" status for.

lcKey = ''for lnI = 1 to LEVEL luKey = evaluate(This.aLevelKey[lnI]) lcKey = lcKey + iif(empty(lcKey), '', ' and ') + ; This.aLevelKey[lnI] + '==' do case case type('luKey') = 'C' lcKey = lcKey + '"' + luKey + '"' case type('luKey') = 'N' lcKey = lcKey + ltrim(str(luKey)) case type('luKey') = 'D' lcKey = lcKey + '{' + dtoc(luKey) + '}' endcasenext lnI

* Turn off the filter so you can see all records, save* the current record pointer, and move to the next* record (the first one under the current one).

set filter tolnRecno = recno()skip

* If you're collapsing a level, collapse all levels* under it. Otherwise, toggle the "visible" flag for* all records under this one.

if not llExpanded replace VISIBLE with .F., EXPANDED with .F. ;

hil &l K

Page 32: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

AddGridColumn() is a custom method that adds a column to the grid and sets various properties:

lparameters tcControlSource, ; tcCaptionlocal lnColumnwith This.grdSales .AddColumn() lnColumn = .ColumnCount .Columns[lnColumn].Name = 'Column' + ; ltrim(str(lnColumn)) .Columns[lnColumn].Width = 75 .Columns[lnColumn].ReadOnly = .T. .Columns[lnColumn].InputMask = '999,999,999' .Columns[lnColumn].Header1.Alignment = 1 .Columns[lnColumn].Header1.Caption = tcCaption

* Create a text box for the new column and make it the* current control for the column.

.Columns[lnColumn].AddObject('DrillText', ; This.cTextBoxClass) .Columns[lnColumn].DrillText.Visible = .T. .Columns[lnColumn].CurrentControl = 'DrillText' .Columns[lnColumn].ControlSource = tcControlSourceendwith

Grid properties

You can't set the RecordSource of the grid or ControlSource of the columns to the LV_SALEScursor at design time because that cursor won't exist until after the grid has been instantiated,which would cause an error. Thus, leave these properties blank for now and set themprogrammatically at runtime in the SetupData() method of the form.

BaseClass Grid (use your own class here) ColumnCount 2 DeleteMark .F. Height 360 Name grdSales Partition 268 (only approximate; will be set at runtime to theproper value) ReadOnly .T. RecordMark .F. RecordSource None (it will be set at runtime) Width 569 Column1.ControlSource None (it will be set at runtime) Column1.Width 13 Column1.Header1.Caption None

Page 33: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

Column2.ControlSource None (it will be set at runtime) Column2.Width 232 Column2.Header1.Caption Name

Grid methods

Init() sets the left panel to not have a scroll bar. This can't be done at design time, so the grid willappear to have two vertical scroll bars. At runtime, however, this eliminates the scroll bar for theleft panel, making the grid look more like an Excel spreadsheet with a frozen panel. Init() alsoadjusts the partition position so the left panel includes just the first two columns.

with This .PanelLink = .F. .Panel = 0 .ScrollBars = 1 .Panel = 1 .PanelLink = .T. .Partition = .Column1.Width + .Column2.Width + 2endwith

Scrolled(), which is fired whenever the user scrolls the grid, ensures that the appropriate columnsare displayed in the appropriate panel by forcing the grid to scroll unwanted columns out of viewif necessary:

LPARAMETERS nDirectionlocal lnIwith This do case

* If either of the first two columns is visible in the* right panel, scroll until they're no longer there.

case .Panel = 1 and .LeftColumn <= 3 for lnI = .LeftColumn to 2 .DoScroll(5) next lnI

* If any column but the first two is visible in the * left panel, scroll until it's no longer there.

case .Panel = 0 and .LeftColumn > 1 for lnI = .LeftColumn to 2 step - 1 .DoScroll(4) next lnI endcaseendwith

Column1.Text1.GotFocus() ensures that the right panel doesn't show the first two columns by

Page 34: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

setting focus to column 3 (the first column in the right panel) if necessary. This makes the gridact more like a spreadsheet:

with This.Parent.Parent if .Partition <> 0 and .Panel = 1 .Column3.SetFocus() endif .Partition <> 0 ...endwith

Column1.Text1.LostFocus() moves to the third column (the first column you want displayed inthe right panel). This makes the grid act more like a spreadsheet:

with This.Parent.Parent if .Partition <> 0 and .Panel = 0 .Panel = 1 .Column3.SetFocus() endif .Partition <> 0 ...endwith

Column1.Text1.KeyPress() accepts Enter as a signal to expand or collapse the row, and ignoresany keypress that would put a value into the column:

LPARAMETERS nKeyCode, nShiftAltCtrldo case case nKeyCode = 13 Thisform.Expand() nodefault case between(nKeyCode, 32, 127) nodefaultendcase

Column1.Text1.Click() expands or collapses the current row:

Thisform.Expand()This.SelStart = 0This.SelLength = 1nodefault

Column2.Text1.When() prevents focus from being set to the Name column:

return .F.

Page 35: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

DrillDownGridTextBox class

This class is used to create text box controls in the columns in the right panel of the grid atruntime.

BaseClass TextBox (use your own class here) BorderStyle 0 - NoneMargin 0

When() prevents the text box from receiving focus if there isn't a partition, or if focus is in theleft panel:

with This.Parent.Parent return .Partition = 0 or .Panel = 1endwith

Using the drill-down form

SALES.PRG is a sample program that uses the drill-down form. This program does thefollowing:

• Instantiates a DrillDownForm object.

• Sets the cTable and cOrder properties to the name of the drill-down table and the tag thatorders the data properly, then calls the SetupData() method to create the LV_SALEScursor and set the data source properties of the grid (they can't be set at design timebecause this cursor won't exist until after the grid has been instantiated).

• Sets the nMaxLevels property to the number of levels that can be expanded (one less thanthe total number of levels) and initializes the aLevelKey array to the names of the fieldscontaining the "key" value for each level.

• Initializes the aColorLevel array to the color desired for each level. Using color todistinguish levels is very important; if each level is the same color, it's difficult tovisually tell what data you're looking at. Although the colors are hard-coded inSALES.PRG, you'll probably want to read these values from a user preferences table orfrom the Windows Registry. Row 1 of aColorLevel is for level 0, row 2 is for level 1, andso on.

• Creates columns of the grid to display the sales values using the customAddGridColumn() method.

• Shows the DrillDownForm object.

Here's the code for SALES.PRG:

Page 36: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

#include FOXPRO.Hset talk off

* Create an instance of the DrillDownForm class * into a public variable so it exists when this * program is done.

public oSalesset classlib to DRILLDWNoSales = createobject('DrillDownForm')with oSales

* Initialize the properties containing the name and * order of the data table and set up the data for * the drill down.

.cTable = 'SALES' .cOrder = 'SALES' .SetupData()

* Initialize the levels and level keys you need.

.nMaxLevels = 3 dimension .aLevelKey[.nMaxLevels] .aLevelKey[1] = 'PROD_NAME' .aLevelKey[2] = 'EMP_NAME' .aLevelKey[3] = 'COUNTRY'

* Initialize color settings.

dimension .aColorLevel[.nMaxLevels + 2] .aColorLevel[1] = COLOR_RED .aColorLevel[2] = COLOR_BLACK .aColorLevel[3] = COLOR_BLUE .aColorLevel[4] = COLOR_GREEN .aColorLevel[5] = COLOR_MAGENTA

* Create grid columns you want to display. Pass the* control source and header caption.

.AddGridColumn('lv_sales.sales93', '93 Sales') .AddGridColumn('lv_sales.sales94', '94 Sales') .AddGridColumn('lv_sales.sales95', '95 Sales') .AddGridColumn('lv_sales.sales96', '96 Sales') .AddGridColumn('lv_sales.qty93', '93 Qty') .AddGridColumn('lv_sales.qty94', '94 Qty') .AddGridColumn('lv_sales.qty95', '95 Qty') .AddGridColumn('lv_sales.qty96', '96 Qty')endwith

* Display the drill-down form.

oSales.Show()

Page 37: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

Conclusion

The DrillDownForm class described in this article can be made even more generic by using asubclass of the Grid control rather than a Form. The advantage of that approach is you coulddrop one of these objects on any form to provide drill-down capabilities as well as other formcontrols. Another enhancement would be to use a property containing the number of columns inthe left panel rather than hard-coding it in various methods. You also could use a different classin place of DrillDownGridTextBox (if you need different behavior, for example); you just haveto set the cTextBoxClass property of the DrillDownForm class to tell it which class to use for thegrid text boxes. I hope you enjoy the techniques I outlined in this article!

Doug Hennig is a partner with Stonefield Systems Group Inc. in Regina, Saskatchewan, Canada. He isthe author of Stonefield's add-on tools for FoxPro developers, including Stonefield Database Toolkit forVisual FoxPro and Stonefield Data Dictionary for FoxPro 2.x. He is also the author of The VisualFoxPro Data Dictionary in Pinnacle Publishing's "The Pros Talk Visual FoxPro" series. He was aMicrosoft Most Valuable Professional (MVP) in 1996. [email protected].

Shared Private DataSessions John V. PetersenI have a form with a private DataSession that calls amodal form with its DataSession setting of default.Once the modal form is closed, my calling form'sDataSession is lost. Why does this happen and howcan I prevent this behavior?

The first order of business is to explain why this behavior exists. When the modal form with thedefault datasession closes, its data environment object is destroyed. Unfortunately, the modalform happened to occupy the same datasession as the calling form. Therefore, the shareddatasession is destroyed. There are several ways to counteract this behavior.

If the modal form doesn't need to have access to the same buffered tables as the calling form,simply set the modal form's DataSession Property to Private. When the modal form closes, itsDataSession will be destroyed -- not the DataSession of the calling form.

If the modal form needs to have access to the buffered tables of the calling form, the process getsa bit more complicated. The easiest way to deal with this situation is to create a FormSet. Thisscenario represents one of the few advantages a FormSet has over separate forms: the ability fortwo or more forms to share the same Private DataSession. Unfortunately, this option usuallyentails some re-work -- and it isn't too appealing.

Page 38: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

Fortunately, there's an alternative. The solution lies in creating a form with a PrivateDataSession, and at the same time retaining access to the calling form's buffered tables. Followthese steps in making changes to the called (modal) form:

1. In the Load() Event of the modal form, place the following code:

Set DataSession To (ThisForm.DataSessionID-1)

The datasession just needs to be decremented by 1. This will point the modal form to theDataSession of the calling form.

2. In the Destroy() Event of the modal form, place the following code:

Set DataSession To (This.DataSessionID)

This is a very important step. To prevent the modal form from destroying the calling form'sDataSession, the modal form must be pointed back toward its own private DataSession.

3. In the DataEnvironment Object of the modal form, make the following property settings:

AutoOpenTables = .F.AutoCloseTables = .F.

Because the modal form is reading data from another datasession, there's no need to openanother instance of the tables.

John V. Petersen, MBA, specializes in developing FoxPro and Visual FoxProbased solutions formarketing departments in major corporations. John has written for developer publications and hasspoken at user group meetings and conferences. John is a Microsoft Most Valuable Professional (MVP),a Technical Resource Specialist (TRS) on the CompuServe VFOX, FOX, and FOXUSER Forums, as wellas co-author of Developing Visual FoxPro 5.0 Enterprise Applications from Prima Publishing.610-651-0879, [email protected].

TTOC(), LockScreens, and a CALCULATEGPF Barbara Peisch and Paul MaskensWatch Out for TTOC()

Paul: While doing research for the tip on indexing views, which appeared in last month'scolumn, I stumbled upon some not-very-nice behavior of the TTOC() function. It seems that,

Page 39: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

although using TTOC(<date>, 1) works just fine for date fields within a table, it doesn't work fordate memory variables, unless the memory variables are in American format. Now that youknow about this problem, the workaround is simple. Substitute calls to TTOC() with your ownfunction that changes the date setting:

Funtion MyTTOCParameter pdate

Private lcDateSet, lcReturn

lcDateSet = Set("Date")Set date to American

lcReturn = TTOC(pdate, 1)

Set date to &lcDateSet

Return lcReturn

A Reliable Way to Set LockScreen = .F.

Barbara: Brian Neese has a tip for those of you who like to use LockScreen to hide clunkyscreen painting. The tough part of setting LockScreen = .T. is knowing where to set LockScreen= .F., especially in Pageframes when you have code in various pages' Activate and Deactivatemethods. Brian's answer is to create a timer class with these two lines of code in the TimerEvent:

ThisForm.LockScreen = .F.This.Enabled = .F.

When creating this class, make sure you change the Timer Event to at least 1. You may want toset it higher, though, because Brian points out, "This technique uses a timer and is subject to allthe quirks inherent with them such as how they queue up ticks during periods of heavyprocessing and dialogs. Normally, you won't notice this behavior unless you turn on thedebugger in VFP 5.0. For this reason I recommend you set the timer interval to 100 or greater."

To take advantage of this class, simply drop it on your form. When the timer is activated, it willunlock the screen and disable itself. Any method where you set LockScreen = .T. must also havethis line:

ThisForm.tmrLockScreen1.Enabled = .T.

The timer class will fire when the screen painting is done! One exception to this rule involvesPageFrames. If you do your screen painting in the Activate method of a page, make sure the

Page 40: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

Deactivate method of the other pages has LockScreen = .T. andThisForm.tmrLockScreen1.Enabled = .T.

A CALCULATE Anomaly

Barbara: Here's one that isn't easy to find, but it's a real puzzler when you do. Doug Bakerfound this one for us. The following code will cause a GPF every time:

* Will not fail type other than currency * or physical tableCREATE CURSOR test( nAmount Y)CALCULATE ALL SUM( nAmount) TO m.ntotal

? m.nTotal = 0 && This line will GPF

A few key conditions cause the failure. One is that the nAmount field must be of type "Y"(currency). Another is that you must do a SUM() using the CALCULATE command. Thecommand will fail only when doing comparisons to 0 or 1, and only when the cursor is empty.The command SUM <field name> TO <variable> doesn't have this problem, and neither do theother functions that can be used with CALCULATE.

Barbara Peisch, president of Peisch Custom Software in San Diego, is a Microsoft [email protected].

Paul Maskens is a consultant for FoxWare in Chelmsford, U.K. [email protected].

The Year 2000 Problem Les PinterI don't take pleasure in other people's misfortune. Even after you "told them so," and your youngfriends insist on experiencing the consequences of mistakes you made years earlier, watching thefertilizer hit the ventilator is not amusing. But I'm almost ready to make an exception in the caseof the "Year 2000 Problem."

COBOL is a terrible waste of a human life. I spent seven years there, and I took an 80 percentpay cut to get out of it and learn how to program PCs in other languages. It was so boring!MOVE. PERFORM. ACCEPT. That's pretty much a COBOL programmer's life. But businesseslike COBOL because it's simple and straightforward. And programmers are easy to train. If aprogrammer keels over, you can just shove the body out of the way, bring in a replacementprogrammer unit, and continue. Skill means nothing.

As a result of this low-tech mentality, simple solutions have prevailed. The six-character datefield is probably the worst of these. After 123197 comes 010198. After 123198 comes 010199.After 123199 comes . . . 010100. Sort that, sucker! Well, don't blame the programmers. Nowherein the specs did it say that there would be a year 2000.

Page 41: Using VFP 5.0 Top-Level Formsportal.dfpug.de/dFPUG/Dokumente/FoxTalk/PDF2000/FT0797.pdf · Windows Help System. The help system also spawns other top-level forms ... This setting

FoxPro programmers don't understand the year 2000 problem because FoxPro doesn't have ayear 2000 problem. Our dates are, and always have been, correctly sortable because they containa four-digit year field. SET CENTURY on and you'll see it. It's been there since Vulcan --dBASE II to you newbies.

Estimates of the cost of fixing all those COBOL dates run as high as $700 billion. The Frenchminister of information estimates that 10 percent of all companies, and 5 percent of allgovernments, will go bankrupt when dawn comes up like thunder in 2001. There aren't enoughCOBOL programmers in the world to fix all the defective programs that have been slumberingall these years.

As if that weren't enough, a fair percentage of COBOL source code is missing. I know of aFortune 100 company in Houston that's still running some of their payroll programs under 7070emulation because they couldn't find the source code to recompile the programs when they gottheir new IBM 360 -- in 1964!

Fortunately, the solution doesn't lie in reconstructing those COBOL programs, fixing what ailsthem, and recompiling them in COBOL. Most of them are obsolete anyway. The other reasonthey weren't recompiled was that by the time they were first installed they already neededenhancements, and the staff wasn't there to recode them. COBOL, in addition to being verbose,is slow for development. I remember taking a month to write a program in COBOL that wouldtake a day in Visual FoxPro. So COBOL is the problem, not the solution.

The solution to the year 2000 problem is to throw away all of those ancient, decrepit COBOLprograms and rewrite them in a modern database development environment. Applications thattook tens of thousands of dollars to write and required a mainframe to run can be rewritten inweeks or days in FoxPro. The amounts of data handled by many mainframe applications arepitifully small, and the cost and performance of hard drives on PC servers can easily handlemany, if not most of them.

So the reason I don't feel sorry for companies that are facing the year 2000 problem is that I havethe year 2000 solution. It's called FoxPro.

Les Pinter publishes the Pinter FoxPro Letter in the United States and Russia. 916-582-0595,[email protected].