Billentyűzetbemenet használata

Az ablak billentyűleütési üzenetek és karakterüzenetek formájában fogadja a billentyűzet bemenetét. Az ablakhoz csatolt üzenethurknak tartalmaznia kell a billentyűleütési üzenetek megfelelő karakterüzenetekké való fordítására szolgáló kódot. Ha az ablak billentyűzetbemenetet jelenít meg az ablakterületén, létre kell hoznia és meg kell jelenítenie egy kurzort, amely jelzi a következő karakter beírásának helyét. A következő szakaszok a billentyűzetbemenet fogadásában, feldolgozásában és megjelenítésében részt vevő kódot ismertetik:

Billentyűleütési üzenetek feldolgozása

A billentyűzetfókuszú ablak ablakműtétje billentyűleütési üzeneteket kap, amikor a felhasználó be gépel a billentyűzeten. A billentyűleütési üzenetek a következők: WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWNés WM_SYSKEYUP. Egy tipikus ablakművelet figyelmen kívül hagyja az összes billentyűleütési üzenetet, kivéve WM_KEYDOWN. A rendszer közzéteszi a WM_KEYDOWN üzenetet, amikor a felhasználó lenyom egy billentyűt.

Amikor az ablak eljárás megkapja a WM_KEYDOWN üzenetet, meg kell vizsgálnia az üzenetet kísérő virtuáliskulcs-kódot, hogy megállapítsa, hogyan kell feldolgozni a billentyűleütést. A virtuális kulcs kódja az üzenet wParam paraméterében található. Az alkalmazás általában csak a nemcharacter billentyűk által generált billentyűleütéseket dolgozza fel, beleértve a függvénykulcsokat, a kurzormozgatási kulcsokat és a speciális célú kulcsokat, például az INS, a DEL, a HOME és a END billentyűket.

Az alábbi példa azt az ablakeljárási keretrendszert mutatja be, amelyet egy tipikus alkalmazás használ a billentyűleütéses üzenetek fogadásához és feldolgozásához.

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; 

		default: 
			// Process other non-character keystrokes. 
			break; 
	}

Karakterüzenetek fordítása

Minden olyan szálnak, amely karakterbemenetet kap a felhasználótól, tartalmaznia kell a TranslateMessage függvényt az üzenethurkában. Ez a függvény megvizsgálja egy billentyűleütési üzenet virtuáliskulcs-kódját, és ha a kód egy karakternek felel meg, egy karakterüzenetet helyez az üzenetsorba. A karakterüzenet el lesz távolítva és elküldve az üzenethurok következő iterációján; az üzenet wParam paramétere tartalmazza a karakterkódot.

A szál üzenethurkjának általában a TranslateMessage függvénnyel kell lefordítani az összes üzenetet, nem csak a virtuális kulcsú üzeneteket. Bár TranslateMessage nincs hatása más típusú üzenetekre, garantálja, hogy a billentyűzet bemenete megfelelően legyen lefordítva. Az alábbi példa bemutatja, hogyan foglalhatja bele a TranslateMessage függvényt egy tipikus szálüzenet-hurokba.

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); 
        } 
    } 
}

Karakterüzenetek feldolgozása

Az ablakos eljárás akkor kap karakterüzenetet, ha a TranslateMessage függvény egy karakterkulcsnak megfelelő virtuáliskulcs-kódot fordít le. A karakterüzenetek a következők: WM_CHAR, WM_DEADCHAR, WM_SYSCHARés WM_SYSDEADCHAR. Egy tipikus ablakművelet figyelmen kívül hagyja az összes karakterüzenetet, kivéve WM_CHAR. A TranslateMessage függvény WM_CHAR üzenetet hoz létre, amikor a felhasználó lenyomja az alábbi kulcsok bármelyikét:

  • Bármely karakter billentyű
  • BACKSPACE
  • ENTER (kocsivissza)
  • ESC
  • SHIFT+ENTER (vonalas)
  • TABULÁTOR

