WindowsDevCenter.com
oreilly.comSafari Books Online.Conferences.

advertisement


AddThis Social Bookmark Button

C# Key Processing Techniques
Pages: 1, 2

Now, let's discuss the KeyPressed method that is called every time a key is pressed. First, the KeyPressed method must be registered to become the handler of the KeyPress event. We do it in the class's constructor.


this.KeyPress += new KeyPressEventHandler(KeyPressed);

The handling of keys is not that complex. Basically, our simple WhiteBoard control accepts all alphanumeric characters and punctuation marks. The method also detects the keying of backspace (ASCII 8) and makes backspace function properly.


      char c = e.KeyChar;
      int i = (int) c;
      if (i==8)
      {
        if (caretX==0)
        {
          caretX = columnCount - 1;
          caretY--;
          if (caretY<0)
            caretY = rowCount - 1;
        }
        else
        {
          caretX--;
        }
        board[caretX, caretY] = ' ';

For non-backspace characters, the method moves the caret forward by one character.


        board[caretX, caretY] = c;
        caretX++;
        if (caretX == columnCount) 
        {
          caretX = 0;
          caretY++;
          if(caretY== rowCount)
            caretY = 0;
        }
      }

It then invalidates the control so that the Graphics object is repainted. Calling the Invalidate method without an argument will repaint the whole area of the Graphics object.


      this.Invalidate();
      this.Update();

The code in Listing 2 is a form that uses the WhiteBoard control.

Listing 2. A form that uses the WhiteBoard control


using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing;

namespace KeyProcessor
{
	public class Form1 : System.Windows.Forms.Form
	{
		private KeyProcessor.WhiteBoard whiteBoard;
		private System.ComponentModel.Container components = null;

		public Form1()
		{
			InitializeComponent();
		}

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

		#region Windows Form Designer generated code

    private void InitializeComponent()
		{
      whiteBoard= new WhiteBoard();
      this.SuspendLayout();
      whiteBoard.Location = new Point(20,20);
      whiteBoard.Size = new Size(190, 220);

      this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
      this.ClientSize = new System.Drawing.Size(292, 273);
      this.Controls.AddRange(new System.Windows.Forms.Control[] {whiteBoard});
      this.Name = "Form1";
      this.Text = "Small Whiteboard";
      this.ResumeLayout(false);

    }
		#endregion

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

When you compile and run the code in Listing 2 for the first time, you'll probably get excited that so little code produces a text area with a caret that flashes on and off! After a while, however, you'll notice that our WhiteBoard control is not perfect. Here is a list of the control's imperfections. You can probably add more items to this list.

  • Pressing Ctrl+H fools the control into thinking that the backspace key has been pressed. In most text processing applications, Ctrl+H is used to display the Replace dialog box.
  • The arrow keys don't move the caret.
  • Pressing the Control key in combination with an alphanumeric character sends an ASCII character that has no visual representation (those with values less than 27). This will be displayed as a small box, as shown in Figure 2.

Figure 2. Pressing the control character and an alphanumeric
character at the same time results in a box.

So, you can see that KeyPress can only handle characters. While this limitation does not create a fuss for small and simple applications, most applications require the proper handling of all keys, not just alphanumeric and punctuation mark keys. The KeyDown event allows you to capture function keys, but not arrow keys. The KeyUp event lets you capture all keys, but this event is only triggered when the user releases the key, by which time it is probably too late to handle the key pressing. The next technique we can resort to is the ProcessDialogKey method, which enables us to capture all keys.

Processing All Keys

The ProcessDialogKey method in the System.Windows.Forms.Control class is called automatically when a key or a combination of keys on the keyboard is pressed. Unlike the KeyPress event, ProcessDialogKey can capture any key, including the Control keys. However, it does not give you the character associated with the key; it only tells you which key is being pressed. For example, it tells you the A key was pressed, but it doesn't tell you whether the character should be the capital or lower-case A. There is a way to check the character case, but this requires more code.

For a simpler solution, ProcessDialogKey can be used in conjunction with the KeyPress event. The KeyPress event is invoked after the ProcessDialogKey method is called. Therefore, you can use a flag to tell the KeyPress event handler whether or not it needs to handle a key press. This flag is controlled by the ProcessDialogKey method. If the key press is a combination of control keys and a character key, the ProcessDialogKey will handle it and reset the flag. On the other hand, if the key pressed is a character key, the ProcessDialogKey will set the flag and let the KeyPress event handler take care of the key press. When overriding the ProcessDialogKey method, you should return true when the key was handled and false otherwise.

Normally, when your ProcessDialogKey method does not process the key, you call the ProcessDialogKey method of the base class and return whatever value is returned by the base class's ProcessDialogKey method. The ProcessDialogKey method receives one of the System.Windows.Forms.Keys enumeration values as its argument. The Keys enumeration allows a bitwise combination of its values. You should look up the .NET Framework class library reference for the list of values of the Keys enumeration.

The argument sent to the ProcessDialogKey method depends on the key(s) being pressed. For instance, if the user pressed the A key, the method will receive Keys.A; if Ctrl+S is pressed, the value sent is the bitwise combination of the Control key and the S key. The down arrow sends Keys.Down, the up arrow Keys.Up, and the right and left arrows Keys.Right and Keys.Left respectively. The F1 sends Keys.F1 and Alt+F4 sends the bitwise combination of Keys.Alt and Keys.F4. When the Shift key is pressed, the method receives Keys.Shift, enabling you to know whether an upper/lower case character is being sent by the user.

The modified version of the WhiteBoard control in Listing 3 overcomes the "flaws" in the code in Listing 1 by also handling control keys via the overriding of the ProcessDialogKey. It uses a flag called keystrokeProcessed to indicate whether or not the KeyPress event handler needs to handle the key press. The code in Listing 3 is similar to the code in Listing 1, except that we now override the ProcessDialogKey method, and that the KeyPress event handler is only executed if the key has not been handled by ProcessDialogKey.

The overriding ProcessDialogKey in Listing 3 captures the following keys:

  • Arrow keys: move the caret.
  • Ctrl+R, Ctrl+G, Ctrl+B: change the background color to red, green, and blue, respectively.
  • Ctrl+Alt+R, Ctrl+Alt+G, Ctrl+Alt+B: change the text color to red, green, and blue, respectively.
  • Escape: changes the background color back to white.
  • Alt+F4: exits the application.
  • F1: displays a message box.

You can use the code in Listing 2 to see the modified control in action. Figure 3 shows the control with a red foreground color.


Figure 3. The modified control that can handle all keys

View Listing 3: Handling All Keys

Conclusion

Key processing is one of the most important tasks in Windows programming, used to capture and process user keyboard input. The System.Windows.Forms.Control triggers the KeyDown, KeyPress, and KeyUp events when a key is pressed; the easiest way to process a key press is by providing a handler for the KeyPress event. However, the KeyPress event only handles characters, and is useless when a control key or a combination of control and character keys are pressed. For this, the ProcessDialogKey method can be overriden to provide the handling of control keys. The ProcessDialogKey method does not give the character when a character key is pressed, therefore ProcessDialogKey can be used in conjunction with the KeyPress event for simple key press handling.

Budi Kurniawan is a senior J2EE architect and author.


Return to the .NET DevCenter.