Bagikan melalui


Menggunakan Input Keyboard

Jendela menerima input keyboard dalam bentuk pesan penekanan tombol dan pesan karakter. Perulangan pesan yang dilampirkan ke jendela harus menyertakan kode untuk menerjemahkan pesan penekanan tombol ke dalam pesan karakter yang sesuai. Jika jendela menampilkan input keyboard di area kliennya, jendela harus membuat dan menampilkan tanda sisipan untuk menunjukkan posisi di mana karakter berikutnya akan dimasukkan. Bagian berikut menjelaskan kode yang terlibat dalam menerima, memproses, dan menampilkan input keyboard:

Memproses Pesan Penekanan Tombol

Prosedur jendela jendela yang memiliki fokus keyboard menerima pesan penekanan tombol saat pengguna mengetik di keyboard. Pesan penekanan tombol WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, dan WM_SYSKEYUP. Prosedur jendela umum mengabaikan semua pesan penekanan tombol kecuali WM_KEYDOWN. Sistem memposting pesan WM_KEYDOWN saat pengguna menekan tombol.

Ketika prosedur jendela menerima pesan WM_KEYDOWN , itu harus memeriksa kode kunci virtual yang menyertai pesan untuk menentukan cara memproses penekanan tombol. Kode kunci virtual ada di parameter wParam pesan. Biasanya, aplikasi hanya memproses penekanan tombol yang dihasilkan oleh kunci noncharacter, termasuk kunci fungsi, kunci gerakan kursor, dan kunci tujuan khusus seperti INS, DEL, HOME, dan END.

Contoh berikut menunjukkan kerangka kerja prosedur jendela yang digunakan aplikasi umum untuk menerima dan memproses pesan penekanan kunci.

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

Menerjemahkan Pesan Karakter

Setiap utas yang menerima input karakter dari pengguna harus menyertakan fungsi TranslateMessage dalam perulangan pesannya. Fungsi ini memeriksa kode kunci virtual dari pesan penekanan tombol dan, jika kode sesuai dengan karakter, menempatkan pesan karakter ke dalam antrean pesan. Pesan karakter dihapus dan dikirim pada iterasi berikutnya dari perulangan pesan; parameter wParam pesan berisi kode karakter.

Secara umum, perulangan pesan utas harus menggunakan fungsi TranslateMessage untuk menerjemahkan setiap pesan, bukan hanya pesan kunci virtual. Meskipun TranslateMessage tidak berpengaruh pada jenis pesan lain, ini menjamin bahwa input keyboard diterjemahkan dengan benar. Contoh berikut menunjukkan cara menyertakan fungsi TranslateMessage dalam perulangan pesan utas umum.

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

Memproses Pesan Karakter

Prosedur jendela menerima pesan karakter saat fungsi TranslateMessage menerjemahkan kode kunci virtual yang sesuai dengan kunci karakter. Pesan karakter WM_CHAR, WM_DEADCHAR, WM_SYSCHAR, dan WM_SYSDEADCHAR. Prosedur jendela umum mengabaikan semua pesan karakter kecuali WM_CHAR. Fungsi TranslateMessage menghasilkan pesan WM_CHAR saat pengguna menekan salah satu tombol berikut:

  • Kunci karakter apa pun
  • BACKSPACE
  • ENTER (pengangkutan kembali)
  • ESC
  • SHIFT+ENTER (linefeed)
  • TAB

Ketika prosedur jendela menerima pesan WM_CHAR , prosedur harus memeriksa kode karakter yang menyertai pesan untuk menentukan cara memproses karakter. Kode karakter ada di parameter wParam pesan.

Contoh berikut menunjukkan kerangka kerja prosedur jendela yang digunakan aplikasi umum untuk menerima dan memproses pesan karakter.

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

Menggunakan Caret

Jendela yang menerima input keyboard biasanya menampilkan karakter jenis pengguna di area klien jendela. Jendela harus menggunakan tanda sisipan untuk menunjukkan posisi di area klien tempat karakter berikutnya akan muncul. Jendela juga harus membuat dan menampilkan tanda sisipan saat menerima fokus keyboard, dan menyembunyikan dan menghancurkan tanda sisipan saat kehilangan fokus. Jendela dapat melakukan operasi ini dalam pemrosesan pesan WM_SETFOCUS dan WM_KILLFOCUS . Untuk informasi selengkapnya tentang caret, lihat Carets.

Menampilkan Input Keyboard

Contoh di bagian ini menunjukkan bagaimana aplikasi dapat menerima karakter dari keyboard, menampilkannya di area klien jendela, dan memperbarui posisi tanda sisipan dengan setiap karakter yang di ketik. Ini juga menunjukkan cara memindahkan tanda sisipan sebagai respons terhadap tombol PANAH KIRI, PANAH KANAN, BERANDA, dan TOMBOL AKHIR, dan memperlihatkan cara menyoroti teks yang dipilih sebagai respons terhadap kombinasi tombol SHIFT+RIGHT ARROW.

Selama pemrosesan pesan WM_CREATE , prosedur jendela yang ditunjukkan dalam contoh mengalokasikan buffer 64K untuk menyimpan input keyboard. Ini juga mengambil metrik font yang saat ini dimuat, menyimpan tinggi dan lebar rata-rata karakter dalam font. Tinggi dan lebar digunakan dalam memproses pesan WM_SIZE untuk menghitung panjang baris dan jumlah baris maksimum, berdasarkan ukuran area klien.

Prosedur jendela membuat dan menampilkan tanda sisipan saat memproses pesan WM_SETFOCUS . Ini menyembunyikan dan menghapus tanda sisipan saat memproses pesan WM_KILLFOCUS .

Saat memproses pesan WM_CHAR , prosedur jendela menampilkan karakter, menyimpannya di buffer input, dan memperbarui posisi tanda sisipan. Prosedur jendela juga mengonversi karakter tab menjadi empat karakter spasi berturut-turut. Karakter backspace, linefeed, dan escape menghasilkan bip, tetapi tidak diproses.

Prosedur jendela melakukan gerakan caret kiri, kanan, akhir, dan rumah saat memproses pesan WM_KEYDOWN . Saat memproses tindakan tombol PANAH KANAN, prosedur jendela memeriksa status tombol SHIFT dan, jika tidak berfungsi, memilih karakter di sebelah kanan tanda sisipan saat tanda sisipan dipindahkan.

Perhatikan bahwa kode berikut ditulis sehingga dapat dikompilasi baik sebagai Unicode atau sebagai ANSI. Jika kode sumber mendefinisikan UNICODE, string ditangani sebagai karakter Unicode; jika tidak, mereka ditangani sebagai karakter 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; 
}