Introduction
A user "AlwaysLearningNewStuff
" was trying to create a window control that validates number entry.
The usual way this is done is by writing either a dialog or window handler dealing with all the validation and while it works, there is little opportunity to reuse code.
I discussed subclassing controls and he had already obviously attempted it but found it difficult. This tip is an attempt to answer the how to make a validating number entry control generally and lay the foundation for a whole range of validating controls. A word of warning to the novice programmer... the bottom subclass code is not for the faint hearted, the ease of use of the code is however very easy.
I have created the actual validating control windows in a "C" file rather than a "C++" file because I use the controls on an embedded system which will be the subject of a future article. If you feel like converting the code to a C++ class, there are plenty of hints in the discussion and code. The private data structures would become internal class data and the interface routines would become class specific functions.
The upshot of that is the strange comments around the usual #include
to bring a C file into a C++ main and there is nothing tricky beyond that. If it really annoys you, rename the .c file a .cpp and be done with it.
extern "C" {
#include "ValCtrls.h" // include our fancy validating controls
}
Background
So what is the basic idea behind a validating control class? At its most basic, the control should not accept anything that isn't valid or warn the user something isn't valid. Many windows dialogs have a hideous behaviour that the user is expected to enter a series of data and when they click on the OK or confirm button, only then does the error actually get reported.
The most classic of these is edit entry boxes provided to enter real numbers and the user types in letters or other trash characters. The other usual thing is a valid number is typed in but it is outside a given range or valid value. So what would be useful is a control that only allowed number entry and displayed any range error as soon as the number is entered.
So on an example that we wanted an entry box that accepted numbers between 0-1000 for example, we would like these behaviours.
Word of warning: The controls while they are in error will not allow focus to be dropped from them until you fix the entry error. This includes any other standard windows controls, button, forms, etc. The one exception that has been allowed is a dialog box that can be canceled while they are in error.
IF THE CONTROL IS IN ERROR (RED), YOU HAVE TO FIX THE ENTRY ERROR YOU CAN'T MOVE FOCUS THAT IS THE WHOLE POINT OF VALIDATION.
The Esc key plays a special role. It will return the last valid entry typed. So if you start typing or are in error, a simple press off the escape key will return the last valid entry.
Extending the concept out, I added a validating combo box and buttons to show how the idea can be extended.
Using the Code
Using the controls is extremely easy. The key to validating is providing a suitable validation routine and all validation routines must be in the format.
typedef BOOL VALIDATEPROC (HWND hWnd);
A typical validation function in the code looks like this:
BOOL CheckWidth (HWND Wnd){
CFLOAT x;
x = GetNumber(Wnd);
if (( x >= (CFLOAT)0.0) && (x <= (CFLOAT)1000.0)) return (TRUE);
else return (FALSE);
};
It is completely fine if you want to use the controls but have them not validate to simply provide a value of NULL
or 0
for the validate function. The sample indeed provides the same two set of number inputs, one validating one not to show this behaviour.
The controls will also correctly work and display in both a Dialog (modal or modeless) and a standard windows frame. I added in some fancy stuff just for fun and visuals like the static
transparent colored text for use in labels, etc.
A word on the use of the term CFLOAT
. As I explained, I use these controls on an embedded system and so often I require real numbers to be actual float
s rather than double
s. CFLOAT
is a simple #define
at the top of "valctrls.h" file to decide which form the controls use and it is defaulted to double as the default in the sample. For novice programmers, anywhere you see the word CFLOAT
you can simply think in your head that is says double
.
So let's look at how we create a number entry and it is remarkably similar to how you create a normal window for obvious reason. It really is no harder and remember the usual rule if you don't need an input value then simply set it to 0
or NULL
as appropriate.
HWND CreateNumberEdit (HWND parent, int x, int y, int cx, int cy, long exstyle, int id, unsigned short lp, unsigned short dp, CFLOAT iv, VALIDATEPROC* proc, HFONT hFont, COLORREF selectcolor);
The test application simply inserts the controls into a test dialog or test window and you should see the following outputs:
Points of Interest
What I hope was illustrated was that creating new UI controls and functionality does not require elaborate frameworks and in many cases the frameworks simply slow the controls down or get in the way of desired behaviour.
History
- Version 1.0 Initial release