Tuesday, 07 September 2010
PicoSearch

The LostFocus of Validation

This column appeared in the December 1998 edition of EXE, and covered the new Validate keyword


This month I’m having a look at a new feature that has been provided in Visual Basic 6, namely the new Validate event and the associated CausesValidation property. I’m then providing a review of the Graphics Server product from Bits Per Second, a powerful and flexible alternative to the Microsoft Chart control that ships with Visual Basic.

Problems with validation routines

Up until now there have been three ways of validating user input in a Visual Basic form. First, all validation can be performed at the end of the information gathering, such as when the user presses ‘OK’ or ‘Apply’. The first field that fails the validation, if there is one, is then made the focus target and perhaps a message box will be displayed. This approach is actually fine as long as you don’t mind the fact that the validation around each individual field will not take place while the user’s attention is placed directly upon it. The second approach is to place code directly into either the KeyPress or Change events which only allows for limited validation because you can’t be sure at this stage whether the user has completed the entire field input. It is unfriendly to throw up a validation message in this instance because the user might well have mistyped a character and could be intending to hit the backspace and retype it – a message box at this stage will be unwelcome. Therefore the only useful work that can be done here is to filter out invalid characters. The third way is to write the code into the LostFocus event, but this can lead to all sorts of problems. Consider the following code extracts for two text boxes where we are validating against empty fields (and both fields are in fact empty):

Private Sub Text1_GotFocus()
  Debug.Print "Text1.GotFocus"
End Sub

Private Sub Text1_LostFocus()
  Debug.Print "Text1.LostFocus"
  If Text1.Text = "" Then
    Text1.SetFocus
  End If
End Sub

Private Sub Text2_GotFocus()
  Debug.Print "Text2.GotFocus"
End Sub

Private Sub Text2_LostFocus()
  Debug.Print "Text2.LostFocus"
  If Text2.Text = "" Then
    Text2.SetFocus
  End If
End Sub

This code highlights a difficulty with event ordering in Visual Basic. If you run the above code when both Text1 and Text2 are empty and you press the tab button on the keyboard to move on to Text2 then you will almost certainly end up in a loop. The immediate window will display a continuous run of something along the lines of

Text1.LostFocus
Text2.GotFocus
Text2.LostFocus
Text1.GotFocus
Text1.LostFocus
Text2.GotFocus

    ..and so on. The following sequence of events seem to be occurring here:

    1. Once Text1 has had the focus taken away from it then Text2 is flagged to receive the focus next. Then the code in Text1_LostFocus is run. However by run I do not mean that everything happens as one would expect. The Debug.Print statement is often displayed relatively quickly within the immediate window, but the command to set the focus back to Text1 remains within the applications message queue from where it will be processed in turn. However, because of the previous tab action the message to set the focus to Text2 is already further ahead in the queue and so will probably be executed beforehand.

    2. As soon as the Text2.SetFocus message has been processed the Text1.SetFocus message is encountered and an attempt is made to execute it.

    3. As a result of the call to switch focus back to Text1 the Text2.LostFocus event will automatically fire. The validation code will place a call to Text2.SetFocus into the message queue because the validation fails.

    4. The call to Text1.SetFocus will fire, followed immediately by the call to Text2.SetFocus, which will automatically call the Text1.LostFocus routine, which will fail. And so on…

I should point out at this stage that this sequence of events is only an approximation of the true order of processing. In reality Windows does not guarantee the order in which messages are processed. When I was performing my own tests for this article (under Windows NT 4) there was nothing else of note taking place on the machine. However different load conditions, different versions of Windows, or even the fact that a butterfly just flapped its wings in the South American jungle could slightly alter the dynamics of this message queuing. Having emaphasised this point, the likely order of processing can be demonstrated by removing the If..EndIf block of code from the Text2_LostFocus routine. A tab action from the Text1 control (again, empty) will probably just show the following four messages:

Text1.LostFocus
Text2.GotFocus
Text2.LostFocus
Text1.GotFocus

Understanding the nature of the ordering that takes place here can be very useful in resolving the type of looping problems that I described above.

The new Validate event

As a means of helping the developer to overcome the problems described above, the Visual Basic team has introduced the Validate event. This event is designed to run before the control loses focus, thereby preventing the need to place a SetFocus call into our code. The syntax for the event is defined as

Private Sub object_Validate(KeepFocus As Boolean)

The KeepFocus argument (called Cancel in the actual code declaration) is the means by which you can prevent the focus from being lost. If the validation fails then set this value to True and the focus won’t go anywhere. A simple piece of code to demonstrate this use could be:

Private Sub Text1_Validate(Cancel As Boolean)
  If Text1.Text = "" Then
    Debug.Print "Text1 validation failed"
    Cancel = True
  End If
