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

Ideas For a Smart Grid
Eric Moore, January 1, 2001
One of the questions most often asked on the Universal Thread by beginning to intermediate users is ‘How can I use my control classes in my grids?’ Well, answering that is easy. Right-click on the grid, choose edit from the popup menu, click on the column in which you want your new control to go, cl...
One of the questions most often asked on the Universal Thread by beginning to intermediate users is ‘How can I use my control classes in my grids?’ Well, answering that is easy. Right-click on the grid, choose edit from the popup menu, click on the column in which you want your new control to go, click on your control in the toolbar, and drop into the grid on your selected column. Then go to the properties sheet, select the default control from the members list, click on the grid, and press delete. Then double check to the column’s currentcontrol property to make sure that your control is specified. Then just do this for every single column in every single grid in which you want your controls. For a decent sized app, this would only take a couple of days.

If you’re a good programmer (all good programmers are lazy), you’ve got to be asking yourself ‘There has got to be an easier way.’ Well, there is. VFP’s authors weren’t generous enough to give us ‘defaultcontrol’ properties for our grids that would allow us to specify our custom classes as the default controls for the grid, but that doesn't mean we can’t make them.

Since these controls can’t be automatically loaded at design time, the key is to write a class that will automatically add them at run time. This is done using ADDOBJECT() and REMOVEOBJECT(). All that is needed is to loop through each of the columns of the grid, remove the default classes, and add our classes. This is easily done using the grid’s columns collection. Grid.columns(n) is an array used to refer to the nth column in a grid. When looping through all of the columns, use the following syntax:

FOR I = 1 TO this.columncount && holds # of columns 
	…
ENDFOR
So to loop through the columns and remove the default textbox control and add an instance of your custom textbox class ‘mytext’ that displays important data in the Wingdings font and beeps on every keypress, you would loop through like this.
FOR I = 1 TO this.columncount && columncount holds the number of columns in a grid
	this.columns(I).RemoveObject(“text1”)
	this.columns(I).AddObject(“mytext1”,”mytext”)
ENDFOR
Many programmers want to use their own header class as well. This is not quite so simple, for one reason: VFP doesn’t allow us to visually subclass grid column headers. This means we have to define them ourselves in code. So before you go telling your grid to add columns from your custom column class, make sure you have defined that class using
DEFINE CLASS:

DEFINE CLASS “myheader” AS “header”
	Name = “header1”
	Fontbold = .T.
ENDDEFINE
A header is a member of a column just like a data-bound control, so you can remove and replace them just like a data-bound control.
FOR I = 1 TO this.columncount 
	this.columns(I).RemoveObject(“header1”)
	this.columns(I).AddObject(“myheader1”,”myheader”)
ENDFOR
If you put this code in the init of your grid class, you now have a grid that uses your textbox and header instead of the default controls. But what if you don’t always want that particular class in your grid? Some of your applications don’t require that data is displayed in the Wingdings font, and you don’t want to have to change code to change the class. The answer is to add a custom property to your grid class that stores the name of the control class that you want. Then in your init code, simply refer to the value of that property instead of the name of the class directly. Then after you drop the grid on a form, just fill out that property.

Taking this a step further, what if you want your custom textbox class for all the character fields in your datasource, but would like your editbox to display the data in your memo fields? And further still, you want all of your logical fields to be represented by a checkbox? AND you want to use your custom header class? Well, among other things, this requires more custom properties for your grid. While you’re at it, go ahead and create a property to hold the name of the class that you want to use to display each of the VFP data types. In my example, I’ll call the properties:

textclass numericclass logicalclass memoclass integerclass dateclass datetimeclass currencyclass generalclass myheader

Now you can specify a control for each datatype, and a custom header class. Granted, few of us have a separate control that we use for integers and still another for numerics, but this doesn't mean that we can’t use the same control for more than one data type.

The key to implementing different controls for different columns lies in checking the datatype of the controlsource before assigning a control to that column. We use the following syntax to do so:

Lctext = this.textclass
IF  TYPE(this.columns(I).controlsource) = "C" 
		this.columns(I).RemoveObject(“text1”)
		this.columns(I).AddObject(“mytext1”,lctext)
		this.columns(I).mytext.visible = .T.
ENDIF
But since we are dealing with more than one or two data types, this sort of statement is much better suited to a DO CASE statement. All this, keep in mind, will be happening for every column in the grid, so the above code will be inside the columns() loop.
lctext = this.textclass
lcnumeric = this.numericclass
lccurrency = this.currencyclass
lcdate = this.dateclass
lcdatetime = this.datetimeclass
lclogical = this.logicalclass
lcmemo = this.memoclass
lcgeneral = this.generalclass
lcheader = this.headerclass

if type('lcheader') = "C" &&Don't use a custom header if one has not been specified
	llUseCustomHeader = .T.
else
	llUseCustomHeader = .F.
endif

FOR lnCount = 1 to this.ColumnCount && Loop through all of the columns of the grid
	if llUseCustomHeader then
		lcColumn.RemoveObject("header1")
	      lcColumn.AddObject("myheader1",lcheader)
	endif
	lcColumn = this.Columns[lnCount]
	do case 
		case type(lccolumn.controlsource) = "C"
			lcColumn.RemoveObject("text1")
			lcColumn.AddObject("mytext1",lctext)
			lcColumn.mytext1.visible = .T.
		case type(lccolumn.controlsource) = "N"
			lcColumn.RemoveObject("text1")
		   	lcColumn.AddObject("mynumeric1",lcnumeric)
			lcColumn.mynumeric1.visible = .T.
		case type(lccolumn.controlsource) = "L"
			lcColumn.RemoveObject("text1")
               lcColumn.AddObject("mylogical1",lclogical)			
			lcColumn.mylogical1.visible = .T.		      
		case type(lccolumn.controlsource) = "Y"
			lcColumn.RemoveObject("text1")
			lcColumn.AddObject("mycurrency1",lccurrency)
			lcColumn.mycurrency1.visible = .T.
		case type(lccolumn.controlsource) = "D"
			lcColumn.RemoveObject("text1")
			lcColumn.AddObject("mydate1",lcdate)
			lcColumn.mydate1.visible = .T.
		case type(lccolumn.controlsource) = "T"
			lcColumn.RemoveObject("text1")
			lcColumn.AddObject("mydatetime1",lcdatetime)
			lcColumn.mydatetime1.visible = .T.
		case type(lccolumn.controlsource) = "M"
			lcColumn.RemoveObject("text1")
			lcColumn.AddObject("mymemo1",lcmemo)
			lcColumn.mymemo1.visible = .T.
		case type(lccolumn.controlsource) = "G"
			lcColumn.RemoveObject("text1")
lcColumn.AddObject("mygeneral1",lcgeneral)
			lcCcolumn.mygeneral1.visible = .T.
		otherwise
			lcCcolumn.RemoveObject("text1")
			lcCcolumn.AddObject("mytext1","Mytext")
	endcase
ENDFOR
For your controls to show in every row, and not just the selected row, you will have to set the column’s sparse property to .F. This means that the current control will be used to display data in all of the rows, and not just the active one. If you set the grid’s columncount at design time, and specify column properties from the form designer, then this is where you should do it. But if you just set the grid’s recordsource and let the grid configure itself at run time, then you can’t change column properties at design time. In this case you would want to set the sparse property inside the loop. How you do this is completely up to you, and it depends on if you usually set all column’s sparse properties the same way. If for you, sparse is either all columns or none, then you could add another custom grid property to set whether your columns will be sparse. On the other hand, if you want some columns set differently from others, you should be setting this property at design time.

However you decide to take care of the details, this should get you pointed in the right direction for creating a grid that uses your custom classes.

Eric Moore, Digital Equipment Corporation