TechSpoken

[retired content] SoftSpoken and FoxSpoken words from long ago and far away


   FoxSpoken Volume 2

>L<'s Page
Most Recent Volume, Fox Stuff     Other Retired Fox Volumes     MC Framework

articles on this page retired Nov.97


Before you use this material, please be sure you have read and understand the various provisos *3 with which I provide it for your use.

Contents of this page:

  1. DevCon '96 Output Session Source: The Mysteries Revealed
  2. The Future of FoxPro
  3. A Container weevil to pick out of your base classes?
  4. Tales from the Framework: Programmatic use of Edit Menu Options
  5. The Whipped Tip Dept: Grid Partitions? Revisited? (But why?? and onwards)
  6. Class Browser add-on for #INCLUDE: fix
  7. What's New in the MC Framework

First, a slight apology:

In this volume of FoxSpoken, I completely "out" myself as an X-Files viewer. I've made references to the show before, but somehow I couldn't get it out of my mind while I was writing, today... so, if you've never watched the show, you are going to find my sense of humor even stranger than usual, I'm afraid.

If you wish to research this topic, well, you know where to find Yahoo and AltaVista. I'm not going to encourage you by placing links here -- heck, this stuff is addictive!

 

Grab My Output Session Code
Before the Cigarette Smoking Man Gets You

Pertinent files: LSN_REP.ZIP updated Sep.96, ~44k. A separate text file, LSN_REP.TXT ~3k, is available if you want more information about the contents before downloading. This text file is included in the .ZIP.

A funny thing happened on the way to my source files getting on the DevCon 96 CD. The only trouble is, I have no idea what the joke was, so I can't tell you about it.

All I do know is that I sent my Output session source in to be placed on the CD, in plenty of time. In fact, I sent it in at the same time I sent my RAD session stuff, which made it onto the disk fine. However, my Output session code files didn't appear on the CD, and neither did those of several other hapless presentors.

Unfortunately, I have this habit of planning both my presentations and my session notes to whet your appetite for many happy hours to be spent spelunking in my code. Those examples, in other words, are darned useful.

I believe that Microsoft and Advisor Publications placed a temporary "secret URL" for DevCon attendees to download the missing material from the Net. A photocopied sheet was distributed at DevCon to this effect, warning that its availability would be strictly for people who had paid to attend the conference (no other link to this URL was supposed to be available) and very short-lived.

Double-unfortunately, most DevCon attendees either didn't get this sheet or didn't attempt the download until 'way after it was removed.

(Look on the positive side -- the people who handled this particular maneuver probably have bright futures on the production team of The X-Files: the Web Version. You know, "the truth is out there... somewhere... probably in an ancient script or at least a really strange font... ")

As I write this it's, what, six months since DevCon? Yet I get requests for examples that I know are contained in this material, practically daily. This frustrates me.

I think it's time that we got democratic about this, don't you? I hereby make these files available on this site, no proof of DevCon 96 attendance necessary.

NB: as shown by the "update date" in the link above, I have not updated the files you're getting past the versions that the DevCon attendees received (or even looked at them recently). Probably some of them could use additional example code to flesh things out, especially since I'm not posting my session notes and you may not have been to the presentations. Feel free to ask me questions about this stuff to goose me along on any re-vamping that you think is required, but as always please do so on the newsgroups,*2 not e-mail, okay? TIA.

The Future of FoxPro:
Fox Molders?? I don't think so...

I am so tempted to leave this section blank.

Not because I think that Fox has no future, and not as an April Fools' joke, but because I want to make visual reference to the fact that I have no secret knowledge about this topic.

Folks, please stop writing to me with questions about this. I know that you have real concerns as you try to figure out exactly what it is you do for a living these days. But I am not in a position to advise you.

You have to take what I say below as applying to my situation, and delivered without the aid of a crystal ball. If it helps you, fine. If not, please refrain from e-mailing me with the details of your personal and professional situation. Fair enough?

I do know that excellence speaks for itself.

There are areas of the product that are truly excellent, and others that are not. In the former category I'd place a stunning array of commands and functions to manage data and manipulate text. In the latter category I'd place the DBF format.

