‘m sure you are as anxious as I am to explore new properties, events, and methods, new or improved commands, and FoxPro Foundation Classes (FFCs). Let’s dive right in!
Before Visual FoxPro 8, many lines of code had to be written in order to properly resize onscreen controls whenever a form was resized. In Visual FoxPro 8, that task got slightly easier with the introduction of the BindEvent function. In Visual FoxPro 9, it gets way easier with the introduction of anchoring. Anchors allow a control to maintain its proportional relationship with other objects and controls within a form, no matter what size the form becomes.
The Anchor property is obeyed when the control sits on containers such as Forms, CommandGroups, and Page objects. Whenever those containers get resized, the controls within it that have the Anchor property set are resized and/or repositioned depending on the settings used. Make sure to check out the Anchor Property topic in the Help file for available settings.
|Figure 1. Docking Example: Docking user-defined windows is simple.|
Docking allows toolbars and other controls to be “attached” to any user-chosen edge of a Form and maintain that positioning despite resizing or scrolling. Visual FoxPro 8 introduced the ability to dock objects, such as the Command, Document View, and Properties windows, as well commands and functions that handled docking programmatically.
Visual FoxPro 9 goes a step further and introduces the docking capability to user-defined forms as well. That enables creating dockable Forms on the IDE or on the application delivered to the users.
In order to support this feature, new members were added to the Form class, such as the Dockable and Docked properties, the Dock and GetDockState methods, and the AfterDock, BeforeDock, and UnDock events. Listing 1 shows a simple example that produces the result in Figure 1.
|As is common with 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 Help file, because there’s a lot more in there.|
NewObject() Creates Objects without Raising Initialization Code
The NewObject function now accepts 0 as the third parameter in order to create the object without raising code in the Init method. That’s useful when the interface of an object must be analyzed (using the AMember function, for example), where neither the code on the Init or Destroy methods should run.
When the following code snippet runs, you’ll notice that the Customer table doesn’t open when the Init method fires:
loCustomer = NewObject("CustomerBizObj",; "NewObjectSample.prg", 0) AMembers(laMembers, loCustomer, 1) Display Memory like laMembers Define Class CustomerBizObj As Session CustomerID = "" CustomerName = "" Procedure Init Use Home(1)+"samples orthwindCustomers" EndProc Procedure GetCustomersByPk(lcPK As String) EndProc EndDefine
Set Path Command Enhancements
An Additive clause was added to the Set Path command. When that clause is used, the specified path is added to the path setting, if it isn’t there already. Besides that, the Set Path command’s maximum character limit has been increased to 4095 characters.
AppState Property Detects an Application Losing or Receiving Focus
The _Screen object has a new property called AppState. This property is read-only and available only at runtime. It indicates whether the Visual FoxPro application has focus (value 1), or not (value 0). The value changes automatically whenever the application loses or receives focus, such as when the user uses Alt+Tab. A Timer object, or event handler (using the BindEvent function) can be used to query this property and execute code whenever its value changes.
Specify Where Focus is Assigned in the Valid Event
It’s always been a pain when something happens inside a Valid event and you want the focus to go to another control, as calling SetFocus from within a Valid event is not allowed. That’s been solved in Visual FoxPro 9 by allowing you to return the execution to a specified object from within the Valid event:
The IIF() (immediate if) function has always been very useful, but the more complex the expressions it contains, the harder it is to read the code, particularly when multiple IIF calls are needed. The ICASE (immediate case) function solves that problem. The following code snippet shows an example written using both the IIF and the ICASE functions:
Local lcAnswer lcAnswer = "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 Format
Visual FoxPro 9 joins the ranks of great software XML adopters. The TTOC function has been improved and receives a value of 3 as the second parameter, indicating that the resulting string must be formatted as an XML DataTime format.
The settings Set Century, Set Hours, or Set Seconds don’t affect the result of the TTOC function when the parameter 3 is passed.
Log Execution Plan’s Output from SYS(3054) Using SYS(3092)
The SYS(3054) function is very helpful for optimizing SQL queries, as it produces an execution plan of which indexes, if any, are in use, and enables optimization of queries by creating new indexes or fine-tuning the way the queries are written. The new SYS(3092) function provides the ability to write the output of SYS(3054) directly to a file on disk.
*-- Turn on execution plan. Sys(3054, 12) *-- Set up output file. Sys(3092, "c:ExecutionPlan.txt", .T.) *-- Run Query. Select * ; from Home(1)+"samples orthwindCustomers" ; into cursor curCustomers *-- Turn off output and close file. Sys(3092, "") *-- Let's see the log. Modify File "c:ExecutionPlan.txt"
Extended SQL Capabilities
There are many improvements to the SQL subset in Visual FoxPro 9. With each new release, the VFP team moves Visual FoxPro’s SQL closer to T-SQL (SQL Server’s SQL implementation).
Some of the most notable improvements are:
- The limit of nine join clauses and sub-queries was removed from the SQL Select statement, as were the nine Union clauses limit, the 30 tables limit, and the referenced aliases limit.
- The level of nesting for sub-queries in a SQL Select statement, formerly limited to only one level, is now unlimited.
- The list of fields and the FROM clause can now have a sub-query. This works for the ON clause on joins as well.
- The Like clause is now optimized.
Make sure to read the Data Features Enhancements section under the “What’s New” topic in the Visual FoxPro Help file for more specific information about all the improvements on these subjects.
Set Coverage Command Available at Run Time
The Set Coverage command is now available at run time. Anywhere inside an application, you can call the Set Coverage command like this:
SET COVERAGE TO C:MyApp.log
The Set Coverage command creates a log file for the code being executed. The Coverage Profiler tool (available on the Visual FoxPro IDE from the Tools menu) can then be used to analyze this log file. The Coverage Profiler tool shows the code’s coverage, indicating which lines are being executed, and which ones never get executed; it also shows statistics, such as how many times a line is executed, and how long that takes, helping you find code bottlenecks.
This new ability of enabling coverage during runtime is also useful for debugging errors that occur during runtime, but that never happen in interactive mode (through the Visual FoxPro IDE).
Populate an Array with Aliases Used by a Specified Table
The AUsed function improved to allow passing a third parameter, one that indicates that the array created by this function should only look for aliases of a specific table. The following code snippet shows a simple example:
Select * ; from Home(1)+"samples orthwindCustomers" ; into cursor curCustomers Select * ; from Home(1)+"samples orthwindemployees" ; into cursor curEmployees ? AUSED(gaInuse, Set("Datasession"), "Employees") DISPLAY MEMORY LIKE gaInuse
In the preceding code, an array shows the aliases and work area numbers for the Employees table available at the current data session.
SQLIdleDisconnect() Temporarily Disconnects SQL Pass-Through Connections
The new SQLIdleDisconnect function is a great new addition to the SQL Pass-Through set of functions. This function temporarily disconnects a connection handle passed to it (or it disconnects all connections, if you pass a 0).
That’s a great feature for disconnected scenarios, where an application doesn’t have to hold a connection to the database when the user is only reading retrieved data, rather than sending data back and forth regularly. Plus, when the connection must be enabled again to process something, it happens automatically, using the original connection string.
Retrieving Active SQL Connection Statement Handles
The new ASQLHandles function creates an array with the handles for all active SQL connections. Those handles can then be used with other functions such as SQLExec and SQLDisconnect.
lnConn1 = SQLStringConnect(; "driver=sql server;server=(local); "+; "database=northwind;") lnConn2 = SQLStringConnect(; "driver=sql server;server=(local); "+; "database=pubs;") ASQLHandles(laHandles) Display Memory like laHandles
Get Cursor and Count Records Affected by SQL Pass-Through Execution
The SQLExec function has been improved to make it possible to immediately get cursor and count records affected by the SQL Pass-Through command. A parameter is passed determining the name of the created array with a list of aliases and a record 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 Browser
The Class Browser now has the ability to manage program-based (PRG) classes. In previous versions, only Visual Class Library-based (VCX) classes could be viewed. For developers who write several PRG-based classes (like me!), this is definitely a very-welcome feature.
GDI+ FFC classes
|Figure 3. Image Manipulation: You can now draw images directly onto a form using GDI+.|
Visual FoxPro 9 has a new FFC (FoxPro Foundation Class) library for the Graphics Device Interface (GDI) called _GDIPlus.vcx. This library has many classes working as wrappers around the GDI+ API.
By using GDI+, you can manipulate graphics and images, so this is a really powerful feature. Entire articles could be devoted to this topic, exploring the different classes, how to use them, and when to use them, but it’s beyond the scope of this article. I’ll just mention these classes here so that you don’t overlook this new feature in your excitement over the other new features.
One simple example of image manipulation is a small program that loads an existing bitmap file from disk, and then creates a new JPEG file from it, as shown in the following code snippet:
*-- Create an Image object. Local oLogoImage As GpImage ; Of Home(1)+"ffc/_gdiplus.vcx" oLogoImage = ; Newobject('GpImage',Home(1)+'ffc/_gdiplus.vcx') *-- 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 images and draw them on a device such as a form. Listing 2 shows an implementation of that, and Figure 3 shows the result. Notice that the image shown on the form was drawn right onto it, rather than added using the Visual FoxPro Image object.
|Figure 4. FFC Classes: The new FFC classes for GDI+ are similar to the ones found in the .NET Framework.|
The important thing to notice is that you can manipulate the image before drawing it, perhaps adding a logo or watermark, or drawing text on it. It’s also worth mentioning that the new Report engine in Visual FoxPro can leverage the power of GDI+, so if you’re planning on doing powerful stuff with reports, take a serious look at the GDI+ FFC classes.
These classes (shown in Figure 4) map to the classes available in the .NET Framework. I strongly suggest that you read the articles about using GDI+ with the .NET Framework published in previous editions of CoDe Magazine, so that you can understand what sorts of things can be done with this technology. Then you should be able to translate most of the .NET code into Visual FoxPro using the FFC classes.
There are many improvements and new features in Visual FoxPro 9 that are both productivity tools and exciting new ways of building interesting and useful applications. I’ve only briefly covered some of the developments you’ll find when you start playing with this new version. Make sure you take a close look into the “What’s New in Visual FoxPro” section on the Help file, and have fun!
Collection Object Support in ComboBox and ListBox Controls
The RowSourceType property on ComboBox and ListBox objects has a new option added to the list of possible sources: value 10, for the Collection object.
A collection could be populated with Business objects or objects holding some sort of data, and that Collection can be used to populate ComboBoxes and ListBoxes. The following code snippet shows an example of that, using a collection named colEmployees contained in the form, and the ComboBox’s RowSourceType property is set to 10.
Select EmployeeId, FirstName; from employees into cursor curTemp Scan Local loEmployee as Object Scatter NAME loEmployee Blank Scatter NAME loEmployee additive This.colEmployees.Add(loEmployee,; Transform(loEmployee.EmployeeId)) EndScan This.cboEmployees.RowSource =; "Thisform.colEmployees, FirstName, EmployeeId"
Rotating Label, Line, and Shape Controls
|Figure 2. The AutoHideScrollbar Property: The ListBox’s AutoHideScrollbar property provides several useful options.|
For a long time now, Visual FoxPro developers have waited for the capability to rotate labels. The option is now available through a new Rotation property, which receives a number indicating the degrees the control will rotate. The Rotation property is also available for Line and Shape controls.
ListBoxControls Can Hide ScrollBars
A new AutoHideScrollbar property was added to the ListBox class. This property determines whether or not the ListBox shows the scrollbar at all, and whether it is shown if the control only has a few entries.
If the default value 0 is used, the scrollbar is always visible, while a value of 1 determines that the scrollbar is only visible when the ListBox is full. This makes a lot of sense, as a scrollbar is of no use when all the items on the ListBox fit in a single pane. The value 2 causes the scrollbar to be invisible. If there are more items on the ListBox than can be viewed in a single pane, up and down arrows appear on the control. Figure 2 shows a snapshot of a form with ListBoxes using the three available settings.