Ross Mack - GUI Online Productions
I believe that one of the problems with VB is that it does too much for you. Don't get me wrong, I actually like VB (I'd have to) and I like the fact that you can get right down to the task of writing the app, or solving the problem, without worrying about too much miscellany introduced by the nature of the language. Once you've finished your design you can get stuck right into it. The cool thing is that you can still do some fairly low level stuff via APIs, DLLs, or VBXs designed for such things. The classic example is using a product like Desaware's Spyworks to subclass controls or using a Delphi DLL to do some high speed GDI work. That makes VB a pretty cool, no nonsense, language.
Unfortunately there is a down side to this, an insidious down side. We VB programmers can become accustomed to this higher level approach. We don't need to know so much about what goes on underneath and, in a lot of cases, we don't need to write a lot of fiddly code to achieve small things. Not like we did when (and if) we worked with Pascal or C or whatever in the good old days <g>. We are always writing code to move data between big database engines and groovy custom controls. That's fine until you come to a point where you have to do the things that you've forgotten how to do. Never forget.
The other unfortunate mind-set that this tends to create is what I call the 'VBX Solution' mind-set. This is the style of thinking that we get into when encountering anything big that we may have to write, or we face anything really challenging, and we start looking around for a VBX (or OCX) that will do it for us. And why not? - It seems to work most of the time. Well, I believe there are problems with that. The major problem is that the VBX you choose may actually not be suitable or the best solution. Then you get half the job done, and spend a large amount of time coding around for the VBX to do the rest of the job - that it really isn't designed to do. I know I've spent a bunch of time coding around VBXs to give them extra functionality, that they didn't really have or support. The other really bad situation is when you need to produce three or four simple reports (for example) for your app and your client doesn't really care how they are done, so long as they print out. So you use the tool that you think you can get them done quickest, you're on a deadline after all. Which is cool except that the tool you use (no names) ships with so many runtime DLLs that it bumps your install set size by three disks. All of which is because it supports huge amounts of functionality, but your user doesn't really need it. And then a week later your user calls and says 'Hey, the report is doing this…' and you have to say 'Sorry, that's the reporting engine we are using - it always does that'.
This sort of thing is certainly not limited to the realm of reporting tools, grids, graphical controls, 3D controls (or is that 'dreaded 3D controls'?), specialised input controls, or anything really. They can all come back and bite you the same way. The 'VBX Solution' is not always the best solution, sometimes 'you just have to write some code'. Sometimes the things we use VBXs for are simple enough to be solved relatively easily in code - you just need an algorithm and a few hours, instead of the 15 minutes it normally takes to install the control. Now, hang on - you may not have a few hours.
You may find that a few hours spent up front - resulting in a solution that you have complete control over the nature of - will be better than getting something in place that you will need to spend many hours coding around later, when the user says Can we have it do this too?'; or even the hour you spend on the phone to the client explaining why he now has to ship two more disks in each of the 500 copies of the app he intends to distribute.
Again, don't get me wrong. There are a large number of VBXs out there that are really cool and that I certainly wouldn't want to do without.
Witness for the Defence: A Reporting Library.
When I was doing some work comparing reporting tools recently I decided to write some demonstration code showing how VSView2 can be used as a standard reporting tool fairly easily, 'you just have to write some code'. Now this may seem paradoxical, in that I'm talking about not using VBXs in some situations - and then my example chooses to use a VBX. Well, I never said that you shouldn't use any VBXs. I just said that you should make sure they are appropriate for what you are using them for. In this case, VSView2 is a very capable printer engine, and all I'm using it for is to provide access to the printer and its print preview functionality (which is particularly good in this software). Essentially I am using VSView2 for exactly what it was designed to do. So, it is appropriate.
Now, I have not designed a complete reporting solution. It's really quite simple, but it demonstrates how you can quickly build something that works, provides a slab of functionality, and it's easily extensible - because you have the source code and you can add whatever you like to it. That's the point.
When you think about it, most reports are essentially a list of data. So this is how I first built the reporting wrapper - it just dumped a list of data to the VSPrinter object. Now, that is a quite a trivial sort of report and typically not useful. However, it is the bare bones and provides a suitable foundation to build on, to and add the sort of reporting features you need (which I wanted to demonstrate could be achieved). After a short amount of time I had built this up into a reporting library that supported the following functions:
Now, that this is actually approaching to becoming a useful tool, and I can deal with any shortcomings by simply changing the code or adding new calls, properties or whatever. But how long did it take to write? About 3 hours start to finish. Really. Not all that much of an investment to produce something with a great deal of potential utility. It also demonstrates the simplicity with which functionality can be added from a reasonable base. The whole thing is implemented as a code module and a form. The form acts as the user interface (surprisingly), while the code module is the programmatic interface to it.
|The central routine is VSReport.|
You will note that there are two main sections to this code (which could be broken into separate routines). The first section ensures that all the parameters and settings are in place and builds a SQL statement to retrieve the data we want to report on. The second section simply loops through that data and prints it to the report. Neither of those sections are particularly complex, their bulk is increased by their need to check for all the special conditions that may arise through the need to handle groups, sorting and totaling.
There is quite a bit of code but I don't think that any of it is particularly obscure. It is all based on a fundamental algorithm which has been extrapolated upon to support various features. And, after all, 'sometimes you just have to write some code'.
I use function calls to set Fonts for different sections and to control totaling and subtotaling, so that it's a little more modular and decreases the code in any one place. You will also notice that I use some function calls and inline code to generate table strings that are in a format suitable for specifying tables for the VSPrinter. The advantage of tables is that they handle the layout of fields nicely and that they allow the wrapping of long field contents within their own columns very easily. The VSPrinter just does it. That is the main reason why I chose to use VSView2 as the underlying tool.
The fonts are handled like this: I maintain a number of structures that contain certain font attributes - Bold, Italic, Face name - that can be applied to various sections as they are printed. When I want a particular format I call a function passing the name of the format I want (like "Header" or "GroupHeader") and the VSPrinter control, so that font attributes can be applied directly to it. At module level, for the VSReport module, I keep a variable that indicates whether or not they have been initialised - by default this is false. When it is true, we know that the section font attributes have been at least set to their defaults. When a call is made to customise some section attributes, the function that handles this checks this flag and, if it is false, initialises all the section attributes to their defaults before it sets the customisations. If no customisations are requested, then the section's font attributes are not initialised until VSReport itself is called. This makes it very flexible and easy to use. Other customisable features can be implemented the same way. The attribute setting function is shown below. The initialisation function (InitSectionAttrs) simply sets the initialisation flag and calls SetSectionAttr for each section to ensure that each section has a default format.
Sub SetSectionAttr (ByVal sSection As String, ByVal iBold As Integer, ByVal iItalic As Integer, ByVal iUnderLine As Integer, ByVal iSize As Integer, ByVal sName As String) Dim FS As FontSpec If Not miSectionAttrInit Then InitSectionAttrs End If ' Set up a font definitoion as requested FS.Bold = iBold FS.Italic = iItalic FS.Size = iSize FS.Name = sName FS.Underline = iUnderLine ' Assign it to the correct place Select Case sSection Case "Header" Header = FS Case "Footer" Footer = FS Case "GroupHeader" GroupHeader = FS Case "DetailHeader" DetailHeader = FS Case "Detail" Detail = FS End Select End Sub
Like everything in the VSReport library, all settings are initialised to their starting values, at the end of each report, so that they need not be initialised manually; nor will legacy settings from previous reports effect subsequent reports. If you want to alter the default font attributes (for example) for every report then you can simply adjust the initialisation code to suit your needs.
The downloadable zip file - reporter.zip (20Kb) - includes a sample app using the VSReport library in VB3 (including a compiled copy). It reports off biblio.mdb which it expects to find in its own directory. It also includes a spec document for all the calls exposed by the library. You will need VSView2 to run this app. If you do not have a copy of VSView2 I recommend that you download a demonstration version from VideoSoft's FTP server at ftp://ftp.videosoft.com/pub/videosoft/vsview2.zip. This will allow you to run the demonstration application and to play with the code. It also gives you a good look at a great control.
VSView 2.0, as with all VideoSoft Software, is available within Australia from GUI Computing.