In practice, this means that I do not waste my time attempting to remedy deficiencies and concentrate on what the product does well. For example, if a client needs security I am not going to kludge encryption in Fox tables, no matter how clever this seems; I am going to use a back end that understands security and I'm going to manage that back end data, superbly, in Visual FoxPro.

Yes, I realize that nothing compares to the speed of Fox -- running with Fox tables. Well, fine. Where real speed is the paramount issue, I hope you are all still using FoxPro for DOS 2.6, a developer's dream of a product, whenever you can?

There are compromises and choices to be made, but I promise you that you will always have this problem, no matter what development platform you use.

One more issue: Microsoft support of the product, either now or the future. (Those of you who are in the States have no idea how bad this can get, by the way.) All I can say is, if we worried like this when Ashton Tate ruled supreme, most of us would have not switched to Fox.

Fox was never a product for lemmings, any more than it could be tortured into a product for end-users... we could never make the mistake that the vendor, any more than the product, would do all our work for us.

Incidentally I have yet to see any dbms product that did a developer's work. I'm amazed at vast numbers of people who think that wizards+decent documentation = application development. I haven't noticed VB professionals having such an easy time of it, nor Delphi programmers, despite the best efforts of vendors on their behalf. And that's before any of them worry about efficient, capable data storage or intelligent data models!

Is it time for a change?

Did you start using Fox on the basis of Fox Software's clout (not bloody likely!)? Or even its reputation for technical support hand-holding (grossly overrated in people's fond memories, IMHO) ? Or did you switch because Microsoft bought it? Or because it then became a corporate standard? Or (heaven forfend) did you switch because some magazine reviewer told you to do it? If so, it may well be time you switched to another product.

More: Do you not truly love the process of putting pieces together, and solving puzzles, without somebody giving you all the answers? If so, maybe you need to switch to another occupation. (In taking this hard line, I admit to being heavily influenced by Bob Lewis, a columnist in Infoworld. I hope you all get a chance to be "infected" by Mr. Lewis' take-no-prisoners opinions and literary style, at least occasionally.)

But if you are the kind of developer who switched to Fox because it was the right product for you to use, you have to make the same level-headed judgement about its capabilities, and those of other products, and your own, now. And let excellence -- wherever you find it -- speak for itself.

No hard feelings, either way.

A Container.SetFocus() Bug,
and some bonus collection-handling advice

Pertinent file: CONTBUG.TXT updated Apr.97, ~3k This short text file is actually a runnable PRG to demonstrate the bug. I gave it a .TXT extension so you can look at it more easily on-line.

The Container.SetFocus() doesn't appear to know what your true control order (as set by TabIndex) is.

It's quite common to want to adjust TabIndex when you subclass a container to have more controls; you need to do this if any of the "new" ones are supposed to come before the parent class's controls in the container's tab order. When the container gets focus normally (whether you tab to it, or make it first in the Form's tab order, or whatever), the TabIndex properties that you have set for its members will be respected, no matter what order you dropped them into the container, or what container class level they belong to.

However, if you run the SetFocus() method for this container, VFP is demonstrably going to the first control in its collection rather than checking TabIndex as it should. The controls collection is always determined by the order in which you added the controls to the container -- or, in a non-visual class, the order of the ADD OBJECT statements for the container members. The code in the short text file above will demonstrate this behavior.

The code contains a commented-out method, which I'll repeat below, and which I suggest you review for possible inclusion in your base container class. I haven't done this myself, yet, because I hate to include bug-workarounds in my MasterClass base code for later removal, and am waiting to hear if this is a bug scheduled for any quick fixes. However this is the way I will implement the fix if necessary. This code does the check that I think VFP should be doing internally:

      *&* code suitable
      *&* for your 5.x container base class,
      *&* to work around native VFP SetFocus bug
      PROC SetFocus
          LOCAL loControl
          FOR EACH loControl in THIS.Controls
              IF loControl.TabIndex = 1
                 loControl.SetFocus()
                 NODEFAULT              
                 EXIT
              ENDIF
          ENDFOR
      ENDPROC        

Notice that I do not issue the NODEFAULT unless a command with TabIndex = 1 has been found. It's possible to have a container object with no object having a TabIndex of 1, and in this case I feel that the default behavior is a reasonable choice.

