Team LiB
Previous Section Next Section

The Keyboard and You

You have used the keyboard KeyPress event to grant access to or deny certain keys. This is called on-the-fly validation. It works quite well in the case of allowing only certain characters to be entered in a TextBox.

The KeyPress event is not the only key event you can use, however. You can use two others: the KeyDown event and the KeyUp event. These events come in the following order:

Why have three events? Because you can get different information about what happened from each event.

The KeyDown event is raised every time a key is pressed. This means that if a user holds a key down, this event is raised for each repeated key.

The KeyUp event is raised when the user lets up on a key. If the user holds down the letter K for 20 seconds, you will get a few dozen KeyDown events and only one KeyUp event. In most cases, a KeyDown event is used to initiate some action and a KeyUp event is used to end it.

The KeyPress event is where you trap most of the keystrokes. It gets passed a KeyPressEventArgs object that allows you to compare a key that is pressed against a set of keys that you allow. It also allows you to discard the key if you don't want it.

The KeyDown and KeyUp delegates have the same signature. That is, they both get passed in a KeyEventArgs object. Table 4-1 shows what you get from the KeyEventArgs object.

Table 4-1: KeyEventArgs Properties

Method/Property

Meaning

Alt

True if Alt is pressed; false otherwise

Control

True if Ctrl is pressed; false otherwise

Shift

True if Shift is pressed; false otherwise

Modifiers

Bitwise combination of all modifier keys pressed (see Keys enum)

KeyData

KeyCode combined with modifier keys

KeyValue

Integer representation of KeyData information

KeyCode

The keyboard code for the key pressed

Handled

True if the event was handled; false otherwise

There is quite a bit of information in this object. The following small example should give you a good handle on how you can use these properties.

Open up a new C# or VB Windows project. Mine is called "Keys." Add eight labels, four of which have text in them and the other four of which are empty but have a visible border. Figure 4-1 shows you what your screen should look like.

Click To expand
Figure 4-1: The key press display

Can you guess why I did not put any buttons or controls that can receive focus on this form? If I did that, then the form itself could never receive focus and I could not capture key events that happen on the form. (I cover a way around this later in the chapter.)

Now for the code. This is so simple, I show only the C# code here. Make a KeyDown delegate and assign it to the form's KeyDown event.

KeyDown Delegate

    private void MyKeyDown(object sender, KeyEventArgs e)
    {
      keycode.Text = e.KeyCode.ToString();
      keydata.Text = e.KeyData.ToString();
      keyvalue.Text = e.KeyValue.ToString();
      keymod.Text = e.Modifiers.ToString();
    }

Constructor

    public frmKeys()
    {
      InitializeComponent();

      //This is only valid if the form has focus
      //The form only has focus if there are no visible
      controls that can accept focus on it.
      this.KeyDown += new KeyEventHandler(this.MyKeyDown);
    }

Compile and run the program. Start pressing keys. When you press Alt-Ctrl-H, you should see something similar to Figure 4-2.

Click To expand
Figure 4-2: The result of pressing keys

What is important to see here, and I am sure you noticed it, is that you get this event for every key that is pressed. So in this case you got three events. You can trap any key or key combination you want.

If you have trapped the KeyDown event and the KeyUp event handler also has the same signature, why trap this KeyUp event? There are two reasons, really. As I already said, the KeyUp event happens only once per key press. It also has one other feature. Suppose you pressed Ctrl-Alt-Enter. How would you know which key was the last up? The KeyUp event will tell you this.

Before I get to the next example I want to mention the KeyPress event. It has a signature that includes the KeyPressEventArgs object. This object does not break down the individual keys that were pressed like the KeyEventArgs object does. It provides only the resulting composite key that was pressed. It gives you an actual Char data type as a result.

What you can do with this event is provide a delegate that checks this value and lets it through or throws it away before the control actually gets it. I have shown you this already in the three previous chapters.

The only good way to see what goes on with these three events is to see the interaction of all three at once. You will do this by extending your "Keys" example:

  1. Put all the existing Labels inside a GroupBox on your screen. The GroupBox should read KeyDown event.

  2. Change the bordered labels to lblDownCode, lblDownData, lblDownValue, and lblDownMod, respectively.

  3. Add a new Label inside this GroupBox called lblDownTime. I set the Label's background color to Linen.

Your screen should look like Figure 4-3.

Click To expand
Figure 4-3: The KeyDown GroupBox

