天天看点

在窗口中绘制鼠标拖曳框

 Chapter I:在WM_MOUSEMOVE中绘制.

有时候要求在一个窗口中绘制鼠标的拖曳框,就像是用户在桌面上拖曳图标显示的框一样.对于这样的矩形框windows的API提供了一个函数DrawFocusRect,当然这个函数是可以的,不过这个函数有两个小问题,那就是它的第二个参数是一个RECT参数,而且这个矩形的必须有以下特点:left&lt;right,top&lt;bottom.否则,绘制必定失败,而且也没有办法设置矩形框的颜色.当然,计算这样的矩形并不是什么困难的事情,不过这并不是这篇文章的重点.在这里,提供一种自己绘制矩形的办法,这样可以根据需要自己来改写.下面是这个函数的实现(两个):<b></b>

void DrawDragRect(HDC hDC, RECT&amp; rc) 

    for (int j=rc.top; j&lt;rc.bottom; j++) 

    { 

        if (j%2==0) 

            SetPixel(hDC, rc.left, j, RGB(255, 0, 0)); 

    } 

    for (int i=rc.left; i&lt;rc.right; i++) 

        if (i%2==0) 

            SetPixel(hDC, i, rc.top, RGB(255, 0, 0)); 

            SetPixel(hDC, rc.right, j, RGB(255, 0, 0)); 

            SetPixel(hDC, i, rc.bottom, RGB(255, 0, 0)); 

void DrawDragRect(HDC hDC, POINT ptSource, POINT ptDest) 

    int left = ptSource.x &lt;= ptDest.x ? ptSource.x : ptDest.x; 

    int right = left + abs(ptDest.x - ptSource.x); 

    int top = ptSource.y &lt;= ptDest.y ? ptSource.y : ptDest.y; 

    int bottom = top + abs(ptDest.y - ptSource.y); 

    ASSERT(left&lt;=right &amp;&amp; bottom&lt;=top); 

    for (int j=top; j&lt;bottom; j++) 

            SetPixel(hDC, left, j, RGB(255, 0, 0)); 

    for (int i=left; i&lt;right; i++) 

            SetPixel(hDC, i, top, RGB(255, 0, 0)); 

            SetPixel(hDC, right, j, RGB(255, 0, 0)); 

            SetPixel(hDC, i, bottom, RGB(255, 0, 0)); 

可以发现,上面两个函数都是绘制矩形时都是通过在DC上设置像素的颜色实现的,GDI/GDI+没有提供绘制鼠标拖曳框的办法(如果有哪位网友发现了请告诉笔者,不胜感激).绘制矩形框最关键的部分好绘制的时机,主要是:

1)捕捉起点,这个主要是在鼠标在窗口客户区按下时刻作为矩形的起点.

2)捕捉过程点,用户按下鼠标左键,在窗口中拖曳,此时鼠标滑过的轨迹就会绘制矩形框的终点.

3)结束点,用户释放鼠标左键.

另外就是绘制过程中对背景擦除问题,只要用户释放鼠标,就要重绘背景,将矩形框擦除.

下面是代码.

RECT g_rcDrag = {0};//待绘制的矩形框. 

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 

    ... 

    case WM_LBUTTONDOWN: 

        { 

            // 在客户区按下左键,作为绘制的起点. 

            g_rcDrag.right = g_rcDrag.left = LOWORD(lParam); 

            g_rcDrag.bottom = g_rcDrag.top = HIWORD(lParam); 

            ::SetCapture(hWnd);//必须调用,否则收不到WM_LBUTTONUP. 

        } 

        break; 

    case WM_MOUSEMOVE: 

            if (wParam == MK_LBUTTON) 

            { 

                POINT pt; 

                pt.x = LOWORD(lParam); 

                pt.y = HIWORD(lParam); 

                // 待绘制的拖曳矩形的4个点. 

                if (pt.x &lt;= g_rcDrag.left)      g_rcDrag.left = pt.x; 

                if (g_rcDrag.left != pt.x)      g_rcDrag.right = pt.x; 

                if (pt.y &lt;= g_rcDrag.top)       g_rcDrag.top = pt.y; 

                if (g_rcDrag.top != pt.y)       g_rcDrag.bottom = pt.y; 

                // 发出WM_PAINT消息,以绘制该矩形. 

                InvalidateRect(hWnd, NULL, TRUE);// 注意,最后一个参数必须为TRUE,如果为FALSE,上次绘制的矩形不消失. 

                UpdateWindow(hWnd); 

            } 

    case WM_LBUTTONUP: 

            ::ReleaseCapture(); 

            g_rcDrag.left = g_rcDrag.right = 0; 

            InvalidateRect(hWnd, NULL, TRUE);// 注意,最后一个参数必须为TRUE,如果为FALSE,上次绘制的矩形不消失. 

            UpdateWindow(hWnd); 

    case WM_PAINT: 

            hdc = BeginPaint(hWnd, &amp;ps); 

            if (0 &lt; g_rcDrag.right-g_rcDrag.left) 

                DrawDragRect(hdc, g_rcDrag); //::DrawFocusRect(hdc, &amp;g_rcDrag);也可以 

            ::DrawDragRect(hdc, ) 

... 

按住鼠标左键在窗口客户区拖动吧,可以发现一个红色的矩形框.

Chapter II:更好的绘制办法

Chapter I的代码能够正常工作,不过这是一种理想的情况,但有时候会碰到这样一种情况:某些时候程序会对窗口背景或前景进行绘制,例如贴一个图片,画一些复杂的图形等待,这些情况会导致一种情况:那就是当在窗口中拖曳鼠标时,WM_MOUSEMOVE不是那么灵光,具体表现在当在客户区拖曳鼠标后,矩形的4个边绘制的不完整.总而言之,在WM_MOUSEMOVE绘制矩形不正常,在这种情况下,为了达到绘制的平滑效果,可以通过计时器来绘制.

RECT g_rcDrag = {-1}; 

#define ID_TIMER    1 

    switch (message) 

    case WM_CREATE: 

        ::SetTimer (hWnd, ID_TIMER, 100, NULL) ; //创建定时器,每10微妙产生一个WM_TIMER消息 

    case WM_TIMER: 

        if (-1 != g_rcDrag.left) 

            POINT pt = {0}; 

            GetCursorPos(&amp;pt); 

            ScreenToClient(hWnd, &amp;pt); 

            // 待绘制的拖曳矩形的4个点. 

            if (pt.x &lt;= g_rcDrag.left)      g_rcDrag.left = pt.x; 

            if (g_rcDrag.left != pt.x)      g_rcDrag.right = pt.x; 

            if (pt.y &lt;= g_rcDrag.top)       g_rcDrag.top = pt.y; 

            if (g_rcDrag.top != pt.y)       g_rcDrag.bottom = pt.y; 

            InvalidateRect(hWnd, NULL, TRUE); 

            ::SetCapture(hWnd); 

    case WM_DESTROY: 

        KillTimer (hWnd, ID_TIMER) ;          //销毁定时器 

SetTimer的间隔时间越短,绘制效果越好.

本文转自jetyi51CTO博客,原文链接: http://blog.51cto.com/jetyi/640776,如需转载请自行联系原作者

继续阅读