【Windows編程】系列第十篇:文本插入符

來源:互聯網
上載者:User

標籤:

大家知道,在使用微軟的編程環境建立工程時會讓你選擇是控制台模式還是Windows應用程式。如果選擇控制台的console模式,就會在運行時出現一個黑洞洞的字元強制回應視窗,裡面就有等待輸入一閃一閃的插入符。輸入游標從DOS時代就存在,但是在Win32中賦予了更強大的功能。就是Windows的CMD視窗,其中的輸入焦點就是插入游標:

要注意的是這裡的插入符或插入游標並不是Windows中另外一個“游標”,這裡是指示插入字元的位置,而不是用於滑鼠,手寫輸入等可以定位、移動的游標(Cursor),而是插入符Caret,本文也成為插入游標,注意插入二字,為了方便,以下在本文中也簡稱為游標或插入符,但要注意此游標非彼游標。

為什麼會有插入游標(插入符)?瞭解了這個基本問題,就成功了一半了。我們知道電腦可以通過鍵盤來輸入各種字元和控制符,那麼自然就存在一個問題,輸入的字元應該放到螢幕的什麼位置?這個就是游標產生的原因,游標實際上就是一個字元插入標識。簡單的說就是我們想把字元輸入到哪個位置,就先把插入符設定到那裡,設定時其實就是告訴電腦輸入的字元你給我在哪裡顯示!從這個我們也可以推斷,插入符在同一時刻只能有一個。

  • 游標相關API函數

要使用游標,首先得建立一個游標,建立游標的API函數為:

BOOL CreateCaret(HWND hWnd, HBITMAP hBitmap, int nWidth, int nHeight);

hWnd參數表示游標是屬於哪個視窗。

hBitmap參數是一個位元影像的控制代碼,電腦將使用這個控制代碼的位元影像來作為游標的形狀。

既然游標是給使用電腦的人插入字元用的,那就得有形狀讓使用者能看到,因此游標需要有一個可見的小表徵圖。當然為了不同的情況和需求,Windows讓我們可以自訂游標的形狀。常見的位元影像建立或者載入API函數如CreateBitmap、CreateDIBitmap、LoadBitmap等都可以建立或載入一個位元影像,將控制代碼作為該參數。

nWidth和nHeight分別是位元影像的寬和高。

游標建立之後是不會自動顯示的,也就是預設是隱藏狀態,需要主動調用下面的顯示函數:

BOOL ShowCaret(HWND hWnd);

當然有顯示光線標也可以隱藏游標:

BOOL HideCaret(HWND hWnd);

以上兩個函數的參數很簡單,都是要顯示視窗的控制代碼。

要設定插入符的位置,使用下面API函數:

BOOL SetCaretPos(int X, int Y);

參數X、Y分別是相對於視窗客戶區的座標。

我們可以用如下API函數擷取當前游標的位置:

BOOL GetCaretPos(LPPOINT lpPoint);

參數lpPoint返回當前游標所在的位置。

我們知道游標會閃爍,這個閃爍的時間間隔是可以設定的,我們可以用如下API來設定和擷取插入游標的閃爍時間:

BOOL SetCaretBlinkTime(UINT uMSeconds);UINT GetCaretBlinkTime(VOID);

參數uMSeconds為閃爍的間隔毫秒數。

最後不再使用時需要銷毀游標:

BOOL DestroyCaret(VOID);

 

  • 游標處理相關訊息

與插入游標相關的訊息主要有WM_SETFOCUS、WM_KILLFOCUS。通常在WM_SETFOCUS中建立和顯示光線標,而在WM_KILLFOCUS中銷毀游標。一般應有中再結合WM_KEYDOWN和WM_CHAR訊息,實現文本的輸入。

  • 游標應用執行個體

以下是一個簡單的虛擬終端,我們常見的很多終端軟體都是這樣來實現的,比如常見的SecureCRT、Tera Term、XShell、putty等等。本執行個體就是用了插入游標來實現字元輸入、插入,完整執行個體代碼如下:

#include <windows.h>static TCHAR szWinName[] = TEXT("VirtualTerminal");#define TEXTMATRIX(x, y)  *(pTextMatrix + ((y) * nLineChars) + (x))static TEXTMETRIC tm; //metric dimention of virtual terminalstatic TCHAR *pTextMatrix; //VT character bufferstatic int nCharWidth, nCharHeight; //the width and height of characetersstatic int nCaretPosX, nCaretPosY; //the current position of caretstatic int nVTWidth, nVTHeight; //the width and height of virtual terminal windowstatic int nLineChars, nRowChars; //character number in one line and rowstatic int nCaretOffsetY; //the offset of vertical heightstatic COLORREF TextColor; //text color for terminal window, that is fore colorstatic LRESULT CALLBACK VTWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);HWND CreateVirtualTerminal(HWND hWndParent, int left, int top, int width, int height, int id){    HWND hWnd;    WNDCLASS wndclass;    HINSTANCE hInst = GetModuleHandle(NULL);    wndclass.style         = CS_HREDRAW | CS_VREDRAW;    wndclass.lpfnWndProc   = VTWndProc;    wndclass.cbClsExtra    = 0;    wndclass.cbWndExtra    = 0;    wndclass.hInstance     = hInst;    wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);    wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);    wndclass.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);    wndclass.lpszMenuName  = NULL;    wndclass.lpszClassName = szWinName;    if (!RegisterClass(&wndclass))    {        return 0;    }    hWnd = CreateWindowEx(0, szWinName, NULL, WS_CHILD|WS_VISIBLE,                         left, top, width, height, hWndParent, (HMENU)id, hInst, NULL);    return hWnd;}static void DrawChar(HDC hDC, int x, int y, TCHAR *str, int num){    RECT rect;    SelectObject(hDC, GetStockObject(SYSTEM_FIXED_FONT));    SetTextColor(hDC, TextColor);    SetBkMode(hDC, TRANSPARENT);    rect.left = x;    rect.top = y;    rect.right = x + num * nCharWidth;    rect.bottom = y + nCharHeight;    FillRect(hDC, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));    TextOut(hDC, nCaretPosX * nCharWidth, nCaretPosY * nCharHeight,             &TEXTMATRIX(nCaretPosX, nCaretPosY), nLineChars - nCaretPosX);}static LRESULT CALLBACK VTWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){    int x, y;    HDC hDC;    switch (message)    {    case WM_CREATE:        hDC = GetDC(hWnd);        SelectObject(hDC, GetStockObject(SYSTEM_FIXED_FONT));        GetTextMetrics(hDC, &tm);        ReleaseDC(hWnd, hDC);        nCharWidth = tm.tmAveCharWidth;        nCharHeight = tm.tmHeight;        TextColor = RGB(255, 255, 255);        nCaretOffsetY = 12;        return 0;    case WM_SIZE:        nVTWidth = LOWORD(lParam);        nLineChars = max(1, nVTWidth/nCharWidth);        nVTHeight = HIWORD(lParam);        nRowChars = max(1, nVTHeight/nCharHeight);                if (pTextMatrix != NULL)        {            free(pTextMatrix);        }        pTextMatrix = (TCHAR *)malloc(nLineChars * nRowChars);        if (pTextMatrix)        {            for (y=0; y<nRowChars; y++)            {                for (x=0; x<nLineChars; x++)                {                    TEXTMATRIX(x, y) = TEXT(‘ ‘);                }            }        }        SetCaretPos(0, nCaretOffsetY);        return 0;    case WM_LBUTTONDOWN:        SetFocus(hWnd);        break;    case WM_KEYDOWN:        switch (wParam)        {        case VK_HOME:       // Home             nCaretPosX = 0;            break;         case VK_END:        // End             nCaretPosX = nLineChars - 1;            break;         case VK_PRIOR:      // Page Up             nCaretPosY = 0;             break;         case VK_NEXT:       // Page Down             nCaretPosY = nRowChars -1;             break;         case VK_LEFT:       // Left arrow             nCaretPosX = max(nCaretPosX - 1, 0);             break;         case VK_RIGHT:      // Right arrow             nCaretPosX = min(nCaretPosX + 1,                 nLineChars - 1);             break;         case VK_UP:         // Up arrow             nCaretPosY = max(nCaretPosY - 1, 0);             break;         case VK_DOWN:       // Down arrow             nCaretPosY = min(nCaretPosY + 1,                 nRowChars - 1);             break;         case VK_DELETE:     // Delete             // Move all the characters that followed the             // deleted character (on the same line) one             // space back (to the left) in the matrix.              for (x = nCaretPosX; x < nLineChars; x++)                 TEXTMATRIX(x, nCaretPosY) = TEXTMATRIX(x + 1, nCaretPosY);             // Replace the last character on the             // line with a space.             TEXTMATRIX(nLineChars - 1, nCaretPosY) = ‘ ‘;             // The application will draw outside the             // WM_PAINT message processing, so hide the caret.             HideCaret(hWnd);             // Redraw the line, adjusted for the             // deleted character.             hDC = GetDC(hWnd);             DrawChar(hDC, nCaretPosX * nCharWidth, nCaretPosY * nCharHeight,                    &TEXTMATRIX(nCaretPosX, nCaretPosY), nLineChars - nCaretPosX/nCharWidth);            ReleaseDC(hWnd, hDC);             // Display the caret.             ShowCaret(hWnd);         break;         }         // Adjust the caret position based on the         // virtual-key processing.         SetCaretPos(nCaretPosX * nCharWidth, nCaretPosY * nCharHeight + nCaretOffsetY);         return 0;    case WM_SHOWWINDOW:        SetFocus(hWnd);        break;    case WM_SETFOCUS:        CreateCaret(hWnd, NULL, nCharWidth, 2);        SetCaretPos(nCaretPosX * nCharWidth, nCaretPosY * nCharHeight + nCaretOffsetY);        ShowCaret(hWnd);        break;    case WM_KILLFOCUS:    case WM_DESTROY:        HideCaret(hWnd);        DestroyCaret();        break;    case WM_CHAR:        switch (wParam)        {        case 0x08: //process a backspace.            if (nCaretPosX > 0)            {                nCaretPosX--;                SendMessage(hWnd, WM_KEYDOWN, VK_DELETE, 1L);            }            break;        case 0x09: //process a tab.            do            {                SendMessage(hWnd, WM_CHAR, TEXT(‘ ‘), 2L);            } while (nCaretPosX % 4 != 0);            break;        case 0x0D: //process a carriage return.            nCaretPosX = 0;            if (++nCaretPosY == nRowChars)            {                nCaretPosY = 0;            }            break;        case 0x1B:        case 0x0A: //process a linefeed.            MessageBeep((UINT)-1);            break;        default:            TEXTMATRIX(nCaretPosX, nCaretPosY) = (TCHAR)wParam;            HideCaret(hWnd);            hDC = GetDC(hWnd);            DrawChar(hDC, nCaretPosX * nCharWidth, nCaretPosY * nCharHeight, &TEXTMATRIX(nCaretPosX, nCaretPosY), 1);            ReleaseDC(hWnd, hDC);            ShowCaret(hWnd);            if (++nCaretPosX == nLineChars)            {                nCaretPosX = 0;                if (++nCaretPosY == nRowChars)                {                    nCaretPosY = 0;                }            }            break;        }        SetCaretPos(nCaretPosX * nCharWidth, nCaretPosY * nCharHeight + nCaretOffsetY);        return 0;    case WM_PAINT:        {            PAINTSTRUCT ps;            hDC = BeginPaint(hWnd, &ps);            SelectObject(hDC, GetStockObject(SYSTEM_FIXED_FONT));            SetBkMode(hDC, TRANSPARENT);            SetTextColor(hDC, TextColor);            for (y=0; y<nLineChars; y++)            {                TextOut(hDC, 0, y * nCharHeight, &TEXTMATRIX(0, y), nLineChars);            }            EndPaint(hWnd, &ps);        }        return 0;    default:        break;    }    return DefWindowProc (hWnd, message, wParam, lParam);}

