Multi-Field Edit Controls


This article addresses a new implementation of a popular class of Edit Controls
I’ve created that I call Multi-Field Edit Controls. A Multi-Field Edit Control
is an Edit Control in which the edit box contains multiple fields, each of which
may behave as though it were an individual control. I had the idea to create
this class recently after I developed a couple of classes for the entry and
display of geographic Latitude and Longitude values. These classes, CGCLatitudeCtrl
and CGCLongitudeCtrl, incorporated functionality that I recognized to be potentially
useful in many other types of controls. I identified this functionality and
extracted it, refined it, and added to it to create a new, abstract (pure virtual)
class, CGCMultiFieldEdit.

The demo is illustrated in the following figure:

Click here for larger image

The CGCMultiFieldEdit class provides a building-block upon which you may easily create
your own Multi-Field Edit Control of any kind. Specifically, the CGCMultiFieldEdit
class provides the following capabilities:

  • To define as many fields within an edit control as desired.
  • To control the widths of each field.
  • To specify any field separators desired of any length desired.
  • To navigate to any field using the left/right cursor control (arrow)
  • keys or the Space Bar.
  • To select a field using the left mouse button.
  • To programmatically select a field.
  • To programmatically determine which field is currently selected.
  • When the operator tabs input focus to the control, it should select the field selected when the control last had input focus during the session.

The class hierarchy for the Multi-Field Edit Control Class is shown in the following

The abstract class CGCMultiFieldEdit is derived from another of my classes, CGCColorEdit.
That class provides the functionality to select the font and background and text colors
used in your Multi-Field Edit control. I find that proportional (fixed pitch) fonts
work best with Multi-Field Edit controls. They prevent the fields from shifting
left and right as characters of different widths are displayed.

I will provide an overview of the methods provided by the CGCMultiFieldEdit class.
After this overview, I will provide some insight into the design for the class.
Then I will introduce you to some classes I created to support you when you derive
your own control class from CGCMultiFieldEdit. I will then wrap up with a look
at the demo project included with this article.

Overview of the Public Methods

When deriving a new control class from the CGCMultiFieldEdit class, the constructor
for the derived class should call the following public method:

BOOL Format(const vector<CString> FieldContents,
            const vector<CString> FieldSeparators,
            const CString Prefix = "",
            const CString Postfix = "");

Calling this method is the mechanism by which the number of fields, the width of the
fields and the field separators to be used by the control are established.

The method accepts two parameters of type vector<CString>. The “vector” is a template
class provided by the Standard Template Library (STL) and behaves somewhat like
an array. The statement “vector<CString>” declares a vector of CString objects.
The first parameter, “FieldContents”, contains the initial contents of each field. The
CGCMultiFieldEdit class uses this vector to determine how many fields the control is
to contain. If the “FieldContents” vector contains 4 CString objects, the
Multi-Field Edit Control will contain 4 fields. Further, the CGCMultiFieldEdit class
utilizes this vector to determine the widths of each field. The length of each string in
“FieldContents” is the length that will be set for that field. The fields are ordered
in the order of the items in the “FieldContents” vector. That is, the first item in
the vector (index 0) will be the left-most field in the Multi-Field Edit Control.

The second CString vector parameter, “FieldSeparators”, provides for the specification
of the field separators. The field separators are not limited to a single character.
Nor is the developer restricted to using the same field separator to separate all fields.
If the “FieldSeparators” vector contains a single CString object, then that CString will be
used for all field separators. Otherwise, the “FieldSeparators” vector must contain
at least one less as many CString objects as there are in the “FieldContents” vector.

Note that the Format() method checks to make sure that each element in the FieldContents
parameter specifies a string of greater than or equal to one character in length.
If any entry has a length of zero, or the FieldContents vector contains no
elements, the Format() call fails and returns FALSE.

Two additional parameters are provided that allow you to specify a Prefix and a Postfix
for the control. Ordinarily, when implementing a Multi-Field Edit Control, these are
not used. For example, if you were defining a control with two fields, the control would
contain the first field on the left, then a single Field Separator followed by the last
field on the right. If you wish, you can specify a Prefix which will be a string displayed
to the left of the first field. The Postfix parameter allows you to specify a string to
be displayed to the right of the last field in the control.

The Format() method is intended to be called once by the constructor of the derived
class. When the derived class must update the contents of the fields only, a more
efficient public method is provided:

BOOL CGCMultiFieldEdit::Update(const vector<CString>& FieldContents);

The Update() method allows the derived class to update the contents of the fields only.
It accepts a single CString vector containing the new contents to display in each field.
It does NOT allow the field widths to change. If the “FieldContents” vector contains a
CString object for a field that is longer than that field’s width, the new field content
will be truncated on the right to fit in the previously-specified field width.

However, if you wish to change the number of fields in the control, the
width of either field, or the field separator(s) in use, you are free to call
the Format() method to do so. The Format() method of the CGCMultiFieldEdit class
provides for changing the format of the Multi-Field Edit Control at any time.

A public method is provided to allow for programmatically selecting a field in the control:

BOOL CGCMultiFieldEdit::SelectField(const int FieldNumber);

This method accepts a single integer parameter that is a zero-based index of the field
to select. The left-most field is field 0. The method returns TRUE if the specified
field was selected.

An additional method is provided to allow for programmatically determining which field is
currently selected:

int CGCMultiFieldEdit::GetSelectedField() const;

This method returns the zero-based index of the currently selected field.

The operator is prevented from selecting a Field Separator using the mouse. The operator
can only select a field. Like the Field Separators, the Prefix and Postfix can not be selected.

Design of the CGMultiFieldEdit Class

Like any programming problem, there are typically many ways in which to implement it. This
part of the article looks at the way I chose to implement the CGCMultiFieldEdit class.

The CGCMultiFieldEdit class is designed to provide just the functionality required to assist
you in creating your own Multi-Field Edit Control. It makes no assumption as to what information
you want to display in any field. Since the CGCMultiFieldEdit class is designed to provide a
building block on which to construct any kind of new control class, CStrings were selected as
the data type for specifying the field contents. This allows virtually any kind and format of data to
be displayed in the fields, keeping the CGCMultiFieldEdit class as “generic” as possible.
The class that is derived from CGCMultiFieldEdit will convert the data to be displayed in the fields
into CString objects and pass the CStrings to the CGCMultiFieldEdit class for display in the fields.
You can display numeric data, text data, or a combination. By converting the information that
you want to display within each field to CString variables, you are free to decide what you want
to display.

When you examine the code, you will notice that I have overridden the SetWindowText() method
inherited from the MFC CEdit class. Since Multi-Field Edit Controls require that a rigid format
be maintained in the information displayed and entered, allowing an application free use of the
SetWindowText() method would defeat the CGCMultiFieldEdit class’ ability to control the format
of the display. I provide a “do nothing” override to remind you that this should not be used.
You should make use of the Format() and Update() methods only when programmatically setting the control
to display information.

Other “do nothing” overrides are also implemented for WM_CHAR, WM_LBUTTONDBLCLK, WM_MOUSEMOVE
and WM_RBUTTONUP. The handler for WM_CHAR is overridden to prevent the handler from CEdit
from being inadvertently called. If this were allowed to happen, the displayed data and
format could be corrupted.

The CGCMultiFieldEdit class must allow for the selection of individual fields within the control.
I did not want the operator to be able to select the entire contents for the control. Therefore,
I provided a “do nothing” override for the WM_LBUTTONDBLCLK message. Should the operator
double-click with the left mouse within the control, the control will not select the entire contents.

Also, with the standard CEdit control, the operator could single-click with the left-mouse
button and drag the cursor to select part or all of the contents of the control. Again, to
ensure that only specific fields are selected, I provided a “do nothing” override for the
WM_MOUSEMOVE message to prevent this from happening.

Since the standard context menu provided by the MFC CEdit class would not logically apply to
most Multi-Field Edit Controls, I did not want that context menu to ever be displayed. When
deriving a specific control from CGCMultiFieldEdit, you should provide your own context menu
if one is required. The “do nothing” override provided by the CGCMultiFieldEdit class ensures
that the CEdit context menu isn’t inadvertently displayed.

I do allow you to call the GetWindowText() inherited from CWnd if you wish to obtain the entire
contents of the control as a string.

A more ideal solution would be to privately inherit from CEdit but that would cause more work
than is necessary. So it is up to the developer to properly use the Multi-Field Edit Control and
not circumvent the interfaces provided.

When you call CGCMultiFieldEdit::Format(), the class looks at the contents of the FieldContents vector
and calculates the starting and ending character positions for each field within the control. The
class stores these values in a private array “m_FieldSpecs” with elements of type “FIELD_SPEC_TYPE”.
It stores a copy of the FieldContents vector in an internal vector, m_FieldContents for later use.
It also stores the field separators in the private vector “m_FieldSeparators”.

With each call to the Format() method, CGCMultiFieldEdit throws away any previous contents of the
m_FieldContents and m_FieldSeparators private vectors and recreates them using the new specifications
passed into the Format() method. The private function, FormatAndDisplay() is then called by the
Format() method to update the display. When you call the CGCMultiFieldEdit::Update() public method,
the m_FieldContents vector is updated and FormatAndDisplay() is again called to update the display.

Also, in most cases with Multi-Field Edit Controls, the caret would typically be an annoyance.
Therefore I call the HideCaret() method inherited from CWnd where necessary to ensure that the
caret isn’t displayed.

The CGCMultiFieldEdit class overrides the WM_KEYDOWN handler to provide for selection of the fields
by using the cursor-control (arrow) keys and the space bar.

I provide no methods in the CGCMultiFieldEdit class for setting the fields individually. I consider
that to be a specialization that belongs in the control class you derive from the CGCMultiFieldEdit
class. If you need such a capability, provide it within your derived class, then call the Update() method
(or Format() if needed) to update all fields at once.

Support Classes

The file GCMultiFieldEdit.h (and its associated cpp file) provides a set of support classes
to assist you when developing your on Multi-Field Edit Control derived from CGCMultiFieldEdit. Objects
of these classes are not passed into the CGCMultiFieldEdit class by any means. They are to be used
within your derived class as needed to help you implement your class’ functionality.

In each case, objects of the support classes are typically manipulated in the OnChar() and OnKeyDown()
overrides of the class you derive from CGCMultiFieldEdit. The comments in the example control classes
in the supplied demo project will give further details. I tried to supply sufficient comments in the
source to make the code as easy to understand as possible.

The class hierarchy for the field support classes is shown in the following illustration:

Click here for larger image

Once you study the code for these support classes you may determine that some of it isn’t needed. Indeed,
I do not always use all of the code presented. I simply provide a framework that you may alter as needed.

You will not that I have provided macros that you can undefine to eliminate unwanted code if you wish.

Support Class CGCIntegerField

The first support class, CGCIntegerField, will aid you in adding an integer (or long integer)
field to your Multi-Field Edit Control. For every integer (or long) field in your derived Multi-Field
Edit Control, you will want to create a CGCIntegerField class object to support it.

Support Class CGCDoubleField

The next support class, CGCDoubleField, will aid you in adding a floating point (or double)
field to your Multi-Field Edit Control. For every float (or double) field in your derived Multi-Field
Edit Control, you will want to create a CGCDoubleField class object to support it.

Support Class CGCTextField

The next support class, CGCTextField, assists you in creating a simple text field to allow
the entry and display of free text. For every simple text field in your derived Multi-Field Edit
Control, you will want to create a CGCTextField class object to support it.

Support Class CGCStringListField

The final support class provided, CGCStringListField, allows you to create a field in your derived
Multi-Field Edit Control that can be used to display any one entry from a predefined list of values.

A CGCStringListField field operates somewhat like a combo box without the drop down list.
You define the list of items (CStrings) that may be displayed and selected in the field
using an overloaded “+” operator.

The Demonstration Program

The demo project includes several Multi-Field Edit controls to demonstrate the use of the
CGCMultiFieldEdit class and the support classes. The project was developed using Visual C++ Pro V6.0
on Windows 98 and should run on NT 4.0 Workstation as well.

I provide one complete class for the entry and display of Social Security Numbers (SSN).
This class illustrates a basic Multi-Field Edit Control with 3 integer fields and the field
separators. This class is complete and could be used as is in any application where you
need to enter an SSN. This class (as well as some of the others described below) also illustrates
the use of the CGCIntegerField support class.

I also provide some INCOMPLETE control classes to illustrate the use of other field types (and their
support classes) and the use of the Prefix and Postfix (see comments in GCMultiFieldEdit.h). Remember,
the following control classes are INCOMPLETE classes provided as EXAMPLES of the use of the
MultiFieldEdit class.

The control class CGCSimpleLongDistance primarily illustrates the use of the Prefix parameter in
the call to the Format() method of CGCMultiFieldEdit. It also utilizes the CGCIntegerField support class.

The control class CGCSAEWeight is provided to illustrate the use of the Postfix in the call to the Format()
method of the CGCMultiFieldEdit class. It too uses the CGCIntegerField support class.

The control class CGCInventoryItem is provided to illustrate the use of simple text fields and
the CGCTextField support class. It too uses the CGCIntegerField support class.

The control class CGCMonthDay is provided to illustrate the use of String List Fields and the CGCStringListField
support class. This one also uses the CGCIntegerField support class.

And finally, the control class CGCXYCoordinate illustrates the use of floating point fields and the
CGCDoubleField support class. It also illustrates the use of negatives numbers.

Start with the complete Multi-Field Edit Control class CGCSocialSecurityNumberCtrl. Study the
code and the comments. Play with the Social Security Number Control in the demo. Step through the
code in the debugger. After you understand how the Social Security Number Control works, then
look at the source for the other example controls. In all cases, pay close attention to the class
constructors for the example control classes derived from the CGCMultiFieldEdit class as well as their
OnChar() and OnKeyDown() overrides and their Display() methods. These contain the statements of
interest in the use of the CGCMultiFieldEdit class and its support classes.

This may seem complicated, but after a careful review of the Multi-Field Edit class and the
example control classes derived from it, you should understand how to use it and soon be developing your
own Multi-Field Edit Controls.

What other controls could you use the CGCMultiFieldEdit class for? How about deriving a control for
entering insurance policy numbers that typically contain multiple groups of numbers? Bank account
numbers? Rectangular or polar coordinate entry controls? Measurement controls that allow
entry/display in feet and inches or meters and centimeters? Or cost controls that display costs in
dollars and cents? There could be many instances where Multi-Field Edit Controls would be useful.
I hope that you will find the CGCMultiFieldEdit class and its support classes useful too.


Download self extracting demo and source code – 123 Kb

More by Author

Must Read