A NODEFAULT always-issued, without regard for this problem, would leave the focus outside the container, so this alternative is unacceptable. Looking for the control with the container's lowest TabIndex value, no matter what it is, might be the best behavior for VFP to use internally if the bug is fixed, but creating this behavior ourselves is slow -- and probably won't give the form designer who's issuing the SetFocus() command the results s/he expects, anyway!

Collection Iteration in VFP 3.0

The code above is VFP 5.x code -- you can tell because of the use of FOR EACH to iterate through the Controls collection. The bug, however, exists in all versions of VFP, as far as I can tell.

To do the same job in VFP 3, you would have to use the following code. Although I have made an effort to speed this process by using the local variable loControl to hold references to the current member of the Controls collection (rather than using the expression THIS.Controls(liControl) repeatedly in the loop), this code will still be slower than the 5.x FOR EACH equivalent:

      *&* code suitable for your
      *&* 3.x Container base class
      PROC SetFocus
          LOCAL liControl, loControl
          FOR liControl = 1 TO THIS.ControlCount
              loControl = THIS.Controls(liControl)
              IF loControl.TabIndex = 1
                 loControl.SetFocus()
                 NODEFAULT              
                 EXIT
              ENDIF
          ENDFOR
      ENDPROC        

Tips and Philosophy from the MC Framework *5  :
When aliens abduct your system menu editing options

Far away and long ago on another planet with the quaint name of FoxForum, we often heard this question: "I've lost the editing keys in my applications!" Often this question was preceded by "arggh!" and much gnashing of teeth, and there were many other variations, but the answer was always the same.

"Grasshopper," the ancients would reply, "the editing shortcuts are the gift of the Fox menuing system. Simply add the appropriate system menu bars, such as _med_paste for Paste, and give them the appropriate menu shortcut keys, and you'll be right as rain."

Times have changed, but the common editing functions are still the fief of native system menu options in FoxPro. This being so, you'd think that the answer was still the same, but it isn't.

At first glance, you'd think that the answer had gotten easier. We have a new SYS(1500) function designed to let us invoke native system menu options whenever we want, for example:

   *&* for Paste:
   SYS(1500,"_med_paste","_medit") 

... but life isn't all that simple. The system menu bars still have to be DEFINEd, and the popup-in-question still has to exist. It doesn't have to visible on a system menu anywhere, but it has to exist. And even though SET SYSMENU OFF has vanished in the mists of time, and we rarely hear even DOS developers sniff "I have my own menus, I refuse to use _SYSMENU" anymore... there are more reasons than ever why it wouldn't exist.

Why wouldn't the popup or the relevant system menu bar exist, you say? You might, for example, wish your editing shortcuts to work on a modal dialog with no menu. You might be using a top form, which has a menu but (for extremely good reasons) will not use the native system menu popup names. You might put editing shortcuts on a right-click menu that is not defined until the first rightclick action occurs (so the key equivalents wouldn't be available until the first rightclick, obviously not acceptable).

Consequently, SYS(1500) notwithstanding, this is probably still The FoxForum Question That Refused To Die, and its clones on Internet Fox newsgroups prosper mightly, yea unto the VFP-fifth generation.

In the MasterClass Framework, I use the following approach to guarantee the availability of these options (this is a sketch of the method, which is more generic in the original):

   
     *&* in case the popup doesn't exist yet:
     IF CNTBAR("_MEDIT") = 0
        DEFINE POPUP _MEDIT
     ENDIF
     
     *&* re-defining the bar will not hurt,
     *&* no reason to check for it before doing so:
     DEFINE BAR _med_paste OF _MEDIT PROMPT "Paste" KEY "Ctrl+V"

     = SYS(1500,"_med_paste","_medit")

You knew it wouldn't be this easy

I am not paranoid, but I believe that there exists, in Agent Scully's immortal words, "a cult of lawlessness" among top forms. You can make the system menu options happen, using the code above, but if these menu options involve a system dialog (such as the Find window), you still need to make that dialog come up in the right place. Whether _SCREEN is OFF or Visible, whether you've DEFINEd the Find dialog menu option to exist on more than one menu bar or just one, whether you've used the native _medit popup name on your top form menu or not, you'll still have this problem.

