Udostępnij za pośrednictwem


Korzystanie z wprowadzania za pomocą klawiatury

Okno odbiera dane wejściowe z klawiatury w postaci komunikatów o naciśnięciach klawiszy i komunikatów o znakach. Pętla komunikatów związana z oknem musi zawierać kod, aby przetłumaczyć komunikaty naciśnięć klawiszy na odpowiednie komunikaty znakowe. Jeśli w oknie są wyświetlane dane wejściowe klawiatury w obszarze klienta, powinno ono utworzyć i wyświetlić kursor wskazujący miejsce, gdzie zostanie wpisany następny znak. W poniższych sekcjach opisano kod zaangażowany w odbieranie, przetwarzanie i wyświetlanie danych wejściowych klawiatury:

Przetwarzanie komunikatów o naciśnięciach klawiszy

Procedura okna z aktywnym fokusem klawiatury odbiera komunikaty związane z naciśnięciem klawisza, gdy użytkownik pisze na klawiaturze. Komunikaty naciśnięcia to WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWNi WM_SYSKEYUP. Typowa procedura okna ignoruje wszystkie komunikaty naciśnięć klawiszy z wyjątkiem WM_KEYDOWN. System publikuje komunikat WM_KEYDOWN, gdy użytkownik naciśnie klawisz.

Gdy procedura okna odbiera komunikat WM_KEYDOWN, powinna zbadać kod klawisza wirtualnego, który towarzyszy komunikatowi, aby określić sposób przetwarzania naciśnięcia. Kod klucza wirtualnego znajduje się w parametrze wParam komunikatu. Zazwyczaj aplikacja przetwarza tylko naciśnięcia generowane przez klucze nietypowe, w tym klucze funkcji, klucze ruchu kursora i klucze specjalnego przeznaczenia, takie jak INS, DEL, HOME i END.

Poniższy przykład pokazuje struktury procedury okna, której typowa aplikacja używa do odbierania i przetwarzania komunikatów o naciśnięciach klawiszy.

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

Tłumaczenie wiadomości postaci

Każdy wątek, który odbiera dane wejściowe znaków od użytkownika, musi zawierać funkcję TranslateMessage w swojej pętli komunikatów. Ta funkcja sprawdza kod klucza wirtualnego komunikatu naciśniętego, a jeśli kod odpowiada znakowi, umieszcza komunikat znaku w kolejce komunikatów. Komunikat zawierający znak jest usuwany i wysyłany w następnej iteracji pętli komunikatów; parametr komunikatu wParam zawiera kod tego znaku.

Ogólnie rzecz biorąc, pętla komunikatów wątku powinna używać funkcji TranslateMessage do tłumaczenia każdego komunikatu, a nie tylko komunikatów klucza wirtualnego. Mimo że TranslateMessage nie ma wpływu na inne typy komunikatów, gwarantuje poprawne tłumaczenie danych wejściowych klawiatury. W poniższym przykładzie pokazano, jak dołączyć funkcję TranslateMessage w typowej pętli komunikatów wątku.

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

Przetwarzanie wiadomości znaków

Procedura okna odbiera wiadomość znakową, gdy funkcja TranslateMessage tłumaczy kod klucza wirtualnego odpowiadający klawiszowi znaku. Komunikaty znaków to WM_CHAR, WM_DEADCHAR, WM_SYSCHARi WM_SYSDEADCHAR. Typowa procedura okna ignoruje wszystkie komunikaty dotyczące znaków z wyjątkiem WM_CHAR. Funkcja TranslateMessage generuje komunikat WM_CHAR, gdy użytkownik naciska dowolny z następujących klawiszy.

  • Dowolny klawisz znaku
  • BACKSPACE
  • ENTER (powrót karetki)
  • ESC
  • SHIFT+ENTER (kanał wiersza)
  • ZAKŁADKA

Gdy procedura okna odbiera komunikat WM_CHAR, powinien zbadać kod znaku, który towarzyszy komunikatowi, aby określić sposób przetwarzania znaku. Kod znaku znajduje się w parametrze wParam komunikatu.

W poniższym przykładzie przedstawiono strukturę procedury obsługi okna, której typowa aplikacja używa do odbierania i przetwarzania wiadomości znakowych.

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

Korzystanie z karetki

Okno odbierające dane wejściowe klawiatury zwykle wyświetla znaki, które użytkownik wpisze w obszarze klienta okna. Okno powinno użyć kursora, aby wskazać położenie w obszarze roboczym okna, w którym pojawi się następny znak. Okno powinno również utworzyć i wyświetlić kursor po otrzymaniu fokusu klawiatury, a ukryć i zniszczyć kursor, gdy traci skupienie. Okno może wykonywać te operacje w przetwarzaniu komunikatów WM_SETFOCUS i WM_KILLFOCUS. Aby uzyskać więcej informacji na temat daszków, zobacz Carets.

Wyświetlanie danych wejściowych klawiatury

W przykładzie w tej sekcji pokazano, jak aplikacja może odbierać znaki z klawiatury, wyświetlać je w obszarze klienta okna i aktualizować położenie kursora przy każdym wprowadzeniu znaku. Pokazuje również, jak przenieść kursor w odpowiedzi na naciśnięcia klawiszy STRZAŁKA W LEWO, STRZAŁKA W PRAWO, HOME i END oraz jak wyróżnić zaznaczony tekst w odpowiedzi na kombinację SHIFT+STRZAŁKA W PRAWO.

Podczas przetwarzania komunikatu WM_CREATE procedura okna pokazana w przykładzie przydziela bufor 64K do przechowywania danych wejściowych klawiatury. Pobiera również metryki aktualnie załadowanej czcionki, zapisując wysokość i średnią szerokość znaków w czcionce. Wysokość i szerokość są używane do przetwarzania komunikatu WM_SIZE w celu obliczenia długości linii i maksymalnej liczby wierszy na podstawie rozmiaru obszaru klienta.

Procedura okna tworzy i wyświetla kursor podczas przetwarzania komunikatu WM_SETFOCUS. Ukrywa i usuwa kursor podczas przetwarzania komunikatu WM_KILLFOCUS.

Podczas przetwarzania komunikatu WM_CHAR procedura okna wyświetla znaki, przechowuje je w buforze wejściowym i aktualizuje położenie kursora. Procedura okna konwertuje również znaki tabulatora na cztery kolejne znaki spacji. Znaki backspace, linefeed i escape generują sygnał dźwiękowy, ale nie są przetwarzane w inny sposób.

Procedura okna wykonuje ruchy kursora lewo, prawo, koniec i home podczas przetwarzania komunikatu WM_KEYDOWN. Podczas przetwarzania akcji klawisza STRZAŁKA W PRAWO procedura okna sprawdza stan klawisza SHIFT i, jeśli jest wciśnięty, wybiera znak po prawej stronie kursora, gdy kursor jest przenoszony.

Należy pamiętać, że poniższy kod jest napisany tak, aby można go było skompilować jako Unicode lub ANSI. Jeśli kod źródłowy definiuje kod UNICODE, ciągi są obsługiwane jako znaki Unicode; w przeciwnym razie są one obsługiwane jako znaki ANSI.

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