C# Key Processing Techniques
2004-11-24 20:07
489 查看
The keys on a computer keyboard, unlike those in a conventional typewriter, fall into several categories:
Normal input characters, including alphanumeric characters and punctuation marks.
Function keys: F1 to F12.
Control keys: Control, Alt, Shift, the arrow keys, and others.
Processing the complete set of keys not only requires the detection of the pressing of individual key, but also the pressing of keys in combination. For this article, I have written two versions of a user control called WhiteBoard. The first version, given in Listing 1, captures keys in the first category; however, it cannot capture function and control keys. The second version, given in Listing 3, captures all keys.
Both versions are covered in the sections "Processing Characters" and "Processing All Keys." Note that in my testing with the current version of the .NET Framework SDK (version 1.0), the
Processing Characters
If the user presses a key on the keyboard when a control has focus, three events of the control are triggered. The three key events occur in the following order:
KeyDown. Occurs when the user starts pressing the key, i.e., when the key is down.
KeyPress. Occurs when a key is pressed, after the
KeyUp. Occurs when the user releases the key.
The easiest way to capture keyboard input from the user is to use the
Handled. A boolean indicating whether the key has been handled.
KeyChar. A read-only property from which the corresponding character of the pressed key can be obtained.
Since the
char c = e.KeyChar;
int i = (int) c;
The backspace key will have an integer value of 8 and the carriage-return key 13. The use of the KeyPress event is illustrated in the WhiteBoard control displayed in a form in Figure 1.
Figure 1. The WhiteBoard control that captures characters.
The WhiteBoard control extends the
As can be seen in Figure 1, the WhiteBoard control is a two-dimensional array of characters that has a visual interface. The two-dimensional array is represented by the variable
The dimensions are indicated by the variables
[/code]
Every time the
Like most decent text-based controls, our WhiteBoard control uses a caret to tell the user the location of the current character insertion point. That's right, a caret does not come free. You have to draw your own caret and make it blink (animate). In our WhiteBoard control, the location of the caret is determined by two integers:
The animation of the caret is achieved by the use of the
Therefore, when the control is disposed, the
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.
[/code]For non-backspace characters, the method moves the caret forward by one character.
Listing 2. A form that uses the WhiteBoard control
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
For a simpler solution,
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
The overriding
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[/i]
Budi Kurniawan is an IT consultant specializing in Internet and object-oriented programming, and has taught both Microsoft and Java technologies.
[/code]
Normal input characters, including alphanumeric characters and punctuation marks.
Function keys: F1 to F12.
Control keys: Control, Alt, Shift, the arrow keys, and others.
Processing the complete set of keys not only requires the detection of the pressing of individual key, but also the pressing of keys in combination. For this article, I have written two versions of a user control called WhiteBoard. The first version, given in Listing 1, captures keys in the first category; however, it cannot capture function and control keys. The second version, given in Listing 3, captures all keys.
Both versions are covered in the sections "Processing Characters" and "Processing All Keys." Note that in my testing with the current version of the .NET Framework SDK (version 1.0), the
KeyDownevent in
System.Windows.Forms.Controlis not triggered when the user presses an arrow key. However, the
KeyDownevent in
System.Windows.Forms.Formis raised when an arrow key is pressed. The
Formclass extends the
ContainerControlclass, which extends the
ScrollableControlclass. The
ScrollableControlclass is a direct child class of the
Controlclass. The class library reference in the .NET Framework SDK documentation does not state that the
KeyDownevent or the
OnKeyDownmethod are overriden in any of
ContainerControl,
ScrollableControl, or
Form. Therefore, the behavior of this event in both the
Formand the
Controlclasses must be the same. This leads to the following possibilities: either the documentation is not up to date or there is a bug in the class library.
Processing Characters
If the user presses a key on the keyboard when a control has focus, three events of the control are triggered. The three key events occur in the following order:
KeyDown. Occurs when the user starts pressing the key, i.e., when the key is down.
KeyPress. Occurs when a key is pressed, after the
KeyDownevent is triggered.
KeyUp. Occurs when the user releases the key.
The easiest way to capture keyboard input from the user is to use the
KeyPressevent of a control. The event handler for this event receives a
System.Windows.Forms.KeyPressEventArgsobject containing two properties:
Handled. A boolean indicating whether the key has been handled.
KeyChar. A read-only property from which the corresponding character of the pressed key can be obtained.
Since the
KeyCharproperty gives you the character of the key being pressed, displaying the character, for example, is very straightforward. However, some keys do not have visual representation and are not meant to be displayed. The backspace key, for instance, is normally used in a text-based control to delete the character to the left of the caret and move the caret back one character. In this case, you can simply convert the character into an integer and compare the integer with the ASCII value of the character.
char c = e.KeyChar;
int i = (int) c;
The backspace key will have an integer value of 8 and the carriage-return key 13. The use of the KeyPress event is illustrated in the WhiteBoard control displayed in a form in Figure 1.
Figure 1. The WhiteBoard control that captures characters.
The WhiteBoard control extends the
System.Windows.Forms.UserControlclass and its code is given in Listing 1.
As can be seen in Figure 1, the WhiteBoard control is a two-dimensional array of characters that has a visual interface. The two-dimensional array is represented by the variable
board.
private char[,] board;
The dimensions are indicated by the variables
columnCountand
rowCount.
board = new char[columnCount, rowCount];
[/code]
Every time the
OnPaintmethod of the control is invoked, the value of each element of the array is drawn using the
DrawStringmethod of the
Graphicsobject of the control. The location of each character is determined by
characterWidth,
characterHeight, and
lineSpace. The latter indicates the distance between two lines in pixels.
protected override void OnPaint(PaintEventArgs e) { Graphics graphics = e.Graphics; Font font = new Font("Courier new", characterWidth); Brush brush = new SolidBrush(foreColor); for (int i=0; i<rowCount; i++) { for (int j=0; j<columnCount; j++) { graphics.DrawString(board[i, j].ToString(), font, brush, new Point(i*characterWidth, j*(lineSpace+characterHeight))); } } . . .
Like most decent text-based controls, our WhiteBoard control uses a caret to tell the user the location of the current character insertion point. That's right, a caret does not come free. You have to draw your own caret and make it blink (animate). In our WhiteBoard control, the location of the caret is determined by two integers:
caretXand
caretY. They indicate the horizontal and vertical coordinates in the visual area. Every time the user presses a key, the caret is moved forward by one character. When it reaches the last column of the last line, it will move back to the first column of the first line.
The animation of the caret is achieved by the use of the
System.Threading.Threadclass. Surprisingly, thanks to the classes in the
System.Threadingnamespace, multi-threaded programming is relatively easy. The
Threadclass represents a thread in the program. In the WhiteBoard control, we use a thread called
caretThreadfor displaying and animating the caret in the right position:
private Thread caretThread;. When the control is created,
caretThreadmust already be ready. Therefore, we start the caret thread in the class's constructor.
caretThread = new Thread(new ThreadStart(ShowCaret)); caretThread.Start();In the above code, the first line constructs a
Threadobject and informs the thread that it is to handle the
ShowCaretmethod. The second line starts the thread, i.e., starts executing the
ShowCaretmethod. Before we look at the
ShowCaretmethod, however, bear in mind that you are responsible for stopping and destroying the thread when it is no longer needed. Failure to do so will make the program unable to exit properly. In our WhiteBoard control, this is handled by overriding the control's
Disposemethod.
protected override void Dispose(bool disposing) { if (disposing) { caretThread.Abort(); } base.Dispose(disposing); }
Therefore, when the control is disposed, the
Threadclass's
Abortmethod will be called to terminate the thread. The
ShowCaretmethod, as seen in Listing 1, employs an indefinite
whileloop that makes the thread execute the same piece of code. The code in this
whileloop is simple. It invalidates the part of the
Graphicsobject that is occupied by the caret. The
Updatemethod is then called. This will invoke the
OnPaintmethod, but the control will only redraw the area indicated by the rectangle passed to the
Invalidatemethod. Whether or not the caret is visible is determined by the
caretVisiblevariable. If it is true, the vertical line is drawn. Otherwise, nothing is drawn, making the caret invisible. The rate at which the caret blinks is determined by the value passed to the
Sleepmethod of the
Threadclass. Here we use 350 (milliseconds). The
caretVisiblevariable is also toggled to create the blinking effect.
this.Invalidate( new Rectangle(caretX * characterWidth, caretY * (characterHeight + lineSpace), caretX * characterWidth + 2 * penWidth, (caretY +1) * (characterHeight + lineSpace))); this.Update(); Thread.Sleep(350); caretVisible = !caretVisible; The caret itself is drawn when the OnPaint method is called: //draw caret here; if (caretVisible) { int x = caretX * characterWidth; int y = caretY * (lineSpace + characterHeight); graphics.DrawLine(pen, x, y, x, y + lineSpace + characterHeight); } Now, let's discuss themethod must be registered to become the handler of theKeyPressedmethod that is called every time a key is pressed. First, the [code]KeyPressed
KeyPressevent. 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] = ' ';
[/code]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
Graphicsobject is repainted. Calling the
Invalidatemethod without an argument will repaint the whole area of the
Graphicsobject.
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
KeyPresscan 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
KeyDownevent allows you to capture function keys, but not arrow keys. The
KeyUpevent 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
ProcessDialogKeymethod, which enables us to capture all keys.
Processing All Keys
TheProcessDialogKeymethod in the
System.Windows.Forms.Controlclass is called automatically when a key or a combination of keys on the keyboard is pressed. Unlike the
KeyPressevent,
ProcessDialogKeycan 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,
ProcessDialogKeycan be used in conjunction with the
KeyPressevent. The
KeyPressevent is invoked after the
ProcessDialogKeymethod is called. Therefore, you can use a flag to tell the
KeyPressevent handler whether or not it needs to handle a key press. This flag is controlled by the
ProcessDialogKeymethod. If the key press is a combination of control keys and a character key, the
ProcessDialogKeywill handle it and reset the flag. On the other hand, if the key pressed is a character key, the
ProcessDialogKeywill set the flag and let the
KeyPressevent handler take care of the key press. When overriding the
ProcessDialogKeymethod, you should return
truewhen the key was handled and
falseotherwise. Normally, when your
ProcessDialogKeymethod does not process the key, you call the
ProcessDialogKeymethod of the base class and return whatever value is returned by the base class's
ProcessDialogKeymethod. The
ProcessDialogKeymethod receives one of the
System.Windows.Forms.Keysenumeration values as its argument. The
Keysenumeration allows a bitwise combination of its values. You should look up the .NET Framework class library reference for the list of values of the
Keysenumeration. The argument sent to the
ProcessDialogKeymethod 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.Rightand
Keys.Leftrespectively. The F1 sends
Keys.F1and Alt+F4 sends the bitwise combination of
Keys.Altand
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
keystrokeProcessedto indicate whether or not the
KeyPressevent 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
ProcessDialogKeymethod, and that the
KeyPressevent handler is only executed if the key has not been handled by
ProcessDialogKey.
The overriding
ProcessDialogKeyin 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[/i]
Conclusion
Key processing is one of the most important tasks in Windows programming, used to capture and process user keyboard input. TheSystem.Windows.Forms.Controltriggers the
KeyDown,
KeyPress, and
KeyUpevents when a key is pressed; the easiest way to process a key press is by providing a handler for the
KeyPressevent. However, the
KeyPressevent only handles characters, and is useless when a control key or a combination of control and character keys are pressed. For this, the
ProcessDialogKeymethod can be overriden to provide the handling of control keys. The
ProcessDialogKeymethod does not give the character when a character key is pressed, therefore
ProcessDialogKeycan be used in conjunction with the
KeyPressevent for simple key press handling.
Budi Kurniawan is an IT consultant specializing in Internet and object-oriented programming, and has taught both Microsoft and Java technologies.
[/code]
相关文章推荐
- C#对称加密与非对称加密实例
- C#添加、读取Word脚注尾注的方法
- 谁来关心我们这些程序员
- ATL实现Connection Point的一种简单的方法
- 哪位仁兄帮一下编个小程序
- c#接简单数据库操作类
- 一个非常不错的业务规则管理器
- DirectX.Capture
- B/S结构系统
- 学ASP.NET学什么?
- 使用C#开发COM+组件
- 初步研究方向 web services and VR
- 纯编码实现数据库的建立或压缩
- 从数据库中动态选取下拉列表的方法
- 用ASP备份数据库
- thinking in c++ 卷2
- thinking in c++卷2
- thinkng in c++卷2
- 初学者的问题----csdn系列
- 【Python之旅】第四篇(四):基于面向对象的模拟人生游戏类