Chapter I:在WM_MOUSEMOVE中绘制.
有时候要求在一个窗口中绘制鼠标的拖曳框,就像是用户在桌面上拖曳图标显示的框一样.对于这样的矩形框windows的API提供了一个函数DrawFocusRect,当然这个函数是可以的,不过这个函数有两个小问题,那就是它的第二个参数是一个RECT参数,而且这个矩形的必须有以下特点:left<right,top<bottom.否则,绘制必定失败,而且也没有办法设置矩形框的颜色.当然,计算这样的矩形并不是什么困难的事情,不过这并不是这篇文章的重点.在这里,提供一种自己绘制矩形的办法,这样可以根据需要自己来改写.下面是这个函数的实现(两个):<b></b>
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));
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 <= 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++)
SetPixel(hDC, left, j, RGB(255, 0, 0));
for (int i=left; i<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 <= 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);
}
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, &ps);
if (0 < g_rcDrag.right-g_rcDrag.left)
DrawDragRect(hdc, g_rcDrag); //::DrawFocusRect(hdc, &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(&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);
::SetCapture(hWnd);
case WM_DESTROY:
KillTimer (hWnd, ID_TIMER) ; //销毁定时器
SetTimer的间隔时间越短,绘制效果越好.
本文转自jetyi51CTO博客,原文链接: http://blog.51cto.com/jetyi/640776,如需转载请自行联系原作者