Then follow these steps:

  1. Add a new Label below the GroupBox whose text reads Key Press.

  2. Add a new Label called lblPress next to the Label you created in step 1.

  3. Add a linen-colored Label called lblPressTime below the Labels you created in steps 1 and 2.

Your screen should now look like Figure 4-4.

Click To expand
Figure 4-4: Adding a Key Press display

Complete the form by following these steps:

  1. Copy the KeyDown GroupBox and add the new GroupBox below the Key Press time label.

  2. Change the GroupBox's text to KeyUp event.

  3. Rename the new time label to lblUpTime.

  4. Change the bordered labels to lblUpCode, lblUpData, lblUpValue, and lblUpMod, respectively.

Figure 4-5 shows the complete form.

Click To expand
Figure 4-5: The completed key event form

Now for the code. Listings 4-1a and 4-1b show the code for this example.

Listing 4-1a: C# Code for the Complete Keys Example
Start example
using System;
using System.Threading;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace Keys_c
{
  /// <summary>
  /// Shows interaction of key events
  /// </summary>
  public class frmKeys : System.Windows.Forms.Form
  {
    private System.Windows.Forms.GroupBox groupBox1;
    private System.Windows.Forms.Label label7;
    private System.Windows.Forms.Label label5;
    private System.Windows.Forms.Label label3;
    private System.Windows.Forms.Label label2;
    private System.Windows.Forms.Label lblDownTime;
    private System.Windows.Forms.GroupBox groupBox2;
    private System.Windows.Forms.Label lblUpTime;
    private System.Windows.Forms.Label label10;
    private System.Windows.Forms.Label label12;
    private System.Windows.Forms.Label label14;
    private System.Windows.Forms.Label label16;
    private System.Windows.Forms.Label label9;
    private System.Windows.Forms.Label lblPress;
    private System.Windows.Forms.Label lblPressTime;
    private System.Windows.Forms.Label lblUpMod;
    private System.Windows.Forms.Label lblUpValue;
    private System.Windows.Forms.Label lblUpData;
    private System.Windows.Forms.Label lblUpCode;
    private System.Windows.Forms.Label lblDownMod;
    private System.Windows.Forms.Label lblDownValue;
    private System.Windows.Forms.Label lblDownData;
    private System.Windows.Forms.Label lblDownCode;

    private System.ComponentModel.Container components = null;

    public frmKeys()
    {
      InitializeComponent();

      //This is only valid if the form has focus
      //The form only has focus if there are no visible controls on it.
      this.KeyDown += new KeyEventHandler(this.MyKeyDown);
      this.KeyPress += new KeyPressEventHandler(this.MyKeyPress);
      this.KeyUp += new KeyEventHandler(this.MyKeyUp);

    }


    protected override void Dispose( bool disposing )
    {
      if( disposing )
      {
        if (components != null)
        {
          components.Dispose();
        }
      }
      base.Dispose( disposing );
    }

    #region Windows Form Designer generated code
...
...
...
    #endregion


    [STAThread]
    static void Main()
    {
      Application.Run(new frmKeys());
    }

    private void frmKeys_Load(object sender, System.EventArgs e)
    {
    }

    #region events


    private void MyKeyDown(object sender, KeyEventArgs e)
    {
      lblDownCode.Text = e.KeyCode.ToString();
      lblDownData.Text = e.KeyData.ToString();
      lblDownValue.Text = e.KeyValue.ToString();
      lblDownMod.Text = e.Modifiers.ToString();

      lblDownTime.Text = DateTime.Now.Ticks.ToString();
      Thread.Sleep(2);

    }

    private void MyKeyPress(object sender, KeyPressEventArgs e)
    {
      lblPress.Text = e.KeyChar.ToString();
      lblPressTime.Text = DateTime.Now.Ticks.ToString();
      Thread.Sleep(2);

    }

    private void MyKeyUp(object sender, KeyEventArgs e)
    {
      lblUpCode.Text = e.KeyCode.ToString();
      lblUpData.Text = e.KeyData.ToString();
      lblUpValue.Text = e.KeyValue.ToString();
      lblUpMod.Text = e.Modifiers.ToString();

      lblUpTime.Text = DateTime.Now.Ticks.ToString();
      Thread.Sleep(2);

    }

    #endregion

    }
  }
End example
Listing 4-1b: VB Code for the Complete Keys Example
Start example
Option Strict On

Imports System.Threading

Public Class frmKeys
  Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "
