Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Articles
Search: 

How to use a grid as a pick list (part II)
Rafael Copquin, June 1, 2002
In the April 2002 issue, I showed the way to use a grid as a picklist, and in order to make the example clear and readily understandable, I did not worry about code reusability. The example simply showed how to program methods in the form containing the picklist grid in order to enable th...
In the April 2002 issue, I showed the way to use a grid as a picklist, and in order to make the example clear and readily understandable, I did not worry about code reusability.

The example simply showed how to program methods in the form containing the picklist grid in order to enable the user to select a customer name from a customer table, and place the returned value in a textbox on the calling form. In this issue I will demonstrate the use of a generic class, that can be used with any table, for the same purpose.

Some considerations

If we want to browse any table and use it as a picklist, we have to tell our class a number of things:

  1. the table name and location
  2. the number of fields to show in the grid
  3. the names of the fields to show
  4. the column captions
  5. the indexes to sort the columns by
  6. what columns to return the values from
  7. the form caption for the grid, related to the table being shown
Now, VFP offers us several possibilities in respect of the manner to pass the above data to the class. I will mention the following:
  1. a list of parameters
  2. an array with the above data
  3. an object containing properties with the above data
I personally prefer to use an object, because it offers the flexibility of modification on the fly. In fact, during the development of this class, I had to add a different number of properties as the example evolved. Using an object, this is very easy. If on the other hand I had chosen to use a list of parameters, then it would have been more cumbersome and less clear. An array I ruled out from the start, simply because dealing with elements in an array by their number and having to keep track of them would have made my task heavier.

Hands on

Therefore I chose to create a custom class, placed in a prg file called PICK_OBJECT.PRG, as follows:

DEFINE CLASS pickobject AS custom
   Name = "pickobject"
   cTableName = .F.
   nFieldsToShow = .F.
   cField1 = .F.
   cField2 = .F.
   cField3 = .F.
   cField4 = .F.
   cCaption1 = .F.
   cCaption2 = .F.
   cCaption3 = .F.
   cCaption4 = .F.
   cIndex1 = .F.
   cIndex2 = .F.
   cFormCaption = .F.
   cIndex3 = .F.
   cIndex4 = .F.
   cFieldToReturn = .F.
ENDDEFINE
As I was developing the example I noticed that showing any more than three fields in the grid would have made the form too big. Nevertheless, for the sake of showing how flexible we can be with objects, I created properties to hold up to 4 fields and four index tags.

In my example, however, I show a form with two buttons, that call the grid with two different tables, one with only two fields to show, the other showing three fields.

Figure 1: The calling form

This form is very simple and has no mystery: It will simply call the grid, instantiating the pickobject, and after the selection is made, the chosen value will be shown in the textbox. The key to doing this is to place the following code in the click event of the button. In this example, I will show the code for the Choose a customer button.

oPicker = newobject('pickobject',"pick_object.prg")

with oPicker
    .cTableName = "customers"
    .nFieldsToShow = 3
    .cField1 = "account"
    .cField2 = "company"
    .cField3 = "address"
    .cCaption1 = proper(.cField1)
    .cCaption2 = proper(.cField2)
    .cCaption3 = proper(.cField3)
    .cIndex1 = "custnum"        && index tags from cdx file
    .cIndex2 = "custalf"
    .cIndex3 = "cusaddress"
    .cFieldToReturn = "company"
    .cFormCaption = 'CUSTOMER SELECTION LIST'
endwith

do form picker name oPickList linked with oPicker
with thisform
    .myTextBox.value = oPickList.cFieldToReturn
    .refresh
endwith
oPickList.release
oPickList = null
oPicker = null
As seen above, we instantiate the object, fill its properties with the data we want to show and send the object as a parameter to the form containing the grid. When a selection is made, the called form will place the value in its cFieldToReturn property, this property will be picked up by the calling form and the value placed in the textbox.

