原文地址:
http://msdn.microsoft.com/zh-cn/library/ms646268(en-us,VS.85).aspx
窗体以按键消息(keystroke message)和字符消息(character message)的方式接收键盘输入。拥有消息循环的窗体,必须包含将按键消息翻译成相对应的字符消息的代码。如果窗体要在它的客户区(client area)显示键盘输入,那么应该创建并显示一个输入光标(caret)来指示下一个字符将要被输入的位置。下面几个小节将告诉你如何编写涉及接收、处理和显示键盘输入的代码。
处理按键消息
翻译字符消息
处理字符消息
使用输入指示光标
显示键盘输入
1.处理按键消息
当使用者按下按键时,拥有输入焦点的窗口的窗口消息处理函数(Window Procedure)将会接收到按键消息。按键消息有WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN和WM_SYSKEYUP。一般的窗口消息处理函数会忽略所有按键消息,除了WM_KEYDOWN。当使用者按下一个按键时,系统会Post WM_KEYDOWN消息。
当窗体消息处理函数接收到WM_KEYDOWN消息,应该检查消息中包含的虚拟码,然后决定如何处理按键消息(it should examine the virtual-key code that accompanies the message to determine how to process the keystroke)。虚拟码被保存在消息的wParam参数中。一般的,应用程序只处理产生自非字符按键(noncharacter keys)的按键消息,包括功能键(function keys)、光标移动按键(cursor movement keys)和专用按键(special purpose keys),比如INS、DEL、HOME和END。
下面的例子展示了一般应用程序的窗口消息处理函数如何接收并处理按键消息
引用:
case WM_KEYDOWN:
switch (wParam)
{
case VK_LEFT:
// Process the LEFT ARROW key.
break;
case VK_RIGHT:
// Process the RIGHT ARROW key.
break;
case VK_UP:
// Process the UP ARROW key.
break;
case VK_DOWN:
// Process the DOWN ARROW key.
break;
case VK_HOME:
// Process the HOME key.
break;
case VK_END:
// Process the END key.
break;
case VK_INSERT:
// Process the INS key.
break;
case VK_DELETE:
// Process the DEL key.
break;
case VK_F2:
// Process the F2 key.
break;
// Process other non-character keystrokes.
default:
break;
}
2.翻译字符消息
任何能够接收字符输入的线程都必须在他的消息循环中包含TranslateMessage函数。这个函数能够检查按键的虚拟码,如果虚拟码可以对应一个字符,就往消息队列(Message Queue)发送一条字符消息。字符消息将在下一次消息循环的重复中被移除或发送(The character message is removed and dispatched on the next iteration of the message loop)。消息的wParam参数包含了字符码(character code)。
一般来说,线程的消息循环应该使用TranslateMessage函数来翻译所有的消息,不仅仅是虚拟按键消息(not just virtual-key messages),即使TranslateMessage对其他类型的消息没有效果。但是这能保证键盘输入被正确翻译。
下面的例子展示了如何在一般线程的消息循环中使用TranslateMessage函数
引用:
MSG msg;
BOOL bRet;
while (( bRet = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0)
{
if (bRet == -1);
{
// handle the error and possibly exit
}
else
{
if (TranslateAccelerator(hwndMain, haccl, &msg) == 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
3.处理字符消息
当使用TranslateMessage函数将一个虚拟码翻译成对应的字符键时,窗口消息处理函数会接收到字符消息。字符消息包括WM_CHAR、WM_DEADCHAR、WM_SYSCHAR和WM_SYSDEADCHAR。一般来说,窗口消息处理程序会忽略所有字符消息,除了WM_CHAR。当使用者按下下面的任一键时,TranslateMessage函数会产生WM_CHAR消息:
l任何字符键
lBACKSPACE
lENTER
lESC
lSHIFT+ENTER
lTAB
当窗口消息处理函数接收到WM_CHAR消息时,应该检查消息中的字符码,决定如何处理字符。字符码保存在消息的wParam参数中。
下面的例子展示了一般的窗口消息处理函数如何接受并处理字符消息:
引用:
case WM_CHAR:
switch (wParam)
{
case 0x08:
// Process a backspace.
break;
case 0x0A:
// Process a linefeed.
break;
case 0x1B:
// Process an escape.
break;
case 0x09:
// Process a tab.
break;
case 0x0D:
// Process a carriage return.
break;
default:
// Process displayable characters.
break; }
4.使用输入指示光标
一个接收键盘输入的窗体一般会在窗体的客户区显示使用者键入的字符。窗体应该使用输入光标来指示下一个字符将会出现的位置(A window should use a caret to indicate the position in the client area where the next character will appear)。当一个窗口拥有输入焦点时,应该创建并显示指示光标。而当一个窗口失去焦点时,应该隐藏或销毁指示光标。窗体可以在处理WM_SETFOCUS和WM_KILLFOCUS消息时执行这些操作。
5.显示键盘输入
这个小节中的例子展示了一个应用程序如何能从键盘中接收字符消息,在窗体的客户区显示他们。并且在键入字符后如何更新输入光标的位置。例子也示范了如何在LEFT ARROW、RIGHT ARROW、HOME和END按键之后相应的移动输入光标。并且显示了在按下SHIFT+RIGHT组合键之后如何高亮选中的字符。
在处理WM_CREATE消息期间,例子中的窗体消息处理函数分配了一个64K的缓冲区来保存键盘输入。他也会取回当前加载字体的度量(It also retrieves the metrics of the currently loaded font),并保存字体中字符的高度和平均宽度。高度和宽度用于处理WM_SIZE消息,计算行的长度和每行最大可容纳的字符数,以客户取得大小为基准(The height and width are used in processing the WM_SIZE message to calculate the line length and maximum number of lines, based on the size of the client area. )。
窗体消息处理函数会在处理WM_SETFOCUS消息时创建并显示输入光标。输入光标会在处理WM_KILLFOCUS消息时被隐藏或删除。
在处理WM_CHAR消息时,窗体消息处理函数会显示字符,并且在输入缓冲区中保存它们,然后更新输入光标的位置。窗体消息处理函数也会将TAB字符转换成4个连续的空格字符。Backspace、Linefeed和Escape字符会产生鸣叫(Beep),但是并不做不同的处理(but are not otherwise processed)。
窗体消息处理函数会在处理WM_KEYDOWN消息时,执行LEFT、RIGHT、END和HOME输入光标的移动(The window procedure performs the left, right, end, and home caret movements when processing the WM_KEYDOWN message)。当处理RIGHT ARROW键时,窗口消息处理函数会检查SHIFT的状态。如果SHIFT被按下,则会选中输入光标右边的字符,并且输入光标也会往右移动(selects the character to the right of the caret as the caret is moved. )。
引用:
#define BUFSIZE 65535
#define SHIFTED 0x8000
LONG APIENTRY MainWndProc(HWND hwndMain, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc; // handle to device context
TEXTMETRIC tm; // structure for text metrics
static DWORD dwCharX; // average width of characters
static DWORD dwCharY; // height of characters
static DWORD dwClientX; // width of client area
static DWORD dwClientY; // height of client area
static DWORD dwLineLen; // line length
static DWORD dwLines; // text lines in client area
static int nCaretPosX = 0; // horizontal position of caret
static int nCaretPosY = 0; // vertical position of caret
static int nCharWidth = 0; // width of a character
static int cch = 0; // characters in buffer
static int nCurChar = 0; // index of current character
static PTCHAR pchInputBuf; // input buffer
int i, j; // loop counters
int cCR = 0; // count of carriage returns
int nCRIndex = 0; // index of last carriage return
int nVirtKey; // virtual-key code
TCHAR szBuf[128]; // temporary buffer
TCHAR ch; // current character
PAINTSTRUCT ps; // required by BeginPaint
RECT rc; // output rectangle for DrawText
SIZE sz; // string dimensions
COLORREF crPrevText; // previous text color
COLORREF crPrevBk; // previous background color
size_t * pcch;
HRESULT hResult;
switch (uMsg)
{
case WM_CREATE:
// Get the metrics of the current font.
hdc = GetDC(hwndMain);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwndMain, hdc);
// Save the average character width and height.
dwCharX = tm.tmAveCharWidth;
dwCharY = tm.tmHeight;
// Allocate a buffer to store keyboard input.
pchInputBuf = (LPTSTR) GlobalAlloc(GPTR,
BUFSIZE * sizeof(TCHAR));
return 0;
case WM_SIZE:
// Save the new width and height of the client area.
dwClientX = LOWORD(lParam);
dwClientY = HIWORD(lParam);
// Calculate the maximum width of a line and the
// maximum number of lines in the client area.
dwLineLen = dwClientX - dwCharX;
dwLines = dwClientY / dwCharY;
break;
case WM_SETFOCUS:
// Create, position, and display the caret when the
// window receives the keyboard focus.
CreateCaret(hwndMain, (HBITMAP) 1, 0, dwCharY);
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
ShowCaret(hwndMain);
break;
case WM_KILLFOCUS:
// Hide and destroy the caret when the window loses the
// keyboard focus.
HideCaret(hwndMain);
DestroyCaret();
break;
case WM_CHAR:
// check if current location is close enough to the
// end of the buffer that a buffer overflow may
// occur. If so, add null and display contents.
if (cch > BUFSIZE-5)
{
pchInputBuf[cch] = 0x00;
SendMessage(hwndMain, WM_PAINT, 0, 0);
}
switch (wParam)
{
case 0x08: // backspace
case 0x0A: // linefeed
case 0x1B: // escape
MessageBeep((UINT) -1);
return 0;
case 0x09: // tab
// Convert tabs to four consecutive spaces.
for (i = 0; i < 4; i++)
SendMessage(hwndMain, WM_CHAR, 0x20, 0);
return 0;
case 0x0D: // carriage return
// Record the carriage return and position the
// caret at the beginning of the new line.
pchInputBuf[cch++] = 0x0D;
nCaretPosX = 0;
nCaretPosY += 1;
break;
default: // displayable character
ch = (TCHAR) wParam;
HideCaret(hwndMain);
// Retrieve the character's width and output
// the character.
hdc = GetDC(hwndMain);
GetCharWidth32(hdc, (UINT) wParam, (UINT) wParam,
&nCharWidth);
TextOut(hdc, nCaretPosX, nCaretPosY * dwCharY,
&ch, 1);
ReleaseDC(hwndMain, hdc);
// Store the character in the buffer.
pchInputBuf[cch++] = ch;
// Calculate the new horizontal position of the
// caret. If the position exceeds the maximum,
// insert a carriage return and move the caret
// to the beginning of the next line.
nCaretPosX += nCharWidth;
if ((DWORD) nCaretPosX > dwLineLen)
{
nCaretPosX = 0;
pchInputBuf[cch++] = 0x0D;
++nCaretPosY;
}
nCurChar = cch;
ShowCaret(hwndMain);
break;
}
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
break;
case WM_KEYDOWN:
switch (wParam)
{
case VK_LEFT: // LEFT ARROW
// The caret can move only to the beginning of
// the current line.
if (nCaretPosX > 0)
{
HideCaret(hwndMain);
// Retrieve the character to the left of
// the caret, calculate the character's
// width, then subtract the width from the
// current horizontal position of the caret
// to obtain the new position.
ch = pchInputBuf[--nCurChar];
hdc = GetDC(hwndMain);
GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwndMain, hdc);
nCaretPosX = max(nCaretPosX - nCharWidth,
0);
ShowCaret(hwndMain);
}
break;
case VK_RIGHT: // RIGHT ARROW
// Caret moves to the right or, when a carriage
// return is encountered, to the beginning of
// the next line.
if (nCurChar < cch)
{
HideCaret(hwndMain);
// Retrieve the character to the right of
// the caret. If it's a carriage return,
// position the caret at the beginning of
// the next line.
ch = pchInputBuf[nCurChar];
if (ch == 0x0D)
{
nCaretPosX = 0;
nCaretPosY++;
}
// If the character isn't a carriage
// return, check to see whether the SHIFT
// key is down. If it is, invert the text
// colors and output the character.
else
{
hdc = GetDC(hwndMain);
nVirtKey = GetKeyState(VK_SHIFT);
if (nVirtKey & SHIFTED)
{
crPrevText = SetTextColor(hdc,
RGB(255, 255, 255));
crPrevBk = SetBkColor(hdc,
RGB(0,0,0));
TextOut(hdc, nCaretPosX,
nCaretPosY * dwCharY,
&ch, 1);
SetTextColor(hdc, crPrevText);
SetBkColor(hdc, crPrevBk);
}
// Get the width of the character and
// calculate the new horizontal
// position of the caret.
GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwndMain, hdc);
nCaretPosX = nCaretPosX + nCharWidth;
}
nCurChar++;
ShowCaret(hwndMain);
break;
}
break;
case VK_UP: // UP ARROW
case VK_DOWN: // DOWN ARROW
MessageBeep((UINT) -1);
return 0;
case VK_HOME: // HOME
// Set the caret's position to the upper left
// corner of the client area.
nCaretPosX = nCaretPosY = 0;
nCurChar = 0;
break;
case VK_END: // END
// Move the caret to the end of the text.
for (i=0; i < cch; i++)
{
// Count the carriage returns and save the
// index of the last one.
if (pchInputBuf[i] == 0x0D)
{
cCR++;
nCRIndex = i + 1;
}
}
nCaretPosY = cCR;
// Copy all text between the last carriage
// return and the end of the keyboard input
// buffer to a temporary buffer.
for (i = nCRIndex, j = 0; i < cch; i++, j++)
szBuf[j] = pchInputBuf[i];
szBuf[j] = TEXT('\0');
// Retrieve the text extent and use it
// to set the horizontal position of the
// caret.
hdc = GetDC(hwndMain);
hResult = StringCchLength(szBuf, 128, pcch);
if (FAILED(hResult))
{
// TODO: write error handler
}
GetTextExtentPoint32(hdc, szBuf, *pcch,
&sz);
nCaretPosX = sz.cx;
ReleaseDC(hwndMain, hdc);
nCurChar = cch;
break;
default:
break;
}
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
break;
case WM_PAINT:
if (cch == 0) // nothing in input buffer
break;
hdc = BeginPaint(hwndMain, &ps);
HideCaret(hwndMain);
// Set the clipping rectangle, and then draw the text
// into it.
SetRect(&rc, 0, 0, dwLineLen, dwClientY);
DrawText(hdc, pchInputBuf, -1, &rc, DT_LEFT);
ShowCaret(hwndMain);
EndPaint(hwndMain, &ps);
break;
// Process other messages.
case WM_DESTROY:
PostQuitMessage(0);
// Free the input buffer.
GlobalFree((HGLOBAL) pchInputBuf);
UnregisterHotKey(hwndMain, 0xAAAA);
break;
default:
return DefWindowProc(hwndMain, uMsg, wParam, lParam);
}
return NULL;
}
A.版权声明
原文来自MSDN的Using Keyboard Input,由KingsamChen翻译。请勿将译文用于非法用途,否则发生版权纠纷与译者无关。
Copyright © KingsamChen [MDSA Group]
My Blog:kingsamchen.blog.cfan.com.cn MSN:
kingsamchen@hotmail.com
QQ:106047259
E-mail:
kingsamchen@hotmail.com /
kingsmchen@gmail.com
MSDA BBS:
http://mdsa-group.5d6d.com CFAN BBS:bbs.cfan.com.cn
[
本帖最后由 KingsamChen 于 2008-9-14 11:40 编辑 ]