当前位置:C++技术网 > 精选软件 > win32长按退格键删除全部文字的功能实现

win32长按退格键删除全部文字的功能实现

更新时间:2016-01-03 22:21:56浏览次数:1+次

    在文章《win32使用退格键BackSpace从后到前的删除字符串的字符》中我已经详细描述如何实现删除效果。当然这里采用了文章提到的第二种效果,因为这个比较简单。
    在文章《键盘的重复速度属性对长按的按键消息产生频率的影响》中,详细描述了键盘长按时重复速度的属性。这是在用户级别的了解,不过跟程序内部的WM_KEYDOWN消息密切相关,所有先讲述了这个概念。同时也顺带讲了长按按键的重复延迟的属性,参见文章《键盘的重复延迟属性对按键长按事件检测的影响》。
    其实在“键盘的重复速度属性对长按的按键消息产生频率的影响”这篇文章的插图中可以看到长按退格键删除全部文字的效果。因为前面是铺垫,所以本文就开始讲如何实现长按退格键删除全部文字的效果。
    我们在阅读了重复速度的文章后,你就可以从效果上感知重复速度的作用。然而这个对于我们编程密切相关,因为它就影响这WM_KEYDOWN消息。如何影响的呢?既然它表示的是重复速度,自然是和速度有关。和消息有关的速度,很快你应该会想到的就是长按时产生WM_KEYDOWN消息的速度。
    可能你会一直很疑惑,长按下去之后,是如何控制WM_KEYDOWN消息的产生的呢?有多快?太快了程序处理不过来怎么办?诸如此类的问题,几乎都没有什么人讲过。因为一般很少涉及这方面的编程,都站的很高,看不到脚下的基础技术了。在《Windows程序设计》的书中,都只是简单提及,并没有详细说明。因此在学习到这里的时候,我直接打了一个打问号。如今第二次阅读,积累了不少开发经验,所以也慢慢的解开这个谜团。
    如果我们只是一下下的单击按键,每次单击马上又松开按键,就不会有长按按键这种消息,也自然不会涉及到长按按键产生消息速度的问题。然而有时候却需要长按,比如连续删除一大堆文字,你自然没有耐心一下下的按。每次按下键盘都有一个WM_KEYDOWN消息的产生,然后松手就有一个WM_KEYUP消息产生。然而如果你长按按键,就表示要连续的产生WM_KEYDOWN消息。也就是这个消息才可以让程序可以连续的处理按下的消息,从而完成连续的文字删除或者触发长按功能,就一次性删除所有的光标前面的文字。
    那么按下按键,按键一直处于按下的状态,如何来切断按下的状态,从而分割出很多的按下按键的消息。这个就是从时间上来分割的。比如你长按了10秒钟,如果以2秒钟切割一次,那么就产生了5个WM_KEYDOWN消息。如果按照1秒钟来切割,就产生了10个WM_KEYDOWN消息。前面的产生消息的速度就是0.5个/秒,后面就是1个/秒。自然是后面的一秒一个消息产生的快,这个就表示它的重复速度快。这也就是按键的文字重复速度属性的来历。产生一个字符,也就是对应一个WM_KEYDOWN消息,所以文字重复速度属性,其实就决定了WM_KEYDOWN消息的产生速度。
    你可以通过系统的设置调整这个重复速度,在《键盘的重复速度属性对长按的按键消息产生频率的影响》已经说明了。作为程序员,这个必须要知道,否则也不清楚自己做的这个长按功能有什么制约因素,不知道还可以这样调整重复速度,那岂不是笑掉大牙了。事实上,很少有程序员知道这个事情。
    既然是可以大量产生WM_KEYDOWN消息,自然可以连续输入相同的文字。如果按的是退格键或者del键,自然是要完成删除掉功能。我们编程就是看到的不断涌来的WM_KEYDOWN消息而已。大量的消息潮水般涌来,如果对于每一个消息处理的时间比较长,就可能导致处理不过来。如此一来,就会出问题,比如程序卡死,或者消息丢失。
    系统在WM_KEYDOWN消息的lParam参数的低字部分累加重复产生的次数,而不增加一大堆的WM_KEYDOWN消息到消息队列。所以,如果我们程序处理不及时,比如长按按键时,自然快速的消息产生来不及处理,就会递增这个计数,那么我们就检测这个计数,如果计数达到一个值,我们就删除所有的文字,就可以实现长按按键删除所有文字的效果了。如果你对长按按键产生重复的WM_KEYDOWN消息的重复计数的原理感兴趣,想摸清楚这个累计计数的机制,请阅读《长按键盘按键的WM_KEYDOWN消息的重复计数原理和作用分析及代码验证》。
    不过对于本功能的实现,知道利用WM_KEYDOWN消息的lParam参数的低字部分的重复计数即可。至于说多久的延迟会导致重复产生一个消息,看你在按键文字重复速度属性里设置的值。而且不同的键盘和电脑肯定有差异,所以延迟时间越长,累计当然是越明显。大部分情况,单击下不会增加重复计数的,因为很快就处理完了。而在实际的开发中,我们可能要对消息做一定的处理,从而产生了延迟。我们人工模拟一下延迟,就使用Sleep函数,我电脑测试70毫秒就产生了重复消息,但是你的电脑不一定哦,以测试为准。重复产生WM_KEYDOWN消息后,重复计数就变为2了。表面在你处理消息队列的消息时,这个消息已经产生了两次了。我们要达到长按删除,就检测这个累计值。我们就检测值为2时就认为是长按了,就可以执行删除所有文字的操作。
    所以代码如下:
