CoDe Focus Magazine - Visual FoxPro 9.0 - WebMasters

68

Transcript of CoDe Focus Magazine - Visual FoxPro 9.0 - WebMasters

CoDe_Focus_Fox Pro 09 30.11.2004 12:41 Uhr Seite 1

CoDe_Focus_Fox Pro 09 30.11.2004 12:41 Uhr Seite 2

CoDe_Focus_Fox Pro 09 30.11.2004 12:41 Uhr Seite 3

Features8 Walking on Cloud 9.0 of Visual FoxPro

So just what was the Visual FoxPro team thinking whenthey planned Visual FoxPro 9.0? Here is what they want-ed to accomplish—straight from the VFP team.Microsoft Visual FoxPro Development Team

12 What’s New with Data in Visual FoxPro 9.0With support for new data types and removing many lim-its from the SQL language, it's now easier to develop asingle code base that works with the Visual FoxPro 9.0native data engine and SQL Server. David T. Anderson

22 The Visual FoxPro 9.0 Report WriterExplore the new features of the Report Writer, includingdealing with legacy data, report protection, user interfaceenhancements, layout objects, and data groups, plus thegreat new multiple detail band feature. Cathy Pountney

36 Controls, Events, Commands, and MoreThe new productivity tools help you build interesting anduseful applications with just a few carefully chosen bits ofcode.Claudio Lassala

44 Extending the Visual FoxPro 9.0 ReportingSystemThe new reporting engine splits responsibility for report-ing between the report engine, which now deals onlywith data handling and object positioning, and a new ob-ject known as a report listener, which handles renderingand output. Doug Hennig

54 Visual FoxPro 9.0 IDE EnhancementsA little bit here and a little bit there make Visual FoxPro9.0 even more productive. Rod provides an overview ofthe latest IDE enhancements to your favorite tool.Rod Paddock

56 Interop: Making .NET and Visual FoxPro Talk to Each OtherThere’s no doubt that many Visual FoxPro developersneed to live in a world that supports older applicationsand new development architectures. But why rewritebusiness rules that already work? Our writers offer thehighlights to make it all work together.Claudio Lassala, Markus Egger, Rod Paddock

Departments

6 CoDe Compilers

41 Advertisers Index

CoDe Focus Magazine is a supplement of Code Component Developer Magazine. Code Focus reports in-depth on newly released specifictechnologies. The supplement is available to CoDe Component Developer Magazine subscribers that have noted interest in the specifictechnologies covered in the Code Focus supplements.

Subscribe to Code Component Developer Magazine

US subscriptions are US $29.99 for one year; Canada, Mexico, and US territories are US $44.99; Europe is US $49.99; other countries, US$59.99. Payments should be made in US dollars drawn on a US bank. American Express, MasterCard, Visa, and Discover credit cards areaccepted. Bill me option is $5 additional and available only for US subscriptions. Back issues are available. For subscription information,email [email protected] or contact customer service at 832-717-4445 ext 10.

Subscribe online at www.code-magazine.com

