Sue Cunningham emailed me earlier this evening and told me about a cool use she had found for the backstyle_access technique I had explained in an earlier blog entry. If you don’t have time to wade through that blog entry here’s the condensed version: I showed that by using the backstyle_access method of a control contained in a grid’s column you can do just about anything you want since that method will fire as each visible row in the grid is painted. There really isn’t even a need for all those DynamicWhatever methods that the MS Fox Team added to the column object when you use backstyle_access. It allows for much more complex operations and visual effects to be performed within the grid rows as well. Anyways, Sue emailed me and said that she had devised a relatively simple way to produce bar graphs. Something that looks a lot like the recent gradient bars that Calvin Hsia blogged about (see screen shot on Andrew MacNeill’s blog), but much simpler (no need for GDI+) and minus the gradient.
I decided to give it a try
Sue didn’t provide the code to her solution, but based on the explanation she had given in her email, I felt I had the basic idea and decided to try my hand at it and see what I could create. The result (see screen shot below) was as pleasing as it was easy. Sue also said that she was encountering some unpredictable results when placing the grid within a container control, so I also produced an example (Example2 form in the source that can be downloaded below) that also uses this technique to display graphs on two separate pages of a pageframe. I didn’t run into any of the inconsistent results (using VFP 9.0 SP1 here) that Sue did, so I’m not sure what is happening for her there. The technique appears to work flawlessly (Note: the bars will get truncated and/or have paint problems if I allow the grid to show both panels at the same time and the left panel is a CHANGE panel. The Change panel is the only place I saw this unwanted behavior, other than that all is right).
What all is involved
I have a grid subclass that contains two columns. The first column is used for the description of whatever the item is I am graphing… you can think of it as the axis label I guess. The second column contains a pretty simple container subclass that contains a shape and a label subclass. The shape is used to produce the bars and the label is used to show the value that was graphed. The magic for all of this is in the container control (that is in column2) backstyle_access method. Here’s the actual code which is quite short…
LOCAL lnTotalTicks, lnValue, lnWidth
lnTotalTicks = (this.Parent.Parent.MaxValue – This.Parent.Parent.MinValue)
lnValue = EVALUATE(this.Parent.Parent.recordsource + “.value”)
lnWidth = (This.Parent.Width – 35) * (lnValue/lnTotalTicks) && – 35 to leave room for the value caption
This.cshape1.Width = lnWidth
This.clabel1.Caption = TRANSFORM(lnValue)
This.cLabel1.Left = lnWidth + 5 && leave a five pixel margin
That’s basically all there is to it. The bars will be whatever color the shape is in the container. You could admittedly improve on what I am providing here by adding a barcolor property for the grid or perhaps showing the bars in different rows in different colors based on some criteria, or by calling a method of the grid from the container’s backstyle_access so that bit of code above isn’t so hidden. I am just providing the basic premise here, this is certainly not a finished product with all the bells and whistles that are possible. Also, as you’re looking at the examples you may notice that there are MinValue and MaxValue properties that have been added to grid subclass. These are so we don’t have to always graph on 0 – 100 or whatever (see code above). The values graphed could be anything, such as 1000-10000, 250-350, etc.
Finally, to see the very simple cursors that the bar graph grids run off of, look in the example forms’ load events where you’ll find something like the following code…
CREATE CURSOR crsValues (Descript c(25), Value I)
FOR lnCounter = 1 TO 100
INSERT INTO crsValues (Descript, Value) VALUES (“Item ” + TRANSFORM(lnCounter), INT(100 * RAND( ) + 1))
GO TOP IN crsValues
As you can see the cursor that is needed for this is extremely simple, just two fields in fact, one for the description and the other for the values. In any event, this just once again shows the value of Visual FoxPro’s grid class when coupled with the backstyle_access method. With very few lines of code we are able to produce a pretty useful class for visually displaying data to our users. Thanks for the emailed tip Sue and it was a pleasure meeting you at Southwest Fox 2005!
Source Code and Screen Shot
When you get the zip file downloaded, extract the contents. Open the project in Visual FoxPro and run the Example and Example2 forms.
Download Grid Graph Source Code with Examples (23 KB approx.)