Chapter I:在WM_MOUSEMOVE中绘制.
有时候要求在一个窗口中绘制鼠标的拖曳框,就像是用户在桌面上拖曳图标显示的框一样.对于这样的矩形框windows的API提供了一个函数DrawFocusRect,
当然这个函数是可以的,不过这个函数有两个小问题,那就是它的第二个参数是一个RECT参数,而且这个矩形的必须有以下特点:
left<right,top<bottom.否则,绘制必定失败,而且也没有办法设置矩形框的颜色.当然,计算这样的矩形并不是什么困难的事情,不过这并不是这篇文章的重点.
在这里,提供一种自己绘制矩形的办法,这样可以根据需要自己来改写.下面是这个函数的实现(两个):
- void DrawDragRect(HDC hDC, RECT& rc)
- {
- for (int j=rc.top; j<rc.bottom; j++)
- {
- if (j%2==0)
- SetPixel(hDC, rc.left, j, RGB(255, 0, 0));
- }
- for (int i=rc.left; i<rc.right; i++)
- {
- if (i%2==0)
- SetPixel(hDC, i, rc.top, RGB(255, 0, 0));
- }
- for (int j=rc.top; j<rc.bottom; j++)
- {
- if (j%2==0)
- SetPixel(hDC, rc.right, j, RGB(255, 0, 0));
- }
- for (int i=rc.left; i<rc.right; i++)
- {
- if (i%2==0)
- SetPixel(hDC, i, rc.bottom, RGB(255, 0, 0));
- }
- }
- void DrawDragRect(HDC hDC, POINT ptSource, POINT ptDest)
- {
- int left = ptSource.x <= ptDest.x ? ptSource.x : ptDest.x;
- int right = left + abs(ptDest.x - ptSource.x);
- int top = ptSource.y <= ptDest.y ? ptSource.y : ptDest.y;
- int bottom = top + abs(ptDest.y - ptSource.y);
- ASSERT(left<=right && bottom<=top);
- for (int j=top; j<bottom; j++)
- {
- if (j%2==0)
- SetPixel(hDC, left, j, RGB(255, 0, 0));
- }
- for (int i=left; i<right; i++)
- {
- if (i%2==0)
- SetPixel(hDC, i, top, RGB(255, 0, 0));
- }
- for (int j=top; j<bottom; j++)
- {
- if (j%2==0)
- SetPixel(hDC, right, j, RGB(255, 0, 0));
- }
- for (int i=left; i<right; i++)
- {
- if (i%2==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 <= g_rcDrag.left) g_rcDrag.left = pt.x;
- if (g_rcDrag.left != pt.x) g_rcDrag.right = pt.x;
- if (pt.y <= 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);
- }
- }
- break;
- case WM_LBUTTONUP:
- {
- ::ReleaseCapture();
- g_rcDrag.left = g_rcDrag.right = 0;
- InvalidateRect(hWnd, NULL, TRUE);// 注意,最后一个参数必须为TRUE,如果为FALSE,上次绘制的矩形不消失.
- UpdateWindow(hWnd);
- }
- break;
- case WM_PAINT:
- {
- hdc = BeginPaint(hWnd, &ps);
- if (0 < g_rcDrag.right-g_rcDrag.left)
- DrawDragRect(hdc, g_rcDrag); //::DrawFocusRect(hdc, &g_rcDrag);也可以
- ::DrawDragRect(hdc, )
- break;
- ...
- }
按住鼠标左键在窗口客户区拖动吧,可以发现一个红色的矩形框.
Chapter II:更好的绘制办法
Chapter I的代码能够正常工作,不过这是一种理想的情况,但有时候会碰到这样一种情况:某些时候程序会对窗口背景或前景进行绘制,例如贴一个图片,画一些
复杂的图形等待,这些情况会导致一种情况:那就是当在窗口中拖曳鼠标时,WM_MOUSEMOVE不是那么灵光,具体表现在当在客户区拖曳鼠标后,矩形的4个边绘制的
不完整.总而言之,在WM_MOUSEMOVE绘制矩形不正常,在这种情况下,为了达到绘制的平滑效果,可以通过计时器来绘制.
- RECT g_rcDrag = {-1};
- #define ID_TIMER 1
- LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
- {
- ...
- switch (message)
- {
- case WM_CREATE:
- ::SetTimer (hWnd, ID_TIMER, 100, NULL) ; //创建定时器,每10微妙产生一个WM_TIMER消息
- break;
- case WM_TIMER:
- if (-1 != g_rcDrag.left)
- {
- POINT pt = {0};
- GetCursorPos(&pt);
- ScreenToClient(hWnd, &pt);
- // 待绘制的拖曳矩形的4个点.
- if (pt.x <= g_rcDrag.left) g_rcDrag.left = pt.x;
- if (g_rcDrag.left != pt.x) g_rcDrag.right = pt.x;
- if (pt.y <= g_rcDrag.top) g_rcDrag.top = pt.y;
- if (g_rcDrag.top != pt.y) g_rcDrag.bottom = pt.y;
- InvalidateRect(hWnd, NULL, TRUE);
- UpdateWindow(hWnd);
- }
- break;
- case WM_LBUTTONDOWN:
- {
- g_rcDrag.right = g_rcDrag.left = LOWORD(lParam);
- g_rcDrag.bottom = g_rcDrag.top = HIWORD(lParam);
- ::SetCapture(hWnd);
- }
- break;
- case WM_PAINT:
- {
- hdc = BeginPaint(hWnd, &ps);
- if (0 < g_rcDrag.right-g_rcDrag.left)
- DrawDragRect(hdc, g_rcDrag); //::DrawFocusRect(hdc, &g_rcDrag);也可以
- }
- break;
- case WM_DESTROY:
- KillTimer (hWnd, ID_TIMER) ; //销毁定时器
- break;
- ...
- }
- ...
- }
SetTimer的间隔时间越短,绘制效果越好.
本文转自jetyi51CTO博客,原文链接: http://blog.51cto.com/jetyi/640776
,如需转载请自行联系原作者