我是新手,我想用鼠标编写一个简单的程序来绘制线条。 我在绘制这些线条时遇到问题,因为它留下了痕迹。
这是我的问题的图片:
这是我的代码示例:
LRESULT APIENTRY WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:PostQuitMessage(0);break;
case WM_LBUTTONDOWN:
hdc = GetDC(hwnd);
last_x = LOWORD(lParam);
last_y = HIWORD(lParam);
isDown = true;
break;
case WM_MOUSEMOVE:
if (isDown)
{
Pen = CreatePen(PS_SOLID, 3, RGB(0, 0, 255));
Box = (HPEN)SelectObject(hdc, Pen);
int x = LOWORD(lParam);
int y = HIWORD(lParam);
MoveToEx(hdc, last_x, last_y, NULL);
LineTo(hdc, x, y);
}
break;
case WM_LBUTTONUP:
isDown = false;
ReleaseDC;
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
编辑: 现在它运行良好,但是如果您可以向我解释一下,当我绘制新线时,如何使我的旧线留在客户区?因为现在我只能画一条线。我应该使用位图保存屏幕或其他内容吗?
编辑:编辑: 好的,我使用Vector来保存每条线的坐标。谢谢大家的帮助!
答案 0 :(得分:1)
由于在绘制新线之前没有擦除旧图形,或者在每次移动中至少更新last_x
和last_y
从而使新线连接到末尾,所以您会残留痕迹前一行,例如Microsoft's example中的行。
但是,您实际上根本不应该直接在鼠标消息处理程序中绘制窗口。一旦窗口出于任何原因需要重新粉刷,您的所有绘图都将丢失。解决此问题的正确方法是改为在WM_PAINT
消息处理程序中执行所有绘图。使用鼠标消息根据需要跟踪线信息,然后仅在WM_PAINT
中进行所有实际绘制。
例如:
LRESULT APIENTRY WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
Pen = CreatePen(PS_SOLID, 3, RGB(0, 0, 255));
break;
case WM_DESTROY:
DeleteObject(Pen);
PostQuitMessage(0);
break;
case WM_LBUTTONDOWN:
x = last_x = LOWORD(lParam);
y = last_y = HIWORD(lParam);
isDown = true;
InvalidateRect(hwnd, NULL, TRUE);
break;
case WM_MOUSEMOVE:
if (isDown)
{
x = LOWORD(lParam);
y = HIWORD(lParam);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
case WM_LBUTTONUP:
isDown = false;
InvalidateRect(hwnd, NULL, TRUE);
break;
/* if your WNDCLASS sets hbrBackground=NULL, uncomment this handler...
case WM_ERASEBKGND:
{
HDC hdc = (HDC) wParam;
draw a background on the hdc as needed...
return 1;
}
*/
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
if (last_x != x) || (last_y != y)
{
HPEN OldPen = (HPEN) SelectObject(hdc, Pen);
MoveToEx(hdc, last_x, last_y, NULL);
LineTo(hdc, x, y);
SelectObject(hdc, OldPen);
}
EndPaint(hwnd, &ps);
break;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
这将画一条直线,从第一次按下鼠标的点开始,然后跟随鼠标四处移动。
或者,如果要在按住鼠标的同时端对端绘制多条线,请尝试以下操作:
std::vector<POINT> points;
LRESULT APIENTRY WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
Pen = CreatePen(PS_SOLID, 3, RGB(0, 0, 255));
points.clear();
break;
case WM_DESTROY:
DeleteObject(Pen);
PostQuitMessage(0);
break;
case WM_LBUTTONDOWN:
{
points.clear();
POINT pt;
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
points.push_back(pt);
isDown = true;
InvalidateRect(hwnd, NULL, TRUE);
break;
}
case WM_MOUSEMOVE:
if (isDown)
{
POINT pt;
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
points.push_back(pt);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
case WM_LBUTTONUP:
isDown = false;
InvalidateRect(hwnd, NULL, TRUE);
break;
/* if your WNDCLASS sets hbrBackground=NULL, uncomment this handler...
case WM_ERASEBKGND:
{
HDC hdc = (HDC) wParam;
draw a background on the hdc as needed...
return 1;
}
*/
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
if (points.size() > 1)
{
HPEN OldPen = (HPEN) SelectObject(hdc, Pen);
MoveToEx(hdc, points[0].x, points[0].y, NULL);
for (size_t i = 1; i < points.size(); ++i) {
LineTo(hdc, points[i].x, points[i].y);
}
SelectObject(hdc, OldPen);
}
EndPaint(hwnd, &ps);
break;
}
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
答案 1 :(得分:0)
我在这里参加聚会很晚,对于经验丰富的Win32程序员来说可能很明显,但是我想在Remy Lebeau示例中添加一个重要说明:
布尔isDown
和4个整数x
,y
,last_x
和last_y
都必须是静态的 ,否则整个解决方案将无法正常工作。