Recipe 11.3 Classify Keypresses in a Language-Independent Manner
11.3.1 Problem
You
need to be able to classify a keypress as a character, a digit, or
neither. You also need to know if a character is uppercase or
lowercase. You know you can write code to handle this, but if you do
that, you're limiting yourself to a single national
language, since languages classify their characters differently.
Since Windows knows about various character sets, is there some way
you can use Windows to do this work for you?
11.3.2 Solution
You could write VBA code to
classify characters, but it wouldn't be
language-independent. For example, the ANSI character 65 is an
uppercase character in the standard multinational character set, but
it may be different in another character set. If you want your
applications to work in various languages, you must not assume
specific character ranges. The Windows API includes a number of
functions you can call to categorize characters based on their ANSI
values. The isCharAlpha and isCharAlphaNumeric functions both are
faster than the built-in VBA functions and are able to deal with
international issues. Luckily, an ANSI value is exactly what the
KeyPress event procedure in Access sends you, so you can use these
functions from within KeyPress event procedures that you write.In addition to the necessary function declarations, the sample
database 11-03.MDB includes a demonstration form
showing all the ANSI characters and their classifications. Load and
run frmCharClasses from
11-03.MDB , and you'll see a
display like that in Figure 11-3. By scrolling
through the form, you'll be able to see all 255 ANSI
characters and their classifications.
Figure 11-3. frmCharClasses shows all the ANSI characters and their classifications
To use this functionality in your own applications, follow these
steps:
- Import the module basClassifyChars from
11-03.MDB into your application. - To classify an ANSI value, call one or more of the functions in Table 11-2. Each of these functions takes as its
parameter a value between 1 and 255. Each function returns a nonzero
value if the character code you passed is a member of the
function's tested group, or 0 if
it's not. (As you can see from Table 11-2, some of the functions come directly from the
Windows API and others return values based on those functions.) These
functions will return correct values no matter which language version
of Windows is running.
Function | API? | Inclusion class |
acb_apiIsCharAlphaNumeric | Yes | Language-defined alphabetic or numeric characters |
acb_apiIsCharAlpha | Yes | Language-defined alphabetic characters |
acbIsCharNumeric | No | Alphanumeric, but not alphabetic |
acbIsSymbol | No | Not alphanumeric |
acb_apiIsCharUpper | Yes | Language-defined uppercase characters |
acb_apiIsCharLower | Yes | Language-defined lowercase characters |
typed into a text box, and the number of allowable characters
isn't known until runtime. In addition, you want to
allow only alphabetic or numeric values, but that
isn't known until runtime either. Although you could
programmatically control the input masks, creating a new one each
time conditions change, it is simpler to handle this problem using
the KeyPress event and some code that checks the state of the current
keypress. The sample form, frmInputTest (Figure 11-4), shows a simple test form. The text box
labeled "Enter some characters"
allows you to enter up to as many characters as shown in the
"Maximum number of characters" text
box, and you can enter only characters whose type
you've chosen in the character type option group.
Figure 11-4. frmInputTest uses character classifications to disallow keypresses
The code attached to txtCharTest's KeyPress event
looks like this:
Sub txtCharTest_KeyPress (KeyAscii As Integer)
' Always allow a backspace.
If KeyAscii = vbKeyBack Then Exit Sub
' If txtChars is non-null and greater than 0, and txtCharTest
' is non-null and has too many characters, set KeyAscii to 0.
If Not IsNull(Me.txtChars) Then
If Me.txtChars > 0 Then
If Not IsNull(Me.txtCharTest.Text) Then
If Len(Me.txtCharTest.Text) >= Me.txtChars Then
KeyAscii = 0
End If
End If
End If
End If
' In any case, if the keypress isn't the correct type,
' set KeyAscii to 0.
If Me.grpCharType = 1 Then
If (acb_apiIsCharAlpha(KeyAscii) = 0) Then KeyAscii = 0
Else
If (acbIsCharNumeric(KeyAscii) = 0) Then KeyAscii = 0
End If
End Sub
In the KeyPress event, Access sends you the parameter
KeyAscii, which contains the ANSI value of
the key that was just pressed. To tell Access to disregard this key,
modify its value to 0 during the event procedure. In this case, if
there's no room left in the field (based on the
number in Me.txtChars) or if the character is not the right type
(based on calls to acb_apiIsCharAlpha and
acbIsCharNumeric), the code sets the value of
KeyAscii to 0, causing Access to disregard
the keypress. Play with the sample form, changing the values, to see
how the code works.
11.3.3 Discussion
Windows internally maintains
information about the currently selected language and character set.
For each language, certain characters are treated as uppercase and
others aren't. Some characters in the character set
represent alphabetic characters and others don't. It
would be impractical to maintain this information for each language
your application might use. Luckily, you don't have
to manage this. The Access UCase and
LCase functions handle case conversions for you,
but Access doesn't include case-testing functions.
That's the role of the functions introduced in this
solution: they allow you to test the classification of characters, no
matter what the language. Attempting to perform this task in VBA will
cause you trouble if you plan on working internationally.You may not need these routines often, but when you do, the API
versions are both faster and more reliable than handwritten code
would be. Don't count on specific ANSI values to be
certain characters, uppercase or lowercase, because these values
change from version to version of internationalized Windows.