当前位置:C++技术网 > 精选软件 > InvalidateRect无效矩形的图文分析和在字符串中移动光标

InvalidateRect无效矩形的图文分析和在字符串中移动光标

更新时间:2015-12-05 23:36:46浏览次数:1+次

    在使用InvalidateRect函数时,总是不知道第三个参数效果是什么样的,似乎每次又看不到效果。为了研究清楚,就写了一个代码来测试。
    而测试代码本身也很有意思。测试代码的功能就是,用字符-作为当前字符显示的字符串中移动,单击一次移动一个字符位置,移动到的字符就被-字符替换。因为显示的字符串“C++技术网http://www.cjjjs.com”有中文也有大写字母,这样用-字符替换了一些字母后,会使当前新字符串的长度比原先的字符串短。如果使用InvalidateRect函数设置无效区域产生WM_PAINT消息,则导致窗口重绘,这样就会重绘客户区。然而InvalidateRect第三个参数时设置是否擦除原先绘制的东西。原先这些东西相对于现在要画的内容就称为背景。当前绘制的内容叫做前景。
    所以,如果当前字符串比原先的字符串短,而InvalidateRect又不擦除上一次画的文字(即背景内容),那么上次比这次多出的区域中的字迹就看得到,也就是当前字符串没有完全覆盖掉上次的字符串,产生字迹残留。而如果InvalidateRect第三个参数设置了擦除背景的话则每次重绘都会删除上次绘制的内容。因此,删除了背景之后,即使当前字符串比上次的短,也不会看到上次的字符串的残留了。InvalidateRect第三个参数如果为TRUE则删除背景,为FALSE则不删除。
    删除和不删除背景的效果如下面两个图所示:

    背景未擦除,导致上次字符残余

    背景擦除,导致上次字符残余被删除

    从图中可以看出,C++技术网http://www.cjjjs.com字符串的“技”字被字符“-”替换,未被删除背景的图中,看到了两边都有残余,看起来很不舒服。而删除背景的无论何时都看的干干净净的。
    测试代码如下,你可以自行生产程序玩玩,很有意思哦:
#include "windows.h"
#include <tchar.h>
// - 项目是Unicode字符集
LRESULT CALLBACK WinProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    RECT rect;
    static TCHAR str[]=_T("C++技术网http://www.cjjjs.com");
    static int i=0;
    static TCHAR ch;
    switch (message)
    {
     case WM_PAINT:
         hdc = BeginPaint(hwnd,&ps);
         GetClientRect(hwnd,&rect);
         DrawText(hdc,str,-1,&rect,DT_SINGLELINE | DT_VCENTER |DT_CENTER);
         str[i-1]=ch;// - 恢复上一个字符
         
         break;
     case  WM_LBUTTONDOWN:
         hdc = GetWindowDC(hwnd);
         GetClientRect(hwnd,&rect);
         InvalidateRect(hwnd,&rect,TRUE);

         ch = str[i];// - 保存上一个字符到ch,在WM_PAINT显示文本后恢复字符串
         str[i]='-';// - 替换当前一个字符为-,利用-字符比较小,让当前的字符串比上一次字符串占的宽度小,从而会显露出上一次显示文字多余的痕迹
         i++;
         break;
     case WM_DESTROY:
         PostQuitMessage(0);
     default:
         return DefWindowProc(hwnd, message, wParam, lParam);
    }
    
}
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrev,LPSTR lpCmd,int iShow)
{
    TCHAR ClassName[] = _T("MyClass");
    TCHAR title1[] = _T("C++技术网www.cjjjs.com");
    WNDCLASS wndClass;
    wndClass.cbClsExtra=0;
    wndClass.cbWndExtra=0;
    wndClass.hbrBackground= (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
    wndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
    wndClass.hInstance = hInstance;
    wndClass.lpfnWndProc = WinProc;
    wndClass.lpszClassName = ClassName;
    wndClass.lpszMenuName=NULL;
    wndClass.style=CS_HREDRAW|CS_VREDRAW;

    if(!RegisterClass(&wndClass))
    {
        return 0;
    }

    HWND hwnd = CreateWindow(ClassName,title1,WS_OVERLAPPEDWINDOW,0,0,440,400,NULL,NULL,hInstance,NULL);
    ShowWindow(hwnd,SW_SHOWNORMAL);

    MSG msg;
    while (GetMessage(&msg,NULL,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
    当然,测试代码本身也是挺有意思的,你可以在此基础上改进哦。实现替换当前的字符的功能代码标注了注释,已经不难理解。
    你要清楚,窗口过程函数每次一个消息来都会执行一次,执行完后,这个函数里面的局部变量都释放了,所以要用static变量或者用全局变量来存储要持续记录的信息。同时,因为WM_PAINT会有很多情况都会产生这个消息,所以,不要在这个消息里递增i的值,否则就可能多增加了N次。
    另外,这个代码工作下Unicode字符集下,如果你发现你单击窗口移动-字符时看到乱码,则说明你的项目是多字节字符集,注意切换过来。而且,字符串str使用的是通用字符版本,所以,能够正常的替换掉每一个字符,而不是根据字节来替换的。