CoDe Component Developer Magazine (ISSN # 1547-5166) is published bimonthly by EPS Software Corporation, 6605 Cypresswood Drive.,Suite 300, Spring, TX 77379. POSTMASTER: Send address changes to CoDe Component Developer Magazine, 6605 Cypresswood Drive.,Suite 300, Spring, TX 77379.

TAB

LE O

F C

ON

TEN

TS

Table of Contents www.code-magazine.com4

CoDe_Focus_Fox Pro 09 30.11.2004 12:41 Uhr Seite 4

CoDe_Focus_Fox Pro 09 30.11.2004 12:41 Uhr Seite 5

6

CO

DE

CO

MP

ILER

S

Volume 2 Issue 1

Group Publisher Markus Egger

Associate PublisherRick Strahl

Editor-in-ChiefRod Paddock

Managing EditorEllen Whitney

Content EditorMelanie Spiller

Writers In This IssueDavid T. Anderson Markus EggerDoug Hennig Claudio LassalaKen Levy Cathy PountneyRod Paddock

Technical ReviewersMarkus EggerRod Paddock

Art & LayoutKing Laurin GmbH

[email protected]

ProductionFranz WimmerKing Laurin GmbH39057 St. Michael/ Eppan, Italy

PrintingFry Communications, Inc.800 West Church Rd.Mechanicsburg, PA 17055

Advertising SalesVice President, Sales and MarketingMichelle [email protected]

Sales Managers Erna Egger+43 (664) 151 [email protected]

Tammy Ferguson 832-717-4445 ext [email protected]

Circulation & DistributionGeneral Circulation: EPS Software Corp.

SubscriptionsCoDe Focus Magazine is a supplement of Code Com-ponent Developer Magazine. Code Focus reports in-depth on newly released specific technologies. The supplement is available to CoDe Component Develop-er Magazine subscribers that have noted interest in the specific technologies covered in the Code Focussupplements.

Subscriptions ManagerCleo Gaither832-717-4445 ext 10

[email protected] to CoDe Component Developer MagazineUS subscriptions are US $29.99 for one year; Canada,Mexico & US territories are US $44.99; Europe is US$49.99; other countries are US $59.99. Paymentsshould be made in US dollars drawn on a US bank.American Express, MasterCard, Visa, and Discovercredit cards accepted. Bill me option is $5 additionaland available only for US subscriptions. Back issuesare available. For subscription information, email [email protected] or contact customer service at 832-717-4445 ext 10.

Subscribe online at

www.code-magazine.com

CoDe Component Developer Magazine EPS Software Corporation / Publishing Division 6605 Cypresswood Drive, Ste 300, Spring, Texas 77379 Phone: 832-717-4445Fax: 832-717-4460

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 6

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 7

QU

ICK

ID 0

4040

12

Visual FoxPro 9.0 Overview

Visual FoxPro 9.0 is fully compatible withprevious versions of Visual FoxPro. With itslocal cursor engine, tight coupling betweenlanguage and data, and powerful features,Visual FoxPro 9.0 is a great tool for buildingdatabase solutions of all sizes.

Its data-centric, object-oriented languageoffers developers a robust set of tools forbuilding database applications for thedesktop, client-server environments, or theWeb. Developers will have the necessarytools to manage data—from organizing tablesof information, running queries, and creatingan integrated relational database manage-ment system (DBMS) to programming afully-developed data management applicationfor end users.

Walking on Cloud 9.0 of Visual FoxProThe Visual FoxPro Team at Microsoft is enthused [sic] to announce Visual FoxPro 9.0We’d like to include some details as an overview to the more detailed articles in this special CoDe Focusissue devoted to Visual FoxPro 9.0.

Visual FoxPro 9.0 Goals

Our goals for Visual FoxPro 9.0 were to:

· Maintain backward compatibility· Enhance database language and types· Add end user UI features· Increase developer productivity· Improve the Report Writer significantly· Extend .NET and SQL Server interoper-ability

· Increase extensibility, including Xbasesource code

Visual FoxPro 9.0 Product Highlights

There are so many new features that it’shard to limit the discussion. We’ll start

with the flexibility to build all types ofdatabase solutions, move on to reportingsystem enhancements, and finish up withdata handling and interoperability. Butthese only touch on some the great newfeatures you’ll want to explore on yourown.

Flexibility to Build All Types ofDatabase Solutions

Using Visual FoxPro 9.0, you can create.NET-compatible solutions with hierarchicalXML and XML Web services. You can alsoexchange data with SQL Server throughenhanced SQL language capabilities andnewly supported data types.

You can build and deploy standalone andremote applications for Windows-basedTablet PCs.

You can create and access COM componentsand XML Web services that are compatiblewith .NET technology. Visual FoxPro 9.0allows you to build end-to-end solutions,from data entry forms to complex reportoutputs.

Reporting System Features

To Visual FoxPro’s already strong reportingsystem, we’ve added an extensible newoutput architecture that provides precisioncontrol of report data output and formatting.

There’s multiple-detail band support for datawith multiple one-to-many relationships.

You can customize the Print Previewwindow with improved display quality andmultiple page support. Our new outputreports support XML, HTML, imageformats, and customizable multi-page printpreview windows. The enhanced reportwriter is backward compatible with existingVisual FoxPro reports. And flexible reportchaining allows for more complex printjobs.

Walking on Cloud 9.0 of Visual FoxPro www.code-magazine.com8

Figure 1: Pictured here are some of the Visual FoxPro team members (from left to right): Ken Levy, JohnKoziol, Yair Alan Griver, Calvin Hsia, Randy Brown, Richard Stanton, and Mike Stewart.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 8

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 9

The new ReportListener class provides accessto report generation and rendering events atruntime.

Powerful design-time hooks with customiz-able builders make your development experi-ence smoother.

Data-Handling and Interoperability

Three new data types, VarChar, VarBinary,and BLOB, allow for improved interoper-ability with SQL Server. There are extendedSQL language enhancements as well,including more capabilities with SELECT,INSERT, UPDATE, and DELETE state-ments.

A new function, CAST(), allows you toconvert different data types. You can use thenew binary index to improve performanceusing the deleted tag, and the XMLAdapterprovides improved nested hierarchical XMLand XSD schema support.

There are many improvements to client UIfeatures. You can dock forms, anchor formelements to control movement on a formduring resizing, and control the position ofimages on a button using text alignment.

There’s new word wrap support for checkboxcaptions and rotating text for label captions.Using shapes and lines, you can create poly-gons and Bezier curves. The property sheetprovides support for new font and colordisplay options, extended characters, andlong expressions.

You can bind images to non file-basedpictures. You can use List and Combo collec-tions as databinding row sources.

Member data extensibility adds the ability tospecify custom property editors andfavorites. With the extended system capabil-ities, there are no limits beyond availablememory for arrays, procedure size, andnesting levels.

Single line background compiling allows youto see whether or not your syntax is valid asyou type command lines.

There are new and improved task panes suchas the Data Explorer pane.

There are new string functions and a newinline function—ICASE()—that is similar toDO CASE statements.

For international solutions we’ve addedgreater support for using FontCharSets inapplications.

Common Questions

As for what will be added to Visual FoxProafter version 9.0 is released, it is too early todiscuss this at the time of writing this article.The Visual FoxPro team welcomes feedbackon how we can improve our developer toolsin the future based on what is disclosed forVisual FoxPro 9.0 and Visual Studio 2005.Details of how we enhance Visual FoxPro,how it is packaged, what it is called, andwhen updates will be released probably willnot be disclosed until the first half of 2005.

A common question is how Visual FoxPro9.0 relates to Visual Studio 2005 and SQLServer 2005. We improved the XML supportin Visual FoxPro 9.0 which will also improve.NET interoperability. We also added somenew data types that will also help for use withboth SQL Server 2000 and SQL Server 2005.

Visual FoxPro 9.0 is scheduled to be releasedin late 2004, and the schedule for Longhornis not announced yet. We can only discusshow Visual FoxPro relates to Longhorn onceLonghorn is nearly complete and close tobeing released.

Although we will not be including nativePDF support in Visual FoxPro 9.0, we areworking to make it very easy for third partiesand FoxPro developers to create a variety ofsolutions for add-on PDF output support.

We do not have any plans to extend the 2GBdatabase size limit in Visual FoxPro for manyreasons, including the 32-bit architecture thatalready exists within the product. Besidesusing SQL Server 2000, an additional optionis upcoming: SQL Server 2005 Expressextends the 2GB limit of MSDE to 4GB total.

Although Visual FoxPro will remain 32-bitand will not use 64-bit addressing natively, itwill run in 32-bit compatibility mode. VisualStudio 2005 will support creating native 64-bit applications.

It is up to developers and installing organiza-tions to decide which .NET language is bestfor them. C# is generally for developers wholike to write and control all of their code. It’svery source code-centric. Visual Basic is forRAD (rapid application development) andease-of-use while providing access to the fullpower of the .NET Framework. For a high-level overview of the language differences,refer to the Visual Studio Roadmap athttp://msdn.microsoft.com/vstudio/productinfo/roadmap.aspx.

Visual FoxPro 9.0 Availability

Until March 31st, 2005, the free public betaof Visual FoxPro 9.0 will be available for

download at http://msdn.com/vfoxpro. The betacan be installed side by side with VisualFoxPro 9.0 or previous versions of VisualFoxPro. There is a time bomb built into thebeta that expires March 31st, 2005. Do notcontact technical support for questions, asthe public beta is unsupported and is to beused as is.

Visual FoxPro 9.0 is scheduled to be releasedto manufacturing near the end of 2004 andwill be included in all levels of MSDNSubscriptions (Universal, Enterprise, andProfessional). For more information aboutMSDN Subscriptions, refer tohttp://msdn.com/subscriptions/prodinfo/levels.asp.MSDN Subscription members will be first toobtain the released version of Visual FoxPro9.0 (within a few business days of release tomanufacturing). The full product, upgrades,and academic editions will be availableapproximately six to eight weeks after theyare released to manufacturing.

Visual FoxPro 9.0 pricing will be the same asit was for Visual FoxPro 8.0: the full productis $649US, the upgrade is $349US, and theacademic edition is $75US. For a limitedtime, a $50 rebate will be offered with VisualFoxPro 9.0 (for the upgrade version only, inU.S. and Canada only) for licensed users ofVisual FoxPro 8.0.

To obtain Visual FoxPro, MSDN Subscrip-tions, FoxPro gear, and more, go tohttp://FoxToolbox.com. Additional informationabout Visual FoxPro 9.0 including whitepapers, samples, and links to third-partyresources can be found athttp://msdn.com/vfoxpro.

Visual FoxPro 9.0 System Requirements

To install Microsoft Visual FoxPro 9.0 Profes-sional, we recommend:

• A PC with a Pentium-class processor • Windows 2000 with Service Pack 2 or later

operating system • 64MB of RAM; 128MB or higher recom-

mended • 165MB of available hard disk space for a

typical installation, 165MB maximum;20MB of additional hard-disk space forMicrosoft Visual FoxPro 9.0 Prerequisites

• A CD-ROM or DVD-ROM drive • A super VGA 800 X 600 resolution

monitor, or higher, with 256 colors • Microsoft Mouse or compatible pointing

device

Walking on Cloud 9.0 of Visual FoxPro www.code-magazine.com10

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 10

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 11

What's New with Data in Visual FoxPro 9.0 www.code-magazine.com

What's New with Data in Visual FoxPro 9.0?The Microsoft Visual FoxPro team has a great reputation forresponding to community requests and the next version is noexception. Most evident in several changes to the data engine, Visual FoxPro9.0 includes many enhancements for data access and database applicationdevelopment.

Visual FoxPro 9.0 contains more functionalenhancements to the data engine than anyrelease since 3.0. From the new and extended

functionality in SQL to new data types and a binaryindex, this release demonstrates the power of amature development platformfor data-centric solutions.

Changes to the data engine canbe summarized in five majorareas:

· SQL Enhancements: Removalof most hard-coded limits,enhanced sub-query andcorrelation support, supportfor more complex expres-sions, and enhanced UNIONsupport.

· Performance: A new indextype, enhanced performanceof filtered indexes andimproved SQL performance with TOP n,MIN()/MAX(), and LIKE.

· Commands and Functions: Greater ability to fine-tune how data is accessed and committed,functions to supplement SQL showplan, andeasier immediate expression nesting withICASE().

· New Data Types: Support for VarChar,VarBinary, and BLOB data types, a new CAST()function, and enhancements to existing functionsto control and convert data types.

· Remote Data: Enhanced control over transac-tions, better visibility regarding fetched records,rowsets returned from the provider, and CursorAdapter enhancements that bring behaviorin line with remote views.

Many of these changes improve the Visual FoxPro9.0 client/server story by providing stronger inter-operability with SQL Server. With support for newdata types and removing many limits from the SQLlanguage, it's now easier to develop a single codebase that works with the Visual FoxPro 9.0 nativedata engine and SQL Server.

Enough overview, let's dig in!

SQL Enhancements

Certainly the best word to describe changes to theSQL sub-language is MORE! There are no longerhard-coded limits to the number of elements in an

SQL statement. A single SQLSELECT statement can containmore tables, more JOINs, moresub-queries, more nested sub-queries and more UNIONsthan in previous versions.

There are also no hard-codedlimits on the number of itemsin an SQL IN list. In versionsprior to Visual FoxPro 9.0,SQL IN was mapped to theINLIST() function; that depen-dency has been removed. Thischange allows an increase inthe number of arguments forIN and for better optimization.

Visual FoxPro 9.0 stops evaluating expressionsfrom the list as soon as the match is found. This ishelpful if the IN condition is not Rushmore-opti-mized, as performance can be improved by placingthe most-likely-to-match values at the beginning ofthe list. The total number of items is still indirectlylimited by the SYS(3055) function, which controlsbuffer memory size, so the higher the setting, themore items are supported via IN. (See theCommon Error Messages sidebar for more infor-mation.)

No Limits?

No hard-coded limits does not mean limitless.Issues such as available memory and expressioncomplexity can still have an impact on whether ornot a very long and complex statement can be run,but you'll have to work hard to find many real-world limitations.

Enhanced Sub-Query Support

Sub-queries have always been powerful in the SQLlanguage. They can be used as filters by placingthem on the right side of a comparison in the

David T. AndersonDavid is currently serving as acontract tester with the VisualFoxPro team at Microsoft,testing the data enhancementsmade to Visual FoxPro 9.0 andimplementing performancebenchmarks such as the Trans-action Processing Council'sTPC-H.

With 22 years of applicationdevelopment experience in thePC industry for government,military, educational institu-tions, and private industry,David uses his experiencerelating Enterprise Architectureand Organizational Maturity toassist organizations in deter-mining the most appropriatesoftware processes for theirneeds.

You can reach David [email protected]

Fast FactsChanges to the data engine in

Visual FoxPro 9.0 providedevelopers with enhanced power,

flexibility, and performance. From the greatly expanded SQL

sub-language to new data types andRushmore optimizations, this

release demonstrates the FoxProteam’s commitment to producing

a world-class data-centricdevelopment language.

12

QU

ICK

ID 0

4040

22

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 12

WHERE clause. In Visual FoxPro 9, sub-queriescan also be used as part of the SELECT list (calleda projection) and in the FROM clause (often calleda derived table).

When used as a projection, if the sub-query doesn'treturn any records, a NULL value is returned.Correlation is supported with projection (more onthis in a minute).

A SQL statement that uses projection looks like this:

SELECT ;C.CustomerID, ;C.CompanyName, ;(SELECT YTD_Sales FROM Sales_02 WHERE ;C.CustomerID = Sales_02.CustomerID) AS Y02,;

(SELECT YTD_Sales FROM Sales_03 WHERE ;C.CustomerID = Sales_03.CustomerID) AS Y03,;

(SELECT YTD_Sales FROM Sales_04 WHERE ;C.CustomerID = Sales_04.CustomerID) AS Y04 ;

FROM Customers C

This SELECT statement returns the customer IDand company name along with year-to-date salesfrom warehoused tables for the last three fiscalyears.

A restriction on a projection is that the sub-queryshould return only one column and no more thanone record for each record in the containingSELECT.

Another valuable use of a projection is when it’sused as part of an expression.

SELECT ;C.customerID, ;C.companyname, ;SUM(D.quantity*D.unitprice) AS CustTotal ,; (SUM(D.quantity*D.unitprice) / ;(SELECT SUM((quantity*unitprice)-discount) ;

FROM OrderDetails D2) ;)*100 AS PctTotal ;

FROM Customers C ;INNER JOIN Orders O ;

ON C.customerID = O.customerID ;INNER JOIN OrderDetails D ;

ON O.orderid = D.orderid ;GROUP BY C.customerID, C.companyname, O.orderID ;ORDER BY pctTotal DESC

This SELECT statement returns customer ID,company name, total sales, and a percent of totalsales against all customer sales.

Note that the sub-query in the SELECT list is partof a complex expression that includes an aggregatefunction. Now that's flexibility!

A derived table as sub-query allows you to treat theresults of a sub-query as though it were its owntable.

Consider the following example:

SELECT ;C.customerid, ;P.product_count AS p_count;FROM Customers C, ;

(SELECT c2.customerid, ; COUNT(DISTINCT D.productID) AS p_count ;FROM Customers C2 ;INNER JOIN Orders O ;

ON C2.customerid = O.customerid ;INNER JOIN OrderDetails D ;

ON O.orderid = D.orderid ;GROUP BY c2.customerid) AS P ;

WHERE C.customerID = p.customerID ;AND P.p_count >= ;

(SELECT (COUNT(*)*.50) FROM Products) ;ORDER BY p.product_count DESC

This SELECT statement returns customer ID andproduct count for all customers who havepurchased at least 50% of the product line.

Notice that the derived table has an alias of "P"that is designated the same way you would aliasa column, using the AS clause (required). It'salso important to note that the sub-query can becomplex (in this case, joining to two othertables) and that the results from the derived table can be used as a condition of the WHEREclause and in the ORDER BY of the top-mostSELECT.

Unlike a projection, the derived sub-query canreturn more than one column and more than onerecord. It cannot be correlated. All sub-selects areexecuted before the top-most SELECT is evalu-ated.

Sub-queries are also supported in the SET list of aSQL UPDATE statement. Only one sub-query isallowed in a SET clause and if there is a sub-queryin the SET clause, a sub-query in the WHEREclause is not allowed.

Better Correlation Support

The SQL UPDATE and SQL DELETE commandsnow support correlation. A correlated statementincludes a FROM clause to relate the records beingaffected to another table.

Common ErrorMessages

As your SQL statements getlonger and more complex, youwill become familiar with thefollowing errors:

Error # 18: Line is too long

Error # 1812: SQL: Statementis too long

Error# 1814:Queries of thistype are not supported.

Error# 1845: Too Complex

Error# 2189: SQL: Too manyfields in final or intermediateresult have the same name.

If you dynamically build SQL,you may want to consideradding traps for these errors sothat you can isolate them foranalysis.

SYS(3055)

If you are not bound by a lowmemory environment, considerusing SYS(3055) to set thecomplexity level of the FOR andWHERE clauses to itsmaximum value of 2040 whenyou set up the environment foryour applications. This allowsmore complex SQL statementsand more values with IN. Seethe Help files for a list of othercommands and functions thatare affected by this setting.

What's New with Data in Visual FoxPro 9.0www.code-magazine.com 13

A single SQL SELECT statement can contain more tables,

more JOINs, more sub-queries,more nested sub-queries, and more

UNIONs than ever before.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 13

For example:

DELETE products ;FROM mfg ;WHERE mfg.productID = products.productID;AND mfg.discontinued = .t.

This DELETE statement deletes all productsmarked as discontinued from a table provided bythe manufacturer.

A correlated UPDATE looks similar:

UPDATE products ;SET unitprice = mfg.msrp *.90 ;FROM mfg ;WHERE mfg.productID = products.productID

This UPDATE statement sets the unit price for aproduct at 90% of the manufacturer’s suggestedretail price.

You may be tempted to use a correlated sub-queryas this is also supported. Just be aware that whenusing a sub-query it's like doing an outer join. Forevery record that is not found in the sub-query, thevalue returned is NULL. This may not give thedesired results.

UPDATE products ;SET unitprice = ;

(SELECT ( msrp *.90 ) ;FROM mfg ;

WHERE mfg.productID = products.productID)

This UPDATE statement sets the unit price for aproduct at 90% of the manufacturer’s suggestedretail price for every product found in the Manufac-turers table. The price for products not found in theManufacturers table is set to NULL.

Note that this statement operates on every record inthe Products table; in the previous statement, onlyupdated records that matched in the Manufacturerstable were involved.

View and Query Designers

Unfortunately, due to the complexity of the SQLstatements you can write with these enhancements,the Query and View Designers do not support manyof the sub-query changes to SQL.

Also, with the hard-coded limits of SQL INremoved, the Designers no longer convert IN toINLIST(). The INLIST() function still has a limit of24 elements.

Enhanced UNION Support

In addition to having no hard-coded limits for thenumber of UNIONs, you can now use a UNIONinside the result set that is used by an INSERTINTO.

You can now also ORDER BY <fieldname> whenusing UNION. The referenced field must be presentin the SELECT list for the last SELECT in theUNION.

Performance

Regardless of whether you are doing remote dataaccess or relying on the powerful native dataengine, performance has always been a priority forVisual FoxPro. Visual FoxPro 9.0 enhances the dataengine even further.

Binary Indexes

This new index type is a specialized index, designedfor one thing:

INDEX ON DELETED() TAG DELETED BINARY

The new index type can be used with any NOTNULL logical expression. Other restrictionspreclude the use of a FOR expression andASCENDING, DESCENDING, UNIQUE, andCANDIDATE keywords.

SET ORDER TO is not supported and the INDEXON command sets the current order to 0. Also, youcannot use a Binary index with any Seek operation.

The big advantage of a Binary index is its size. ABinary index for a table with 8,000,000 records isapproximately 30 times smaller (1.1MB versus31.5MB). Smaller means faster I/O and fasterAPPEND and REPLACE, all with the same Rush-more optimization as a non-binary index on thesame expression.

There is a trade-off to consider. Rushmore opti-mization is faster if the amount of records returnedis more than 3% of the total records (about 92%faster when all records match the condition).However, Rushmore optimization is slower if theamount of records returned is less than 3% (abouttwo times slower when 0 records match the condi-tion). It is likely that the 3% threshold will becomesmaller as the number of records in the tableincreases.

Turning your DELETED indexes into Binaryindexes is an easy way to start taking immediateadvantage of Visual FoxPro 9.0 performanceenhancements. Just be sure that all clients accessing

John KoziolTest Engineer, Visual FoxPro TeamMicrosoft

An overall area of enhance-ments for Visual FoxPro 9.0includes more event hooks andfewer capacity limitations. Youcan now hook into WindowsAPI events with the BINDE-VENTS() function. Now onlylimited to available memory,you can create arrays largerthan 64K and nest DOs deeperthan 128 levels. Also, many ofthe limits on clauses in SQLstatements have beenextended. You can write yourown report handlers andenhance the Visual FoxProinterface via MemberData.Visual FoxPro 9.0 is a greatupgrade for Visual FoxProdevelopers who want to havefewer limits in building applica-tions!

What's New with Data in Visual FoxPro 9.0 www.code-magazine.com14

Many of these changes improveFoxPro's client/server

capabilities by providing stronger interoperability with

SQL Server.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 14

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 15

What's New with Data in Visual FoxPro 9.0 www.code-magazine.com16

your data are upgraded, as this new index cannot beread by prior versions.

Rushmore Optimizations

There are a few new Rushmore optimizations thatdo not require changes to data and index structures.

Top N [PERCENT], an optimization made to SQL,provides improved performance. This operationreturns only the top number or percent of records ina result set as controlled in the ORDER BY clause.This change in Visual FoxPro 9.0 eliminates recordsfrom the sort process that don't belong in TOP N asearly as possible, reducing comparison operationsand decreasing file I/O in low memory situations.This also has the side-effect of only returningexactly N [PERCENT] records.

In previous versions, if there was a tie for Nth place,all records that matched the tie were included,resulting in getting back more than N records.

If this change in behavior is not desired, considerbracketing the SQL call with SET ENGINEBE-HAVIOR 80.

The only limitation to this optimization is that TOPN PERCENT cannot be used unless the entire resultset can be read into memory at once.

When appropriate, Visual FoxPro 9.0 uses filteredindexes to optimize MIN() and MAX() aggregatefunctions in FOR DELETED() and FOR NOTDELETED() only. This improves MIN()/MAX()performance, if such an index exists.

The Like “sometext%” operator is now fully opti-mizable when the string ends in a wildcard. (Notethat this is not the case when the comparison valuebegins with a wildcard or when the wildcard isembedded within the string.) This optimizationscenario works like WHERE field = “sometext”.

More INDEX Smarts

Visual FoxPro 9.0 is even smarter in how it utilizesexisting indexes to achieve Rushmore optimization.For example:

INDEX ON DELETED() TAG DELETED

This index is used to optimize both NOTDELETED() and DELETED() conditions withoutthe presence of a tag created by INDEX ON NOTDELETED().

Just like the MIN()/MAX() optimization, VisualFoxPro 9.0 uses a FOR NOT DELETED() filter onan index to optimize a DELETED() or NOTDELETED() query. Whenever it is possible to deter-mine that a condition should filter on DELETED()or NOT DELETED(), a filtered index FORDELETED() or FOR NOT DELETED() is used inthe event that no non-filtered indexes exist. Takethis upgrade opportunity to review the indexes you

currently deploy. If you are unable to use a binaryindex, you may find that with these optimizations,you can at least drop a few existing indexes.

If only indexes filtered FOR NOT DELETED() wereused for Rushmore optimization and SETDELETED is ON, additional NOT DELETED()optimization is unnecessary.

Commands and Functions

A few commands and functions have been extendedto provide greater control over how and whenVisual FoxPro reads and writes data to disk.

Fine-Tune How Data is Accessed and Committed

It is now possible to specify fractions of a second forthe second parameter of the SET REFRESHcommand. The second parameter is used to specifythe number of seconds between refreshing localmemory buffers with current data from disk. Youcan also specify a value of –1, which forces VisualFoxPro to always read data from the hard drive. Thelowest setting for the second parameter is .001.

Setting this value to a low number causes someperformance degradation as the number of requestsincrease, especially across a network, so use it spar-ingly.

The SYS(1104) function purges memory cached byprograms and data, and it clears and refreshesbuffers for open tables. In Visual FoxPro 9.0, asecond parameter scopes the operation to a specificwork area or alias. This is valuable because usingSYS(1104) when a large number of buffered tablesare open can result in slow performance while eachbuffered table refreshes.

The FLUSH command is used to ensure thatchanges made to tables, indexes, and files are savedto the disk. In Visual FoxPro 9.0, the FLUSHcommand has been enhanced in two ways: speci-fying FLUSH areas, and calling the FlushFileBuffersfunction.

You can now be specific about the filename, workarea, or alias to be FLUSHed. Although this extragranularity is handy, it's the FORCE keyword that isvery useful in scenarios where Visual FoxPro 9.0

A derived table, such as a sub-query, allows you

to treat the results of a sub-query as though it

were its own table.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 16

What's New with Data in Visual FoxPro 9.0www.code-magazine.com 17

writes data to the disk but the operating systemkeeps the writes cached.

When you use the FORCE keyword, Visual FoxPro9.0 includes a call to the Windows API FlushFile-Buffers function. This ensures that even operatingsystem buffers are written to disk.

Some examples of using the enhanced FLUSHcommand include:

FLUSH "c:\data\customers.dbf"FLUSH "c:\data\customers.dbf" FORCEFLUSH IN 1 FORCEFLUSH IN customer FORCEFLUSH "c:\test.txt" FORCEFLUSH FORCE

In versions prior to Visual FoxPro 9.0, using a SQLSELECT statement meant that the results werealways pulled from disk. This meant that if youwanted to query uncommitted changes from abuffered table, you were forced to use proceduralcommands. Now it's possible to specify for eachtable in a SELECT statement whether to read fromthe disk or from the local buffer using SETSQLBUFFERING and SELECT … WITH(Buffering = <lexpr>).

Some examples of how to use WITH (BUFFERING…) include:

SELECT * FROM Customer WITH (BUFFERING = .t.)

SELECT * FROM Orders WITH (BUFFERING = lUseBuffer)

SELECT DISTINCT c.city, o.shipcity ;FROM customers C WITH (BUFFERING=.T.) ;JOIN orders O WITH (BUFFERING=.T.) ;ON c.customerID = o.customerID

Notice that each table referenced has its own WITHBUFFERING clause. If no BUFFERING clause isused, Visual FoxPro 9.0 respects the SETSQLBUFFERING value (whose default is .f.).

Support for BUFFERING is only available on localVisual FoxPro 9.0 data. It is not supported on datafrom back-end databases, so it should not be usedwith SQL Pass Through.

When working with a table that is involved in ROWbuffering, using the WITH BUFFERING command

causes the current record to be committed beforethe statement is executed.

The SET SQLBUFFERING command is scoped tothe current data session. When the WITHBUFFERING clause is used, it overrides the SETstatement.

CAST()

The new CAST() function is modeled after the SQLServer function by the same name. It is useful bothin and out of SQL. Used inside a SQL statement,you can write SQL code that is more TSQLcompliant. As you might expect, this functionconverts a data expression to another data type.

Used within a SQL statement, the CAST() functionlooks like this:

SELECT CustomerID, ;CAST(nAmount*nRate AS N(8,2)) ;

FROM SALES

SELECT CustomerID, ;CAST(nAmount*nRate AS B NOT NULL) ;

FROM SALES

SELECT CustomerID, ;CAST(nAmount*nRate AS C(10)) ;

FROM SALES

SELECT foo.*, ;CAST(NULL as I) AS IntegerField ;

FROM foo

Notice that there is the ability to specify whetherNULLs are acceptable. If not specified, CAST()inherits NULL behavior from the expression, ifpossible.

ICASE()

Another function, ICASE() emulates TSQL’s CASEbranching construct. This function is similar toIIF(), the immediate IF function. The value ofICASE() is that it doesn't require the kind of uglyand verbose nesting syntax of IIF().

ICASE() works by requiring condition/result para-meter pairings. The first parameter is the conditionexpression to evaluate, and the second parameter isthe result if the condition provided in the first para-meter evaluates to True. If the condition evaluates toFalse, the second parameter is skipped and the nextcondition/result parameter pair is evaluated. Thiscontinues for each parameter pairing. If the parame-ters are not passed in pairs, Error # 11 is thrown.

Just like the CASE/ENDCASE syntax, there is anOtherwise option that can be passed as the lastparameter to the function. If this parameter is notpassed and all other condition parameters evaluateto False, ICASE() returns a NULL.

Regardless of whether you are doing remote data access

or relying on the powerful native data engine, performance

has always been a priority.

Calvin Hsia

Lead Developer, Visual FoxPro Team Microsoft

One of the many cool thingsVFP 9.0 allows you to create isbetter photo database applica-tions without using the generalfield using the new BLOB datatype and enhancements toimage properties. There aremany new features in the Property Sheet includingcustom font and color controlas well as a custom Favoritestab for categorizing propertiesand methods. There are newfunctions and events in VFP 9.0specifically designed forbuilding even more advancedTablet PC based applications.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 17

Here is an example of ICASE() used outside of aSQL statement:

nHour = HOUR(DATETIME())? ICASE( nHour = 8, "breakfast" , ;

nHour = 10, "caffeine" , ;nHour = 12, "lunch" , ;nHour = 15, "caffeine" , ;nHour = 18, "dinner" , ;"snack" ;)

Up to 100 condition/result parameter pairs can bepassed to the ICASE().

SYS(3092) Output to a File

This new SYS() function works in conjunction withSYS(3054), the SQL Showplan function. WithSYS(3092), you can specify a filename to which theresults of SYS(3054) output are sent.

An example of how to use these functions togetheris listed here:

SYS(3054,12,"dummyVar")SYS(3092,"ShowPlan.txt")OPEN DATABASE HOME(2)+"Northwind\Northwind"

SELECT * ;FROM Customers INTO CURSOR temp1

SELECT * ;FROM summary_of_sales_by_year ;INTO CURSOR temp2

SYS(3092,"")

CLOSE DATA ALLMODIFY FILE Showplan.txt NOWAIT

If you don't include a variable name as the thirdparameter for SYS(3054), results are echoed to thecurrent window.

New Datatypes

In an effort to provide better compatibility withback-ends such as SQL Server, Visual FoxPro 9.0has added three new data types, VarChar,VarBinary, and BLOB. These data types can beused as part of a local table, which is created byusing the CAST() function or mapped to whenretrieving results from remote data.

VarChar

The VarChar is a character data type that is notpadded with spaces to the length of the field. This provides similar functionality as SETANSI_PADDING ON in SQL Server.

In addition, if spaces are included with the originalvalue, they are not trimmed.

It's important to note that when using expressionscombining VarChar with Character data types, theresult is always of the type VarChar.

Because a VarChar is a fixed length field in VisualFoxPro 9.0, the maximum field length is 255 char-acters. In SQL Server, it is possible for a singleVarChar field to have a length <= 8060 bytes.

VarBinary

The VarBinary data type is similar to VarChar inthat values assigned to VarBinary fields are notpadded. The only real difference between the two isthat FoxPro does not perform any code page trans-lation for VarBinary types. (See Figure 1 for acomparison of VarChar and VarBinary datatypes.)

BLOB

The BLOB (Binary Large OBject) data type is not afixed length type, like a Memo. Its information isstored in an .FPT file that is external to but refer-enced by the .DBF file. BLOBs have the same limi-tations and issues as Memo fields and do notsupport indexes.

What's New with Data in Visual FoxPro 9.0 www.code-magazine.com18

When using expressions that combine

VarChar with Character data types, the result is

always of the type VarChar.

Figure 1: Comparing Character and VarBinary data types looks like this.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 18

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 19

Like the VarBinary type, Visual FoxPro 9.0 does notperform any code page translation and its content istreated as binary content.

The BLOB datatype is an ideal candidate to replacethe legacy General field. Pictures and other mediacan be stored in a BLOB and then rendered usingthe Image control PictureVal property.

MODIFY MEMO of a BLOB displays a HEX dumpof the binary data.

SET EXACT & Binary Comparison

With Binary data types comes a difference inbehavior with SET EXACT and SET COLLATE.

SET EXACT ON specifies that expressions mustmatch exactly to be equal. However, with VarBinarytypes whose values are padded with CHR(0), thetrailing bytes are ignored for the comparison. Theshorter of the two expressions is padded on theright with CHR(0) bytes to match the length of thelonger expression.

SET EXACT OFF specifies that expressions mustmatch up exactly to the length of the expression onthe right side of the comparison.

The == operator requires that both sides of theexpression contain exactly the same number ofbytes, including CHR(0) bytes.

Further, because Binary data is case-sensitive,comparisons are always case-sensitive regardless ofSET COLLATE. This is different than a comparisonwith a Character type, which is case-insensitive ifSET COLLATE is set to GENERAL.

Learn to Type

Prior to version 9.0, Visual FoxPro allowed a SQLCREATE TABLE statement to include a long type-name, although only the first letter of the type-name was respected. This resulted in issues withdata types such as Character and Currency. Thereis now full support for long typenames with bothCREATE and ALTER TABLE/CURSOR as well asthe new CAST() function. Table 1 provides a list ofFoxPro data types with their long name, singleletter, and, in some cases, abbreviations,supported.

Just like with the new Binary index datatype, usingthese new datatypes requires that all clientsaccessing the data be upgraded to Visual FoxPro

9.0. Tables that include these new types cannot beread by prior versions.

Remote Data

Visual FoxPro has always had a strong remote datastory, and Visual FoxPro 9.0 adds even morecontrol over how remote data is manipulated andretrieved.

Transactions and Connections

A new property, DisconnectRollBack has beenadded to control whether pending transactionsshould be rolled back when the connection isdisconnected. This property is available through theSQLSetProp, SQLGetProp, DBSetProp, andDBGetProp functions.

SQLIdleDisconnect() is a new function that can beused to temporarily disconnect a connection. Inmost cases, this is controlled through the Idle-TimeOut property of a connection. But in multi-threaded run-time (MTDLL), idle tasks are disabled.Calling this function releases the connection untilthe next command requiring a connection is issued.

Fetching and Processing Information

CursorGetProp() receives two new properties,RecordsFetched and FetchIsComplete. These prop-erties allow you to determine the number of recordsfetched and when a fetch is completed during theexecution of SQL Pass Through statements.

The RecordsFetched value might not reflect thenumber of records in the cursor in the event thatsome records in the cursor were locally appendedor deleted. The number of records reported does notrespect filters that may be on the cursor.

The number of records affected by the SQL PassThrough can be determined using the additionalparameter aCountInfo available with the

What's New with Data in Visual FoxPro 9.0 www.code-magazine.com20

Data Type Long Name InitialCharacter Char, Character CDate Date DDateTime Datetime TNumeric Num, Number NFloating Float FInteger Int, Integer IDouble Double BCurrency Currency YLogical Logical LMemo Memo MGeneral General GPicture Picture PVarChar Varchar VVarBinary Varbinary QBLOB Blob W

Table 1: Here are some FoxPro data types with their sup-ported long names and initials.

Many of the changes made to the CursorAdapter were

done to bring behavior in line with remote views.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 20

SQLEXEC() and SQLMORERESULTS() functions.ACountInfo is a two column array containing thealias and count.

Rowsets from the Provider

Three functions have been added to supportreturning result sets from stored procedures whenaccessed via the provider.

SetResultSet() is used to specify which work area inthe current DataSession is to be used as the resultset. Only one cursor in a DataSession can bemarked.

GetResultSet() returns the work area for the cursormarked by SetResultSet.

ClearResultSet() sets the marked result set to 0 andreturns the work area for the previously markedcursor.

Within a stored procedure, you create a cursor withthe results you want returned and use SetRe-sultSet() to identify the work area for the cursor.When executed by the provider, return values arenot respected, but a rowset is created based on thecursor you provided.

CursorAdapter and XMLAdapterEnhancements

Changes to the CursorAdapter and XMLAdapterclasses are worthy of another multi-page article. But forthe purpose of this overview, it's important to brieflynote some of the more significant enhancements.

Many of the changes made to the CursorAdapterbring behavior in line with remote views. Theseenhancements include:

· Support for Timestamp fields. This allowsUPDATE/DELETE commands to use Timestampfields as part of the WhereType method.

· Auto-Refresh support. Several new propertieshave been added so that remote data managed bythe CursorAdapter is automatically refreshed afteran INSERT or UPDATE operation. This is valu-able for retrieving auto-incremented or defaultfield values and timestamps.

· On Demand Record Refresh support. More properties and events have been added to CursorAdapter to support the same kind of func-tionality provided by the REFRESH() function forlocal and remote views.

In addition, properties have been added to supportDEFAULT and CHECK constraints and formapping remote data to the new VarChar andVarBinary data types.

XMLAdapter changes include support for:

· Hierarchical XML· XPath expressions· XML encoding and decoding enhancements

Upgrading

Visual FoxPro 8.0 included changes to FoxPro'sSQL commands, and brought it into greater compli-ance with ANSI SQL standards. These changes mayhave discouraged you from upgrading to version 8because of the impact on existing code. If you'vebeen putting off fixing some of those ambiguousqueries or at least bracketing them with SETENGINEBEHAVIOR, Visual FoxPro 9.0 providesmany compelling reasons to make the investment toupgrade.

Fortunately, the kinds of changes made in VisualFoxPro 9.0 won't require the kind of recoding youmay have found necessary for 8.0.

As in prior versions, using the SET ENGINEBE-HAVIOR command allows you to isolate legacycode that may be problematic.

SET ENGINEBEHAVIOR 90 impacts the behaviorof TOP N and the behavior of aggregate functionswithout a GROUP BY clause. In versions prior to 9,if such a statement resulted in no matching criteria,0 records were returned. To be more ANSIcompliant in version 9.0, FoxPro returns a singlerecord resultset with NULL value(s) for aggregatecolumns.

Final Thoughts

In this article, you’ve seen that the changes to thedata engine in this release are substantial. Acommitment to backward compatibility and an easyupgrade path has made these changes nearly trans-parent when moving from 8 to 9. Once you've madethe move, you can start taking advantage of thesegreat enhancements—some without changing asingle line of code. Other enhancements ensure thatcode based on the new features is more compatible,powerful and maintainable than ever before.

David T. Anderson

Ken LevyProduct Manager, Visual Studio Data TeamMicrosoft

With all the great new featuresincluded in Visual FoxPro 9.0,my favorite feature remains theworld-wide FoxPro community.There is a list of great VisualFoxPro community resourcesat http://msdn.com/vfoxprocommunity. Posting a questionon a community Web site isfree and you usually get a quickresponse from somebody whoknows the answer. Interactingwithin the FoxPro communityis not only educational, but canbe fun and it can result infinding employees oremployers.

Information from the VisualFoxPro team is regularlyincluded in the monthly lettersathttp://msdn.com/vfoxpro/letters.For even more up-to-date newsand information, refer to myblog athttp://blogs.msdn.com/klevy,Calvin Hsia's blog athttp://blogs.msdn.com/calvin_hsia, and the Visual Studio DataTeam blog athttp://blogs.msdn.com/vsdata.All of this information VisualFoxPro 9.0 whitepapers andsamples can be found on theVisual FoxPro home page athttp://msdn.com/vfoxpro.

What's New with Data in Visual FoxPro 9.0www.code-magazine.com 21

As in prior versions, using the SET ENGINEBEHAVIOR

command allows you to isolate legacy code that may be problematic.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 21

The Visual FoxPro 9.0 Report Writer www.code-magazine.com

The Visual FoxPro 9.0Report WriterMicrosoft has significantly improved the Report Writer in Visual FoxPro 9.0. They also recognized the significant investment in existingFRX-based reports and designed the new Visual FoxPro 9.0 Report Writer to becompatible with previous versions of Visual FoxPro Reports. This makes it a greatblend of the old and the new. In this article, you'll learn about some of the newfeatures, including the new reusable data environments, report protection, andseveral user interface enhancements. You’ll also learn about enhancements tolayout objects and data groups. Finally, you’ll learn about one of the bestimprovements to the Visual FoxPro 9.0 Report Writer: multiple detail bands.

Before I explain the new features, you need tounderstand how to use the new Report Designerand how to run reports using the new output

engine.

The Report Designer: By default, the new VisualFoxPro 9.0 Report Writer uses the new XbaseReport Designer. It providesnewer dialog boxes and is easierto use than the old version. Italso provides access to many ofthe new features that are notavailable through the oldReport Designer. You can easilycontrol which Report Designeris used by changing the value ofthe new _REPORTBUILDERsystem variable.

*-- Use the new Report Builder_REPORTBUILDER = HOME() + 'ReportBuilder.app'

*-- Use the old Report Builder_REPORTBUILDER = ''

The Output Engine: Just as with the ReportDesigner, you can control whether the new or oldoutput engine is used. Unlike the Report Designer,which defaults to the new style out of the box,Visual FoxPro 9.0 defaults to the older outputengine. The reason for this is that GDI+ is used inthe new engine, and renders slightly differently thanthe GDI used by the old engine. Therefore, some ofyour existing reports could render differently inVisual FoxPro 9.0, which means you'd have totweak them to make them appear correctly. Youcan switch between the output engines with thefollowing command:

*-- Use the new output engineSET REPORTBEHAVIOR 90

*-- Use the old output engineSET REPORTBEHAVIOR 80

The rest of this article assumes that the new ReportDesigner and the new output engine are both in use.

Data Environments

The Visual FoxPro 9.0 ReportWriter can now share DataEnvironments with otherreports. Data Environmentscan also be saved as a class andthen loaded into reports as

needed. This offers a great reuse scenario fordefining common reporting needs.

Save As Class

To save a Data Environment as a class, start bydefining the Data Environment in a report as usual.While the Data Environment window is still active,select the newSave As Class…option from theFile menu.

After selectingthe Save AsClass… option,the Save AsClass dialog boxappears, asshown in Figure1. The DataEn-vironmentbutton of theSave option

Cathy PountneyCathy Pountney is a MicrosoftVisual FoxPro MVP and hasbeen developing software forover 22 years, thirteen of whichwere as an independentconsultant specializing inFoxPro. In 2001 she had theprivilege of spending sixmonths as a contractor onsitein Redmond with the MicrosoftFox Team. Currently she worksfor Optimal Solutions devel-oping Visual FoxPro applica-tions for schools. Cathy hasspoken at many FoxPro confer-ences and user groups acrossthe U.S., written articles forvarious magazines, and herbook, The Visual FoxPro ReportWriter: Pushing it to the Limitand Beyond, is available fromHentzenwerke Publishing(www.hentzenwerke.com).

[email protected]

www.frontier2000.com

www.optimalinternet.com

Fast FactsThe new

Visual FoxPro 9.0 Report Writer is compatible with previousversions of Visual FoxPro,

making it a great blend of the oldand the new.

22

QU

ICK

ID 0

4040

42

Figure 1: Use the Save As Class dialogbox to declare the name of the newclass and the class library for savingthe Data Environment of a report.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 22

group is the only option button-enabled whensaving a Data Environment of a report.

Enter a name for the class in the Name textbox.Next, enter the name of the class library you wantthe new class saved in. If you enter the name of aclass library that does not exist, the new class libraryis created for you. You can also use the ellipsebutton to brows for the location of an existing classlibrary. Finally, you may optionally enter a descrip-tion of the new class.

Loading a DE

In addition to manually defining the Data Environ-ment for a new report, Visual FoxPro 9.0 also givesyou the option to load the Data Environment froman existing report or from a saved DataEnvironmentclass. The Load Data Environment… option on theReport menu allows you to select which Data Envi-ronment to load.

From a Report

When loading the Data Environment from anotherreport, all the code and members of the original DataEnvironment are copied into the new report. Thismeans any changes made to the original report'sData Environment after the fact are not propagatedinto reports created from the original report.

The Report Properties dialog box, as shown inFigure 2, appears after selecting Load Data Envi-ronment… from the Report menu. Use this menu toselect the report from which you wish to copy theData Environment.

Select the Copy from another report file optionbutton and then click the Select… button. Thisinvokes the Open dialog box so you can choosewhich report to copy from. Once you chose areport, a confirmation dialog box appears.

Visual FoxPro 9.0 is about to copy the Data Envi-ronment from another report to the current report.Visual FoxPro 9.0 notifies you that it's about tooverwrite the current Data Environment, and you

must click Yes to continue. This helps remind youthat anything you have defined in the Data Envi-ronment of the current report is about to be over-written. If you click No, changes are not made andthe process is aborted. When you click Yes, theData Environment is copied and you are notified byanother dialog box.

You are now finished copying the Data Environ-ment. You may manipulate the Data Environmentas needed. However, remember that any changesmade to the original report's Data Environmentafter this point are not propagated to this newreport.

From a DE Class

When loading the Data Environment from a class,code is added to the Data Environment of the newreport to bind to the original DataEnvironmentclass and instantiate it at runtime. This means thatfuture changes made to the DataEnvironment classwill propagate into any reports using the DataEnvi-ronment class.

Using the Report Properties dialog box, shown inFigure 2, click the Link to a visual DE class button.Next, click the Select… button to invoke the Opendialog box that you can use to choose which classlibrary and which class to use. After confirmingyour intentions, the Data Environment is updatedand you are notified of its completion.

At this point, code has been added to five DataEnvironment methods: Init(), BeforeOpenTables(),AfterCloseTables(), Destroy(), and Error(). Some ofthe methods have very simple code with nothingmore than a DODEFAULT() command. The reasonfor this is that BindEvents() does not functionunless the method contains at least one line of code.Look at the code in these methods to see what itdoes, but I do not recommend that you change it.

ProtectionIn Visual FoxPro 9.0, you can create protection forone or more layout objects when using ReportDesigner or Label Designer. This lets your usermodify a report, yet keeps them from makingcertain changes.

Layout objects have five protection modes you canset, and Field objects have an additional protectionoption. Bands have two protection modes you canset. The report itself has a variety of different protec-tion modes you can set.

www.code-magazine.com 23The Visual FoxPro 9.0 Report Writer

Just like the Report Designer, you can control whether the new or

old output engine is used.

Figure 2: Use the Data Environment tab of the Report Prop-erties dialog box to choose which report you want to copy aData Environment from.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 23

Protecting an Object

To protect a layout object in the Report Designer,select the Properties dialog box for the object. TheProperties dialog box can be invoked from theReport menu after selecting the object from theright-click menu of the object, or by double-clickingthe object. Figure 3 shows the Protection tab of theProperties dialog box for a field object. You can setthe following five protection modes for layoutobjects:

· Object cannot be moved or resized prevents usersfrom moving this layout object to a different posi-tion on the design surface and prevents users fromresizing this object.

· Object cannot be edited prevents the user frommaking changes to the properties of this layoutobject.

· Object cannot be deleted prevents the user fromdeleting this object.

· Object cannot be selected prevents users fromselecting this object. When this option is selected,the protection behaviors of Object cannot bemoved or sized. Object cannot be edited andObject cannot be deleted are also imposed.

· Object is not visible in Designer prevents thisobject from appearing in the Report Designer inprotected mode. When this option is selected, theprotection behavior of the other four options isalso imposed.

The Design-time caption portion of this dialog boxonly applies to Field objects. The literal stringentered into this textbox is displayed in the ReportDesigner, instead of the Expression. This gives youthe opportunity to display something that is user-friendly instead of a complicated expression.

Protecting a Band

To protect a band in the Report Designer, select theProperties dialog box for the band. The Propertiesdialog box can be invoked from the Edit Bands…

option on the Report menu, from the right-clickmenu of the band, or by double-clicking the graybar of the band. Figure 4 shows the Protection tabof the Properties dialog box for a band. You can setthe following two protection modes for bands:

· Band cannot be edited prevents the Band Proper-ties dialog box from being accessible to the user.

· Band cannot be resized prevents the user fromresizing the band.

Protecting a Report

To set overall report protection, select the ReportProperties dialog box. You can invoke this dialogbox by selecting Properties from the Report menuor from the right-click menu of the report. Figure 5shows the Protection tab of the Report Propertiesdialog box.

The top portion of this dialog box allows you todefine which tabs of the Report Properties dialogbox are unavailable to the user. For each of theselections made in this area, the applicable tab ofthe Report Properties dialog box is disabled. TheProtection option is always checked and disabled.The Ruler/Grid option is disabled because the tabcannot be protected, although it appears on thedialog box so that the selections are consistent withthe tabs on the Report dialog box.

The bottom portion of this dialog box allows you todefine which menu options are unavailable to theuser. For each of the selections in this area, theapplicable menu option is disabled.

Honoring Protection Flags

To invoke protection during a Report Designer orLabel Designer session, use the PROTECTEDkeyword, as shown in the following examples.

CREATE REPORT MyReport PROTECTEDMODIFY REPORT MyReport PROTECTEDCREATE LABEL MyLabel PROTECTEDMODIFY LABEL MyLabel PROTECTED

If the PROTECTED keyword is not used, the

SUMMARY Issues

Using the SUMMARY clause ofthe REPORT FORM commandprevents the Detail band fromprinting, yet all the PageHeaders and Footers, ColumnHeaders and Footers, andGroup Headers and Footers areprinted. Any On Entry or OnExit expressions in a Detailband are not processed whenthe SUMMARY clause is used.When issuing SUMMARY on aMultiple-Detail band report, theDetail Headers, Detail Footers,and Detail bands are notprocessed.

The Visual FoxPro 9.0 Report Writer www.code-magazine.com24

Figure 3: Use the Protection tab of the Properties di-alog box to set protection modes of a layout object.

Figure 4: Use the Protection tab of the BandProperties dialog box to set protection modesof the band.

Figure 5: Use the Protection tab of the Report Properties di-alog box to set overall protection modes of a report.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 24

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 25

Yair Alan Griver

Group Manager, Visual Studio Data TeamMicrosoft

The new data enhancements—particularly those that targetSQL Server—are among myfavorite new capabilities inVisual FoxPro 9.0. I really likebeing able to use CAST(), opti-mized LIKE, and some of thenew features that give mebetter control over SQLhandles. I especially love theenhanced SQL syntax. Beingable to more closely modelVisual FoxPro and SQL Serverstatements is an awesomecapability!

The Visual FoxPro 9.0 Report Writer www.code-magazine.com26

Report Designer functions as if no protection wereapplied to the layout objects.

UI Enhancements

Many changes have been made to the user interfaceto make designing reports easier and more intuitive.Menus have been overhauled, context menus havebeen changed, and new options have been added tothe Report Designer toolbar. The ExpressionBuilder dialog box and Expression Builder Optionsdialog box have new behaviors, and a few othermiscellaneous user interface enhancements havebeen added to the Visual FoxPro 9.0 Report Writer.

Menus

The report menu system has been overhauled inVisual FoxPro 9.0 to accommodate new options. Inaddition, some options have been relabeled forclarity and some options have been repeated onseveral menus to allow easier access.

· The new Save As Class… option appears on theFile menu.

· A new option has been added for the ReportDesigner Toolbar and horizontal lines have beenadded to separate the Grid Lines and Show Posi-tion options from the other options.

· Many changes appear on the Report menu,including relabeled options, new options, and theaddition of the Print Preview option.

Context Menus

Existing context menus have been improved withadditional items and are now more consistent withthe dialog boxes they invoke. Items in the ReportDesigner that previously didn't have context menusnow do.

· The Global Context menu has new options andone relabeled option.

· Invoke the new Band Context menu by right-clicking on the gray bar of any band.

· Invoke the new Layout Object context menu byright-clicking on any layout object.

Toolbar

The improved Report Designer toolbar, shown inFigure 6, has two new buttons: The Page Setupbutton and the Font Properties button.

Expression Builder Dialog Box

A few changes have been made to the ReportExpression dialog box, including a taller Expressionfor Field editing box, which allows more room forentering a report expression.

When the _REPORTBUILDER system variable isempty, the native behavior of the ExpressionBuilder dialog box is specific. Only tables defined in

the Data Environment are listed in the Fields listbox. Tables opened outside of the Data Environ-ment are not available in the list box.

When the _REPORTBUILDER system variable isset to ReportBuilder.app, the behavior of theExpression Builder dialog box is quite different.First of all, the Expression Builder defined in_GETEXPR is invoked instead of the native Expres-sion Builder.

This Expression Builder dialog box has a combobox for choosing which table should be usedwhen listing fields in the Fields list box. Onlytables that are currently in use are listed in thecombo box. This is an important point toremember because tables defined in the DataEnvironment are not automatically opened by theReport Designer, and do not automatically appearin the combo box.

This gives you the ability to control which tables areavailable to end-users when you allow them tomodify reports within your application. You mayhave defined some tables in the Data Environmentthat you need, but to which you don't want useraccess. Because you have to specifically open thetables you want users to access, you can omit anytables you want to keep from them.

Expression Builder Options Dialog Box

The Field aliases option group is now enabled inthe Expression Builder Options dialog box. Thisoption group allows you to indicate whether or notyou want the table alias added to the report expres-sion when picking fields from the ExpressionBuilder dialog box.

The Always add alias and Never add alias optionbuttons cause Visual FoxPro 9.0 to automaticallyadd the table alias, or to not add the table alias, forall fields. The behavior of the Add non-selectedalias only option button depends on the value of the _REPORTBUILDER system variable. If the_REPORTBUILDER system variable is empty, anyfield chosen from a table that is not the InitialSe-lectedAlias is prefixed with the table alias. Fieldsfrom the InitialSelectedAlias table are not prefixedwith the table alias.

If the _REPORTBUILDER system variable is set toReportBuilder.app, the Add non-selected alias onlyoption uses slightly different logic. The currentlyselected alias is used instead of the InitialSe-lectedAlias for determining whether or not to prefixthe field with the table alias.

In addition to selecting a field from the ExpressionBuilder dialog box, dragging a field from the DataEnvironment to the Report Designer surfacehonors the setting of the Field aliases optiongroup. Also, a new option exists on the Report tabof the Options dialog box to determine what thedefault Field aliases setting is for all newly createdreports.

Figure 6: New options for PageSetup and Font Properties havebeen added to the Report Design-er toolbar.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 26

The Visual FoxPro 9.0 Report Writerwww.code-magazine.com 27

Mouse Cursor

The mouse cursor now changes to provide a visualcue when an object can be resized (see Figure 7).

Multiple Selection Dialog Box

Visual FoxPro 9.0 has a Multiple Selection dialogbox, which allows you to set the Protection andPrint when properties for more than one layoutobject at a time. It also allows you to change any ofthe other properties of any individual layout object.To use this new feature, select more than one layoutobject, and then double-click any one of the objectsto invoke the Multiple Selection dialog box, asshown in Figure 8.

The layout objects that were selected when thisdialog box was invoked are listed in the first tab ofthis dialog box. To work with all layout objectsdefined in a report, use CTRL + A to select alllayout objects before invoking this dialog box.

The Sort by option allows you to sort the list oflayout objects by Type or Location within thereport. The Remove from list button removes theselected layout objects from the list. Double-click onany item in the list and the applicable Propertiesdialog box for the individual object is invoked. TheProperties tab of the Multiple Selection dialog box,shown in Figure 9, is used to change the propertiesof all the items listed in the Selection tab.

Select the Apply these protection settings to theselected objects checkbox to enable the Protec-tion options. Select the Apply this condition tothe selected objects upon saving checkbox toenable the Print when option. Change the Protec-tion and Print when settings as needed, and thenselect OK to close the dialog box and apply thechanges to all the layout objects listed on the firsttab.

More Zoom Levels

The Preview window now has more Zoom levels,ranging from 10% up to 500%.

Layout Object Enhancements

A few improvements have been added for layoutobjects, including an option to control templatecharacters, a trim mode for character expressions,and relative and absolute positioning.

Template Characters

The Field Properties dialog box has a new sectionfor Template characters. The two available optionsare Overlay and Interleave. These determine howspecial characters are used in the format.

When the Overlay option is used, special charactersare treated as part of the data and overlay any otherspecific character in a specified position. For

example, when the Format expression is "999-999",and the data contains "123456", the report shows"123-56". Notice that the "4" is replaced by the dashin the format expression.

When the Interleave option is used, the specialcharacter is inserted in between existing charactersin the data. For example, when the Format expres-sion is "999-999", and the data contains "123456",the report shows "123-456". Notice that the dash isinserted between the "3" and the "4".

Trim Mode for Character Expressions

Prior to Visual FoxPro 9.0, field objects werealways trimmed to the nearest word when the textwas too long. In Visual FoxPro 9.0, a new optionon the Field Properties dialog box allows you todetermine how the text is trimmed. The six trimoptions are:

· Default trimming uses the defaultbehavior, which is the same as theTrim to nearest word, appendellipsis option. This behavior issimilar to prior versions of VisualFoxPro, with the exception ofappending the ellipsis.

· Trim to nearest character trims textto the last full character that fits inthe defined area.

· Trim to nearest word trims text tothe last full word that fits in thedefined area.

· Trim to nearest character, appendellipsis trims text to the last fullcharacter that fits in the definedarea, after an ellipsis is added to thetext that prints.

· Trim to nearest word, appendellipsis trims text to the last fullword that fits in the defined area,after an ellipsis is added to the textthat prints.

· Filespec: Show inner path asellipsis replaces the inner directo-ries of a long path and filenamewith an ellipsis when the full textdoes not fit in the defined area.

Size and Position

Another new feature available onLayout objects is better control of thesize and position of the object. Whenan object is added to the report, thevalues for From page top, From left,Height, and Width are automaticallyset. It's important to note that theFrom page top property is relative tothe top of the page in the ReportDesigner, which means it takes intoaccount the height of any gray barsabove the object. Changing the Frompage top property may inadvertentlymove the object to another band.

Figure 7: Use the new mouse cur-sor to know when an object canbe resized.

Figure 8: Use the Selection tab of the Multiple Selec-tion dialog box to choose which layout objects youwant to work on.

Figure 9: Use the Properties tab of the Multiple Selec-tion dialog box to change the Protection properties andthe Print When logic of the selected layout objects allat once.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 27

Relative Positioning: The From page top propertyand the Height property work together to deter-mine whether absolute positioning or relative posi-tioning is used. When the From page top propertyis set to a value that falls within the Report Designersurface, and the value of the Height property is lessthan or equal to the height of the band in which theobject is located, relative positioning is used. Rela-tive positioning is needed for objects in bands otherthan the Page Header and Page Footer.

Absolute Positioning: When the From page topproperty is set to a value that falls outside the

Report Designer surface, orthe value of the Height prop-erty is greater than the heightof the band in which theobject is located, absolutepositioning is used. Absolutepositioning means the object isprinted in exactly the samelocation on each and everypage.

Absolute Positioning can beused to create a watermark ona report. Place a graphic imagein the Page Header band, andset it to Scale contents, retainshape. Change the From pagetop property and the From leftproperty to indicate the upper-left corner of where you wantthe watermark to begin.Change the Height and Widthproperties to indicate theoverall size of the watermark,making sure not to extendbeyond the printable margins ofthe printer.

Data Group Enhancements

A few enhancements have been made to DataGroups in the Visual FoxPro 9.0 Report Writer,including maximum Data Groups and horizontalcolumns.

Maximum Data Groups

The maximum number of Data Groups has beenincreased from 20 to 74. In actuality, the maximumof 74 was always true, but the user interface onlyallowed 20 Data Groups to be entered.

Horizontal Columns

Previously, reports with more than one columndefined as horizontal with a Data Group wasted alot of space. The first position (row 1, column 1)was left blank and data began in column 2 of row 1.Also, a blank band was wasted in between each setof Data Groups, as shown in Figure 10. Even if theheight of the Data Group Header band is zero,Visual FoxPro still reserved the space, as shown inFigure 11.

In Visual FoxPro 9.0, the behavior of Data Groupsand horizontal columns has been changed. When anew Data Group is encountered, it is printed incolumn 1 of the next full row. The remainder of thisrow is left blank and not used for printing details.The details belonging to the Data Group begin incolumn 1 of the row immediately after the row theData Group is printed in, as shown in Figure 12.Also, no extra space is reserved if the height of theData Group Header band is zero, as shown inFigure 13.

The new behavior, although avoiding the previouslydescribed situation, may break some existing reports.

The Visual FoxPro 9.0 Report Writer www.code-magazine.com28

Figure 10: Prior versions of Visual FoxPro wasted a lot ofspace when using horizontal columns with Data Groups.

Figure 11: Prior versions of Visual FoxPro reserved spacefor Data Group Headers, even when none were defined.

Figure 12: Visual FoxPro 9.0 does not waste as much spaceas prior versions when horizontal columns and Data Groupsare used.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 28

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 29

However, an added benefit of the new behavior isthat the Data Group band can be stretched across allthe columns, as shown in Figure 14.

Multiple-Detail Bands

The new multiple-detail band feature is one of thebiggest, and most often requested, improvements. Itallows you to process multiple child tables for eachrecord in a parent table. An example of this type ofreport is shown in Figure 15.

Tables and Relationships

Understanding how the parent and child tableswork together are key to understanding how to usethis new feature. As an example of a Multiple-Detailscenario, assume you are writing the report shownin Figure 15. The database for this scenario is:

· The Customer table is the parent table andcontains one record for each insurance customer.

· The Members table holds one record for eachfamily member of the customer. The Memberstable is a child table of the Customer table.

· The Vehicles table holds one record for eachvehicle insured by the customer. The Vehiclestable is a child table of the Customer table.

· The Homes table holds one record for each homeinsured by the customer. The Homes table is achild table of the Customer table.

Driving the Report

One table is necessary to drive the report. In thisexample, the Customer table is the driving table. Ifyou use the report's Data Environment to define thetables, set the InitialSelectedAlias property to thistable. If you are using code to define the tables,

make sure the Customer table is the current workarea at the time the report runs.

The Target Alias

The target alias is the term used to describe whichtable is the driving table for a particular detail band.In this example, the Members table is the targetalias for Detail 1 band, the Vehicles table is thetarget alias for Detail 2 band, and the Homes tableis the target alias for Detail 3 band.

If no target alias is defined for a Detail band, thebehavior it takes on is that of prior versions ofVisual FoxPro. In other words, one detail band isprocessed per parent record. However, if you enterthe name of the parent table as the target alias,you'll get very different results. For each record inthe parent table, Visual FoxPro processes throughall records in the entire parent table, printing eachparent record in the Detail band. If you have a tablewith 10 parent records, and you set the target aliasof a detail band to the parent table, the final reportprints 10 sets of 10 records, or a total of 100records.

Relationships

Relationships play a big part in how multiple-detailbands operate. Visual FoxPro uses the relationshipsbetween the parent table and the child tables tonavigate through the records. You may use SETRELATION or SET SKIP to define these relation-ships. If you're opening the tables in the Data Envi-ronment, and relationships are already defined inthe database, these relationships are honored.

If you're opening the tables in code, Listing 1 showshow to set up the tables for the Insurance CustomerListing shown in Figure 15.

The Visual FoxPro 9.0 Report Writer www.code-magazine.com30

Figure 13: Visual FoxPro 9.0 does not reserve extra spacewhen the Data Group Header band is 0" tall.

Figure 14: In Visual FoxPro 9.0, you can expand the DataGroup Header band across multiple columns.

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 30

Defining Multiple-Detail Bands

By default, new reports are created with one Detailband. Additional Detail bands are added throughthe Optional Bands dialog box. To invoke thisdialog box, select Optional Bands… from theReport menu. This is the same dialog box that wasformerly named Title/Summary.

Click the Add button to add another Detail band tothe report. You may define up to 20 Detail bandsfor each report.

Defining the Target Alias

The target alias is assigned to a Detail bandthrough the Detail dialog box. This dialog box canbe invoked by selecting Edit Bands… from theReport menu and selecting the applicable Detailband. You can also invoke this dialog box bydouble-clicking the gray bar of the applicableDetail band.

The Target alias is an expression and you mustwrap the table name in quotes. Once you have eachtarget alias defined, the gray bars representing theDetail bands show the target alias.

The Visual FoxPro 9.0 Report Writerwww.code-magazine.com 31

When creating Multiple-Detail band reports, it'simportant to prefix field names with the applicablealias name. This prevents any confusion as to whichtable a field comes from.

Headers and Footers

Another enhancement of the Multiple-Detail bandis the ability to add Headers and Footers to eachDetail band. These are similar to Group Headersand Footers in some ways, yet different in others.For each parent record that is processed, thefollowing occurs:

• The Detail 1 Header band is processed.• The Detail 1 band is processed once for each

child record in the associated target alias.• The Detail 1 Footer band is processed.• The Detail 2 Header band is processed.• The Detail 2 band is processed once for each

child record in the associated target alias.• The Detail 2 Footer band is processed.• The Detail 3 Header band is processed.• The Detail 3 band is processed once for each

child record in the associated target alias.• The Detail 3 Footer band is processed.• And so on …

To turn on Detail Headers and Footers, place acheck mark in the selection box for DetailHeader/Footer in the Detail dialog box for each ofthe Detail bands. To help sort out the Detail bandsfrom other bands, the triangle preceding the bandname is solid and the other triangles are clear.

It's important to note that even if no detail recordsexist for a particular Detail band, the associatedDetail Header and Detail Footer bands still print.If you do not want the Detail Header and DetailFooter bands under this condition, you may usethe following Print when logic in each layoutobject defined in the bands to suppress theirprinting.

NOT EOF(<target alias>)

In addition to manually defining the Data Environment for a

new report, you have the option to load the Data Environment

from an existing report or from a saved

DataEnvironment class.

Figure 15: This sample Multiple-Detail band report has threeseparate detail bands for each customer.

Listing 1: Setting the relationship between the parent and child tables.

*-- Open the child tablesUSE Members IN 0 ORDER CustomerFKUSE Vehicles IN 0 ORDER CustomerFKUSE Homes IN 0 ORDER CustomerFK

*-- Open the parent tableSELECT 0USE customer ORDER CustomerPK

*-- Set the relations between the parent and childrenSET RELATION TO CustomerPK INTO MembersSET RELATION TO CustomerPK INTO Vehicles ADDITIVESET RELATION TO CustomerPK INTO Homes ADDITIVE

*-- Run the reportREPORT FORM Insurance PREVIEW

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 31

Be sure to place a check mark in the selection boxfor Remove line if blank for these layout objects aswell.

Similar to Group Headers and Footers, DetailHeaders and Footers have some of the sameoptions.

• Start on a new column: Causes the Detail set tostart on a new column of the report. Note that thisoption allows you to assign a Detail set to aspecific column. If one Detail set has enoughinformation to overflow the column, it iscontinued in the next column.

• Start on a new page: Makes the Detail set start ona new page.

• Reset page number to 1 for each detail set: Withthe Start on a new page option, resets the pagenumber to 1 for each new Detail set.

• Start detail set on new page when less than:Helps prevent orphans. The detail set begins on anew page if the indicated amount of space is notavailable.

• Detail Header/Footer: Adds a Detail Header andDetail Footer band around this Detail band.

• Reprint detail header on each page: With theDetail Header/Footer option, makes the DetailHeader band reprint whenever the Detail set over-flows to a new page.

Report Variables and Calculations

With the introduction of Multiple-Detail bands, reportvariables and calculations have some new twists tothem. You need to fully understand how they areprocessed in order to use them effectively. Otherwise,you may not get the results you're expecting.

The Reset at prompt on the Report Variables dialogbox has been renamed Reset based on. This moreclearly defines that the variable is reset based on thechange in value of the selected option. In additionto renaming the control, if more than one Detailband is defined in the report, each Detail band isadded to the drop-down list.

Selecting Detail n as the Reset based on value tellsVisual FoxPro to only process this calculation whenprocessing the detail records in the target alias of thisDetail band. The report variable is not altered whilerecords in other target aliases are being processed.This allows you to tie a report variable to one partic-ular Detail band. The value of the report variable isnot cleared until the Detail Header band of this sameDetail set is processed for the next parent record.

If you chose a Reset based on value other than oneof the Detail bands, the calculation is processed inseveral places. First, for each parent record, the calcu-

The Visual FoxPro 9.0 Report Writer www.code-magazine.com32

CoDe_Focus_Fox Pro 09 30.11.2004 12:42 Uhr Seite 32

lation is applied. Next, the calculation is applied foreach record in the target alias of the first Detail band.Then the calculation is applied for each record in thetarget alias of the second Detail band, and so on.

Tricks with Multiple-Detail Bands

The previous section showed how to print datafrom three different child tables on the same report.But a multiple detail band report doesn't necessarilyhave to have multiple child tables. The same childtable can be used in more than one detail band.

Group Totals

Prior to Visual FoxPro 9.0, printing subtotals in theData Group Header band (shown in Figure 16) wasvery difficult. The data had to be preprocessed tocalculate the totals prior to running the report. WithVisual FoxPro 9.0, no preprocessing is required.Follow these steps to create this report:

1. Create the Data Environment: Add theCustomer and Vehicles tables. Set the InitialSe-lectedAlias property to the Customer table.

2. Create a Data Group: Set the Data Groupexpression to the Customer PK. Do not place anyobjects in the Data Group Header or the DataGroup Footer bands.

3. Create the multiple detail bands: SelectOptional Bands… from the Report menu. Clickthe Add button to add one more detail band tothe report. Click OK to exit the dialog box.

4. Define the Detail #1 band: Double-click the graybar of the Detail #1 band to invoke the Propertiesdialog box. Set the target alias to "Vehicles",remembering to use the quotes. Check the Asso-ciated header and footer bands checkbox. Donot place any objects in the Detail 1 band.

5. Define the Detail #1 Footer band: Add theCustomer Name and Address objects to the band.Add the total vehicles and total premiums Labelobjects to the band. Add a Field object for the totalvehicles; set the expression to vehicles.premium,set the Calculation type to Count, and set theReset based on to Detail1. Add a Field object forthe total premiums; set the expression to vehi-cles.premium, set the Calculation type to Sum,

and set the Reset based on to Detail1.6. Define the Detail #2 band: Double-click the

gray bar of the Detail 2 band to invoke theProperties dialog box. Set the target alias to"Vehicles", remembering to use the quotes.Check the Associated header and footer bandscheckbox. Add any other objects needed inDetail 2 band.

The above report definition tells the Visual FoxPro9.0 Report Writer to process the Vehicles tabletwice for each customer in the Customer table. Thefirst time, it calculates the total records and dollaramount for the customer and then prints them.The second pass of the Vehicles table prints thedetails. This process repeats for each customer inthe Customer table.

Percentages

Another reporting concept is to show percentagesof totals of each detail line, as the detail line prints.This can also be handled with Multiple-Detailbands, as shown in Figure 17. Follow these steps tocreate this report:

1. Define the Data Environment: Add theCustomer and Vehicles tables. Set the InitialSe-lectedAlias property to the Customer.

2. Create a Data Group: Set the Data Groupexpression to the Customer PK. Add theCustomer Name and Address objects to the DataGroup Header band.

3. Create the multiple detail bands: SelectOptional Bands… from the Report menu. Clickthe Add button to add one more detail band tothe report.

The Visual FoxPro 9.0 Report Writerwww.code-magazine.com 33

Figure 16: Use a Multiple-Detail report to print group subto-tals at the beginning of the group.

Figure 17: Use a Multiple-Detail report to print percentagesalong with each detail line.

The Visual FoxPro 9.0 Report Writerincludes one of the

most often requested features:Multiple Detail Bands.

Randy Brown

Lead Program Manager, Visual FoxPro TeamMicrosoft

With Visual FoxPro 9.0, the Fox team brings you thebest desktop database applica-tion software on the market. As one who has always appre-ciated all the great extensibilityhooks that Fox has to offer, I am very excited about someof the new capabilities in VFP 9.0 such as the ability tohook in your own customMenu Designer. You can nowextend your classes withMemberData for classmembers that offer capabilitiessuch as custom propertyeditors, display in a Favoritestab and even XML docssupport. And the new openarchitecture for Visual FoxPro9.0 Reporting blows the doorsoff of anything available in themarket today.

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 33

4. Define the Detail #1 band: Double-click thegray bar of the Detail #1 band to invoke theProperties dialog box. Set the target alias to"Vehicles", remembering to use the quotes. Checkthe Associated header and footer bandscheckbox. Do not place any objects in the Detail1 band.

5. Create some report variables: Create a variablenamed rnTotalPremium, set the Value to store toVehicles.premium, set Calculation type to sum,and set the Reset based on to Detail 1. Create avariable named rnPercent, set the Value to storeto ROUND(100 * vehicles.premium / rnTotal-Premium, 2), and set the Calculation type toNone.

6. Define the Detail #2 band: Double-click thegray bar of the Detail 2 band to invoke the Prop-erties dialog box. Set the target alias to "Vehi-cles", remembering to use the quotes. Check the

Associated header and footer bands checkbox.Add any objects needed in the Detail 2 band.Add the Percent object to the Detail 2 band withan expression of rnPercent.

7. Define the Detail #2 Footer band: Add the totalpremium object, set the expression tovehicles.premium, set the Calculation type toSum, and set the Reset based on to Detail 2. Addthe total percent object, set the expression tornPercent, set the Calculation type to Sum, andset the Reset based on to Detail 2.

The above report definition tells the Visual FoxPro9.0 Report Writer to process the Vehicles tabletwice for each customer in the Customer table. Thefirst time totals the premium so it can be used in thesecond pass. The second pass of the Vehicles tableprints the data for the customer, using the reportvariable that was calculated during the first detailband. This process repeats for each customer in theCustomer table.

A Lot to Learn

The Visual FoxPro 9.0 Report Writer has many newfeatures to help you create better reports. The newreusable data environments allow you to share dataenvironments among reports. Various aspects ofreports, such as objects and bands, can beprotected. A better development experience hasbeen created through the new user interface. Thenew object layout enhancements and data groupenhancements offer more options than previouslyavailable. And finally, the new multiple-detail bandfeature opens up many more reporting options.Together, all these improvements and enhance-ments give you the ability to create more complexreports than ever before.

Cathy Pountney

The Visual FoxPro 9.0 Report Writer www.code-magazine.com34

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 34

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 35

Controls, Events, Commands, and More www.code-magazine.com

Controls, Events,Commands, and MoreMicrosoft Visual FoxPro 9.0 is here and it brings lots of newfeatures. This entire issue of CoDe is dedicated to Visual FoxPro 9.0, providingdetails and scenarios on how you can use the new features and enhancements. Inthis article, I will discuss some of the new controls, events, and commands, and alittle more.

I’m sure you are as anxious as I am to explore newproperties, events, and methods, new or improvedcommands, and FoxPro Foundation Classes

(FFCs). Let’s dive right in!

Anchoring Controls

Before Visual FoxPro 8.0, manylines of code had to be writtenin order to properly resizeonscreen controls whenever aform was resized. In VisualFoxPro 8.0, that task got slightlyeasier with the introduction ofthe BindEvent function. InVisual FoxPro 9.0, it gets wayeasier with the introduction ofanchors. Anchors allow acontrol to maintain its propor-tional relationship with otherobjects and controls within aform, no matter what size the form becomes.

The Anchor property is obeyed when the controlsits on containers such as Forms, CommandGroups,and Page objects. Whenever those containers getresized, the controls within it that have the Anchorproperty set are resized and/or repositioneddepending on the settings used. Make sure to checkout the Anchor Property topic on the Help file foravailable settings.

Docking Forms

Docking allows toolbars and other controls to be“attached” to any user-chosen edge of a Form andmaintain that positioning despite resizing orscrolling. Visual FoxPro 8.0 introduced the ability todock objects, such as the Command, DocumentView, and Properties windows, as well commandsand functions that handled docking programmati-cally.

Visual FoxPro 9.0 goes a step further and intro-duces the docking capability to user-defined formsas well. That enables creating dockable Forms on

the IDE or on the application delivered to theusers.

In order to support this feature, new members wereadded to the Form class, such as the Dockable and

Docked properties, the Dockand GetDockState methods,and the AfterDock, Before-Dock, and UnDock events.Listing 1 shows a simpleexample that produces theresult showed on Figure 1.

Collection ObjectSupport in ComboBoxand ListBox ControlsThe RowSourceType propertyon ComboBox and ListBoxobjects has a new option addedto the list of possible sources:

value 10, for the Collection object.

A Collection could be populated with Businessobjects or objects holding some sort of data, and thatCollection can be used to populate ComboBoxes andListBoxes. The following code snippet shows anexample of that, using a collection named colEm-ployees contained in the form, and the ComboBox’sRowSourceType property is set to 10.

Select EmployeeId, FirstName;from employees into cursor curTemp

Scan Local loEmployee as ObjectScatter NAME loEmployee BlankScatter NAME loEmployee additive

This.colEmployees.Add(loEmployee,; Transform(loEmployee.EmployeeId))

EndScan

This.cboEmployees.RowSource =;"Thisform.colEmployees, FirstName, EmployeeId"

Claudio [email protected]

Claudio Lassala is a SeniorDeveloper at EPS SoftwareCorp. and a part-time UniversalThread Consultant. He haspresented several lectures atMicrosoft events as well as atseveral conferences and usergroups in North America andBrazil. He is a Microsoft C#MVP, and MCSD for .NET, andalso a columnist at MSDNBrazil. He is the author ofseveral training videos that canbe found at Universal Thread,and has had several articlespublished in various maga-zines, such as MSDN BrazilMagazine and FoxPro Advisor.A full-biography can be foundat www.lassala.net/bio.

Fast FactsAs is typical of every new version of Visual FoxPro,

new commands, properties,methods, and events are introduced.

This article highlights some of these new features to make

the introductions, but you’ll want to look at the

”What’s New” section on the Helpfile, because there’s a lot

more in there.

36

QU

ICK

ID 0

4040

52

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 36

Rotating Label, Line, and ShapeControlsFor long time now, Visual FoxPro developers havebeen waiting for the ability to rotate labels. Theoption is now available through a new Rotationproperty, which receives a number indicating thedegrees the control will rotate. The Rotation prop-erty is also available for Line and Shape controls.

ListBoxControls Can Hide ScrollBars

A new AutoHideScrollbar property was added tothe ListBox class. This property determines whetheror not the ScrollBar is shown on the ListBox, andwhether it is shown if the control only has a fewentries.

If the default value 0 is used, the scrollbar is alwaysvisible. Value 1 determines that the scrollbar is onlyvisible when the ListBox is full. This makes a lot ofsense, as a scrollbar is of no use when all the itemson the ListBox fit in a single pane. Value 2 deter-mines that the scrollbar is never visible. If there are

more items on the ListBox than what can be viewedin a single pane, up and down arrows appear on thecontrol. Figure 2 shows a snapshot of a form withListBoxes using the three available settings.

NewObject() Creates Objects withoutRaising Initialization Code

The NewObject function now accepts 0 as the thirdparameter in order to create the object withoutraising code in the Init method. That’s useful whenthe interface of an object must be analyzed (usingthe AMember function, for example), where neitherthe code on the Init or Destroy methods should run.

Converting DataTypes with the Cast()Function

The new Cast() functionconverts expressions from onedata type to another. Get usedto writing SQL statements thatare compatible with SQLServer.

Controls, Events, Commands, and Morewww.code-magazine.com 37

The AppStates property indicateswhether the application has focus

(value 1), or not (value 0).

Figure 1: Docking user-defined windows is simple. Figure 2: The ListBox’s AutoHideScrollbar property provideslots of options.

Listing 1: Docking User-Defined Windows

Local loMainForm as FormLocal loCustomerForm1 as FormLocal loCustomerForm2 as FormLocal loCustomerForm3 as Form

loMainForm = CreateObject("MainForm")loCustomerForm1 = CreateObject("CustomerForm")loCustomerForm2 = CreateObject("CustomerForm")loCustomerForm3 = CreateObject("CustomerForm")

loCustomerForm1.Caption = "Customer 1" loCustomerForm2.Caption = "Customer 2" loCustomerForm3.Caption = "Customer 3"

loCustomerForm1.Dock(3, loMainForm)loCustomerForm2.Dock(2, loCustomerForm1)loCustomerForm3.Dock(2, loCustomerForm2)

loMainForm.Show()

Read Events

Define CLASS CustomerForm as Form

*-- The value of one indicates this form*-- "Supports dock and is dockable"Dockable = 1

EndDefine

Define CLASS MainForm as Form

Dockable = 1

Procedure Destroy()Clear Events

EndProc

EndDefine

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 37

When the following code snippet runs, you’ll noticethat the Customer table doesn’t open by the Initmethod:

loCustomer = NewObject("CustomerBizObj",; "NewObjectSample.prg", 0)

AMembers(laMembers, loCustomer, 1)Display Memory like laMembers

Define Class CustomerBizObj As SessionCustomerID = ""CustomerName = ""

Procedure InitUse Home(1)+"\samples\northwind\Customers"

EndProc

Procedure GetCustomersByPk(lcPK As String)EndProc

EndDefine

Set Path Command Enhancements

An Additive clause was added to the Set Pathcommand. When that clause is used, the path beingspecified is added to the path setting, if it isn’t therealready. Besides that, the Set Path command’smaximum character limit has been increased to4095 characters.

AppState Property Detects an Application Losing or Receiving Focus

The _Screen object has a new property calledAppState. This property is only available read-onlyat runtime, and it indicates whether the VisualFoxPro application has focus (value 1), or not(value 0). The value changes automatically when-ever the application loses or receives focus, such aswhen the user uses Alt+Tab. A Timer object, orevent handler (using the BindEvent function) canbe used to query this property and execute codewhenever its value changes.

Specify Where Focus is Assigned in the Valid Event

It’s always been a pain when something happensinside a Valid event and you want the focus to go toanother control, as calling SetFocus from within aValid event is not allowed. That’s been solved inVisual FoxPro 9.0 by allowing you to return the execu-tion to a specified object from within the Valid event:

Return Thisform.txtCity

ICASE() Function

The IIF() (immediate if) function has always been avery useful function, but the more complex the

expressions, the harder it is to read the code, asmultiple IIF calls are needed. The ICASE (imme-diate case) function solves that problem. Thefollowing code snippet shows an example writtenusing both the IIF and the ICASE functions:

Local lcAnswerlcAnswer = "YES"

? Iif(lcAnswer = "YES", "answered 'yes'",; Iif(lcAnswer = "NO", "answered 'no'",;

'unanswered'))

? ICASE(lcAnswer = "YES", "answered 'yes'",;lcAnswer = "NO", "answered 'no'",;"unanswered")

TTOC() Converts DateTime Expressions to XML DateTime FormatVisual FoxPro 9.0 joins the ranks of great softwareXML adopters. The TTOC function has beenimproved and receives a value 3 as the second para-meter, indicating that the resulting string must beformatted as an XML DataTime format.

? Ttoc(Datetime(),3)

The settings Set Century, Set Hours, or Set Secondsdon’t affect the result of the TTOC function whenthe parameter 3 is passed.

Log Execution Plan’s Output fromSYS(3054) Using SYS(3092)

The SYS(3054) function is very helpful for opti-mizing SQL queries, as it produces an executionplan of which indexes, if any, are in use, andenabling optimization of queries by creating newindexes or fine-tuning the way the queries arewritten. The new SYS(3092) function provides theability to write the output of SYS(3054) directly to afile on disk.

*-- Turn on execution plan.Sys(3054, 12)

*-- Set up output file.Sys(3092, "c:\ExecutionPlan.txt", .T.)

Aleksey TsingauzDeveloper, Visual FoxPro TeamMicrosoft

Visual FoxPro 9.0 significantlyextends SQL capabilities. Somehard-coded limits on theamount of tables, the amountof JOIN operators, the amountof sub-queries, and the amountof UNION operators used in asingle SELECT command areremoved. Also, now you canenjoy multiple sub-querynesting, sub-queries in the SETand SELECT list, and sub-queries in the FROM clause(derived tables). In addition,Visual FoxPro 9.0 supports theFROM clause for UPDATE andDELETE commands, whichallows you to easily modify atable based on data stored inother tables. The new SETSQLBUFFERING command andWITH (BUFFERING = ...) clauseallows you to query tablebuffered data along with thosealready committed. All these,and other SQL enhancements,allow you to efficiently performvery complex queries or modi-fications using a single SQLcommand.

Controls, Events, Commands, and More www.code-magazine.com38

The new Report Engine in VisualFoxPro can leverage the power of

GDI+, so if you’re planning on doingpowerful work with reports, take a

serious look at the GDI+ FFCclasses.

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 38

*-- Run Query.Select * ;from Home(1)+"\samples\northwind\Customers" ;into cursor curCustomers

*-- Turn off output and close file.Sys(3092, "")

*-- Let's see the log.Modify File "c:\ExecutionPlan.txt"

Extended SQL CapabilitiesThere are many improvements to the SQL subset inVisual FoxPro 9.0. With each new release, the VisualFoxPro team moves Visual FoxPro’s SQL closer toT-SQL (SQL Server’s SQL implementation).

Some of the most notable improvements are:

· The limit of nine join clauses and sub-queries wasremoved from the SQL Select statement, as werethe nine Union clauses limit, the 30 tables limit,and the referenced aliases limit.

· The level of nesting for sub-queries in a SQL Selectstatement, formerly limited to only one level, isnow unlimited.

· The list of fields and the FROM clause can nowhave a sub-query. This works for the ON clause onjoins as well.

· The Like clause is now optimized.

Make sure to read the Data Features Enhance-ments section under the What’s New in VisualFoxPro on the Help file for more specific informa-tion about all the improvements on these subjects.

Set Coverage Command Available at Runtime

The Set Coverage command is now available at runtime. Anywhere inside an application, the SetCoverage command can be called like this:

SET COVERAGE TO C:\MyApp.log

The Set Coverage command creates a log file for thecode being executed. The Coverage Profiler tool(available on the Visual FoxPro IDE under theTools menu) can then be used to analyze this logfile. The Coverage Profiler tool shows the code’scoverage, indicating which lines are being executed,and which ones never get executed; it also showsstatistics, such as how many times a line is executed,and how long that takes, helping you find codebottlenecks.

This new ability of enabling coverage duringruntime is also useful for debugging errors thatoccur during runtime, but that never happen ininteractive mode (through the Visual FoxProIDE).

Populate an Array with Aliases Used by a Specified TableThe AUsed function improved to allow passing athird parameter, one that indicates that the arraycreated by this function should only look for aliasesof a specific table. The following code snippet showsa simple example:

Select * ;from Home(1)+"\samples\northwind\Customers" ;into cursor curCustomers

Select * ;from Home(1)+"\samples\northwind\employees" ;into cursor curEmployees

? AUSED(gaInuse, Set("Datasession"), "Employees")

DISPLAY MEMORY LIKE gaInuse

In the sample above, an array shows the aliases andwork area numbers for the Employees table avail-able at the current data session.

SQLIdleDisconnect() TemporarilyDisconnects SQL Pass-Through Connections

The new SQLIdleDisconnect function is a greatnew addition to the SQL Pass-Through set of func-tions. This function temporarily disconnects aconnection handle passed to it (or it disconnects allconnections, if a 0 is passed).

That’s a great feature for disconnected scenarios,where an application doesn’t have to hold aconnection to the database even when the user isreading retrieved data, rather than sending databack and forth regularly. Plus, when the connectionmust be enabled again in order to process some-thing, it happens automatically, using the originalconnection string.

Retrieving Active SQL Connection Statement Handles

The new ASQLHandles function creates an arraywith the handles for all active SQL connections.Those handles can then be used with other func-tions such as SQLExec and SQLDisconnect.

lnConn1 = SQLStringConnect(;"driver=sql server;server=(local); "+; "database=northwind;")

lnConn2 = SQLStringConnect(;"driver=sql server;server=(local); "+; "database=pubs;")

Controls, Events, Commands, and Morewww.code-magazine.com 39

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 39

By using GDI+, you can manipulate graphics andimages, so this is a really powerful feature. Entirearticles could be devoted to this topic, exploring thedifferent classes, how to use them, and when to usethem, but it’s beyond the scope of this article. I’lljust mention these classes here so that you don’toverlook this new feature when you are all excitedby the other new features.

One simple example of image manipulation is asmall program that takes an existent bitmap file ondisk, and creates a new JPEG out of the originalone, as shown in the following code snippet imple-mentation:

*-- Create an Image object.Local oLogoImage As GpImage ;

Of Home(1)+"ffc/_gdiplus.vcx"oLogoImage = ;

Newobject('GpImage',Home(1)+'ffc/_gdiplus.vcx')

Controls, Events, Commands, and More www.code-magazine.com40

ASQLHandles(laHandles)

Display Memory like laHandles

Get Cursor and Count Records Affectedby SQL Pass-Through ExecutionThe SQLExec function has been improved in orderto make it possible to immediately get cursor andcount records affected by the SQL Pass-Throughcommand. A parameter is passed determining thename of the created array with a list of aliases and arecord count produced by the SQL commands.

This code snippet demonstrates this enhancement,producing multiple result-sets:

lnConn = SQLStringConnect("driver=sql server;"+; "server=(local);database=northwind;")

SQLExec(lnConn, ;"Select * from Customers; "+;"Select * from Employees", "", aCount)

Display Memory like aCount

View Classes Inside a Program File(.prg) with the Class BrowserThe Class Browser now has the ability to manageprogram-based (PRG) classes. In previous versions,only Visual Class Library-based (VCX) classes couldbe viewed. For developers who write several PRG-based classes (like me!), this is definitely a very-welcome feature.

GDI+ FFC classes

Visual FoxPro 9.0 has a new FFC (FoxPro Founda-tion Class) library for the Graphics Device Interface(GDI) called _GDIPlus.vcx. This library has manyclasses working as wrappers around the GDI+ API.

Figure 3: The image can bedrawn on a formusing GDI+.

Listing 2: An image can be drawn on a form using GDI+.

Public oForm As FormoForm = Createobject("MyFoxForm")oForm.Show()

Define Class MyFoxForm As Form

DoCreate = .T.Name = "Form1"

Procedure Paint*-- Create Graphics object.Local oGr As GpGraphics Of Home(1)+"ffc/_gdiplus.vcx"oGr = Newobject('GpGraphics',Home(1)+'ffc/_gdiplus.vcx')

*-- Get the Form's (Window's) handle.oGr.CreateFromHWND(Thisform.HWnd)

