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

VFP Grids, Part I
Vlad Grynchyshyn, August 1, 2001
What is the grid and when to use it? Grid control is a set of the VFP objects that allow representing data in a grid-like scrollable list. Grid consist of the grid object itself and a set of the columns. Each column must have a header object and a control that represent a data in the grid col...

What is the grid and when to use it?

Grid control is a set of the VFP objects that allow representing data in a grid-like scrollable list. Grid consist of the grid object itself and a set of the columns. Each column must have a header object and a control that represent a data in the grid column. The control in grid column is used to display and edit the data. Grid appears as a rectangle with the linear grid, headers at top of each column, scrollbars and some other useful things, like record marks, delete marks, split bar etc.

Really, representing of data is not as free as, for example, in the Excel sheet. Grid requires the VFP record source (alias) to display something. Grid column data type is hard to make different for each row. Grid row have the same height for all rows, and column have the same width for all rows in grid. There are also certain other limitations that are quite strange and weird unless we remember that grid is really is a control which code is based on the browse window that comes from earlier versions of the FoxPro. This fact makes an answer to many 'Why' about grid strange things and behaviours. Despite all troubles, grid is quite useful control and it is popular between VFP programmers, so there are also a lot of workarounds and solutions for grids that allow breaking the limitations and making really great things with grids. Limitations or strange things should not be a key thing in the decision whether to use grid control.

Grid control is useful for displaying (browsing) data in a compact form (huge amount of data per one page). Grid is good as a popup or separate page list for search and navigation. It is bad idea to use grid as a persistent control for navigation through data, because grid usually takes a lot of space. Many applications have grid as a main controlling form - complex grid with a lot of functionality that brings child forms for data editing.

Grid is useful for any kind of regular read-only data. It is bad idea to use grid for data editing. Data editing in grids is good and easy to organize for administrative applications only. Data editing in grid has a lot of troubles when trying to break through VFP grid limitations and weirdness. Anyway, such forms, as invoice or order items list, are better and more comfortable for users when represented as grid for editing. Such forms are an exception; do not put the grid on every form just because it is simple to edit data in it. When you do so, you will quickly find self in trouble because grid is such a complex control that easy can went out of control. You will require then put a lot of effort to figure out problem, find a solutions and fix further problems. Usually data are edited using a form with controls for each field and a navigation bar for moving through records, data saving/reverting and some other buttons specific for that form. Making such form as a grid is a way to spend a lot of time. Well, if you have that time and like to play around something weird, grid is an excellent control for spending time to find solutions and workarounds.

Grid automatic re-binding of columns

On of the strange things in grid that appear quite often is automatic change of column control sources. You can find suddenly that grid columns display other data than you specified in the columns ControlSource property. In addition, columns displayed in the order of the fields in the record source despite you rearranged columns. Why is that?
Because RecordSource property of grid is changed in design time. After that all ControlSource values are cleared. Make a backup of these values for such case, usually in the Comment property of the column.

Because RecordSource property of grid is changed in run-time. Well, if you want to do this, save all control sources, then restore.

Possible cause is that grid is reconstructed. Next chapter is about this.

It is not that sudden as the side effects of this behaviour. Usually there are no differences, unless grid has some complex functionality and architecture. For example, column values displayed by expression - expression will be lost and field is just displayed. ID field (key field) column displayed in grid is also bad thing for users. The most dangerous is when you have some unusual controls in the grid. When column has checkbox control, but grid decide to use character type field as a control source for that column, VFP throws error about data type mismatch: 'Type is not supported by control'. Despite all above, there are situations when record source should be altered anyway, for example, for avoiding grid reconstruction behaviour.

Following are code samples of saving the control sources of all columns and restoring them using Comment property of the column.

&& backup the ControlSource of each column
with {grid}
   local nColumnIndex
   for m.nColumnIndex = 1 to .ColumnCount
      .Columns(m.nColumnIndex).Comment = .Columns(m.nColumnIndex).ControlSource
   endfor
endwith

&& restore the ControlSource of each column
with {grid}
   local nColumnIndex
   for m.nColumnIndex = 1 to .ColumnCount
      if !empty(.Columns(m.nColumnIndex).Comment)
         .Columns(m.nColumnIndex).ControlSource = .Columns(m.nColumnIndex).Comment
      endif
   endfor
