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


    

    大家知道,在使用微软的编程环境创建project时会让你选择是控制台模式还是Windows应用程序。假设选择控制台的console模式。就会在执行时出现一个黑洞洞的字符模式窗体,里面就有等待输入一闪一闪的插入符。

    输入光标从DOS时代就存在,可是在Win32中赋予了更强大的功能。

    下图就是Windows的CMD窗体,当中的输入点就是插入光标:

    cmd_window

    要注意的是这里的插入符或插入光标并非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);

    以上两个函数的參数非常easy,都是要显示窗体的句柄。

    要设置插入符的位置,使用以下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 terminal
    static TCHAR *pTextMatrix; //VT character buffer
    static int nCharWidth, nCharHeight; //the width and height of characeters
    static int nCaretPosX, nCaretPosY; //the current position of caret
    static int nVTWidth, nVTHeight; //the width and height of virtual terminal window
    static int nLineChars, nRowChars; //character number in one line and row
    static int nCaretOffsetY; //the offset of vertical height
    static COLORREF TextColor; //text color for terminal window, that is fore color
    
    static 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  9876
    static 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窗体^_^),效果例如以下:

    my_cmd

    本例实现了一个简单的终端模拟小程序。为了读者重用方便,我将终端模拟的小窗体单独作为一个完整的源文件,而且把窗体背景设为黑色。前景色设为白色。看起来更像CMD、Linux等命令行窗体。

    很多其它经验交流能够增加Windows编程讨论QQ群454398517

     

    关注微信公众平台:程序猿互动联盟(coder_online),你能够第一时间获取原创技术文章,和(java/C/C++/Android/Windows/Linux)技术大牛做朋友,在线交流编程经验,获取编程基础知识,解决编程问题。程序猿互动联盟,开发者自己的家。

    【Windows编程】系列第八篇:创建通用对话框

    转载请注明出处http://www.coderonline.net/?p=1905,谢谢合作!

    
  • 相关阅读:
    Idea中Module is not specified解决办法
    Navicat 导入数据时报Incorrect datetime value: '0000-00-00 00:00:00.000000' 错误
    SQL Server错误18456,window身份验证登录失败解决办法
    Linq
    web.config配置数据库连接
    $.ajax()方法详解
    将一张图片上传到指定的文件夹,然后在窗体上的PictrueBox控件中显示出来
    winform中picturebox自适应图片大小
    C#中产生SQL语句的几种方式
    [转]ORACLE触发器详解
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/6956452.html
Copyright © 2020-2023  润新知