*-- Create an Image object.Local oLogoImage As GpImage Of Home(1)+"ffc/_gdiplus.vcx"oLogoImage = Newobject('GpImage',Home(1)+'ffc/_gdiplus.vcx')

*-- Load a bitmap file in memory.oLogoImage.CreateFromFile("c:\Fox.bmp")

*-- Draw Image object on Graphic's surface (here, the Form).oGr.DrawImageAt(oLogoImage,0,0)

EndProc EndDefine

The SQLExec function makes itpossible to get cursor and count

records affected by the SQL Pass-Through command

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 40

*-- Load an image file in memory.oLogoImage.CreateFromFile("c:\Fox.bmp")

*-- Save the image to a JPG file on disk.oLogoImage.SaveToFile("c:\Fox.jpg",;

oLogoImage.GetEncoderCLSID("image/jpeg"))

Another example is the ability to manipulate imagesand draw them on a device such as a form. Listing 2shows off an implementation of it, and Figure 3show the result. Notice that the image shown on theform was really drawn right into it, instead of havingbeen added using the Visual FoxPro Image object.

The important thing to notice is that before beingdrawn, the image can be manipulated, perhapsadding a logo or watermark to it, or text can bedrawn over it. It’s also worth mentioning that thenew Report engine in Visual FoxPro can leveragethe power of GDI+, so if you’re planning on doingpowerful stuff with reports, take a serious look atthe GDI+ FFC classes.