endwith

In above code {grid} is a reference to the grid object.

It is nice idea to make these code snippets as a methods of the grid class. It is good also to call the restoring of control sources in the Init method of the grid class for case when ControlSource values are lost because record source change in design time.

Significant note: do not do any refreshing of the visible controls or grid on the form before full restore of the control sources. Otherwise you will meet a problem with the error message like 'Type is not supported by control' in case you use custom controls in grid columns. This because after spoiling control sources incorrect field types might be used for column as already mentioned before.

Grid automatic reconstruction

Have you ever found a situation when your grid doesn't want to behave as you directed in design time? Custom controls in columns lost? Code of columns, headers or controls event is not running? Grid reconstruction behavior is a complete removing of all grid controls and columns and creating them again using default VFP controls and property settings. These causes lose of all methods, property settings and objects in columns and lose of all column objects. Column CurrentControl property is reset to the default text control. Custom headers are lost. Usually it is a disaster and no clue what is happening with grid. It happens in several cases described below with workarounds.

1. Grid reconstructs self always when RecordSource alias closed. If this is a view, reconstruction usually does not happen when you requery view. If it is SQL statement, it happens when you assign another SQL statement or just close alias used to store results of the query for grid. It also happens when you use SQL Pass-Through to query data again into the alias that used as a record source of grid even when you use it by the same way as view.

To avoid reconstruction when refreshing grid record source, you require assign empty string (not a one space - " ", but empty string - "") to the Record source before any of grid record source refresh actions described above. Check your code accurately if you do that in correct order or any other thing does not spoil the correct order of refreshing process. (You can track the code line that causes reconstruction by putting the breakpoint or SET STEP ON command in the BeforeRowColChange event of the grid.) After record source refresh, assign record source to grid again. Reconstruction in such case does not happen, however, automatic re-binding of columns happens because record source assignment. Following is a sample of how to fix that by little of code.

* store the control sources here ........... {grid}.RecordSource = "" * do refreshing of the record source ........... * restore record source {grid}.RecordSource = "{RecordSourceName}" * restore the control sources of columns here ...........

In above code {grid} is a reference to the grid object, {RecordSourceName} is a name of the alias used as a record source or SQL statement. In generic case with SQL statement as a record source alias name could be get from the ControlSource of the column because VFP always change it to be represented in form 'Alias.FieldName' despite in design time only field name is specified. Above way is also useful to change the SQL statement for grid.

Significant note: do not do any refreshing of the visible controls or grid on the form after statement 'RecordSource=""' and up to full restore of the control sources. Otherwise you will meet a problem with the error message like 'Type is not supported by control' in case you use custom controls in grid columns. This because after spoiling control sources incorrect field types might be used for column. For example, when you have a checkbox in the grid column, after refresh of record source column with checkbox often get a character field control source. If then you refresh a grid, you will get an error or something weird might happen like crashes or bad refresh.

Another approach to prevent grid reconstruction is to use BeforeRowColChange event of the grid. BeforeRowColChange event is fired each time when grid is going to be reconstructed. It happens in any case include when grid alias closed, SQL Pass-Through cursor requeried etc. despite grid visibility, focus and grid configuration. The most amazing is that putting NODEFAULT in this event for duration of data changes prevents grid reconstruction at all. However, in such case grid can show weird behaviour when done inaccurately.

thisform.GridRefreshing = .T. && tell all grid controls that
                              && grid data will to be requeried
... do the data requery
thisform.Grid.RecordSource = thisform.Grid.RecordSource
thisform.Refresh && or grid refresh
DOEVENTS && if needed - just test without this command
&& after this moment grid stops to reconstruct self>
thisform.GridRefreshing = .F.

In the BeforeRowColChange of grid class event put following:

if PEMStatus(thisform,"GridRefreshing",5) AND thisform.GridRefreshing
   nodefault
   return
endif
You can put above code in the grid class so this functionality will be generic, include making the GridRefreshing property as a property of your grid class. Sometimes setting focus outside of grid and set it back to grid is required, because the current cell in grid might show asterisks ('*******') when avoiding reconstruction this way.