if (VK_BACK==wParam)
{
    int ic = LOWORD(lParam);//低字部分存储了按键重复的次数编号     
    if (ic==2)
    {
        iDelCount=lstrlen(cjjjs);//将删除的字数设置为字符串的长度
    }
    else
    {
        iDelCount++;//不累计就删除一个字符
        Sleep(70);//70毫秒的延迟处理,就可能累计重复计数一次
    }
}
    删除的逻辑在文中开始提到的文章有详细解释。其实长按按键删除所有文字的代码本身很简单,难就难在基础知识的深入了解。所谓大道至简就是这回事,只有你了解的足够多,你就可以用最简单的代码实现人家花很多几十行才能够实现的功能。这也就是要学习基础技术的重要性。
    下面是完整的代码:
#include "windows.h"
#include <tchar.h>
// - 项目是Unicode字符集
TCHAR cjjjs[100]=_T("C++技术网http:www.cjjjs.comC++技术网http:www.cjjjs.com");
TCHAR tip2[100]=_T("提示:按下键盘的←(退格键)可以删除了字符");
TCHAR tip[100]=_T("");
LRESULT CALLBACK WinProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    static RECT rect;
    static int iDelCount=0;
    static int sum=0;
    static bool bisFirst=true;
    switch (message)
    {
     case WM_PAINT:
         hdc = BeginPaint(hwnd,&ps);
         SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));
         TextOut(hdc,0,0,cjjjs,lstrlen(cjjjs)-iDelCount);
         TextOut(hdc,0,50,tip2,lstrlen(tip2));
         EndPaint(hwnd,&ps);
         return 0;
     case WM_KEYDOWN:
        if (VK_BACK==wParam)
        {
            int ic = LOWORD(lParam);//低字部分存储了按键重复的次数编号           
            if (ic==2)
            {
                iDelCount=lstrlen(cjjjs);
            }
            else
            {
                iDelCount++;
                Sleep(70);//70毫秒的延迟处理,就可能累计重复计数
            }
        }
         InvalidateRect(hwnd,NULL,TRUE);
         return 0;

     case WM_RBUTTONDOWN:
         {
            iDelCount=0;
            InvalidateRect(hwnd,NULL,TRUE);
         }
         return 0;
     case WM_DESTROY:
         PostQuitMessage(0);
         return 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++技术网http://www.cjjjs.com");
    WNDCLASS wndClass;
    wndClass.cbClsExtra=0;
    wndClass.cbWndExtra=0;
    wndClass.hbrBackground= (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndClass.hCursor=LoadCursor(NULL,IDC_HAND);
    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;
}