Amikor egy ablakművelet megkapja a WM_CHAR üzenetet, meg kell vizsgálnia az üzenetet kísérő karakterkódot, hogy megállapítsa, hogyan kell feldolgozni a karaktert. A karakterkód az üzenet wParam paraméterében található.

Az alábbi példa azt az ablakeljárási keretrendszert mutatja be, amelyet egy tipikus alkalmazás használ a karakterüzenetek fogadására és feldolgozására.

case WM_CHAR:
	switch (wParam)
	{
		case 0x08: // or '\b'
			// Process a backspace.
			break;
			
		case 0x09: // or '\t'
			// Process a tab.
			break;

		case 0x0A: // or '\n'
			// Process a linefeed.
			break;
			
		case 0x0D:
			// Process a carriage return.
			break;

		case 0x1B:
			// Process an escape.
			break;

		default:
			// Process displayable characters.
			break;
	}

A Caret használata

A billentyűzetbemenetet fogadó ablak általában az ablak ügyfélterületén jeleníti meg a felhasználó által beírt karaktereket. Az ablaknak egy kurzort kell használnia az ablakterületen annak jelzésére, ahol a következő karakter megjelenik. Az ablaknak létre kell hoznia és meg kell jelenítenie a kurzort, amikor megkapja a billentyűzetfókuszt, és el kell rejtenie, illetve meg kell semmisítenie a kurzort, amikor elveszíti a fókuszt. Az ablakok végrehajthatják ezeket a műveleteket a WM_SETFOCUS és WM_KILLFOCUS üzenetek feldolgozása során. További információ a simogatókról: Carets.

Billentyűzetbemenet megjelenítése

Az ebben a szakaszban szereplő példa bemutatja, hogyan fogadhat egy alkalmazás karaktereket a billentyűzetről, hogyan jelenítheti meg őket az ablak ügyfélterületén, és hogyan frissítheti a simogató pozícióját minden egyes beírt karakterrel. Azt is bemutatja, hogyan mozgathatja a kurzort a BALRA, JOBBRA, HOME és VÉGE billentyűk lenyomására válaszul, és megmutatja, hogyan emelheti ki a kijelölt szöveget a SHIFT+JOBBRA billentyűkombináció használatával.

A WM_CREATE üzenet feldolgozása során a példában látható ablakművelet 64K puffert foglal le a billentyűzetbemenet tárolásához. Emellett lekéri az aktuálisan betöltött betűtípus metrikáit is, így mentve a karakterek magasságát és átlagos szélességét a betűtípusban. A magasság és a szélesség a WM_SIZE üzenet feldolgozásához használatos a vonalhossz és a sorok maximális számának kiszámításához az ügyfélterület méretétől függően.

Az ablak eljárás létrehozza és megjeleníti a kurzort, amikor a WM_SETFOCUS üzenetet feldolgozza. Elrejti és törli a kurzort a WM_KILLFOCUS üzenet feldolgozásakor.

A WM_CHAR üzenet feldolgozásakor az ablak eljárása karaktereket jelenít meg, a bemeneti pufferben tárolja őket, és frissíti a kurzor pozícióját. Az ablakos eljárás a tabulátorkaraktereket négy egymást követő szóköz karakterre is konvertálja. A backspace, a sorvégejel és az escape karakterek sípszót hoznak létre, de más módon nem kerülnek feldolgozásra.

Az ablakeljárás a WM_KEYDOWN üzenet feldolgozásakor elvégzi a bal, jobb, záró és otthoni kurzormozgásokat. A JOBBRA nyílbillentyű műveletének feldolgozása során az ablakeljárás ellenőrzi a SHIFT billentyű állapotát, és ha le van nyomva, a kurzor mozgatása közben a kurzortól jobbra lévő karaktert választja ki.

Vegye figyelembe, hogy a következő kód úgy van megírva, hogy Unicode vagy ANSI formátumban is lefordítható legyen. Ha a forráskód UNICODE-t határoz meg, a sztringek Unicode-karakterekként lesznek kezelve; ellenkező esetben a rendszer ANSI-karakterekként kezeli őket.

#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; 
}