Welcome To The Home Of The Visual FoxPro Experts  
home. signup. forum. archives. search. google. articles. downloads. faq. members. weblogs. file info. rss. print.
FRIENDLY GRID AND LEARNED SEARCH

One day when I was tired of building yet another regular form with elements for record searching, I decided to formulate requirements for a way to provide the simple and effective method of searching in all my forms. I came up with the following requirements:

- An object that provides a search must be a form floating above another form with needed data.
- The newly created searching object must be linked to the active form if it exists. If there isn't an active form, the object must not be created.
- Each form with Grid objects must have its own searching object (two open forms don't share the same search form).
- The searching object must be destroyed once the form has closed.
- It must find all objects of the Grid class that are flagged as being searchable in the form.
- It must determine a list of columns, each of which is flagged for being searchable, for each Grid object.
- It must provide the user with the list of the detected Grid objects and the list of their columns.
- It must provide the user with not only the searching service but also with record selection. (This was an insistent wish of advanced customers.) The established criterion of selection must operate even after the searching object has been closed but the form still is running.
- Linking the searching object with the data form must not require a serious modification of the existing forms.
- The searching object must easily link with forms in any application. (Later in this article, I'll show how to implement this class in an existing application on a sample of the Tasmanian Traders project.)


Most of the listed considerations are obvious ones. They don't require special solutions, but some of them deserve discussion.

There are two actors on this stage...
Naturally, the searching object can find all Grid objects in the active form—but it must select only available ones meant for this operation. In this phase, two players are appearing in the game. The first player is Mr. Search—he's scanning the form. The second is Mr. Grid—he's signaling Mr. Search with diligence. How and what Mr. Grid can divulge about himself? Certainly, Mr. Grid can have special properties for this purpose, but it's undesirable, as this approach will demand a definition of some subclass of the Grid class. That means that this implementation may require a replacement of the Grid objects in the existing forms. As they may have been working well for a long time, it's a poor idea to mess with them. Let's find another way. As rule, nobody uses the Comment property of the Grid object. However, this property can be a good flag, to be used as a signal about the ability of common business with Mr. Search. Furthermore, its value will look appropriate in the list of the grids that are available for searching.

Now about columns... There can be a large number of columns, and there isn't necessarily a good reason to do a search in each one. Similarly to the previous case, I'll use an unused property to flag the column as being available for searching—in this case, the Tag property. It can store a character value such as "Find" to indicate the ability of searching in this column.

The usage of existing properties for flags satisfies the requirements of a minimum modification in the existing forms.

Where are the magicians hiding?
The first time through, a problem of detection of all of the needed Grid objects in the form looked like a simple job. I thought that it was enough to perform the actions to find Mr. Grid:

1. Get an array of the objects that are placed on the form.
2. Get references to the objects of the Grid class.
3. Store these references in an array to use them later.

However, it turned out that the problem is much more difficult. It seems that Mr. Grid doesn't always like to sit on the surface of the form. Sometimes he prefers the depths of the object hierarchy inside the form.

As a result, it was necessary to create a method that recursively calls itself to sink into the object's hierarchy. Initially, it receives the reference to the form as a parameter and finds all objects that are placed in this level. Then the method scans those found objects with a goal to find the Grid objects with a non-empty Comment property. If a scanned object isn't a grid, then there's still a reason to suppose that it can contain such an object. In this case, it's appropriate to call this method recursively and to pass reference to the object.

If the scanned object is a grid, then it's necessary to record it in the array, which represents the list of the found grids. Every row of such a list stores two values:

- The name of the grid (from the Comment property of the grid)
- The reference to the Grid object

The code of the method looks like this:

PROCEDURE DetectGrids(loCurObject)

	LOCAL ARRAY laGrids(1)
	LOCAL lnElement, lcObjRef, loObjRef
	IF AMEMBERS(laGrids,loCurObject,2)>0
		FOR lnElement=1 TO ALEN(laGrids,1)
			lcObjRef='loCurObject.'+laGrids(lnElement)
			loObjRef=&lcObjRef
			IF UPPER(ALLT(loObjRef.BaseClass))=='GRID' ;
					and !EMPTY(loObjRef.Comment)
				IF THIS.NumberOfGrids=0
					THIS.NumberOfGrids=1
				ELSE
					THIS.NumberOfGrids=THIS.NumberOfGrids+1
					DIMENSION THIS.Grids(THIS.NumberOfGrids,2)
				ENDIF
				THIS.Grids(THIS.NumberOfGrids,1)=loObjRef.Comment
				THIS.Grids(THIS.NumberOfGrids,2)=loObjRef
				IF EMPTY(loObjRef.Tag)
					loObjRef.Tag='gc'+;
						ALLT(STR(INT(RAND(SECONDS())*1000000)))+;
						ALLT(STR(THIS.NumberOfGrids))
				ENDIF
			ELSE
				THIS.DetectGrids(loObjRef)
			ENDIF
		ENDFOR
	ENDIF

ENDPROC

Every ball has its own hole...
Perhaps the manipulation with the Tag property of the grid in the preceding bit of code gives rise to some perplexity. It's invoked to resolve the next problem...

It's a problem of record selection. Both Mr. Search and Mr. Grid should decide who will control the filter variable. Let's suppose that it's Mr. Search's privilege. He stores the filter clause into some variable, starts the filter, and goes away. Oops—our friends have some serious problems:


- If the name of the variable to search on is fixed, then Mr. Search can't do a selection on multiple forms. Several forms could each be open and each of them could have search forms open at the same time.
- The alternative is for Mr. Search to create a new variable with a random name at his every appearance, but it's frightful to think how many variables he'll create. The variable must stay in memory after Mr. Search disappears since the filter is still running.
- And what will happen if Mr. Grid wants to quit the stage before Mr. Search returns a result? (The search form is "always on top," but it's not modal That's right—he'll leave all filter variables in the labyrinth of infinite memory.)

It's quite another matter when Mr. Grid assumes responsibility for the filter variable:

- He can keep it in his pocket for as long as he needs.
- If Mr. Search will need the name of variable to set the filter, he can ask Mr. Grid about it—for example, with code like this:

PROCEDURE SelectedFilterVariable

         LOCAL loGridRef
         loGridRef=thisform.SelectedGridRef()
         return ALLT(loGridRef.Tag)

ENDPROC 

An implementation of the class into an existing project
I won't give a detailed description of the source code here because it's all included in the Download file (3.684 bytes) for this article and doesn't contain any tricks. It will be more interesting and useful to describe an implementation of this class into an existing project. I've chosen the Tasmanian Traders project as an example since every Visual FoxPro programmer has this one. An example of the searching class, which is working with the Customers form, is represented in the next example (see Figure 1).



The implementation of the class requires several simple steps:

1. Place the class definition file in the project's directories structure.
2. Modify the Customers form.
3. Modify the main menu of the application. Class definition and directories of the Tasmanian Traders project

The definition of the class is contained in the file GRIDSEARC.PRG file, which, in turn, is contained in the file 07TRUKHIN.ZIP. It's enough to unpack it and to place into the program directory of the project. This directory is C:\Program Files\Microsoft Visual FoxPro 7\Samples\Tastrade\Progs, which coincides with the standard installation of Visual FoxPro 7.0. However, different PCs can have different paths to this directory. If your samples are installed elsewhere, you can call the HOME(2) function to get the path to it.

Modification of the Customers form
After the class definition is placed in a suitable location, it's time for the next step—modifying both the Customers form and the Grid objects that are placed in this form.

First of all, it's necessary to create the form's FinderRef property and to initiate it with a NULL value. This property will store a reference to the searching object.

FinderRef = NULL

Second, it's necessary to add the StartFinder() method to the form. It will create an instance of the searching class.

PROCEDURE StartFinder

	thisform.FinderRef=NEWOBJECT('GridSearch', 'PROGS\GridSearch.PRG','',THIS)
	LOCAL loRef
	loRef=thisform.FinderRef
	loRef.Show()

ENDPROC

The following step is a modification of the grdList object, which is placed in this form. It may be done like this:

1. To override the Destroy() method of the grid, use the following method. It will release the filter variable:

PROCEDURE Destroy

	IF TYPE('thisform.FinderRef')='O' and !ISNULL(thisform.FinderRef)
		LOCAL loRef
		loRef=thisform.FinderRef
		loRef.RELEASE()
		thisform.FinderRef=NULL
	ENDIF
	DODEFAULT()

ENDPROC

2. To identify the grid as searchable, with a more friendly name that the user will see in the Search form list box, replace the Comment property of the grdList object with the "List of Customers" value:

grdList.COMMENT="List of Customers"

3. Replace the Tag property with the "Find" value for all columns, which have the "Character" datatype and which are needed for a search. For example:

grdList.grcName.TAG="Find" 
grdList.grcContactName.TAG="Find" 

Modification of the main menu of the application
The main menu of the project is defined in the file MENUS\MAIN.MNX. It's enough to add a new item—for example, "Search in Active Form"—in the Edit menu and to write a procedure for this item.

LOCAL loActiveForm, loMrSearch
IF TYPE('_screen.ActiveForm')!='O' OR ISNULL(_screen.ActiveForm)
	RETURN
ENDIF
loActiveForm=_screen.ActiveForm
IF IsMember(loActiveForm,'StartFinder')
	loActiveForm.StartFinder()
ENDIF
RETURN

FUNCTION IsMember(loObject,lcMember)

	LOCAL llPresent,lnNumberOfMembers, lnIndex
	llPresent=.F.
	LOCAL ARRAY laMembers(1)
	lcMember=UPPER(ALLT(lcMember))
	lnNumberOfMembers=AMEMBERS(laMembers,loObject,1)
	IF lnNumberOfMembers>0
		FOR lnIndex=1 TO lnNumberOfMembers
			IF UPPER(ALLT(laMembers(lnIndex,1))) == lcMember
				llPresent=.T.
				EXIT
			ENDIF
		ENDFOR
	ENDIF
	RETURN llPresent

ENDFUNC


So all modifications in the project are made. It's now time to type in the command window a magic "DO MAIN" command to start up Tas Traders, and then open the Customers form.

Conclusion
This class isn't a panacea for all problems that appear in connection with a data-searching requirement, but it can be used easily and with success in most cases. All that's required is that you create both a Form class and a Grid class (shown in Figure 2) that include all properties and methods described earlier, and you're all set for less work in creating your search forms.



Download code
You can download the class source code here. The download is a zipfile. Its size is 3.684 bytes.

This article was first published in the July, 2002 issue of FoxTalk

ABOUT THE AUTHOR: VLADIMIR TRUKHIN

Vladimir Trukhin Vladimir Trukhin is Visual FoxPro developer and author. He has been developing program systems and applications since 1983. He specializes in software system development, user interface design, object oriented programming, developer support, training and other services. Vladimir has written for FoxTalk magazine. Visual FoxPro is his everyday tool and assistant. You can contact him by e-mail at vlt@votges.ru. You can find additional information about Vladimir on home page at http://www.geocities.com/vhpcg/resume.html.

FEEDBACK

maksood ali @ 3/19/2014 8:49:29 AM
we are unable to download your zip file

maksood ali @ 3/19/2014 8:50:15 AM
ERROR IS SERVER NOT FOUND

vicos @ 8/12/2014 11:09:42 AM
The requested URL /downloads/grdsch.zip was not found on this server.



Your Name: 
Your Feedback: 

Spam Protection:
Enter the code shown: