Improving on yesterday
In yesterday’s blog entry I showed a technique that could be used to create simple bar graphs. There was one thing missing from the bars on the graphs however, gradients. In any event, I decided to try my hand at a solution similar to the one shown by Calvin Hsia, but without all the quirky behavior that renders Calvin’s example fairly unusable in a real-world application (to be fair, his was more of a proof-of-concept than anything).
If you want gradients in your Visual FoxPro applications and don’t want to have to go through some of the machinations that I did for the progressbar control I showed in an earlier blog entry, then you are going to have to use GDI+. The combination of GDI+ and the backstyle_access method of an object contained in a grid’s column gives us everything we need to produce some first-class results.
In the solution that I am showing here I use a grid subclass and an image subclass to display the bars. That’s it as far as the classlib is concerned, but if you look in the grid’s Init event and its Drawbar and Drawtext methods you’ll see that there is some fairly heavy reliance on GDI+ and subsequently the _gdiplus.vcx class library that is shipped with Visual FoxPro 9.0. Given the complexity of GDI+ code to those who may be unfamiliar with it, I have heavily commented the code for this solution. I encourage you to read through it. I am hoping that it will be clear what I am doing and why.
Annoying Picture property behavior
One thing I’ve run into when working with GDI+ in Visual FoxPro is the fact that Visual FoxPro will bring up a busy cursor (hourglass) when the picture property of a container or image is set (perhaps other classes as well). This means that when I am using an off-screen bitmap to draw to and I set that bitmap’s fullpath as the Picture property that the mouse cursor will change back and forth from an arrow to an hourglass to an arrow to an…. you get the idea. In a perfect world the Microsoft Fox Team would let us as developers determine when a mouse cursor should show busy, or at the very least allow us a way to shut off this helpful “feature”, but that’s not the case here. Luckily they didn’t do the same thing when they implemented the PictureVal property of the image control. So, in order to get rid of this flickering mouse, I use an image control and after I have written the bitmap to disk I convert it to a string with FILETOSTR() and use it for the PictureVal. Works like a charm, even if there is no doubt some additional overhead with this approach.
Improved grid subclass
I spruced up the grid subclass a bit and provided additional properties that can be set to change the gradient colors (Gradientcolor1 and Gradientcolor2 properties), as well as a GradientDirection property that can be set to change the direction in which the gradient is drawn (Horizontal = 0, Vertical = 1, Forward Diagonal = 2, Backward Diagonal = 3). I’ve also placed the code that is used to draw the bars in a Drawbar method of the grid subclass. This Drawbar method is called by the image subclass (that is contained in column2) backstyle_access method. And finally, since I was already using GDI+, I decided to forego an additional label object and instead I draw the value text at the end of each bar using GDI+. The font that is used for the value text is the same as has been set for the column1 descriptions, so if you change column1’s font it will change the font used to render the value text as well.
Additional improvements possible
There are a number of improvements/enhancements that could no doubt be made to this solution. Some that I can think of would be to allow the developer to define the fields that are to be used for the descriptions and values rather than hardcoding it to a particular cursor/table structure. Another improvement would be to allow for drill-downs in some fashion so the user could double-click on a particular row and be presented with the group of data that made up that particular value. Given that this is using pure GDI+ to draw the bar nearly anything could be drawn there and if the rows had a tall enough height individual pie slices could be shown. This might look really good on a form where the grid is shown next to the entire pie chart.
I also cut some corners when it came to the rendering of the value text. The height of that text for centering is determined from the form’s textheight method which could lead to erroneous results. I also didn’t completely duplicate the font being used by column1, things such as bold or underline or strikethru have been ignored. However, to my credit I did provide the additional constants and such that are needed should you wish to go further with this. I felt that what I had was good enough for this blog posting and I don’t know if I would want the value text to be underlined or whatever even if the developer decided to do that themselves. I guess if I was thinking I would have taken all the values from column2’s font settings (the column that the bars are actually rendered in) and used that… it would have provided a simple and intuitive way for the developer to determine the font used for the value text that is rendered at the end of each bar. I guess I wasn’t thinking, but hopefully you get the idea here which is what I’m striving for.
Sue Cunningham is at it again and has emailed me about the next phase of her solution which will be to provide the users with a way to print the bar graph that is shown in the grid. I’ll let that roll around in my head for a day and see what occurs to me. I’m thinking that task just got a lot easier with this solution since I’m using GDI+ and the _reportlistener is kind enough to expose the GDI+ Graphics object(s) it is using when it renders a report. Seems like I’ve already got most of the pieces… just need to move it from a grid subclass to a _reportlistener subclass. Thanks again Sue, I think you’ve given me another future topic with your email.
Source Code and Screen Shots
When you get the zip file downloaded, extract the contents. Open the project in Visual FoxPro and run the Example and Example2 forms.
Download the Gradient Graph and Examples (63 KB approx.)