The truth is out there. Actually, it's in my framework code <g>.

But it involves an important top-form principle you'd do well to consider with your full attention. So, in best TV cliffhanger tradition, I think I'll stop this section here. If you folks voice some interest in the topic, I'll bring it back for another episode in the next volume of FoxSpoken and give you the answer -- along with the supporting theory that makes the answer possible.

The Whipped Tip Dept:
Grid Partitions, or 'Ugh, the Sequel'

This particular heading covers information I've seen published or available to Fox developers that either needs amplification to be useful or is Just Plain Wrong. Warning: I will not always be good tempered in this section, or perhaps not even on the rest of the page. Sometimes I wonder why I gave up carrying a whip <g>.

Pertinent files: GRIDL2.ZIP updated Apr.97, ~7k The .ZIP file contains a sample form, which will create a cursor for its data, and a classlibrary -- I don't think you need any additional descriptive information beyond what I've written here.

In the last volume of FoxSpoken, I introduced a method of re-inventing grid partitions in a way that might actually work (unlike the native partitions) for people who really needed them.

Not much later, but after I created that version and uploaded the text here, I couldn't believe I'd bothered. I realized that there's a much easier way to create a controllable partition effect, while still having the multiple records and scrolling attributes of a grid: simply park the grid's controls in a container.

Calculated effects are (almost) as easy as calculated fields

The container serves as a "calculated effect" for the grid, putting various visual controls together in whatever way you like, much as you'd concatenate various expressions together in a grid column controlsource, or a BROWSE FIELD expression, to create a "calculated field". (This must have occurred to other people too, right? Why did nobody sent me a nasty piece of e-mail??)

"Lisa, my girl," I said to myself, "you must step outside your usual dominatrix role in the next edition of The Whipped Tip. Time for some serious autoflagellation here." I'd been really stupid, and I deserved to pay. (All in the service of providing the best possible FoxPro code to the public, y'understand.)

I created a small example of the effect I had in mind, which you'll find in files you can download using the link above. Unlike the original example, it uses no tricky code, no triggering of scroll events, in fact really no code at all other than assignments of appropriate sizes and controlsources for the various elements, once you know what data you want to pour into the classes. Cosmetic code, really.

Not a single line of meaningful code. Yet it fills the same requirements I set myself before: something that looked like a Browse on the left with an Edit of a memofield on the right and worked properly (no repeated fields, and so on).

Just as I was getting ready to write my mea culpa and beg your forgiveness here, fate intervened in the shape of the April edition of FoxPro Advisor. By a strange coincidence, here was another GridLock problem (see the article "Lock Columns in a Split Grid")...

... and hot d*mn, Dan LeClair has written even more code, even more kludgily, than I did the first time!

My eyes misted over. I meditatively licked the tip of my pencil and sharpened my whip. Or was that the other way around? No matter; I revised the sample form in my new example to show you a MESSAGEBOX() in its Init(), so you can decide whether you would like to see this instance to perform a Browse/Edit type split or the kind of Browse Lock split Mr. LeClair had in mind.

All the new example required, to show the second type of split, was a subclass of my gridcontainer that contained a child grid in it, rather than the editbox I used in the original.

Then I added a single straightforward piece of "meaningful" code for this child grid: I opened the table a second time for the recordsource of the child grid (in the example I do a SELECT, because the table is a cursor), and SET RELATION TO RECNO(). Not even an index tag, and it will work no matter what order you specify for the "real" grid. A trick you could have done even in dBase II, which lets Fox synch the record pointers in the two grids automatically.

No worries about what happens when you come back into the form from another form and experience wierd mouse behavior, no jerking or calls to DOSCROLL() or refreshing or bouncing the user from column to column, no KEYBOARDing (yes you will find all this in the Advisor version)...

All kludges are equal but some kludges are more equal than others

Dan and I share the whipping this time, I suppose. I hope we both enjoy the experience.

Seriously, my quick sketch of this technique does not handle all the cosmetic details -- I believe they are handle-able but I bet they will take code. For one thing, containers aren't perfect any more than grids are -- see information on a container bug elsewhere on this page. And, either way, it's still a kludge. There may be times and UI situations in which Dan's kludge makes more sense than mine.