...
...
...
#End Region

  Private Sub frmKeys_Load(ByVal sender As System.Object, _
                           ByVal e As System.EventArgs) Handles MyBase.Load
    AddHandler Me.KeyDown, AddressOf MyKeyDown
    AddHandler Me.KeyPress, AddressOf MyKeyPress
    AddHandler Me.KeyUp, AddressOf MyKeyUp

  End Sub

#Region "events"


  Private Sub MyKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
    lblDownCode.Text = e.KeyCode.ToString()
    lblDownData.Text = e.KeyData.ToString()
    lblDownValue.Text = e.KeyValue.ToString()
    lblDownMod.Text = e.Modifiers.ToString()

    lblDownTime.Text = DateTime.Now.Ticks.ToString()
    Thread.Sleep(2)

  End Sub

  Private Sub MyKeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs)

    lblPress.Text = e.KeyChar.ToString()
    lblPressTime.Text = DateTime.Now.Ticks.ToString()
    Thread.Sleep(2)

  End Sub

  Private Sub MyKeyUp(ByVal sender As Object, ByVal e As KeyEventArgs)
    lblUpCode.Text = e.KeyCode.ToString()
    lblUpData.Text = e.KeyData.ToString()
    lblUpValue.Text = e.KeyValue.ToString()
    lblUpMod.Text = e.Modifiers.ToString()

    lblUpTime.Text = DateTime.Now.Ticks.ToString()
    Thread.Sleep(2)

  End Sub

#End Region
End Class
End example

Notice that I included a reference to the Threading namespace. I make each delegate go to sleep for 2 milliseconds so you can better see the time difference between events.

Run this program and press all kinds of keys while the form is in focus. You will see the order of events, which key in a key combination was pressed first, and which key in a key combination was released last.

Press a key until the key repeat kicks in. You will see the KeyDown delegate get called repeatedly. You can see this by looking at the continuously changing time for this event.

Spend some time with this example and play around with the keyboard. Once you see all the keyboard events in action, you may get some ideas on how to use each event in your data entry program. Although this example is fairly simple in construction, you can use what you learn here in some fairly complicated situations.

There's one thing to remember about the key events (as a matter of fact, this goes for any of the standard class events). You should test to see if the Sender object is valid and if the argument object is valid. With most events, there's a way to manually invoke the event using the On<event> statement. In other words, if you want to invoke the KeyDown delegate you call OnKeyDown with the proper KeyEventArgs argument. The same kind of thing goes for the KeyUp and KeyPress events. For these, you use the OnKeyUp and OnKeyPress methods, respectively. Now, why would you do this for a key event?

What if you allow data to be entered in your program through some other means than the keyboard? For instance, you could be reading information from a file, over the network, via a modem, and so on. You could take one route and bypass your validation code for your specialized TextBox and do something like this:

TextBox1.text = SomeValue

Doing this bypasses any on-the-fly validation code you may have in the KeyPress delegate. If all you want is numbers in this control, you could force alpha characters in here as well.

A better way is to read each character as it comes in and pass it to the TextBox control via the OnKeyPress method. This way, you can fill the TextBox with information without regard to what that information is. You know that the KeyPress delegate with your validation code will take care if it. (Chapter 6 shows you just this scenario.)

The caveat I was talking about is that the following line of code is perfectly valid.

C#

OnKeyPress(null);

VB

OnKeyPress(Nothing)

Your nicely coded KeyPress delegate would crash and burn. If you have decided that your delegate should never get called with a null argument, the best thing you could do is use an assertion as the first line of code in your delegate. An assertion is used only in debug mode. This way, you can put all kinds of boneheaded error-checking code that never gets compiled into the release version of your program. This makes for faster release code. To use assertions, you need to include the System.Diagnostics namespace. Here is the C# code for the KeyDown delegate with an assertion:

    private void MyKeyDown(object sender, KeyEventArgs e)
    {
      Debug.Assert(e != null, "BoneHead call");

      lblDownCode.Text = e.KeyCode.ToString();
      lblDownData.Text = e.KeyData.ToString();
      lblDownValue.Text = e.KeyValue.ToString();
      lblDownMod.Text = e.Modifiers.ToString();

      lblDownTime.Text = DateTime.Now.Ticks.ToString();
      Thread.Sleep(2);
    }

Check out the System.Diagnostics namespace. It has a gaggle of classes that are very handy for debugging.


Team LiB
Previous Section Next Section