End Sub

The firing of the Validate event is governed by a property called CausesValidation. This property, however, is relevant on the other controls on the form, specifically on the control that the user attempts to shift focus to. If the CausesValidation property on the second control is set to True then the Validate event on the first control will fire; on the other hand if the property is set to false then the event won’t fire. This property is get/let and is defined as

Object.CausesValidation [ = boolean ]

To use this combination properly all data controls would probably have the CausesValidation property set to True (the default value). As the user tabs from one field to the next the contents of the current field can be properly validated before the actual focus changes. However any other controls, such as a Help command button, should have the CausesValidation property set to False. This will allow the requested course of action to take place without the interference of the validation failure code.

This concept requires just one further discipline on the part of the programmer to really come together well. The actual validation code should not reside within each Validate event itself, but rather a call to a separate validation function would be more efficient. This is because the new approach does not guarantee that all controls will have been subject to its respective validation routine – if the user entered a value, pressed Help, and then pressed OK the Validate routine will not have fired. Therefore it will be sensible to design a means of also calling the various validation routines from the OK button before the data is committed to ensure that everything is correct.

Graphics Server review

Arguably one of the few "core" third party products within the Visual Basic world, it’s a refreshing fact that Graphics Server is actually written by a UK company, Bits Per Second, rather than an American one. The product offers a wider and more flexible range of graph types than the Microsoft Chart control that is supplied as standard with Visual Basic. For example, I was recently writing a front-end tool that displays the status of a set of SQL Server databases. It was my intention to offer a Properties option via a right mouse click that would show a 3D pie chart showing the amount of device space used and remaining. The standard chart control only offers 2D charts and so the result looked rather flat, especially when compared with a Drive Properties dialog when using the Windows Explorer. Using Graphics Server would, of course, have been one solution to this problem.

The current edition, version 5, offers direct support for quite a few languages so a fair few redistributable component types are provided. As well as the OCX control that most Visual Basic programmers would expect there is a DLL version, a C++ class, a Delphi VCL, and a FoxPro FLL. All of these components come in 16 and 32-bit form (there is a 16-bit VBX version too). There is also support for both client and server side Internet applications. All of these come as part of the one product, so there is no need to buy a separate version of the product for each development language. For each of these languages there are plenty of sample projects that illustrate coding for different graph types.

To use the control within the IDE after the developers kit has been installed, check the "Pinnacle-BPS Extended Graph" entry in the Project, Components dialog. Once the new control is added to the toolbox it can be dragged onto a form as normal. A custom properties dialog for the control displays numerous tabs that highlight the many aspects of functionality that the product offers. The control is data aware and can be bound to the standard Data Control. Once a connection is made the resulting chart will be able to automatically update itself if the underlying data changes. There is also a large library of functions that can be called to create, show, and manipulate graphs programmatically.

In addition to the graph types supported by the Microsoft Chart control, Graphics Server offers 3D pie charts, surface graphs (see Figure 1 for an example), time-series graphs, scatter graphs, open-high-low-close ("Wall Street") graphs, bubble graphs, and...Well, you get the idea. To illustrate the positioning of the product within the development community, the American Visual Basic Programmers Journal readership has consistently voted Graphics Server as the best graphing tool for the past seven years.

Do I have any criticisms? Well yes, a couple of minor ones, but both of these are being fixed in the next release. First, the printed documentation, although comprehensive, is a little untidy in that you are provided with two version 4 manuals, two version 4.5 addendum manuals, and a version 5 addendum. On this matter I have been informed that the next release will contain a proper "version 6" set of manuals. My second point is that whenever your application makes use of the graphics server engine a Graphics Server button appears on the Windows taskbar. Clicking the button does not bring up a window of any kind and is therefore, I would argue, an incorrect design feature as it is closer to being a service than an application. Bits Per Second apparently accept this point because the next version will instead display an icon in the system tray area of the taskbar, which is the correct location for a service that wishes to show its presence.

As I mentioned there will soon be a version 6 which, at the time of writing, is due for release around January. Features include full compatibility with Visual Studio 6, and enhancements to the documentation, help files, samples, and the installation routine. Also, there will be improvements to the graphing engine itself in the form of increased data precision, improved printing, user-defined colour palettes, and improved GIF and JPG image handling. Further details on the product can be found at http://www.contemporary.co.uk It is available from most UK software resellers, priced at £245 + VAT. My thanks to Contemporary Software for the review copy.


Copyright ©2002 Jon Perkins I, Jon Michael Perkins, hereby assert and give notice of my right under section 77 of the Copyright, Designs, and Patents Act 1988 to be identified as the author of the foregoing article.