These classes (shown in Figure 4) map to theclasses available on the .NET Framework. I stronglysuggest that you read some articles about GDI+with .NET Framework published in previouseditions of CoDe Magazine, so that you can under-stand what sorts of things can be done with thistechnology, and then you should be able to translatemost of the .NET code into Visual FoxPro using theFFC classes.

Conclusion There are many improvements and new features inVisual FoxPro 9.0 that are both productivity toolsand exciting new ways of building interesting anduseful applications. I’ve only briefly covered some ofthe developments you’ll find when you start playingwith this new version. Make sure you take a closelook into the “What’s New in Visual FoxPro”section on the Help file, and have fun!

Claudio Lassala

Controls, Events, Commands, and Morewww.code-magazine.com 41

Figure 4: New FFC classes for GDI+ are similar to the onesfound in the .NET Framework.

CoDe Magazinewww.code-magazine.com 6, 35, 60

dFPUG c/o ISYS GmbH , Germany www.visualextend.com 25

EPS Software Corp.www.eps-software.com 19, 29, 42-43, 62-63

F1 Technologieswww.f1tech.com 67

Micromega Systemswww.micromegasystems.com 5

Microsoftmsdn.microsoft.com/visual/think 2-3

ProLib Softwarewww.AFPages.com 11

Southwest Fox Conferencewww.southwestfox.com 9