The picklist will be called with the keywords NAME and LINKED. This use of the DO FORM command was explained in detail in the last part of the article on the April 2002 issue, so please refer to that explanation.

In this example we instantiate the form with the NAME clause and create an object called oPickList. When the return value is read from the form, the release method is called and the form, which will be HIDDEN when the user makes the selection, will be definitely destroyed and erased from memory.

The last two lines of code make sure that there is no extant references to any of the two objects

Figure 2: The picklist form

It is a very simple form.

Figura 3: property sheet for this form

* Init method code for the form lParameter tPicker local lcTable,lcFormCaption ** fill local variables and form properties with ** passed-in object properties with tPicker lcTable = .cTableName lcFormCaption = .cFormCaption this.lnFieldsToShow = .nFieldsToShow this.lcCaption1 = .cCaption1 this.lcCaption2 = .cCaption2 this.lcCaption3 = .cCaption3 this.lcCaption4 = .cCaption4 this.cField1 = .cField1 this.cField2 = .cField2 this.cField3 = .cField3 this.cField4 = .cField4 this.cIndexTag1 = .cIndex1 this.cIndexTag2 = .cIndex2 this.cIndexTag3 = .cIndex3 this.cFieldToReturn = .cFieldToReturn endwith ** we open the table to be shown in the grid use (lcTable) in 0 shared noupdate alias PickTable select PickTable ** we determine the field to return and the tool tip text of the help label with this .cFieldToReturn = alltrim( "PickTable." + this.cFieldToReturn ) .caption = lcFormCaption .label1.ToolTipText = 'Double click or press enter on any row to make a choice' endwith this.set_grid() Unload event method code:
use in PickTable
(this is necessary because we use a private data session)

And finally, let us look at the set_grid method:

* Set_Grid method code
local lW,lcCol,lcFname,lcCaps,lnFieldSize,lnGridWidth
local lnHeaderWidth,lnColumnWidth
lnGridWidth = 0

with this.PickGrid

    .ColumnCount = this.lnFieldsToShow 
    .DeleteMark = .f.
    .FontBold = .t.
    .ReadOnly = .t.
    .ScrollBars = 3
    .RecordSource = "PickTable" 

    for i = 1 to this.lnFieldsToShow
        lW = alltrim(str(i))
        lcFname = "this.cField"+lW
        lcFname = &lcFname && get field name
        lnFieldSize = fsize(lcFname) && get field size 
        lcFname = "PickTable."+lcFname && get controlsource
        lcCaps = "this.lcCaption"+lW
        lcCaps = &lcCaps && get header caption

        with .columns(i)
            .controlsource = lcFname && set column's controlsource
            .header1.caption = lcCaps && set column's header caption
            .header1.alignment = 2 && center

            *!* determine column width 
            *!* it should show the full width of the header caption 
            lnColumnWidth = lnFieldSize * 10 
            lnHeaderWidth = len(.header1.caption) * 8

            .width = iif(lnHeaderWidth > ;
                     lnColumnWidth,lnHeaderWidth,lnColumnWidth)
        endwith 
    endfor

    .column1.alignment = 1 && right alignment

    *!* determine grid width
    for i = 1 to .columncount
        lnGridWidth = lnGridWidth + .columns(i).width
    endfor

    .width = lnGridWidth + 35
endwith

this.width = this.PickGrid.width + 50         && set form width

** center grid on form
this.PickGrid.left = ( this.width - this.PickGrid.width ) / 2
this.autocenter = .t.                   && center form on screen

How it works

The form receives the pickobject instance (oPicker) as a single parameter in its init event. All properties are assigned the oPicker object property values. And finally, a call to the set_grid method configures the grid and the form

The form properties contain the number of columns to show, and the fields to show. A series of calculations make the column widths large enough as to show the whole field, the headers centered and never wider than the columns that contain them, center the grid on the form and finally, center the form on the screen.

Each column has the following code snippets:

* textbox keypress event
LPARAMETERS nKeyCode, nShiftAltCtrl
if nKeyCode = 13
    thisform.cFieldToReturn = eval(thisform.cFieldToReturn)
    thisform.hide
endif

* textbox doubleclick event
this.keypress(13)

* each column header click event
set order to (thisform.cIndexTag1)
this.parent.parent.refresh

The call to the cIndexTag1 property would make the column be sorted by the first index tag. The cindextag property should be changed for each index to be used in the other columns. So, the column2.header1.click code would use thisform.cIndexTag2 and so on. Finally, instead of cluttering the form with help messages on an edit box, as I did in the April 2002 example, I decided to use a label and its tooltiptext property. When the user leaves the mouse for a few seconds on the label, a short help message tells him/her how to make a selection.

Figure 5: The picklist

The companies and address data in the above example have been modified, to avoid showing actual company names and addresses. (I used a real table from one of my customers, with due permission). You can download code and data here.

Conclusion

I have shown how to make a generic class using an object to pass parameters to a form, how to receive those parameters as one single object and how to select one field from one record in any table, using a grid.

The selection methods can be easily modified to enable the selection of more than one field.

However this is a limited example on how to use grids for general purposes. In my opinion grids are a great way to show or select data from tables. I will present in a later issue other ways to interact with data by using grids.

Rafael Copquin, Estudio Copquin
Public Accountant and Certified Internal Auditor; Fox programmer since 1987, starting with FPD and upgrading to VFP. Vast experience in conversions from FPD to VFP, specialized in business and accounting systems. Member of the Microsoft Users Group of Argentina's Board of Directors as Treasurer.
More articles from this author
Rafael Copquin, April 1, 2006
This class is especially designed to build cursor adapters by simply passing a few parameters, such as table name, cursor name, cursor adapter object name, updatable or not, empty or not, complex or simple select statement.
Rafael Copquin, April 1, 2002
Those of us who have ever programmed FoxPro DOS applications, are marveled and at the same time overwhelmed with the possibilities of enhancement in the appearance, the performance and the different ways of doing the things that were difficult, or outright impossible to do in DOS, th...
Rafael Copquin, October 1, 2002
Background Early in my development experience, back in the days of Fox Dos, I had to develop a better way to make invoices than the one I had been using. What I had been doing was using the SCROLL statement to make the screen move upwards every time a new item was added. Whenever the user w...
Rafael Copquin, March 1, 2006
In October 2002 the UTMag published the first article on the subject of making invoices with grids. Ever since, I have received numerous emails from readers asking me how to save the invoice thus created in the server tables. This article explains how to use cursor adapters to save the data.
Rafael Copquin, July 1, 2006
When we are examining data from a table, using generally a grid control, even if we did it with a browse window, we are in reality viewing a small part of a, perhaps, enormous quantity of data. We are literally viewing only the amount of records that fit in the grid or the browse window. If we wa...
Rafael Copquin, August 1, 2002
Background Many VFP applications deal with accounting data in ways that, in my opinion, are not very efficient, from the program standpoint, or very clear to the user. An example of this is an account statement. Now, an account statement can be any accounting statement that portrays the tran...
Rafael Copquin, June 5, 2013
When Microsoft discontinued development of Visual FoxPro, many would-be writers on VFP issues stopped sending articles to the UniversalThread and as a result, no more articles on VFP were published, with a few exceptions. Over the years, I kept receiving requests from many readers for a continu...
Rafael Copquin, March 1, 2007
Accounting and computer systems should not be tied to any one language in particular. You can design a computer system to be written in Fortran, Pascal, Cobol, VFP or Visual Basic.NET. However, the love of my life as far as computer languages are concerned is Visual FoxPro and that is why the rest o...
Rafael Copquin, September 1, 2006
The title sounds quizzical, doesn’t it? What has the Visual FoxPro menu system in common with XML? Not much. However, it is possible to use XML as a means to generate separate menus for every user. It consists of a series of case statements that would define pads, popups or menu bars, according to t...