Unfortunately, there are no way to know the reason why BeforeRowColChange event is called to distinguish if it is called for reconstruction or it is called for movement between cells or grid got focus. Use a flag for that as in the sample. If you have a time, you can also make a grid class with the transparent cover shape that catch all mouse events and forward them to the grid after pre-processing. This way grid will know that BeforeRowColChange is fired because row or column is going to change. This requires also catching of the KeyPress event (better do it on the form using KeyPreview=.T. for form). Finally, grid should be put into container to catch the moment when grid gets focus (BeforeRowColChange is fired also when grid gets focus).

Both approaches have a significant disadvantage: additional code required in all places that cause reconstruction. When such places are located in several forms and classes, for example, requery some alias using SQL Pass-Through functions, it is quite hard to locate all these places and it requires a reference to the grid for reconstruction avoiding. It also causes automatic re-binding of columns that sometimes is not acceptable. Grid used often to display data dynamically from view, so it requires to be refreshed by another data. Grid reconstruction does not happen when view is requeried. However, when moving application to remote views programmers often decide to use SQL Pass Through functions to work with data. In such case each requery of the alias by SQL Pass-Through function will cause reconstruction. So, the main programmer mistake here also is just requery view and leave that code as is. Its a single command, so programmers often do this in many places without aware that they're doing something incorrect. When moving to SQL Pass-Through functions all requery statements are replaced by appropriate commands. In addition, when reconstruction behaviour observed, programmer starts to find all places where this alias is requeried. It might be in many places across forms and classes that start to be a big problem. Tip: put data requery, close, ope and re-open (and all other actions with data) in one place - class method or function, and use reference to object of that class to call appropriate functions. Always suggest that any data function might require in future some additional code, even when it is as simple as a requery of view. This way you will help yourself to save a time when find all places where some action done with data in case you need to modify something. Grid reconstruction is one of such cases that you cannot avoid when it appears. For example, make a grid class that will also have all methods require handling the data displayed in it. Then make a reference to object of that grid class. Now in all places that require data refreshing it will be a single function call. Well, probably you're rarely making an application that requires such tremendous thing…

Being frustrated by grid reconstruction behavior, programmers sometimes make create and maintain the cursor as the Record Source of the grid, then refresh such cursor by data deleting and copying. In case you already have such cursor, it is not hard to delete all records from cursor and add them again.

2. Reconstruction happens when grid is initialised and record source property is empty or record source does not exist (alias is not open). In this case grid reconstructs self and use current alias as a record source if opened (or keep self empty if no alias opened in current work area, but all columns destroyed anyway). If you need to open record source in some other event than Load event of the form (before grid initialising), use following techniques.

In the Load event of form create an empty cursor with the same structure as a record source for grid; record source property of grid should use that empty cursor alias name. Then, when you open real data, assign empty string to grid's record source, open data and then assign real data alias again as described for the case A. Another workaround is to put an invisible custom control that will create empty cursor in its Init event. However, assure that Init event of that control fires BEFORE Init event of the grid, otherwise reconstruction will happen. Second approach is to add grid to the form on the fly. Make a grid class and do not put it onto the form in design time. In the Init event code put an AddObject method call for container that should contain that grid to add the grid after required alias is ready for use.

3. Grid reconstructs self when column count changed to 0 or -1. I hope you never do this, do you? ;) Anyway, it could be used in the simple administrative form that allows opening any table and browsing it in a grid. However, because reconstruction, such grid can have limited functionality, or all grid functionality should be put in the classes added to grid in run-time just after reconstruction.

4. Grid reconstructs self when record source goes out of scope. This usually happens when record source assigned in one data session, but grid really initialised in another data session, so when it tries to refresh self, another data session used where record source does not exists. This may occur also in other situations when programmer uses data sessions switching extensively.

Reconstruction could not be prevented by making the additional reference to each grid column - columns are just detached from grid. In addition, making grid with columns already defined in the class does not prevent reconstruction too - this is exception for the rule that child objects of the container class could not be deleted when using that class on the form.