Stonefield Systems Groupwww.stonefieldquery.com 7

Take Note Technologieswww.takenote.com 32

Vision Data Solutionswww.visionds.com 68

West Wind Technologieswww.west-wind.com 15, 34

This listing is provided as a courtesy to our readers and advertisers.

The publisher assumes no responsibility for errors or omissions.

AD

VER

TIS

ING

IND

EXAdvertising Sales:Vice President,Sales and MarketingMichelle [email protected]

Sales ManagersErna Egger+43 (664) 151 0861 [email protected]

Tammy Ferguson832-717-4445 ext [email protected]

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 41

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 42

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 43

Extending the Visual FoxPro 9.0 Reporting System www.code-magazine.com

Extending the Visual FoxPro 9.0Reporting SystemAmong the new and improved features in Microsoft VisualFoxPro 9.0, you’ll find the ability to extend the behavior of thereporting system when running reports. In this article, you’ll learnabout Visual FoxPro 9.0’s report listener concept, how it receives events as areport runs, and how you can create your own listeners to provide different typesof output in addition to print and preview.

There are incredible improvements in the VisualFoxPro 9.0 reporting system. Of several aspects,I’ll discuss just one in this article: the ability to

extend the behavior of the runtime reporting engine.

The Visual FoxPro development team had severalgoals in mind when they worked on the runtimeimprovements, including:

· Handling more types of reportoutput than just printing andpreviewing

· Using GDI+ for report output,providing many significantimprovements, such as moreaccurate rendering, smoothscaling up and down ofimages and fonts, and addi-tional capabilities, such as textrotation.

· Providing a more flexible andextendible reporting system

You have access to both the old report engine andthe new one, so you can run reports under eitherengine as you see fit. But once you see the benefitsof the new report engine, you won’t want to go backto old-style reporting.

Reporting System Architecture

Before Visual FoxPro 9.0, the report engine wasmonolithic; it handled everything and with a fewexceptions (user-defined function, expressions forOnEntry and OnExit of bands, and so forth), youcouldn’t interact with it during a report run.

The new reporting engine splits responsibility forreporting between the report engine, which nowdeals only with data handling and object posi-tioning, and a new object known as a report

listener, which handles rendering and output.Because report listeners are classes, we can nowinteract with the reporting process in ways we couldonly dream of before.

Report listeners produce output in two ways. Page-at-a-time mode renders a page and outputs it, then

renders and outputs the nextpage, and so on. This mode isused when a report is printed.In all-pages-at-once mode, thereport listener renders all thepages and caches them inmemory. It then outputs theserendered pages on demand.This mode is used when areport is previewed.

New Reporting Syntax

Visual FoxPro 9.0 supports running reports usingthe old report engine; you use the REPORTcommand just as you did before (although, as you’llsee in a moment, you can use a new command tooverride the behavior of REPORT). To get new-stylereporting behavior, use the new OBJECT clause ofthe REPORT command. The OBJECT clausesupports two ways of using it: by specifying a reportlistener and by specifying a report type. Microsoftrefers to this as object-assisted reporting.

A report listener is an object that provides new-stylereporting behavior. Report listeners are based on anew base class in Visual FoxPro 9.0 called ReportLis-tener. To tell Visual FoxPro 9.0 to use a specificlistener for a report, instantiate the listener class andthen specify the object’s name in the OBJECT clauseof the REPORT command. Here’s an example:

loListener = createobject('MyReportListener')report form MyReport object loListener

Doug [email protected]

Doug Hennig is a partner withStonefield Software Inc. He isthe author of the award-winning Stonefield DatabaseToolkit (SDT), the award-winning Stonefield Query, andthe MemberData Editor, AnchorEditor, and CursorAdapter andDataEnvironment builders thatcome with Visual FoxPro.

Doug is co-author of the“What’s New in Visual FoxPro”series and “The Hacker’s Guideto Visual FoxPro 7.0.” He wasthe technical editor of “TheHacker’s Guide to VisualFoxPro 6.0” and “The Funda-mentals.” All of these booksare from HentzenwerkePublishing.

Doug has spoken at everyMicrosoft FoxPro DevelopersConference (DevCon) since1997 and at user groups anddeveloper conferences all overNorth America. He is aMicrosoft Most ValuableProfessional (MVP).

For more information aboutStonefield and its products,visit www.stonefield.com andwww.stonefieldquery.com

Fast FactsThe Visual FoxPro 9.0

reporting system raises events in anew base class, called

ReportListener. ReportListener isthe key to the different

types of provided report output, also giving us the ability

to change how a report is rendered.

44

QU

ICK

ID 0

4040

32

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 44

If you’d rather not instantiate a listener manually,you can have Visual FoxPro do it for you automati-cally by specifying a report type:

report form MyReport object type 1

The defined types are 0 for outputting to a printer, 1for previewing, 2 for page-at-a-time mode but not tosend the output to a printer, 3 for all-pages-at-oncemode but not invoke the preview window, 4 forXML output, and 5 for HTML output. Other, user-defined, types can also be used.

When you run a report this way, the applicationspecified in the new _REPORTOUTPUT systemvariable (ReportOutput.APP in the Visual FoxProhome directory by default) is called to determinewhich listener class to instantiate for the specifiedtype. It does this by looking for the listener type in alistener registry table built into the APP (althoughyou can also tell it to use an external table). If itfinds the desired class, it instantiates the class andgives a reference to the listener object to thereporting engine. Thus, using OBJECT TYPE Some-Type in the REPORT command is essentially thesame as:

loListener = .NULL.do (_ReportOutput) with SomeType, loListenerreport form MyReport object loListener

ReportListener

During the run of a report, Visual FoxPro exposesreporting events to objects based on the ReportLis-tener base class as they happen. The Visual FoxProHelp file has complete documentation on the proper-ties, events, and methods (PEMs) of ReportListener,so I’ll only discuss the most useful ones in this article.

Table 1 lists the most commonly used properties ofReportListener.

Table 2 lists the most commonly used events andmethods of ReportListener.

_ReportListener

The FFC (FoxPro Foundation Classes) subdirectoryof the Visual FoxPro home directory includes_ReportListener.VCX, which contains somesubclasses of ReportListener that have more func-tionality than the base class. The most useful ofthese is _ReportListener.

One of the most important features of _ReportLis-tener is support for successors. It’s possible you maywant to use more than one report listener whenrunning a report. For example, if you want to bothpreview a report and output it to HTML at the sametime, more than one report listener must beinvolved. _ReportListener allows chaining oflisteners by providing a Successor property that cancontain an object reference to another listener.

For example, suppose ListenerA and ListenerB areboth subclasses of _ReportListener that eachperform some task, and you want to use bothlisteners for a certain report. Here’s how theselisteners can be chained together:

loListener = createobject('ListenerA')loListener.Successor = createobject('ListenerB')report form MyReport object loListener

The report engine only communicates with thelistener specified in the REPORT or LABELcommand, called the lead listener. As the reportengine raises report events, the lead listener callsthe appropriate methods of its successor, thesuccessor calls the appropriate methods of itssuccessor, and so on down the chain. This type ofarchitecture is known as a chain of responsibility,as any listener in the chain can decide to take someaction or pass the message on to the next item inthe chain.

Another interesting capability of _ReportListeneris chaining reports. The AddReport method adds areport to the custom ReportFileNames collection.You pass this method the name of a report andoptional report clauses to use (such as theRANGE clause) and a reference to anotherlistener object. The RemoveReports methodremoves all reports from the collection. RunRe-ports runs the reports; pass it .T. for the first para-meter to remove reports from the collection after

SET REPORTBEHAVIORInstead of modifying theREPORT commands in yourapplication to use the OBJECTclause, you can use SETREPORTBEHAVIOR 90 to turnon object-assisted reporting bydefault. This means theREPORT command behaves asif you’ve specified OBJECTTYPE 0 when you use the TOPRINT clause or OBJECT TYPE1 when you use the PREVIEWclause. SET REPORTBEHAVIOR80 reverts to behavior fromVisual FoxPro 8.0 and earlier.

Extending the Visual FoxPro 9.0 Reporting Systemwww.code-magazine.com 45

Property DescriptionCurrentDataSession The data session ID for the report’s dataFRXDataSession The data session ID for the FRX cursorGDIPlusGraphics The handle for the GDI+ graphics object used for

renderingListenerType The type of report output the listener produces. The

default is -1, which specifies no output, so you’ll need tochange this to a more reasonable value. The values arethe same as those specified in the OBJECT TYPE clauseof the REPORT command.

OutputPageCount The number of pages renderedQuietMode .T. (the default is .F.) to suppress progress information

Table 1: Some useful properties of the ReportListener class.

Event/Method DescriptionLoadReport Fires before the FRX is loaded and the printer spool is

openedUnloadReport Fires after the report has been runBeforeReport Fires after the FRX has been loaded but before the

report has been runAfterReport Fires after the report has been runBeforeBand Fires before a band is processedAfterBand Fires after a band is processedEvaluateContents Fires before a field is renderedRender Fires as an object is being renderedOutputPage Outputs the specified rendered page to the specified

device

Table 2: Some useful events and methods of the ReportListener class.

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 45

they’re run and .T. for the second parameter toignore any listeners specified in AddReport.Here’s an example that runs two reports as if theywere a single one:

loListener = newobject('_ReportListener', ;home() + 'ffc\_ReportListener.vcx')

loListener.ListenerType = 1loListener.AddReport('MyReport1.frx', ;'nopageeject')

loListener.AddReport('MyReport2.frx')loListener.RunReports()

HTML and XML OutputBecause one of the design goals of the developmentteam was to provide additional types of reportoutput, Visual FoxPro 9.0 includes two subclasses of_ReportListener, called the HTMLListener, and theXMLListener, providing HTML and XML output,respectively. These listeners are built intoReportOutput.APP but are also available in_ReportListener.VCX.

Listener type 5 specifies HTML output and type 4 isfor XML output, so you could just use the followingcommand to output to HTML:

report form MyReport object type 5

However, this doesn’t give you any control over thename of the file to create or other settings. Instead,call ReportOutput.APP to give you a reference tothe desired listener, set some properties, and thentell the REPORT command to use that listener.

The following code creates an HTML file calledMyReport.HTML from the MyReport report. Whenyou specify type 5, ReportOutput.APP uses its built-in HTMLListener class to provide output.

loListener = .NULL.do (_reportoutput) with 5, loListenerloListener.TargetFileName = 'MyReport.html'loListener.QuietMode = .T.report form MyReport object loListener

The following code creates an XML file, calledMyReport.XML from the MyReport report,containing only the data. In this case, the XMLLis-tener class (type 4) is used.

loListener = .NULL.do (_reportoutput) with 4, loListenerloListener.TargetFileName = 'MyReport.xml'loListener.QuietMode = .T.loListener.XMLMode = 0&& 0 = data only, 1 = layout only, 2 = both

report form MyReport object loListener

HTML output actually uses the XML listener toproduce XML and then uses XSLT to produce theHTML end-result.

Both of these listener classes have additional prop-erties you can use to further control the output. Irecommend a look at the Visual FoxPro documen-tation for details. Also, as they are subclasses of_ReportListener, listener classes support the capa-bilities of the _ReportListener class, includingchaining listeners and running multiple reports.Here’s an example that outputs to both XML andHTML at the same time:

use _samples + 'Northwind\Orders'loListener1 = .NULL.do (_reportoutput) with 4, loListener1loListener1.TargetFileName = 'MyReport.xml'loListener1.QuietMode = .T.loListener1.XMLMode = 0&& 0 = data only, 1 = layout only, 2 = both

loListener2 = .NULL.do (_reportoutput) with 5, loListener2loListener2.TargetFileName = 'MyReport.html'loListener2.QuietMode = .T.loListener1.Successor = loListener2report form MyReport object loListener1

Creating Your Own ListenersBecause report listeners are a class, you can createsubclasses that alter the behavior of the reportingsystem when a report runs.

For example, one thing I’ve always wanted is a wayto dynamically format a field at runtime. Undersome conditions, I may want a field to print withred text, and under others, I want it in black.Perhaps a field should sometimes be bold and therest of the time not.

The key to changing the way a field appears in areport is the EvaluateContents method. Thismethod fires for each field object just before it’srendered, and gives the listener the opportunity tochange the appearance of the field. The first para-meter is the FRX record number for the field objectbeing processed and the second is an objectcontaining properties with information about thefield object. (See the Visual FoxPro Help file for alist of the properties this object contains.) You canchange any of these properties to change theappearance of the field in the report. If you do so,set the Reload property of the object to .T. to notifythe report engine that you’ve changed one or moreof the other properties.

Listing 1 shows some code (TestDynamicFormat-ting.PRG) defining a subclass of _ReportListenercalled EffectsListener, that handles different types ofeffects that may be applied to fields in a report.These effects are applied by effect handler objects,which are stored in a collection in the oEffectsHan-dlers property of EffectsListener. Each effecthandler object handles a single effect

As the report is processed, the listener needs to deter-mine which fields will have effects applied to them. It

Extending the Visual FoxPro 9.0 Reporting System www.code-magazine.com46

Richard StantonDeveloper, Visual FoxPro TeamMicrosoft

The enhanced report engineemphasizes backward compati-bility, one of Visual FoxPro'sstrongest aspects. Many of thenew reporting features aredesigned so that they are easyto integrate into existing appli-cations. For example, you canuse the new SET REPORTBE-HAVIOR command to quicklyrun and preview reports usingthe new reporting engine or theold engine. Visual FoxPro 9.0also includes features such asobject-assisted runtime reportprocessing using the newcustom ReportListener class,multiple-detail bands, reportchaining, and many otherdesign time enhancements.You can add all these featuresincrementally to existingreports, making it easy to takeadvantage of the functionalityyou want without major designchanges.

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 46

Handling Datasessions

There are three datasessionsinvolved during a report run.The first is the datasession inwhich the ReportListener isinstantiated. This is often thedefault datasession. Thesecond is the datasession inwhich Visual FoxPro creates acopy of the FRX file into acursor called FRX (this copycan be used by a listener). TheFRXDataSession propertycontains the datasession ID forthis cursor, so use SETDATASESSION TOThis.FRXDataSession if youneed access to the FRX. Thethird datasession is the one inwhich the report’s data resides.If the report has a privatedatasession, it is a uniquedatasession; otherwise, thereport resides in the defaultdatasession. The Current-DataSession property tells youwhich datasession to use.Remember to save theReportListener’s datasessionand switch back to it afterselecting either the FRX orreport data datasession.

Extending the Visual FoxPro 9.0 Reporting Systemwww.code-magazine.com 47

Listing 1: TestDynamicFormatting.PRG shows that the EvaluateContents method of ReportListener can change the way a field appears in a report.

use _samples + 'Northwind\Orders'loListener = createobject('EffectsListener')loListener.OutputType = 1report form TestDynamicFormatting.FRX preview object loListener

* Define a class that knows how to apply effects to objects in a* report.

define class EffectsListener as _ReportListener of ;home() + 'ffc\_ReportListener.vcx'

oEffectHandlers = .NULL.&& a collection of effect handlers

dimension aRecords[1]&& an array of information for each record in the FRX

* Create a collection of effect handler objects and fill it with* the handlers we know about. A subclass or instance could be* filled with additional ones.

function Initdodefault()with This.oEffectHandlers = createobject('Collection').oEffectHandlers.Add(createobject('DynamicForeColorEffect')).oEffectHandlers.Add(createobject('DynamicStyleEffect'))

endwithendfunc

* Dimension aRecords to as many records as there are in the FRX so* we don't have to redimension it as the report runs. The first* column indicates if we've processed that record in the FRX yet* and the second column contains a collection of effect handlers* used to process the record.

function BeforeReportdodefault()with This.SetFRXDataSession()dimension .aRecords[reccount(), 2].ResetDataSession()

endwithendfunc

* Apply any effects that were requested to the field about to be* rendered.

function EvaluateContents(tnFRXRecno, toObjProperties)local loEffectObject, ;loEffectHandler, ;lcExpression

with This

* If we haven't already checked if this field needs any effects, do* so and flag that we have checked it so we don't do it again.

if not .aRecords[tnFRXRecno, 1].aRecords[tnFRXRecno, 1] = .T..aRecords[tnFRXRecno, 2] = ;.SetupEffectsForObject(tnFRXRecno)

endif not .aRecords[tnFRXRecno, 1]

