October 17, 2008
Phoenix in the fall; Arizona in the autumn
If you aren't at Southwest Fox 2008 in Mesa, AZ (http://www.swfox.net/) this weekend, you're missing a real treat. Tamar Granor, Doug Hennig and Rick Schummer have once again done an excellent job organizing a conference that is a tremendous value for your money.
To give you taste, here are some of the sessions I attended today:
- In "Profiling and Refactoring: How to Analyze and Clean up Your Code", Andrew Ross MacNeill talked about a powerful tool in VFPX called Code Analyst that can help you identify "issues" in your code (e.g., too many lines of code in a single routine, too many comments, too many loop structures, has more than one return value, etc.). (VFPX is a Visual FoxPro Community effort to create open source add-ons for Visual FoxPro 9.0). You can select which rules you want run against your code; you can also create your own rules.
You can download Code Analyst for free from the VFPX CodePlex web site: http://www.codeplex.com/VFPX.
Andrew also discussed an add-in for the Coverage Profiler.
- Christof Wollenhaupt gave an introduction to mobile development; employing his product, Guineu (Catalan for "Fox"), you can develop mobile applications from within Visual FoxPro. Cool!
- Doug Hennig talked about Advantage Database Server for VFP Developers; with this alternate back-end, you can either read VFP .DBF files directly or you can move your data into the proprietary ADD format. With Advantage Database Server (ADS), your .DBF files are no longer limited to 2 GB. "In ADS, there isn't a direct limit on the size of the file; instead, the limit is a maximum of 2 billion (2,147,483,648) records. Of course, if your DBF becomes larger than 2 GB, you'll only be able to access it through ADS since VFP will see it as invalid."
"ADS [also] has a fast and powerful full text search (FTS) feature. FTS uses an index on each word in a memo field to provide fast, index-based lookups for desired words."
- Rick Schummer did a presentation focusing exclusively on "Using VFPX Components"; he demo-ed various controls like Themed Controls (e.g., OutlookNavBar), the ctl32 project (e.g., StatusBar and BalloonTips [tooltips on steroids]) and Desktop Alerts. VFPX controls add pizzazz to your VFP applications, giving them a more polished/professional appearance. You can download these controls for free from the VFPX CodePlex web site (http://www.codeplex.com/VFPX) and then implement all of these controls in your VFP applications
- In "Creating Owner Drawn Controls in VFP", Christof Wollenhaupt showed us how to "exploit the power of GDI+ to create our own controls" in VFP forms (e.g., "new pageframes, progress bars, formatted text, chart controls, basic text input or Unicode text output"). He also covered "dealing with mouse and keyboard input."
- Bo Durban showed us how to create custom report controls with VFP 9, employing the GDIPlusX library; rotated text, dynamic text formatting, graphs and custom shapes are a few examples of things you can do/include in your reports.
I continue to see the common thread/theme of EXTENSIBILITY with respect to Visual FoxPro (that was also displayed in a 2-day West-Wind Web Connection training I attended on Tuesday and Wednesay this week [Rick Strahl's product enables you to create web applications using VFP and some other technologies like CSS, JavaScript, JQuery, etc.]).
Posted by abergquist on October 17, 2008 | Permalink | Comments (3) | TrackBack
September 25, 2008
Dump unnecessary leading equals signs ("=")
You will often see a leading equals sign ("=") in older VFP code (especially in code ported over from FoxPro 2.x). A leading equals sign is necessary only in a couple of instances in VFP:
- when calling the SEEK() function and not storing the return value, e.g.,
USE (HOME(2) + 'Data\Customer') IN 0
=SEEK('Great Lakes Food Market', 'Customer', 'Company')
- when denoting an expression in a property in the Properties Sheet/Window; for example, when setting the .Caption property of _Screen:
='My Application (Version # ' + m.gcVersionNo + ')'
Side note, when setting the .Picture property for an Image control, include the equals sign as follows:
='MySplash.JPG'
If you specify MySplash.JPG without the equals sign and character delimiters, VFP will attempt to insert the path in front of the picture's file name; the problem is that that path may not (in fact, most likely will not) be on your customer's PC (when your application is production).
Posted by abergquist on September 25, 2008 | Permalink | Comments (0) | TrackBack
September 04, 2008
Best practices when macro substituting
When working with macro substitutions …
1) bring “closure” to them
Get in the habit of always “closing” your macro substitution with a period (“.”); for example:
&lcSQL.
That way, when you employ macro substitution as part of an object hierarchy, VFP will properly parse the line; for example, if you have a form with a commandbutton whose .Name is ' cmdButton':
The following code sets the .Caption of 'cmdButton' to 'Process':
LOCAL lcCommandButton
lcCommandButton = 'cmdButton'
ThisForm.&lcCommandButton..Caption = 'Process' && Two (2) periods before .Caption
whereas the following code triggers a “Property CMDBUTTONCAPTION is not found” error:
LOCAL lcCommandButton
lcCommandButton = 'cmdButton'
ThisForm.&lcCommandButton.Caption = 'Process' && Only one (1) period before .Caption
2) don’t embed them
lcControlName = 'cmdButton'
Replace the cryptic, embedded macro substitution:
? 'oForm.&lcControlName.'
with the following, easier-to-read (and, therefore, easier-to-maintain) code:
? 'oForm.' + m.lcControlName
(The above should run faster, too, since it does not employ macro substitution.)
Posted by abergquist on September 4, 2008 | Permalink | Comments (1) | TrackBack
August 26, 2008
Don't Think "Tomorrow"; Think "A Decade from Now"
The always entertaining and thought provoking, Rick Schummer, had a blog recently, ( Rick's Blog - “Getting Started with Visual FoxPro") that caused me to stop and reflect upon his heartfelt advice. What I came up with is not particularly pretty for me, but it was a moment of truth for me.
Before I go any further, let me say I have been using FoxPro in one version or another since 1989. I have made a good living thanks to that fine product. My custom license plates read “FOXPRO”. Get the point? I L-U-V the software! I will continue to use and work with the product until the day I retire. Fortunately, for me, that day is not that far away. Timing is everything. For me, the timing is that I should be able to run out my career and the last line of code I write will be... RETURN. However there are others who have 25, 30, 40 years left in the work force and my advice to them is... DON’T WASTE YOUR TIME WITH FOXPRO.
I can hear many of you shouting, “Blasphemer!”. Maybe I should even be shouting at myself, but the truth of the matter is, FoxPro will not be viable software in a few (plug in your own definition of “few”) years. Rick’s advice for anyone wanting to learn FoxPro was dead on. He did an excellent job of covering all of the bases. My question is WHY would anyone want to learn FoxPro at this point in history? The only two reasons I could come up with are because the dwindling ranks of FoxPro developers have caused companies, still using legacy FoxPro application, to get someone, anyone, to maintain and enhance their applications OR the individual is wanting to learn VFP because it is still an awesome hunk of software that is VERY cost effective; particularly in third world countries.
In the first case, the boss probably went to an employee and said, “We need to have you learn FoxPro.” The apparent up front cost of converting a legacy application is, in many cases, too much for a small to medium business to bear in these somewhat difficult economic times. The application, basically, still does what they need it to do, but once in a while, it just needs some tweaking. The “quick fix”, in management’s mind, is to invest a minimum amount and “throw another body” at the problem. This only delays the final decision to rewrite or replace the existing legacy application. Ignoring the thought that if the employee refuses to “waste” their time learning “old technology” they might be fired, any developer worth their weight in salt would prefer to learn current technologies, thus making themselves more valuable both to their present company and when they re-enter the job market. Learning FoxPro serves his company well (momentarily), but it does not serve the developer’s best interests. Soon, that developer will realize his stagnant position and move on causing the problem to arise again and the same solution to be more expensive than it was earlier. See a pattern here?
In scenario number two, each desire to learn VFP has many reasons. I have no problem with anyone weighing their options and deciding that Visual FoxPro is the answer to their solution. Many times, it actually IS the best solution; even now. However, it would seem to me that it would have to be a very special set of circumstances to arrive at that conclusion. In that case, once again, Rick’s advice is the way to go.
Returning to scenario number one... What would Rick’s answer have been had the question been, “How do I get started learning COBOL? My guess is that Rick would have asked WHY they wanted to learn COBOL. Now is the time to convert or rewrite legacy applications. It will never be less expensive; aren’t labor costs always going to increase? It will never be easier; the project will only grow in scope as the legacy app grows. At the very least, a far-seeing IT manager should realize what is inevitable and begin to make plans to move forward. In the meantime, and this will sound like an advertisement, the most cost effective solution in the long run is to hire the right people who already have the experience and knowledge necessary to keep the application afloat while your company prepares to move forward and stay ahead of the competition. The people you hire to maintain the legacy app could even be involved in the conversion.
Comments? I would love to hear them; I have thick skin.
Posted by Dave Aring on August 26, 2008 | Permalink | Comments (4) | TrackBack
August 25, 2008
When a "Watch Window" expression goes in and out of scope
It used to be extremely frustrating to me when a "Watch Window" expression went in and out of scope. Now, I simply employ a technique I learned in Nancy Folsom's short but excellent book Debugging Visual FoxPro Applications (http://www.hentzenwerke.com/catalog/debugvfp.htm).
On p. 53, Nancy indicates that:
"If you know the value of the expression you're looking for, you can create a 'Break when expression is true' breakpoint and enter a conditional IIF() in the Expression field. The expression would look like the following:
IIF(TYPE('SomeExpression') = 'C', SomeExpression = SomeValue, .F.)
"This expression will cause the program to be suspended only when the value of the expression is what you expect."
Here's an example of an expression:
IIF(VARTYPE(RECCOUNT('lv_WDates')) = 'N', RECCOUNT('lv_WDates') = 6, .F.)
Michael Cummings of the Los Angeles FoxPro Users Group (LA Fox) suggested an even better technique [viz., test for VARTYPE(…) = 'U']; here's a translation of the above:
IIF(VARTYPE(RECCOUNT('lv_WDates')) = 'U', .F., RECCOUNT('lv_WDates') = 6)
Testing for an 'U'ndefined data type obviates having to get the variable's data type "right" in the breakpoint expression.
Patrick O'Hara of the Chicago FoxPro User/Developer Group (CFUDG) reminded me that [Ctrl + B] brings up the Breakpoints dialog box.
Posted by abergquist on August 25, 2008 | Permalink | Comments (2) | TrackBack
August 21, 2008
Debugging errors that occur only in production code
There are a few techniques to debug errors that occur only in production:
1. Insert the following line:
MESSAGEBOX('<n>. ' + PROGRAM() + ' ...')
at "strategic" points in the program (e.g., before calling a function, method, procedure, etc.). I start "n" at 1 and increment by 1 in subsequent MESSAGEBOX() calls. The last number that displays in a MESSAGEBOX() before an error occurs helps me narrow down exactly where the problem is occurring. If necessary, I delete the original MESSAGEBOX() calls and then insert new MESSAGEBOX() calls "closer" to where the error is occurring. (Repeat as necessary [and, yes, you can -- and should -- try this at home <g>].)
2. Another technique is to write text to a log file (.Log, .Txt, etc.) at various points of/during program execution. (This technique, by the way, is also useful for debugging COM objects since you cannot step through code in the VFP debugger nor can you [or are supposed to] send output to the screen from a COM object.)
3. In VFP 9, you can do coverage logging not only at design time but also in .EXEs (via the SET COVERAGE command). Your client will need to send you the coverage log file (generated by the SET COVERAGE command) which you can then run through the Coverage Profiler yourself. Put code like the following toward the top of your main calling routine:
ON ERROR SET COVERAGE TO
SET COVERAGE TO <filename of your choice>.Txt
Posted by abergquist on August 21, 2008 | Permalink | Comments (6) | TrackBack
August 16, 2008
Make use of methods for complex Dynamic... grid settings
In a grid's Init() event method:
*--------------------------------------------------------------------------
* Set the .DynamicBackColor property for this column.
*--------------------------------------------------------------------------
This.Column1.DynamicBackColor = [ThisForm.DynamicBackColorForThisColumn()]
In the form's custom DynamicBackColorForThisColumn() method:
*-----------------------------------------------------------------------------------
* Method: DynamicBackColorForThisColumn()
* Purpose: Handles the initialization of DynamicBackColor for the grid's Column
*
* Notes: It is *far* easier to put the logic into this method (and allows
* us to document it thoroughly) rather than trying to "stuff" text
* into the grid's .Column1.DynamicBackColor property.
*
* Plan Codes:
* 01 - Bill full cost
* 07 - Zero AtP (do not send bill)
* 08 - Zero AtP (do send bill)
*-----------------------------------------------------------------------------------
LOCAL lcColor
DO CASE
CASE AtP_PlanCd = '01'
lcColor = 'RGB(255, 255, 255)'
CASE AtP_PlanCd = '07'
lcColor = 'RGB(0, 0, 0)'
CASE AtP_PlanCd = '08'
lcColor = 'RGB(192, 192, 192)'
OTHERWISE
lcColor = '<some default color>'
ENDCASE
RETURN lcColor
The above could have been coded in the grid's Init() event method as follows:
*-----------------------------------------------------
* Set the .DynamicBackColor property for this column.
*-----------------------------------------------------
This.Column1.DynamicBackColor = [IIF(AtP_PlanCd = '01', 'RGB(255, 255, 255)', ;
IIF(AtP_PlanCd = '07', 'RGB(0, 0, 0)', IIF(AtP_PlanCd = '08', ;
'RGB(192, 192, 192)', '<some default color>')))]
Which implementation do you think is easier to read, debug and maintain?
Even with VFP 9's new ICASE() function, it's still challenging to read, debug and maintain:
This.Column1.DynamicBackColor = [ICASE(AtP_PlanCd = '01', 'RGB(255, 255, 255)', ;
AtP_PlanCd = '07', 'RGB(0, 0, 0)', AtP_PlanCd = '08', 'RGB(192, 192, 192)', ;
'<some default color>')]
(By the way, the form-level method could alternatively be implemented as a [class-level] method of a grid class.)
Posted by abergquist on August 16, 2008 | Permalink | Comments (0) | TrackBack
August 12, 2008
Employ SQL-SELECTs "TO SCREEN" clause to determine if a record exists
To determine if a record exists in a table, I used to send the results of a SQL-SELECT to an array, like so:
LOCAL ARRAY la[FieldName][1]
_Tally = 0
SELECT [FieldName] ;
FROM [Database]![TableName] ;
WHERE [FieldName] = [Memory Variable] ;
INTO ARRAY la[FieldName]
IF _Tally = 0
MESSAGEBOX(TRANSFORM([Memory Variable]) + ' is not a valid [Whatever] ',
0, '[MESSAGEBOX() Caption]')
ENDIF
Sending the results of the SQL-SELECT TO SCREEN NOCONSOLE obviates the need to declare an array prior to doing the SQL-SELECT:
_Tally = 0
SELECT [FieldName] ;
FROM [Database]![TableName] ;
WHERE [FieldName] = [Memory Variable] ;
TO SCREEN NOCONSOLE
IF _Tally = 0
MESSAGEBOX(TRANSFORM([Memory Variable]) + ' is not a valid [Whatever] ',
0, '[MESSAGEBOX() Caption]')
ENDIF
In different versions of VFP up to and including VFP 8, _Tally was not always reliably accurate (cf. http://foxproadvisor.com/doc/12139 [Cathy Pountney's "_TALLY trap tip]); if that is your experience, simply send the output to a CURSOR and then check the RECCOUNT() of the cursor.
Enjoy!
Art Bergquist
Posted by abergquist on August 12, 2008 | Permalink | Comments (0) | TrackBack
August 09, 2008
USE IN SELECT() to close a work area
In lieu of:
IF USED(<Work Area>)
USE IN '<Work Area>'
ENDIF
and:
USE IN IIF(USED(<Work Area>) …
employ code like the following:
USE IN SELECT('<Work Area>')
Like the first example above, this one will work regardless of whether the work area is in use or not (the second example will bomb if the specified work area is not in use).
Yours for more concise (yet still readable) code,
Art Bergquist
Posted by abergquist on August 9, 2008 | Permalink | Comments (0) | TrackBack
August 06, 2008
Copy- (or Cut)-and-paste as much as possible; avoid re-typing code!
For example:
LOCAL lnTotalInvoice && Type only one (1) of the instances of 'lnTotalInvoice'
lnTotalInvoice = … && and then copy-and-paste it for all other instances
This helps prevent inadvertent typos (are there any other kind?!) from creeping into your code.
It is also helpful when you communicate with other developers (e.g., via e-mail when posting to an on-line news group or message board) so that you don’t accidentally modify the code in question (again introducing that inadvertent typo <g>).
Doug Carpenter reminded me of a trick when working with memory variables that have been declared LOCAL and PUBLIC (but not PRIVATE):
typing ZLOC followed by the <Spacebar> in a .Prg file/method causes VFP to display an IntelliSense list of available LOCAL and PUBLIC memvars and parameters.
Here is an example of the list that displays after you press the <Spacebar>:
lcString
llCustom
llError
llSuccess
loException
Posted by abergquist on August 6, 2008 | Permalink | Comments (1) | TrackBack