Another popular approach to eliminate the grid reconstruction problem at all is dynamic grid creation. Make a custom grid class with all your code and columns definitions. When requery data, remove grid control from form, requery data, then add grid to form again in run-time and position it again. This requires handling the adding of grid first time, set some properties of grid etc etc. However, this approach is bad because requires creating the class for each particular grid on each form, know that class name, have the routine for each grid - a lot of code that could be avoided.

You can also create grid object in run-time completely and populate it by the custom controls using code. However, after grid reconstructs self, you need to add these custom controls to grid again. This approach used in case when grid reconstruction is not avoidable, for example, in the administrative programs - to show any table content in the same grid, but also allow some functionality in grid like edit boxes to see memo fields, sorting by click on headers etc.

Lock (freeze) column to be always visible despite scrolling

When require lock (freeze) certain column or columns to be always the leftmost despite scrolling, the first thing comes to mind is to use the split grid. Left side remains not scrolled when right side user can scroll. However, it looks quite weird for users, that additional scrollbars… etc etc.

There is a nice property LeftColumn in the grid that is a column order number of the leftmost column currently visible. When grid is scrolled horizontally, this value changes. We can use this to froze column in the Scrolled event:

if nDirection>3
   this.Columns(1).ColumnOrder = this.LeftColumn
endif
And, for keyboard scrolling, in AfterRowColChange add a line:
this.Columns(1).ColumnOrder = this.LeftColumn
First column now appear always the leftmost!

Grid tips

The first tip I want to make is related not only to grids. Search the UT site for information about grids. You will find a lot of useful things! And you can always ask any question there.

When use SQL SELECT statement as a record source, always add 'INTO CURSOR …' at the end of this statement, otherwise browse window will appear at the form loading or just when that query runs.

You can use the expression as a column source! Just make column as read-only and specify the expression to display in the column ControlSource. Unfortunately, the result of expression should be the same type for all grid rows; otherwise you can get weird formatting, asterisks or some other strange effects. For different data types better use the container in grid column…

To add new control to grid column, select the column in the combobox in the properties window, click in the edited form header to select it, select required control and drop it onto the grid. To remove control, select that control in the properties window, select the form by clicking on the form header, then hit 'Del' button.

When there are no horizontal grid lines, the grey lines on their place when Sparse=.F. or HighlightRow=.F. are just whatever is under the grid (usually the form surface). Put the white shape under the grid and they will become white.

You can change the mouse cursor for column by setting the mouse pointer in the MouseMove event of the column.

Grid warnings

This chapter provides warnings for programmers that use grids. There are a lot of them…
Remember that changing of record source property in design time require remembering of the control source of each column. Just after record source change they're all cleared! It is good idea to make a copy of the ControlSource property to the Comment property in the column.

Do not leave Record Source property empty before grid initialising.

Events and properties of the control in grid column are used only for current cell when column sparse is .T. When Sparse is .F., properties are used for displaying, but events are used only for current cell, anyway.

Scrolled event is fired only by scrollbars. When grid is scrolled by keyboard or programmatically in certain cases, Scrolled event is not fired.

RelativeRow and ActiveRow properties are available only when grid is focused. ActiveRow is always zero when current record is out of the visible records.

VFP Grids, Next Parts