* Go through the collection of effect handlers for the field (the* collection may be empty if the field doesn't need any effects),* letting each one do its thing.

for each loEffectObject in .aRecords[tnFRXRecno, 2]loEffectHandler = loEffectObject.oEffectHandlerlcExpression = loEffectObject.cExpressionloEffectHandler.Execute(toObjProperties, lcExpression)

next loEffectendwith

* Do the normal behavior.

dodefault(tnFRXRecno, toObjProperties)endfunc

* Go through each effect handler to see if it'll handle the current* report object. If so, add it to a collection of handlers for the* object, and return that collection.

function SetupEffectsForObject(tnFRXRecno)local loFRX, ;loHandlers, ;loObject

with This.SetFRXDataSession()go tnFRXRecnoscatter memo name loFRX.ResetDataSession()loHandlers = createobject('Collection')for each loEffectHandler in .oEffectHandlersloObject = loEffectHandler.GetEffect(loFRX)if vartype(loObject) = 'O'loHandlers.Add(loObject)

endif vartype(loObject) = 'O'next loEffectHandler

endwithreturn loHandlers

endfuncenddefine

* Create a class that holds a reference to an effect handler and* the expression the effect handler is supposed to act on for a* particular record in the FRX.

define class EffectObject as CustomoEffectHandler = .NULL.cExpression = ''

enddefine

* Define an abstract class for effect handler objects.

define class EffectHandler as Custom

* Execute is called by the EvaluateContents method of* EffectsListener to perform an effect.

function Execute(toObjProperties, toFRX)endfunc

* GetEffects is called to return an object containing a reference* to the handler and the expression it's supposed to work on if the

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 47

does that in the EvaluateContents method by lookingat each field as it’s about to be rendered. Evaluate-Contents calls SetupEffectsForObject, which calls theGetEffect method of each effect handler to let itdecide whether to apply an effect to the field. GetEf-fect looks in the USER memo of the field’s record inthe FRX for a directive indicating what type of effectto apply. If a particular handler is needed for thefield, a reference to the handler is added to a collec-tion of handlers that processes the field (as a fieldmay have more than one effect applied to it).

However, EvaluateContents fires for every field inevery record, and there really isn’t a need to checkfor the effects more than once for a particularfield (doing so would slow down the performanceof the report). So, BeforeReport creates an arraywith as many rows as there are records in theFRX.

If the first column of the array is the default .F., thelistener hasn’t checked for the effects of the fieldbeing rendered yet, so EvaluateContents does that

Extending the Visual FoxPro 9.0 Reporting System www.code-magazine.com48

Listing 1: Continued

* specified report object needs this effect, or return null if not.

function GetEffect(toFRX)local loObjectloObject = .NULL.return loObject

endfunc

* EvaluateExprssion may be called by Execute to evaluate the* specified expression.

function EvaluateExpression(tcExpression)return evaluate(tcExpression)

endfuncenddefine

* Define an abstract class for effect handlers that look for* "*:EFFECTS <effectname> = <effectexpression>" in the USER memo.

define class UserEffectHandler as EffectHandlercEffectsDirective = '*:EFFECTS'&& the directive that indicates an effect is needed

cEffectName = ''&& the effect name to look for (filled in in a subclass)

function GetEffect(toFRX)local lcEffect, ;laLines[1], ;lnRow, ;lcLine, ;lnPos, ;loObject

lcEffect = This.cEffectsDirective + ' ' + This.cEffectNameif atc(lcEffect, toFRX.User) > 0alines(laLines, toFRX.User)lnRow = ascan(laLines, lcEffect, -1, -1, 1, 13)lcLine = laLines[lnRow]lnPos = at('=', lcLine)loObject = createobject('EffectObject')loObject.oEffectHandler = ThisloObject.cExpression = alltrim(substr(lcLine, lnPos + 1))

elseloObject = .NULL.

endif atc(lcEffect, toFRX.User) > 0return loObject

endfuncenddefine

* Define a class to provide dynamic forecolor effects.

define class DynamicForeColorEffect as UserEffectHandlercEffectName = 'FORECOLOR'

* Evaluate the expression. If the result is a numeric value and* doesn't match the existing color of the object, change the* object's color and set the Reload flag to .T.

function Execute(toObjProperties, tcExpression, toFRX)local lnColor, ;lnPenRed, ;lnPenGreen, ;lnPenBlue

lnColor = This.EvaluateExpression(tcExpression)if vartype(lnColor) = 'N'lnPenRed = bitand(lnColor, 0x0000FF)lnPenGreen = bitrshift(bitand(lnColor, 0x00FF00), 8)lnPenBlue = bitrshift(bitand(lnColor, 0xFF0000), 16)if toObjProperties.PenRed <> lnPenRed or ;toObjProperties.PenGreen <> lnPenGreen or ;toObjProperties.PenBlue <> lnPenBluewith toObjProperties.PenRed = lnPenRed.PenGreen = lnPenGreen.PenBlue = lnPenBlue.Reload = .T.

endwithendif toObjProperties.PenRed <> lnPenRed ...

endif vartype(lnColor) = 'N'endfunc

enddefine

* Define a class to provide dynamic style effects.

define class DynamicStyleEffect as UserEffectHandlercEffectName = 'STYLE'

* Evaluate the expression. If the result is a numeric value and* doesn't match the existing style of the object, change the* object's style and set the Reload flag to .T.

function Execute(toObjProperties, tcExpression, toFRX)local lnStylelnStyle = This.EvaluateExpression(tcExpression)if vartype(lnStyle) = 'N' and ;toObjProperties.FontStyle <> lnStyletoObjProperties.FontStyle = lnStyletoObjProperties.Reload = .T.

endif vartype(lnStyle) = 'N' ...endfunc

enddefine

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 48

and then sets the first column of the array to .T. sothe FRX record isn’t examined again.

After determining whether there are any effects tobe applied to the field, EvaluateContents then goesthrough the collection of effect handlers for thefield, calling the Execute method of each one tohave it do whatever is necessary.

DynamicForeColorEffect is one of the effecthandlers. It looks for a directive in the USER memoof a field in the report with the following format:

*:EFFECTS FORECOLOR = expression

(You can access the USER memo of an object in areport from the Other page of the properties dialogbox for that object.)

The ORDERDATE field of the TestDynamicFor-matting report used in Listing 1 has the directive inthe following snippet in its USER memo; it tellsEffectsListener that the DynamicForeColorEffectobject should adjust the color of the field so itdisplays in red if the date shipped is more than 10days after it was ordered or black if not:

*:EFFECTS FORECOLOR = iif(SHIPPEDDATE > ORDERDATE+ 10, rgb(255, 0, 0), rgb(0, 0, 0))

The Execute method of DynamicForeColorEffectchanges the color of the field by setting thePenRed, PenGreen, and PenBlue properties of thefield properties object that was passed to Evaluate-Contents to the appropriate colors and sets Reloadto .T., which tells the report engine that changeswere made.

DynamicStyleEffect uses a similar directive tochange the font style. The style to use must be anumeric value: 0 is normal, 1 is bold, 2 is italics, and3 is bold and italics. The SHIPVIA field in the Test-DynamicFormatting report has the following direc-tive in USER, which causes the field to display inbold if SHIPVIA is 3 (which, because of the expres-sion for the field, actually displays as Mail) ornormal if not:

*:EFFECTS STYLE = iif(SHIPVIA = 3, 1, 0)

DynamicStyleEffect works the same as Dynamic-ForeColorEffect, but changes the Style property ofthe field properties object.

Running TestDynamicFormatting.PRG results in theoutput shown in Figure 1.

Custom Rendering

You aren’t limited to changing the appearance of afield—you can do just about anything you like in areport listener. The Render method of ReportListener

Extending the Visual FoxPro 9.0 Reporting Systemwww.code-magazine.com 49

Figure 1: The code in Listing 1 produced this report, whichshows dynamic formatting for the Shipped Date and ShipVia columns.

Figure 2: This isthe TestColumn-Chart.FRX as itlooks at designtime.

Figure 3: The code in Listing 2 produced this report, whichcreates a column chart rather than traditional output.

The reporting engine in Visual FoxPro 9.0 splits

responsibility for reporting between the report engine, which now just

deals with data handling and object positioning,

and a new object known as a report listener, which handles

rendering and output.

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 49

Listing 2: TestColumnChart.PRG shows how you can do custom rendering in a report to produce a column chart.

* Define some constants we'll use.

#define FRX_OBJCOD_TITLE 0 && OBJCODE for title band#define FRX_OBJCOD_PAGEHEADER 1 && OBJCODE for page header band#define GDIPLUS_FontStyle_Regular 0 && GDI+ font style: regular#define GDIPLUS_Unit_Point 3 && GDI+ units: points

* Open the data for the report.

close databases allopen database _samples + 'Northwind\Northwind'use Category_Sales_For_1997

* Create the listener and run the report.

loListener = createobject('ColumnChartListener')loListener.OutputType = 1report form TestColumnChart object loListener

* The ColumnChartListener class.

define class ColumnChartListener as _ReportListener of ;home() + 'ffc\_ReportListener.vcx'

oGDIGraphics = .NULL.&& reference to GPGraphics _GDIPlus object

dimension aRecords[1]&& array of flags for each FRX record

dimension aValues[1]&& array of labels and values to chart

nCurrentRow = 0&& current row being processed in aValues

dimension aColumnColors[1]&& array of column colors

nSpacing = 100&& space between columns

nLegendSpacing = 300&& spacing between the chart and its legend

nLegendBoxSize = 200&& the size of a legend box

nLegendBoxSpacing = 100&& the spacing between items in the legend

nLegendTextSpacing = 50&& the spacing between boxes and text in the legend

cLegendFontName = 'Arial'&& font name for legend text

nLegendFontSize = 10&& font size for legend text

* Set the colors for the various columns.

function Initwith Thisdimension .aColumnColors[8].aColumnColors[1] = .CreateColor(rgb( 0, 0, 255))&& Blue

.aColumnColors[2] = .CreateColor(rgb( 0, 255, 0))&& Green

.aColumnColors[3] = .CreateColor(rgb(255, 0, 0))&& Red

.aColumnColors[4] = .CreateColor(rgb(255, 0, 255))&& Magenta

.aColumnColors[5] = .CreateColor(rgb(255, 255, 0))&& Yellow

.aColumnColors[6] = .CreateColor(rgb( 0, 255, 255))&& Cyan

.aColumnColors[7] = .CreateColor(rgb(255, 128, 0))&& Orange

.aColumnColors[8] = .CreateColor(rgb(128, 0, 255))&& Purple

endwithdodefault()

endfunc

* Do some setup tasks before the report starts.

function BeforeReportdodefault()with This

* Create a GPGraphics object so we can do GDI+ drawing.

.oGDIGraphics = newobject('GPGraphics', ;home() + 'ffc\_GDIPlus.vcx')

* Dimension aRecords to as many records as there are in the FRX so* we don't have to redimension it as the report runs. The first* column indicates if we've processed that record in the FRX yet,* the second column is .T. for a Column chart object, the third* column is .T. for a field containing values for chart labels, and* the fourth column is .T. for a field containing values for the* chart data.

.SetFRXDataSession()dimension .aRecords[reccount(), 4].ResetDataSession()

endwithendfunc

* Because the GDI+ plus handle changes on every page, we need to* set our SharedGDIPlusGraphics property appropriately and set the* handle for our GPGraphics object.

function BeforeBand(tnBandObjCode, tnFRXRecNo)with Thisif inlist(tnBandObjCode, FRX_OBJCOD_PAGEHEADER, ;FRX_OBJCOD_TITLE)if not .IsSuccessor

.SharedGDIPlusGraphics = .GDIPlusGraphicsendif not .IsSuccessor.oGDIGraphics.SetHandle(.SharedGDIPlusGraphics)

endif inlist(tnBandObjCode ...dodefault(tnBandObjCode, tnFRXRecNo)

endwithendfunc

* Return a SCATTER NAME object for the specified record in the FRX.

procedure GetReportObject(tnFRXRecno)local loObjectThis.SetFRXDataSession()go tnFRXRecnoscatter memo name loObjectThis.ResetDataSession()return loObject

Extending the Visual FoxPro 9.0 Reporting System www.code-magazine.com50

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 50

Listing 2: Continued

endproc

* Handle a shape to see if it's a column chart.

procedure AdjustObjectSize(tnFRXRecno, toObjProperties)local loObjectwith This

* If we haven't already checked if this object is a column chart,* find its record in the FRX and see if its USER memo contains* "COLUMNCHART". Then flag that we have checked it so we don't do* it again.

if not .aRecords[tnFRXRecno, 1]loObject = .GetReportObject(tnFRXRecno).aRecords[tnFRXRecno, 1] = .T..aRecords[tnFRXRecno, 2] = atc('COLUMNCHART', ;loObject.User) > 0

endif not .aRecords[tnFRXRecno, 1]

* If this is supposed to be a column chart, make its width the same* as its height.

if .aRecords[tnFRXRecno, 2]toObjProperties.Height = toObjProperties.WidthtoObjProperties.Reload = .T.

endif .aRecords[tnFRXRecno, 2]endwith

endproc

* Handle a field to see if it's involved in the column chart.

procedure EvaluateContents(tnFRXRecno, toObjProperties)local loObject, lcText, lnRowwith This

* If we haven't already checked if this object is involved in the* column chart, find its record in the FRX and see if its USER memo* contains "LABEL" or "VALUE". Then flag that we have checked it so* we don't do it again.

if not .aRecords[tnFRXRecno, 1]loObject = .GetReportObject(tnFRXRecno).aRecords[tnFRXRecno, 1] = .T..aRecords[tnFRXRecno, 3] = atc('LABEL', loObject.User) > 0.aRecords[tnFRXRecno, 4] = atc('DATA', loObject.User) > 0

endif not .aRecords[tnFRXRecno, 1]

* Get the value for the field, then decide what to do with it.

lcText = toObjProperties.Textdo case

* If this is a label, ensure it's in our array.

case .aRecords[tnFRXRecno, 3]lnRow = ascan(.aValues, lcText, -1, -1, 1)if lnRow = 0lnRow = iif(empty(.aValues[1]), 1, ;alen(.aValues, 1) + 1)

dimension .aValues[lnRow, 2].aValues[lnRow, 1] = lcText.aValues[lnRow, 2] = 0

endif lnRow = 0.nCurrentRow = lnRow

* If this is a data value, add it to the current total.

case .aRecords[tnFRXRecno, 4].aValues[.nCurrentRow, 2] = .aValues[.nCurrentRow, 2] + ;val(lcText)

endcaseendwith

endproc

* If we're supposed to draw a column chart, do so. Otherwise do the* normal rendering.

procedure Render(tnFRXRecno, tnLeft, tnTop, tnWidth, tnHeight, ;tnObjectContinuationType, tcContentsToBeRendered, ;tiGDIPlusImage)with Thisif .aRecords[tnFRXRecno, 2].DrawColumnChart(tnLeft, tnTop, tnWidth, tnHeight)nodefault

endif .aRecords[tnFRXRecno, 2]endwith

endproc

procedure DrawColumnChart(tnLeft, tnTop, tnWidth, tnHeight)local lnMax, lnColumns, lnI, lnColumnWidth, loColumnBrush, ;loPen, loFont, loStringFormat, loPoint, loTextBrush, ;lnColors, lnColor, lnLeft, lnHeight, lnTop

with This

* Figure out the highest value and the width of each column.

lnMax = 0lnColumns = alen(.aValues, 1)for lnI = 1 to lnColumnslnMax = max(lnMax, .aValues[lnI, 2])

next lnIlnColumnWidth = (tnWidth - (lnColumns * .nSpacing))/lnColumns

* Create _GDIPlus objects we'll need for drawing.

loColumnBrush = newobject('GPSolidBrush', ;home() + 'ffc\_GDIPlus.vcx')

loPen = newobject('GPPen', ;home() + 'ffc\_GDIPlus.vcx')

loFont = newobject('GPFont', ;home() + 'ffc\_GDIPlus.vcx')

loStringFormat = newobject('GPStringFormat', ;home() + 'ffc\_GDIPlus.vcx')

loPoint = newobject('GPPoint', ;home() + 'ffc\_GDIPlus.vcx')

loTextBrush = newobject('GPSolidBrush', ;home() + 'ffc\_GDIPlus.vcx')

loPen.Create(.CreateColor(0)) && BlackloFont.Create(.cLegendFontName, .nLegendFontSize, ;GDIPLUS_FontStyle_Regular, GDIPLUS_Unit_Point)

* Draw the border for the column chart.

.oGDIGraphics.DrawLine(loPen, tnLeft, tnTop, tnLeft, ;tnTop + tnHeight)

Extending the Visual FoxPro 9.0 Reporting Systemwww.code-magazine.com 51

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 51

is responsible for drawing each object on the reportpage. You can override this method to performvarious types of output.

A listener that performs custom rendering willalmost certainly have to use GDI+ functions.GDI+ is a set of hundreds of Windows API func-tions that perform graphical manipulations andoutput.

To make it easier to work with GDI+ functions,Visual FoxPro includes _GDIPlus.VCX in the FFCdirectory. _GDIPlus, which was written by WalterNicholls of Cornerstone Software in New Zealand,consists of wrapper classes for GDI+ functions,making them both easier to use and object-oriented.The “GDI Plus API Wrapper Foundation Classes”topic in the Visual FoxPro Help file lists theseclasses and provides a little background about them.This library is a great help in doing GDI+ renderingbecause you don’t have to know much about GDI+to use them; I certainly don’t and yet was able tocreate the listener class discussed next in just acouple of hours.

The code shown in Listing 2, taken from TestColumnChart.PRG, runs the TestColumn-Chart.FRX report, shown in Figure 2, and createsthe output shown in Figure 3. Notice howdifferent the output looks than the report layoutsuggests; the fields and shape don’t appear but acolumn chart graphing the contents of the Cate-gory_Sales_For_1997 view in the sample North-wind database does. That’s partly due to PrintWhen clauses on the fields that prevent themfrom printing, but the biggest reason is that thelistener class used for this report, Column-ChartListener, replaces the shape object in theSummary band with a column chart. Let’s seehow this listener does that.

The Init method of ColumnChartListener initial-izes the aColumnColors array to the color to usefor each column in the chart. Note that GDI+colors are a little different than the values returnedby RGB(), so the CreateColor method is used todo the necessary conversion. If you want to use adifferent set of colors, you can subclass Column-ChartListener or store a different set of colors inthe array after you instantiate ColumnChartLis-tener. Note that only eight colors are defined; ifthere are more than eight columns in the chart, thelistener uses the same colors for more than onebar.

The BeforeReport method instantiates aGPGraphics object into the custom oGDIGraphicsproperty. GPGraphics is one of the classes in_GDIPlus.VCX. It and other _GDIPlus classes areused in the DrawColumnChart method to draw thecomponents of the column chart.

Extending the Visual FoxPro 9.0 Reporting System www.code-magazine.com52

Listing 2: Continued

.oGDIGraphics.DrawLine(loPen, tnLeft, tnTop + tnHeight, ;tnLeft + tnWidth, tnTop + tnHeight)

* Draw the column.

lnColors = alen(.aColumnColors)for lnI = 1 to lnColumnslnColor = (lnI - 1) % lnColors + 1loColumnBrush.Create(.aColumnColors[lnColor])lnLeft = tnLeft + lnI * .nSpacing + ;(lnI - 1) * lnColumnWidth

lnHeight = tnHeight/lnMax * .aValues[lnI, 2]lnTop = tnTop + tnHeight - lnHeight.oGDIGraphics.DrawRectangle(loPen, lnLeft, lnTop, ;lnColumnWidth, lnHeight)

.oGDIGraphics.FillRectangle(loColumnBrush, lnLeft, lnTop, ;lnColumnWidth, lnHeight)

* Draw the legend for the column.

lnLeft = tnLeft + tnWidth + .nLegendSpacinglnTop = tnTop + (lnI - 1) * (.nLegendBoxSize + ;.nLegendBoxSpacing)

.oGDIGraphics.DrawRectangle(loPen, lnLeft, lnTop, ;.nLegendBoxSize, .nLegendBoxSize)

.oGDIGraphics.FillRectangle(loColumnBrush, lnLeft, lnTop, ;.nLegendBoxSize, .nLegendBoxSize)

lnLeft = lnLeft + .nLegendBoxSize + .nLegendTextSpacingloPoint.Create(lnLeft, lnTop)loTextBrush.Create(.CreateColor(0)) && Black.oGDIGraphics.DrawStringA(.aValues[lnI, 1], loFont, ;loPoint, loStringFormat, loTextBrush)

next lnIendwith

endproc

* GDI+ colors are represented by a number as 0xAARRGGBB, where AA* is the alpha, RR is the red, GG is the green, and BB is the blue.* Unfortunately, the VFP RGB() function gives us 0xBBGGRR, so we* need to add the alpha component and reverse the red and blue* component positions.

procedure CreateColor(tnRGB, tnAlpha)local lnAlphalnAlpha = iif(pcount() = 1, 255, tnAlpha)return bitlshift(lnAlpha, 24) + bitand(tnRGB, 0x00FF00) + ;bitlshift(tnRGB, 16) + bitrshift(tnRGB, 16)

endprocenddefine

_ReportListener allows chaining listeners by providing a

Successor property that may contain an object

reference to another listener.

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 52

GPGraphics needs a handle to the GDI+ surfacebeing rendered to. Fortunately, the listener alreadyhas such a handle in its GDIPlusGraphics property.The only complication is that the handle changeson every page, so the BeforeBand method, whichfires just before a band is processed, calls theSetHandle method of the GPGraphics object to giveit the handle when the title or page header bandsare processed.

As the report is processed, the listener needs todetermine where the labels and values for the chartcome from. It does that in the EvaluateContentsmethod by looking at each field as it’s about to berendered. If the USER memo for the field’s recordin the FRX contains LABEL (as it does for the Cate-goryName field), that indicates that this field shouldbe used for the labels for the column chart. DATAin the USER memo (as is the case for the Catego-rySales field) indicates that the field is used as thevalues for the chart. As with the EffectListener classI discussed earlier, there isn’t a need to check theUSER memo more than once, so the same type ofmechanism—storing a flag in an array property toindicate whether the field was processed—is used inthis class.

If the listener hasn’t checked the USER memo forthe field being rendered yet, EvaluateContents doesthat, setting flags in the array indicating whether thefield is used for labels or values, and setting the firstcolumn of the array to .T. so the FRX record isn’texamined again. If the field is used for either labelsor fields, EvaluateContents updates the aValuesarray accordingly.

AdjustObjectSize is similar to EvaluateContentsexcept it fires for shapes rather than fields. Adjus-tObjectSize checks the USER memo of the FRXrecord of the current shape for the presence ofCOLUMNCHART, indicating that this shape is tobe replaced by a column chart. As with Evaluate-Contents, the listener only needs to check once, soit uses similar logic.

The Render method is responsible for drawing anobject on the report. If the object about to be drawnis a shape to be replaced with a column chart, itcalls the custom DrawColumnChart methodfollowed by NODEFAULT to prevent the shapefrom being drawn. Otherwise, the object is drawnnormally. (Notice that there’s no DEDEFAULT();the native behavior is to draw the object, so it isn’trequired).

DrawColumnChart figures out the maximum of thevalues being charted so it knows how big to draw

the columns, and then it creates some objects from_GDIPlus classes needed to perform the drawing. Itcalls the DrawLine method to draw the vertical andhorizontal borders for the chart, and then goesthrough the aValues array, drawing a column foreach value using DrawRectangle and filling it withthe appropriate color via FillRectangle. Draw-ColumnChart adds a box and label to the legend forthe chart using the same DrawRectangle and Fill-Rectangle methods for the box and DrawStringA forthe label.

Some drawing attributes come from values incustom properties, making charting more flexible.For example, cLegendFontName and nLegendFont-Size specify the font and size to use for the legendlabel, and nLegendBoxSize specifies the size of boxto draw. See the comments for these properties nearthe start of the Listing 2.

SummaryMicrosoft has blown the lid off the Visual FoxProreporting system! By passing report events toReportListener objects, they’ve allowed us to reactto these events to do just about anything we wish,from providing different types of output to dynami-cally changing the way objects are rendered. I canhardly wait to see the type of things the VisualFoxPro community does with these new features.

Doug Hennig

Extending the Visual FoxPro 9.0 Reporting Systemwww.code-magazine.com 53

The key to changing the way a field appears in

a report is the EvaluateContents method.

A listener that performs custom rendering will almost

certainly have to use GDI+ functions to do so.

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 53

Visual FoxPro 9.0 IDE Enhancements www.code-magazine.com

Visual FoxPro 9.0 IDEEnhancementsEvery version of FoxPro has always included a number ofenhancements to the IDE, and Visual FoxPro 9.0 is no exception.Visual FoxPro 9.0 includes a number of IDE enhancements that make its alreadyrich development environment considerably richer.

Fonts and Colors Everywhere

One major enhancement to the UI is the ability toconfigure fonts that are used in many designers anddialog boxes. Visual FoxPro 9.0 allows you toconfigure the font used todisplay items in the ProjectManager. To change the fontfor your projects, right-click onyour open project and selectFont… from the shortcut menu.This activates the WindowsFont dialog box, from whichyou can specify the font, style,and size you wish to use.Figure 1 shows the Visual FoxPro Project Managerwith the Comic Sans font, 14pt, and Bold selected.

Microsoft didn’t stop with just the Project Manager,of course. You can also specify the display font forthe Properties sheet. In previous versions of VisualFoxPro, you were limited to changing the font fromsmall to medium or large. You now have full

control over the font in theProperties sheet. You can right-click the Properties sheet andselect Font… from the shortcutmenu to activate the WindowsFont dialog box just like youdid with the Project Manager.

Another enhancement to theProperties sheet is the ability to

specify the colors for different categories of properties.You can specify colors for ActiveX properties, non-default values, custom properties, and instance prop-erties. To specify the colors for custom properties:

1. Right click on the Properties sheet.2. Select Custom Properties Color… from the

shortcut menu.3. Select the color from the Windows Color dialog box.

Figure 2 shows the Properties sheet with a customproperty displayed in red.

Class Upgrades

Visual FoxPro 9.0 added one much-desired featureto its Class designer. You can now specify defaultvalues for custom properties added to your classes.To specify a default values for your custom property:

1. Open your custom class from the ProjectManager or type MODIFY CLASS in theCommand window.

Rod [email protected]

Rod Paddock is the editor ofCoDe Magazine. He has been asoftware developer for morethan 12 years and has workedwith numerous tools includingVisual Studio .NET, SQLServer, Visual Basic, VisualFoxPro, Delphi, and manyothers.

Rod is president of Dash PointSoftware, Inc. Dash Point is anaward-winning software devel-opment firm that specializes indeveloping applications forsmall to large businesses. DashPoint has delivered applicationsfor several corporationsincluding: Six Flags, FirstPremier Bank, Intel, Microsoft,and the US Coast Guard.

Rod is also VP of Developmentfor SQL Server tools maker,Red Matrix Technologies.(www.redmatrix.com)

54

QU

ICK

ID 0

4040

62

Figure 2: The Properties sheet with the Custom Propertycolor set to red.

Figure 3: The newProperty dialog boxwith the default valueset to ADD,EDIT.

Figure 1: The Project Manager with the font set to Comic Sans.

Fast FactsVisual FoxPro 9.0

has a number of IDE Changes that continue

Visual FoxPro’s trend of makingdevelopers more productive.

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 54

2. Select Class then select NewProperty from the Visual FoxPro menu.

3. Specify the name of your property.4. In the Default/Initial Value field, enter the new

default value (as shown in Figure 3).5. Click Add.

You should now see the new default value for yourproperty in the Properties sheet (see Figure 4).

Data Exploration

As you know, Visual FoxPro works very well withdata. Visual FoxPro 9.0 is especially well-suited toworking in client/server development environments.Visual FoxPro 9.0 includes tools to help facilitatedevelopment in a client/server world with a new

feature called the Data Explorer. The Data Explorer isa new tool that is included in the Visual FoxPro TaskPane Manager. To activate the Data Explorer, openthe Task Pane Manager from the Visual FoxPro Toolsmenu and select Data Explorer from the Task PaneManager’s More Panes dropdown list (Figure 5). Thisactivates the Data Explorer in the Task Pane Managerwindow.

You can now add connections to external databasesor use Explorer for databases on your localcomputer. Figure 6 shows the Data Explorerperusing the Master database. From the DataExplorer, you can query tables by right-clicking on atable and selecting Run Query from the shortcutmenu. Selecting Run Query opens the Run Querydialog box (Figure 7), which allows you to executeSQL code in your SQL Server database.

The Data Explorer tool is something that has beensorely missing from Visual FoxPro for some time.Thanks to Microsoft for adding this very necessaryfeature.

In The Background

Visual FoxPro 9.0 also includes a major enhance-ment to the Program Editor. Visual FoxPro 9.0 hasbackground compilation of code. When FoxProcomes across a syntax error, it underlines it. Thisgives you the ability to fix code immediately insteadof waiting for a compile to run.

Visual FoxPro 9.0 IDE = More Produc-tivity

Even though this list may be small, the IDE changesin Visual FoxPro 9.0 pack a real punch. Back-ground compilation preventsbugs at the source, font changesmake it easier to see what youare looking for, and the DataExplorer makes dealing withclient/server data that muchsimpler.

Rod Paddock

Mike StewartTest Engineer, Visual FoxPro TeamMicrosoft

The enhanced BINDEVENTS()functionality brings new waysfor Visual FoxPro developers tointeract with the Windowsoperating system. Prior toVisual FoxPro 9.0, when eventssuch as a system-level color orfont changes occurred, it wasdifficult or impossible for aVisual FoxPro developer toexecute code in response. InVisual FoxPro 9.0, developerscan execute their own codewhen users insert a USB drive,connect to a network drive,power down their computer, ornearly any other event notifica-tion. In addition, Visual FoxProdevelopers can now hook intoVisual FoxPro's IDE windowsmore easily than before. Thisbrings with it possibilities forcustom context menus and IDEwindow hooks with just a smallamount of native Visual FoxProcode.

Visual FoxPro 9.0 IDE Enhancementswww.code-magazine.com 55

Visual FoxPro 9.0 facilitates development in a

client/server world with a new feature called

the Data Explorer.

You can specify colors for ActiveX properties,

non-default values, custom properties,

and instance properties.

Figure 4: This Properties sheet shows the default value forthe ceditmode property.

Figure 5:Here’s theMore Panesdropdownlist from theTask PaneManager dia-log box.

Figure 6:Data Explorershows thecontents ofthe Masterdatabase.

Figure 7: The Run Query dialog box shows the results of aquery.

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 55

COM Interop: Making .NET and VFP Talk to Each Other www.code-magazine.com

Interop: Making .NET andVFP Talk to Each OtherMany companies have relied on COM components in the lastcouple of years. That includes Microsoft. Using COM components made itpossible for different programming languages to reuse logic between them, byagreeing to a standard defined by the COM specification.

Many developers wrote VFP applicationsusing COM components, usually for dataaccess logic and business logic. As a VFP

developer you’ll be relieved to know that you canreuse those components in .NET, allowing you toeasily create a .NET UserInterface (a Web application,for instance) that uses thoseVFP components, instead ofthrowing them away andrewriting everything fromscratch. On the other hand,the .NET Framework comeswith many classes that VFPdeveloper might want to usein their applications, and thatis also possible.

Whether you use a COMcomponent from .NET, or a.NET component from a COM-enabled environment (such asVFP), the mechanism that allows for that is calledCOM Interop.

Why COM Interop?

COM-enabled languages can use COM compo-nents created in any language because thosecomponents conform to the standards defined byCOM. Most languages have different types, or treatcommon types in a different way, and therefore, inorder to make components created in differentlanguages talk to each other, they have to becompatible, and it is COM that determines thecommon rules.

.NET goes a step further trying to address issueswith the COM standards (such as DLL hell), and ituses different approaches that lead to a verydifferent set of standards. COM components and.NET components are not compatible by default.However, keeping in mind that many companieshave put a lot of work into COM components,Microsoft added a mechanism to .NET so that .NETcomponents can see COM components as if theywere .NET components, and COM components cansee .NET components as if they were COM compo-nents.

Calling VFP Components from .NETIn order for .NET to see COM components, youmust create a proxy (or wrapper). This proxy,

called Runtime CalledWrapper (or just RCW), is anobject that sits between COMand .NET, and translates callsfrom .NET to COM. To the.NET client, the proxy lookslike any other .NET compo-nent, and the proxy takes careof interpreting the calls toCOM. Creating the RCW isnot a daunting task, as you willsee in a minute.

You first create a COMcomponent in VFP. Thefollowing code creates a sortof business object class that

.NET will use. (We say sort of business objectbecause the data access code is there too, butseparating layers is not the point we’re trying tomake here):

Define Class CustomerBizObj As Session OlePublicDataSession = 2 && Private Session

Procedure InitUse Home(2) + "\Northwind\Customers.dbf"

EndProc

Procedure GetCustomerList() As String

Local lcOut As StringlcOut = ""Cursortoxml("Customers","lcOut",1,0,0,"1")Return lcOut

EndProc EndDefine

The GetCustomerList method retrieves a list ofcustomers, returning the results in an XML string.Note that you must declare the return type, other-wise VFP will define the return type to be of type

Claudio [email protected]

Cladio Lassala is a SeniorDeveloper at EPS SoftwareCorp. and a part time UniversalThread Consultant. He haspresented several lectures atMicrosoft events as well asseveral conferences and usergroups in North America andBrazil. He is a Microsoft C#MVP, MCSD for .NET, and alsoa columnist at MSDN Brazil. Heis the author of several trainingvideos that can be found at theUniversal Thread, and he hasseveral articles published invarious magazines includingMSDN Brazil Magazine andFoxPro Advisor. A full-bio canbe found atwww.lassala.net/bio.

Fast Facts.NET is growing

more and more important, making interoperability between

.NET and VFP a hot topic. There are a few

different approaches to combining the two tools,

such as COM Interop, Web services,

and interoperability on the database level.

56

QU

ICK

ID 0

4040

72

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 56

COM Interop: Making .NET and VFP Talk to Each Otherwww.code-magazine.com 57

Markus [email protected]

Markus is an internationalspeaker who presents sessionsat numerous conferencesincluding a number of confer-ences in North America andEurope. Markus has writtennumerous articles for publica-tions including CoDe Magazine,Visual Studio Magazine,asp.netPro Magazine, FoxProAdvisor, Fuchs, FoxTalk andMicrosoft Office & DatabaseJournal. Markus is thepublisher of CoDe Magazine.

Markus is also the Presidentand Chief Software Architect ofEPS Software Corp., a customsoftware development andconsulting firm located inHouston, Texas. He specializesin consulting for object-oriented development, Internetdevelopment, B2B and Webservices. EPS does most of itsdevelopment using MicrosoftVisual Studio (.NET). EPS hasworked on numerous softwareprojects for Fortune 500companies including PhilipMorris, Qualcomm, Shell, andMicrosoft. Markus has alsoworked as a contractor for theMicrosoft Visual Studio teamwhere he is mostly responsiblefor object modeling and otherobject and component relatedtechnologies.

Markus also received theMicrosoft MVP Award (96, 97,98, 99, 2000, 2001, 2002,2003, and 2004) for his contri-butions to the developercommunity. Visual LandPro 98,one of the applications Markuswas in charge of, was nomi-nated for the Microsoft Excel-lence Awards three times.

variant in the type library (a file that defines themethods and other things that are in a COMcomponent). A variant is really bad because .NETdoesn’t support a variant type. On the .NET side,the developer must know in advance what datatype is actually getting returned in order to be ableto use it.

You declare the class using the OlePublic keyword,marking it to be exposed as a COM component. Forthis demo we created a project called COMInterop-Sample and added the CustomerBizObj.prg to theproject. We need to build the project as a Multi-threaded COM server (.dll).

You can use the following code to test the COMcomponent in VFP:

*-- Instantiate the object.oCustomerBizObj=;

CreateObject("COMInteropSample.CustomerBizObj")

*-- Call the method a save XML returned to file.StrToFile(oCustomerBizObj.GetCustomerList(),;

"c:\CustomerList.xml")

*-- Release the object.Release oCustomerBizObj

*-- Show XML.Modify File c:\CustomerList.xml

Next you can create any sort of .NET application.For this example we’ve created an ASP.NET Webapplication, and we chose to use C#, but thelanguage really doesn’t matter. After we created theproject, we added a reference to the COM compo-nent in the .NET project. You can do this by goingto the Add Reference option on the Project menu,or by right-clicking the References node on theproject through the Solution Explorer window(Figure 1 shows that). From the dialog box, clickthe Browse button, and navigate to theCOMInteropSample.dll that was created when theVFP project was compiled.

Next we created a CustomerList.aspx Web Form,and added a DataGrid control (named dgCus-tomers) to it.

The CustomerBizObj class created in VFP will becontained within a namespace called cominterop-sample, so we added the line using cominterop-sample; at the top of the code-behind the Webform. Inside that namespace you’ll find the classnamed CustomerBizObjClass. This Web formdisplays the list of Customers returned by theGetCustomerList method on the business object.The following code snippet shows the Page_Loadmethod on the Web form, which runs every timethe page loads:

private void Page_Load(object sender,System.EventArgs e) {

CustomerBizObjClass customer = new CustomerBizObjClass();

DataSet dsCustomers = new DataSet();

dsCustomers.ReadXml(new StringReader(customer.GetCustomerList()));

this.dgCustomerList.DataSource = dsCustomers;this.dgCustomerList.DataBind();

}

As you can see, the code just instantiates theCustomerBizObjClass as well as a DataSet. TheDataSet is then filled with data based on the XMLreturned from GetCustomerList. The DataSet’sReadXml() method takes care of the transformationfrom XML to ADO.NET data. Finally, the DataSetis bound to the DataGrid. Other than the specificsof using DataSets and StringReaders, using the VFPcomponent is just a matter of instantiating objectsand calling methods, as the VFP developer is veryused to doing in VFP. Figure 2 shows the results ofrunning that page.

Remember what seemed to be a daunting task ofcreating the RCW (that proxy that intermediates.NET calls to COM components)? That’s been

Figure 1: Adding a referenceto a .NET project.

Figure 2: A .NET Web application that uses VFP compo-nents and data.

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 57

Rod [email protected]

Rod Paddock is the editor ofCoDe Magazine. He has been asoftware developer for morethan 12 years and has workedwith numerous tools includingVisual Studio .NET, SQLServer, Visual Basic, VisualFoxPro, Delphi, and manyothers.

Rod is president of Dash PointSoftware, Inc. Dash Point is anaward-winning software devel-opment firm that specializes indeveloping applications forsmall to large businesses. DashPoint has delivered applicationsfor several corporationsincluding: Six Flags, FirstPremier Bank, Intel, Microsoft,and the US Coast Guard.

Rod is also VP of Developmentfor SQL Server tools maker,Red Matrix Technologies.(www.redmatrix.com)

COM Interop: Making .NET and VFP Talk to Each Other www.code-magazine.com58

created automatically by the Visual Studio .NETIDE as soon as a reference to the COM componentwas added to the .NET project. If you select thecominteropsample reference on the SolutionExplorer window and look at its Path property, youshould see something like the following:

C:\YourProject\obj\Interop.cominteropsample.dll

YourProject should be whatever path you have tothe .NET project you’ve created. The importantdetail to notice here is that the path doesn’t pointdirectly to the cominteropsample.dll (created byVisual FoxPro). Instead, it points to anInterop.cominteropsample.dll. This DLL is theRCW created by .NET. This proxy will help .NETto communicate with the COM component. It hasa class with the same name as the one exposed bythe COM component, but with the class wordadded to it (thus, the CustomerBizObjClass that’sinstantiated in the .NET sample). In other words,whenever your application instantiates that class in.NET, the proxy will know how to instantiate theCOM component, and whenever a method is calledin that class, the proxy will know how to translatethe .NET call into a COM call.

The Type Library Importer Tool

When a reference to a COM component is addedto a .NET project by using the Visual Studio .NETIDE, Visual Studio uses a tool called the TypeLibrary Importer, accepting default values for it.Some of those defaults determine that the proxywill be named after the COM DLL, but precededby the word “interop” (such as inInterop.cominteropsample.dll), and the proxyclass will be placed inside a namespace alsonamed after the .dll (such as cominteropsample).

Many developers want to have more control overthe process of creating the RCW. This means theywant to have more control over the namespacewhere the proxy is going to be placed, and wherethe proxy DLL is going to be created. Developerscan use the Type Library Importer tool(Tlbimp.exe) for that. This command-line toolthat comes with the .NET SDK is in the folder

"C:\Program Files\Microsoft Visual Studio .NET2003\SDK\v1.1\Bin\. You can run the tool at a DOS prompt like this: (We broke the lines for better readability, but this is typed in oneline.)

tlbimp.exe "C:\YourVFPProject\BizObjects.dll"/out:"C:\YourDotNetProject\bin\Proxy.BizObjects.dll"/namespace:BizObjects

Notice that you specify the COM DLL, and thenuse the switch out in order to specify where youwant to locate the proxy and what you want toname it. Use the namespace switch to specify thename of the namespace the proxy class will becontained on.

At this point, you can remove the reference createdpreviously in the .NET project for the COM compo-nent. You can add a new reference pointing to theProxy.BizObjects.dll you just created. (The RCW isalready a .NET class so Visual Studio .NET won’ttry to create another proxy). You can rewrite theusing statement at the top of the Web Form asusing BizObjects.

Calling .NET Components from Visual FoxPro

There are many .NET classes that can be useful forthe Visual FoxPro developer, and you can use thoseclasses through COM interop. For instance, youmight want to use the classes that provide GDI+features. Listing 1 shows a class created in .NETthat wraps up some GDI+ functionality. Wecompiled the class into a class library project in.NET. The most important thing to note here is thatthe project has been marked as “Register for COMInterop.” To do that, right-click on the project,select Properties, select Configuration Properties –Build, and then set the Register for COM Interopoption to True to expose a .NET class as a COMcomponent (Figure 3).

After you compile the project you can immedi-ately use the class through COM from VFP.However, in order to provide a better experiencefor the user of such class, you might want to applysome attributes to the class. For instance, the classshowed in Listing 1 has the following attributesapplied:

[ClassInterface(ClassInterfaceType.AutoDual)][ProgId("VFPAndDotNet.ImageHelper")]

The ClassInterface attribute, set to ClassInter-faceType.AutoDual, enables the IntelliSensesupport in VFP. The ProgId attribute specifies theProgId that VFP uses when instantiating the .NETcomponent as a COM component. For example,you can use the ImageHelper class in VFP like so:

Figure 3: A .NET project set up for COM Interop.

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 58

COM Interop: Making .NET and VFP Talk to Each Otherwww.code-magazine.com 59

Listing 1: An ImageHelper class that wraps up GDI+ funcionalities

using System;using System.Drawing;using System.Drawing.Imaging;using System.Drawing.Drawing2D;using System.Drawing.Text;using System.Runtime.InteropServices;

namespace MiscClasses{/// <summary>/// The ImageHelper class wraps up some GDI+ functionalities./// </summary>[ClassInterface(ClassInterfaceType.AutoDual)][ProgId("VFPAndDotNet.ImageHelper")] public class ImageHelper{public ImageHelper() {}

private string strImageFile;public string ImageFile{set { this.strImageFile = value; }get { return this.strImageFile; }

}

private string strCopyright;public string Copyright{set { this.strCopyright = value; }get { return this.strCopyright; }

}

private string strSaveFileAs;public string SaveFileAs{set { this.strSaveFileAs = value; }get { return this.strSaveFileAs; }

}

public void ProcessImage(){// Declare some variables.Bitmap SourceBitmap = null;Bitmap TargetBitmap = null;Graphics bmpGraphics = null;Image thumbnail = null;

try{// Create a bitmap representation of the source image.SourceBitmap = new Bitmap(this.ImageFile);

// Create an empty bitmap for the target image.TargetBitmap =

new Bitmap(SourceBitmap.Width, SourceBitmap.Height);

// Create a graphic object based on our target bitmap size.bmpGraphics = Graphics.FromImage(TargetBitmap);

// set Drawing Quality for the image.

bmpGraphics.InterpolationMode = InterpolationMode.High;bmpGraphics.TextRenderingHint =

TextRenderingHint.AntiAlias;bmpGraphics.SmoothingMode = SmoothingMode.HighQuality;

// Creates the rectangle where we'll draw the image.Rectangle compressionRectangle =

new Rectangle(0, 0, SourceBitmap.Width, SourceBitmap.Height);

// Draw the original image on the graphic object.bmpGraphics.DrawImage(SourceBitmap, compressionRectangle);

// Add logo.Image imgPhoto = Image.FromFile(@"c:\MyCompanylogo.jpg");Bitmap bmPhoto =

new Bitmap(imgPhoto.Width, imgPhoto.Height, PixelFormat.Format24bppRgb);

// Draw logo on source image.Graphics grPhoto = Graphics.FromImage(bmPhoto);bmpGraphics.DrawImage(imgPhoto,

TargetBitmap.Width - imgPhoto.Width, 1);

// Add copyright info.bmpGraphics.DrawString(@"Copyright © 2004 - " +

this.Copyright, new Font("Arial Black",12),new SolidBrush(Color.FromArgb(153, 255, 255, 255)),3,

SourceBitmap.Height - 24);

// Save image to disk.TargetBitmap.Save(this.SaveFileAs, ImageFormat.Jpeg);

// Create thumbnail.thumbnail = TargetBitmap.GetThumbnailImage(

72,98,null,IntPtr.Zero);ImageFormat format = TargetBitmap.RawFormat;thumbnail.Save(this.SaveFileAs + "_thumbnail.jpg", format);

}catch{throw;

}finally{// Dispose objects.if (SourceBitmap != null)SourceBitmap.Dispose();

if (TargetBitmap != null)TargetBitmap.Dispose();

if (bmpGraphics != null)bmpGraphics.Dispose();

if (thumbnail != null)thumbnail.Dispose();

}}

}}