下面是主程式檔案,該檔案調用上面的CreateVirtualTerminal函數來建立一個視窗。

#include <windows.h>#define IDC_VTID  9876static TCHAR szAppName[] = TEXT("Caret demo");static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);extern HWND CreateVirtualTerminal(HWND hWndParent, int left, int top, int width, int height, int id);int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){     HWND     hWnd;     MSG      msg;     WNDCLASS wndclass;     wndclass.style         = CS_HREDRAW | CS_VREDRAW;     wndclass.lpfnWndProc   = WndProc;     wndclass.cbClsExtra    = 0;     wndclass.cbWndExtra    = 0;     wndclass.hInstance     = hInstance;     wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);     wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);     wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);     wndclass.lpszMenuName  = NULL;     wndclass.lpszClassName = szAppName;     if (!RegisterClass(&wndclass))     {          MessageBox (NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);          return 0;     }          hWnd = CreateWindow(szAppName,             // window class name                          szAppName,            // window caption                          WS_OVERLAPPEDWINDOW,  // window style                          CW_USEDEFAULT,        // initial x position                          CW_USEDEFAULT,        // initial y position                          400,              // initial x size                          300,              // initial y size                          NULL,                 // parent window handle                          NULL,                 // window menu handle                          hInstance,            // program instance handle                          NULL);                // creation parameters          ShowWindow(hWnd, iCmdShow);     UpdateWindow(hWnd);          while (GetMessage(&msg, NULL, 0, 0))     {          TranslateMessage(&msg);          DispatchMessage(&msg);     }     return msg.wParam;}static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){    HDC         hDC;    PAINTSTRUCT ps;    switch (message)    {    case WM_CREATE:        CreateVirtualTerminal(hWnd, 10, 10, 360, 240, IDC_VTID);        return 0;    case WM_PAINT:        hDC = BeginPaint(hWnd, &ps);        ;        EndPaint(hWnd, &ps);        return 0;    case WM_DESTROY:        PostQuitMessage(0);        return 0 ;    }    return DefWindowProc (hWnd, message, wParam, lParam);}

樣本程式運行後,在視窗中輸入部分文本(模仿上面的cmd視窗^_^),效果如下:

本例實現了一個簡單的終端類比小程式,為了讀者重用方便,我將終端類比的小視窗單獨作為一個完整的源檔案,並且把視窗背景設為黑色,前景色彩設為白色,看起來更像CMD、Linux等命令列視窗。

 更多經驗交流可以加入Windows編程討論QQ群454398517

 

關注公眾平台:程式員互動聯盟(coder_online),你可以第一時間擷取原創技術文章,和(java/C/C++/Android/Windows/Linux)技術大牛做朋友,線上交流編程經驗,擷取編程基礎知識,解決編程問題。程式員互動聯盟,開發人員自己的家。

轉載請註明出處http://www.coderonline.net/?p=1905,謝謝合作!

【Windows編程】系列第十篇:文本插入符

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.