The upcoming articles should discuss the events model of grid, what is a cover shape and how to use it, the power of Dynamic* properties of column, exact positioning over grid cell, grid column and header tricks, sorted grids, sorting indicator and more tips and warnings!
Vlad Grynchyshyn, Soft Serve
Vlad Grynchyshyn is senior developer and project manager at the Ukrainian company "Soft Serve Ltd.", that provides custom-developed software solutions in several areas with use of wide range of development tools and languages. Vlad works in the MS Solutions department of the company, and has experience working with many MS development tools, components, utilities and documentation. Most experience is related to Visual FoxPro, MS SQL Server, Visual Studio and Visual Basic. He is active member of Visual FoxPro and SQL Server community, author of several articles for Universal Thread WEB site and other VFP community forums. He was MS MVP 2001-2002. He has a MS MCP certificate in Visual FoxPro area.
More articles from this author
Vlad Grynchyshyn, May 28, 2002
VFP does not support 256-color icons as project's icon (icon displayed for EXE in Windows Explorer). But 16-color icons usually looks ugly and too simplified. Here is an approach that allows organize 256-color icons for VFP EXE application. The approach is based on use of Resource Hacker appli...
Vlad Grynchyshyn, February 8, 2002
There is a situation often when we need some way to position a text cursor in the text of Editbox or RTF ActiveX controlwhen right click on the control. For example, we want to display a shortcut menu on right click. In menu, user select an option that insert some text into the control on place of c...
Vlad Grynchyshyn, March 16, 2001
Have you ever found a situation when your grid don't want to behave as you directed in design time? Custom controls in columns lost? Code of columns, headers or controls event not running? Read this FAQ then. Grid reconstruction behavior is a complete removing of all grid's controls and columns a...
Vlad Grynchyshyn, September 21, 2001
In VFP there are no direct way to assign code to the method or event in run-time mode. However, some workaround is possible with use of additional, 'hook' class. The sample below describes the approach. It is possible to make it generic. The approach is very useful to catch, for example, events of t...
Vlad Grynchyshyn, February 1, 2002
In VFP6 Grid value of the ToolTipText property of controls is displayed only for the entire grid or for current control when mouse is over it. Becuase there are a lot of parts in the grid, it is often useful to display tool tip for different parts separately. Unfortunately, VFP interprets grid as a ...
Vlad Grynchyshyn, February 8, 2002
The approach is based on a simple principle. LeftColumn property of the grid represents a column that is currently the leftmost visible column in the grid with current horizontal scrolling. As we scroll the grid horizontally, this property changes. We can assign this number to the ColumnOrder proper...
Vlad Grynchyshyn, October 6, 2000
Windows form caption properties stored in the user preferences. You can change them using Windows Control Panel\Display\Appearance. To get these parameters programmatically in VFP, you can use following routine as a sample.
Vlad Grynchyshyn, February 6, 2001
After some investigation, I managed to make a collection-like property of object. The following is a simple sample of how to organize collection-like access (rough, need to improve it to work as real collection). It is just to show an approach.
Vlad Grynchyshyn, June 20, 2001
This article describes how to configure grid to appear as a list similar to the list box or the drop-down list of combo box. It is for appearance only. All other things are as usual for any grid. This is useful for case when need to replace list box by a grid to display more records, because list...
Vlad Grynchyshyn, April 9, 2002
The SQLEXEC() function in VFP allows downloading of data from any data source through ODBC. Unlike remote views, SQL Pass-Through approach does not provide default (automatic) settings in the returned cursor to update data on the server after changes in the cursor at the client side. Anyway, it is p...
Vlad Grynchyshyn, September 4, 2001
There is a little problem for query to get something like a sum() for a character field to concatenate character values from all records in the group. Here is described solution. The approach below have an limitation to max 30 records in group for concatenation. Test it if it will work for more r...
Vlad Grynchyshyn, February 5, 2001
Suggest Grid1 is a name of grid required to put onto the form, grid should contain 10 columns an display data from the MyAlias alias. Grid should contain a combo box in the second colunm with the name Combo1. Following sample routine will allow you to see a generic idea of how to put grid on the for...
Vlad Grynchyshyn, January 26, 2001
I have seen many questions about sorting of a grid by clicking on its header and proper refreshing of grid after sorting. There is a reliable way to sort (index) grid cursor and properly refresh the grid without change of the record number AND with very good looking. I made this in my grid class and...
Vlad Grynchyshyn, January 11, 2001
Text field on SQL Serevr is not good to store binary data because it is not reliable and often SQL Server corrupts binary data in text field. varbinary type could be too short. There are certain difficulties to use image field on SQL Server to store and read binary data in VFP. Image field on SQL...
Vlad Grynchyshyn, September 1, 2002
Hussars Very often VFP applications are associated with Microsoft Office programs. Users want to use the formatting abilities of MS Word to create documents, send letters using Outlook and to carry out statistical analysis of data in Excel. "Smart tags" is a new technology used in O...
Vlad Grynchyshyn, February 5, 2002
SQL Pass Through commands often can take a lot of time for running and returning the result set to the client. It is often usefult to show a progress bar in case of such lengthy process. Also, FetchAsNeeded option, available for views, could be very useful for SPT cursors as well. However, how we ca...
Vlad Grynchyshyn, September 1, 2001
In this part, I will discuss about grid columns and header tricks on how to make development with grids go faster. I will also discuss about detecting the exact positioning over grid header or cell. As usual, I have included some tips and warnings. Grid column and header tricks - how to make de...
Vlad Grynchyshyn, October 1, 2001
In this part, I will discuss about grid sorting by header click, grid sorting marker (indicator) and resizing of grid column by double click to the width of data. As usual, I have included some tips and warnings. Grid sorting by header click Last time, sorting of information by a click on...
Vlad Grynchyshyn, October 1, 2003
This is the monthly column of Vlad Grynchyshyn SQL Server Tips. This column includes four monthly tips in regards to SQL Server which covers various topics either discussed online on the Universal Thread or sent as a contribution by another person.
Vlad Grynchyshyn, November 1, 2003
This is the monthly column of Vlad Grynchyshyn SQL Server Tips. This column includes four monthly tips in regards to SQL Server which covers various topics either discussed online on the Universal Thread or sent as a contribution by another person.
Vlad Grynchyshyn, December 1, 2003
This is the monthly column of Vlad Grynchyshyn SQL Server Tips. This column includes four monthly tips in regards to SQL Server which covers various topics either discussed online on the Universal Thread or sent as a contribution by another person.
Vlad Grynchyshyn, September 1, 2003
Vlad Grynchyshyn is senior developer and project manager at the Ukrainian company "Soft Serve Ltd.", that provides custom-developed software solutions in several areas with use of wide range of development tools and languages. Vlad works in the MS Solutions department of the company, and has experie...
Vlad Grynchyshyn, August 1, 2003
Vlad Grynchyshyn is senior developer and project manager at the Ukrainian company "Soft Serve Ltd.", that provides custom-developed software solutions in several areas with use of wide range of development tools and languages. Vlad works in the MS Solutions department of the company, and has experie...
Vlad Grynchyshyn, July 1, 2003
Vlad Grynchyshyn is senior developer and project manager at the Ukrainian company "Soft Serve Ltd.", that provides custom-developed software solutions in several areas with use of wide range of development tools and languages. Vlad works in the MS Solutions department of the company, and has experie...
Vlad Grynchyshyn, June 1, 2003
Vlad Grynchyshyn is senior developer and project manager at the Ukrainian company "Soft Serve Ltd.", that provides custom-developed software solutions in several areas with use of wide range of development tools and languages. Vlad works in the MS Solutions department of the company, and has experie...
Vlad Grynchyshyn, February 1, 2003
Vlad Grynchyshyn is senior developer and project manager at the Ukrainian company "Soft Serve Ltd.", that provides custom-developed software solutions in several areas with use of wide range of development tools and languages. Vlad works in the MS Solutions department of the company, and has experie...
Vlad Grynchyshyn, January 1, 2003
Vlad Grynchyshyn is senior developer and project manager at the Ukrainian company "Soft Serve Ltd.", that provides custom-developed software solutions in several areas with use of wide range of development tools and languages. Vlad works in the MS Solutions department of the company, and has experie...
Vlad Grynchyshyn, March 1, 2003
Vlad Grynchyshyn is senior developer and project manager at the Ukrainian company "Soft Serve Ltd.", that provides custom-developed software solutions in several areas with use of wide range of development tools and languages. Vlad works in the MS Solutions department of the company, and has experie...
Vlad Grynchyshyn, May 1, 2003
Vlad Grynchyshyn is senior developer and project manager at the Ukrainian company "Soft Serve Ltd.", that provides custom-developed software solutions in several areas with use of wide range of development tools and languages. Vlad works in the MS Solutions department of the company, and has experie...
Vlad Grynchyshyn, April 1, 2003
Vlad Grynchyshyn is senior developer and project manager at the Ukrainian company "Soft Serve Ltd.", that provides custom-developed software solutions in several areas with use of wide range of development tools and languages. Vlad works in the MS Solutions department of the company, and has experie...
Vlad Grynchyshyn, December 1, 2002
Vlad Grynchyshyn is senior developer and project manager at the Ukrainian company "Soft Serve Ltd.", that provides custom-developed software solutions in several areas with use of wide range of development tools and languages. Vlad works in the MS Solutions department of the company, and has experie...