*-- Instantiate the .NET class as a COM component.oHelper = CreateObject("VFPAndDotNet.ImageHelper")

*-- Set some properties.oHelper.Copyright = "Claudio Lassala"oHelper.ImageFile = "C:\MyImage.BMP"oHelper.SaveFileAs = "C:\CopyOfMyImage.jpg"

*-- Call a method on the class.oHelper.ProcessImage()

From the VFP side, there is no indication that theobject being used is a .NET object.

Calling .NET Web Services from VFP

The one .NET feature that was (and still is)mentioned more than any others is the ability to use.NET to create XML Web services. Web services aremethods of functions that are exposed to the Webthrough a standardized protocol called SOAP.SOAP enables you access to components in a plat-

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 59

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 60

COM Interop: Making .NET and VFP Talk to Each Otherwww.code-magazine.com 61

Recommended Linksto InteroperationBetween VFP and.NET:A lot of information related tothis topic can be found onwww.VFPConversion.com. Thefollowing is a list of detailedreading recommendations:

.NET Interop for Visual FoxProApplications:http://west-wind.com/presenta-tions/VfpDotNetInterop/vfpDot-NetInterop.htm

Using Visual FoxPro to call.NET Web services:

http://www.eps-cs.com/VFPCon-version/Article.aspx?quickid=030074

VFP COM interoperability withVisual Basic .NET:

http://www.eps-cs.com/pdf/whitepaper_vfpcom-interop.pdf

COM Interop in Visual Studio.NET:http://www.eps-cs.com/VFPCon-version/Article.aspx?quickid=0203031

form and language-neutral fashion. This means thatany language and operating system can call anyWeb service no matter how the Web service wascreated. This, of course, means that Visual FoxProcan call Web services created in Visual Studio.NET.

We’ll show you how to create a .NET Web servicebefore we call one. You can easily do this using theVisual Studio .NET IDE. (Note: If you do not haveVisual Studio .NET installed, you can probablyfollow the example by calling an existing Webservice such as one of the many Web services foundlisted on www.UDDI.org).

If you’re following along, you have Visual Studio.NET loaded. First create a new ASP.NET Webservice project. The language you choose to usedoes not matter. This example will use Visual Basic.NET but if you are more familiar with C#, youshould have no difficulty following the examples.Figure 4 shows the New Project dialog box.

When you create a new ASP.NET Web serviceproject, the Visual Studio .NET IDE automaticallyincludes all the required references and creates aWeb service source code file (Service1.asmx), witha hello world method as a template. For ourpurposes, we’ll delete that method and insteadchange the code to what you see in Listing 2. Youmay have noticed that most of the code in Listing 2is inside a “designer region,” which means thatdevelopers should never have to touch it. Theimportant part of Listing 2 is the following method:

<WebMethod()> _Public Function GetCurrentTime() _As DateTimeReturn DateTime.Now

End Function

This method simply returns the current date andtime as a DateTime data type. The only unusualaspect about this is the <WebMethod()> attribute.This attribute tells the ASP.NET runtime that thismethod is to be exposed through a Web serviceaccording to the SOAP standard.

You can start your Web service project (simplypress F5) to see a test bench interface in InternetExplorer. In this example, the service is rathersimple since it only has one exposed method. Clickthe link to that method and then click the “Invoke”button to run the service. (Note: If your methodaccepted parameters, this interface providestextboxes to enter those parameters.) You can seethe result in Figure 5. The return value of themethod is wrapped in XML, which is the key mech-anism that allows you to call this service fromVisual FoxPro.

You can register a Web service in VFP through theTask Pane Manager under its special “WebServices” tab. Click the first link provided in thiswindow, Register an XML Web Service. In theWeb Service Registration dialog box (Figure 6)you’ll specify a URL that describes the Web serviceand tells VFP what methods as well as parametersand return values are supported by the service.ASP.NET-based Web services provide a WSDL(Web Service Description Language) URL thatprovides exactly that information. You can find the

Figure 5: Testing our simple Web service using the provided test bench application through InternetExplorer.

Figure 6: Adding a Web service reference using the VisualFoxPro 9.0 Task Pane.

Figure 4: Creating a new Web service project in Visual Studio .NET.

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 61

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 62

CoDe_Focus_Fox Pro 09 30.11.2004 12:43 Uhr Seite 63

URL by launching the test bench in Visual Studio.NET (click F5), to launch the service test benchstart page. At the very top of the page there is alink to the “Service Description” of the Webservice. In our example, the URL is similar to thefollowing:

http://localhost/ExampleService/Service1.asmx?WSDL

Note that in a real-life scenario, you need to replace “localhost” with the name of thedomain the Web service resides on (such aswww.codefocus.com).

After you register a Web service with the VFP TaskPane, you can test it immediately through the TaskPane Manager. Simply pick the service you wouldlike to test (“Service 1” in our example) and themethod you would like to call, and click on theicon next to the method drop-down list. You cansee the result in Figure 7. You now know that theWeb service works in VFP and you can start usingit from within a VFP program. Doing this requiresa little bit of code. The good news is that the TaskPane also provides sample code (the bottom left ofFigure 7 shows the start of the sample code) thatyou can use directly by dragging and dropping thatcode into a source code window. Listing 3 showscode created based on the sample code providedby the Task Pane. Note that Listing 3 contains alot of code that is not strictly required includingerror handling. The important code is in this nextcode snippet.

COM Interop: Making .NET and VFP Talk to Each Other www.code-magazine.com64

Figure 7: Our sample Web service is registered in the VFP Task Pane Manager and ready to be tested.

Many companies have relied on COM

components in the last couple of years.

That includes Microsoft.

Listing 2: A first Web service created by ASP.NET and the Visual Studio .NET IDE

Imports System.Web.Services

<System.Web.Services.WebService(Namespace := _"http://tempuri.org/ExampleService/Service1")> _

Public Class Service1Inherits System.Web.Services.WebService

#Region " Web Services Designer Generated Code "

Public Sub New()MyBase.New()

'This call is required by the Designer.InitializeComponent()

'Add your own initialization code after 'the InitializeComponent() call

End Sub

'Required by the Web Services DesignerPrivate components As _System.ComponentModel.IContainer

'NOTE: The following procedure is required by the 'Web Services Designer 'It can be modified using the Designer. 'Do not modify it using the code editor.

<System.Diagnostics.DebuggerStepThrough()> _Private Sub InitializeComponent()

components = New _System.ComponentModel.Container()

End Sub

Protected Overloads Overrides Sub Dispose( _ByVal disposing As Boolean)'CODEGEN: This procedure is required by 'the Web Services Designer'Do not modify it using the code editor.If disposing Then

If Not (components Is Nothing) Thencomponents.Dispose()

End IfEnd IfMyBase.Dispose(disposing)

End Sub

#End Region

<WebMethod()> _Public Function GetCurrentTime() As DateTime

Return DateTime.NowEnd Function

End Class

CoDe_Focus_Fox Pro 09 30.11.2004 12:44 Uhr Seite 64

COM Interop: Making .NET and VFP Talk to Each Otherwww.code-magazine.com 65

loWSHandler = NEWOBJECT("WSHandler",;

IIF(VERSION(2)=0,"",;HOME()+"FFC\")+"_ws3client.vcx")

loSvc = loWSHandler.SetupClient(;

"http://.../Svc.asmx?WSDL",;"Service1", "Service1Soap")

MessageBox(loSvc.GetCurrentTime())

Note: We shortened the URL to make it more read-able. Please replace the URL with the URL of theservice you created.

The sample code instantiates the WSHandler object,which is VFP’s connector to a Web service. Thisobject is then configured with the WSDL URL.Subsequently, we call the GetCurrentTime()method, which returns a .NET DateTime variable.VFP automatically assigns the return value to a VFPDataTime variable even though the two formatsinternally differ slightly. Since the value is returnedas a DateTime you can perform additional opera-tions on it. For instance, you can retrieve the timeportion using the following commands:

LOCAL ldCurrentDateTimeldCurrentDateTime = ;loService1.GetCurrentTime()

? TToC(ldCurrentDateTime,2)

Note that automatic type assignment does nothappen all the time. It is possible, some would evensay likely, that the Web service will return a datatype that is not natively supported in Visual FoxPro.This typically happens when the return value is acomplex object, such as an ADO.NET DataSet. Inthat case the return value would be complex XML,

that you must parse before VFP can use it. In thecase of a DataSet, VFP has an XMLAdapter class.(Note: For more information about theXMLAdapter class, see the “What’s New with Datain Visual FoxPro 9.0” article in this issue, or searchfor “XMLAdapter” on www.code-magazine.com.) Forother complex objects, parsing the XML may be alittle more complex, but using tools like XMLDOM,it is never overly hard.

Exposing VFP Objects as Web Services

Visual FoxPro does not support a native way toexpose VFP objects as Web services, but there areseveral other Microsoft tools and technologies thatyou can use to accomplish this goal. In previousversions of VFP, Microsoft recommended the SOAPToolkit (and in fact provided tools to automaticallypublish VFP Web services using this toolkit). Thisapproach is now not recommended anymore,mainly because the SOAP Toolkit uses either ASPor ISAPI “listeners” to react to incoming requests.Neither technology is recommended at this point,and is only supported by Microsoft based on thestandard Microsoft support policy. The better wayto go at this point is to expose VFP objects throughmodern ASP.NET Web services.

The overall idea for this approach is simple: First,create a VFP object and expose it as a COM object.You can access this COM object from ASP.NETusing a simple wrapper service to expose individualmethods. For instance, consider the following VFPobject:

DEFINE CLASS TestClass AS Custom OLEPublicFUNCTION GetName() AS String

RETURN "John Smith"ENDFUNC

ENDDEFINE

Listing 3: Calling the ASP.NET Web service from within a VFP program

LOCAL loService1 AS "XML Web Service"* LOCAL loService1 AS "MSSOAP.SoapClient30"* Do not remove or alter following line. It is used to* support IntelliSense for your XML Web service.*__VFPWSDef__: loService1 =http://megger05/ExampleService/Service1.asmx?WSDL , Service1 ,

Service1Soap

LOCAL loException, lcErrorMsg, loWSHandlerTRY

loWSHandler = NEWOBJECT("WSHandler",;IIF(VERSION(2)=0,"",HOME()+"FFC\")+;"_ws3client.vcx")

loService1 = loWSHandler.SetupClient(;"http://megger05/Svc/Service1.asmx?WSDL", ;"Service1", "Service1Soap")

LOCAL ldCurrentTime as Datetime ldCurrentTime = loService1.GetCurrentTime()MESSAGEBOX(Transform(ldCurrentTime))

CATCH TO loExceptionlcErrorMsg="Error: "+;TRANSFORM(loException.Errorno)+;" - "+loException.Message

DO CASECASE VARTYPE(loService1)#"O"

* Handle SOAP error connecting * to web service

CASE !EMPTY(loService1.FaultCode)* Handle SOAP error calling methodlcErrorMsg=lcErrorMsg+CHR(13)+;

loService1.DetailOTHERWISE

* Handle other errorENDCASE* Use for debugging purposesMESSAGEBOX(lcErrorMsg)

FINALLYENDTRY

CoDe_Focus_Fox Pro 09 30.11.2004 12:44 Uhr Seite 65

Here is the wrapper class used to expose this objectthrough ASP.NET as a Web service:

Imports System.Web.Services

< WebService(Namespace := _"http://MySvc.org/Service1")> _

Public Class TestServiceInherits WebService

#Region " Designer Generated Code "

<WebMethod()> _Public Function GetName() _As StringDim oVFPObject As New _TestProject.TestClass()

Return oVFPObject.GetName()End Function

End Class

For more details on how to use VFP COM objectsin .NET, please refer to the earlier section on COMInterop.

Visual FoxPro and OLE DB

Another way to interact with Visual FoxPro datafrom .NET is via the Visual FoxPro OLE DBprovider. Listing 4 demonstrates querying data fromthe sample NorthWind.DBC file and displaying iton an ASP.NET page.

You can simply call this method from an event inan ASP.NET Web Form (such as the Load event).The code first opens an OLE DB connection to aVFP database container. Next the code executes aSELECT statement and fills the results into anADO.NET DataSet using a DataAdapter. You canthen use this DataSet like any other ADO.NETDataSet. In this example, we use it as the bounddata source for a DataGrid.

Further details of accessing VFP data through OLEDB is beyond the scope of this article. The coreconcept however is relatively simple and prettysimilar to accessing SQL Server data.

Conclusion

COM Interop makes it easier for the developer touse VFP components in a .NET application,preventing the developer from rewriting portions oflogic such as data access and business rules whentime constraints and budget don’t allow for that.The same mechanism also enables the developer touse .NET classes from VFP, adding even morepower to existing VFP applications.

Web services are a more open process and allowyour VFP application to work with environmentsthat do not support COM or .NET. Web serviceswork over the Internet, hence automatically addingremote invocation as a free benefit.

Interop on the database level is also a viable option.This works both ways: .NET can access VFP datathrough OLE DB. VFP, on the other hand, canaccess many of the data sources .NET uses, such asSQL Server. (We skipped this topic because SQLServer data access with VFP has been discussedmany times).

Claudio Lassala,Markus Egger,

and Rod Paddock

COM Interop: Making .NET and VFP Talk to Each Other www.code-magazine.com66

Listing 4: Code to query Visual FoxPro data from ASP.NET

Public Sub GetFoxProData()Dim cConnString As StringcConnString = "Provider=vfpoledb.1;"cConnString += Data Source=\\JAWATEST\NorthWind\NorthWind.dbc"cConnString += ";Exclusive=false;Nulls=false"

'-- connect to databaseDim oConn As New OleDbConnectionoConn.ConnectionString = cConnStringoConn.Open()

'-- create a command object to queryDim oCMD As New OleDbCommandoCMD.CommandText = "select * from customers"oCMD.Connection = oConn

'-- create the adapter class for filling'-- our datasetDim oAdapter As New OleDbDataAdapteroAdapter.SelectCommand = oCMD

'-- go get the dataDim oDS As New DataSetoAdapter.Fill(oDS, "customers")

'-- clean upoConn.Close()

'-- view the dataMe.DataGrid1.DataSource = oDS.Tables("customers")Me.DataGrid1.DataBind()

End Sub

XML Web services have been promoted more than any other feature in

.NET, and they are indeed very useful.

CoDe_Focus_Fox Pro 09 30.11.2004 12:44 Uhr Seite 66

CoDe_Focus_Fox Pro 09 30.11.2004 12:44 Uhr Seite 67

CoDe_Focus_Fox Pro 09 30.11.2004 12:44 Uhr Seite 68