I do think that in the long run your absorption of the advantages of the container in the grid is going to give you more mileage, for more different types of grid problems, than either of these two "browse-lock-managing" tricks.

But there is a more important lesson, beyond that somewhat-tardy epiphany I had about the container, and perhaps even beyond grids.

It has been said by just about everybody: FoxPro has a hundred ways to do just about anything. (We might increase that estimate by an order of magnitude for grids, without even breathing hard!) How do you figure out the "best way" to do something, given all the choice?

This is where most novices get into trouble, and where "everybody" lets them down, IMHO. We either settle for the first kludge that comes to hand, or we each argue for some solution for which we happen to have a special, completely irrelevant, fondness.

The truth is, we pay for every kludge, and every inappropriate solution, down the line. We pay in maintenance time, we pay in backpeddling when our cleverness interferes with another kludge on the same form, or we pay when Microsoft changes some behavior in the next version. The best kludge is always going to be no kludge at all.

I can already hear you snickering, but I'd like you to take this principle seriously, especially when it comes to interfering with native events: The best action is always going to be the least action. It will repay you to figure out how Fox might do this "naturally". If you can't, perhaps you could get a natural response by calling the Windows API, or taking advantage of an OCX or somebody's DLL.

When you're having what seems like unwarranted problems with an implementation, why is Fox fighting you so hard (and why are you fighting back)? It could be because you've hit an "under-implementation" (aren't euphemisms wonderful?). But it could also be that Fox is expecting something else.

Ask yourself what situation Fox, and its designers, are expecting; you may discover that they had a completely different scenario in mind than your present strategy but that, in their scenario, there are legitimate, painless ways of reaching your goal.

Tough love for smart developers

It's tough, I know. I spend most of my time telling you, as developers, that you can't impose your interface expectations or values on your end users. If the UI doesn't work for them, it doesn't work, full stop. Then I turn around and tell you that, as users, you have to deal with the fact that Fox's designers are imposing their expectations on you and that, if you're smart, you'll learn to deal with it.

Oh, grow up.<g>  Or go read The Art of War or something until this all starts making sense.

Class Browser Add-On for #INCLUDE files, fix

Pertinent files: ADDINCL.ZIP updated Apr.97, ~10k. A text file from this .ZIP, ADDINCL.TXT ~3k, is also available for viewing before download if you wish.

If you haven't learned about this utility earlier, please check out the information in the relevant past FoxSpoken article.

My original upload contained a slight bug, which could cause ADDINCL to give #INCLUDE file information inappropriately to some records... I hope I've fixed it now, by telling ADDINCL to ignore member object records in a VCX even when it's acting globally on the classes in that VCX.

Fix or no fix, remember that you are editing Fox system files directly when you use this utility. Please make a backup of your VCX/SCX before editing its records' #INCLUDE files in this manner! And all the usual caveats and disclaimers *3 apply.

MasterClass Framework  *5  Files and News

If you are a SoftSpoken client or a user of one of the conference-released versions of the MC Framework, check the MasterClass Framework page for the latest information about MC, including updated files.

Here are some highlights of recent changes:

  • Mature "top form" and "top form menu" philosophy and techniques. Top Forms are not one of VFP 5's best-thought-out improvements, but they are one of the most useful ones. Even toolbars, even WAIT WINDOWs can be made to behave properly with top forms, if you know what you're doing! There are many changes in the Framework classes and template menus to support this important feature, whether you're using one top form in place of _SCREEN or many.

  • MC Registry improvements and new process types. This change, along with a new Instantiate method of the controller, serves to insulate you from the details of class library type, location, and availability. Another new controller method gives you access to parameters you've passed to Registered forms and formsets before their Init()s (for example, in their Load()s).


  • A more intelligent, and re-entrant, GENMC.APP. GENMC, MC's delivery and application-startup device, now allows you to integrate your own application-startup-requirements more fully with both the standard MC framework application-specific data-driven and #DEFINEd values and the standard template files (for non-OOP application components). It also stores and re-reads the application-specific values of an existing MC application, so you can now re-run GENMC against existing values to re-generate EXTERNAL references and other project components, based on current values for this app.

Later, 

Lisa Slater Nicholls