[c++]WinApi - 画线的简单程序
[c++]WinApi - Simple Program drawing lines
我是新手,我正在尝试编写一个用鼠标画线的简单程序。
我在画这些线时遇到了问题,因为它会留下痕迹。
这是我的问题的图片:
这是我的代码示例:
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 来保存每一行的坐标。谢谢大家的帮助!
您得到残留痕迹是因为您在绘制新线之前没有擦除旧绘图,或者至少在每次移动时更新 last_x
和 last_y
以便新线连接到上一行的结尾,如 Microsoft's example.
但是,您真的根本不应该直接在鼠标消息处理程序中绘制 window。一旦 window 出于任何原因需要重新绘制,您的所有绘图都将丢失。处理此问题的正确方法是在 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;
}
我来晚了,这对有经验的 Win32 程序员来说可能很明显,但我想在 Remy Lebeau 示例中添加一个重要说明:
布尔值 isDown
和 4 个整数 x
、y
、last_x
和 last_y
都必须是静态的,否则整个解决方案将无法工作。
我是新手,我正在尝试编写一个用鼠标画线的简单程序。 我在画这些线时遇到了问题,因为它会留下痕迹。
这是我的问题的图片:
这是我的代码示例:
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 来保存每一行的坐标。谢谢大家的帮助!
您得到残留痕迹是因为您在绘制新线之前没有擦除旧绘图,或者至少在每次移动时更新 last_x
和 last_y
以便新线连接到上一行的结尾,如 Microsoft's example.
但是,您真的根本不应该直接在鼠标消息处理程序中绘制 window。一旦 window 出于任何原因需要重新绘制,您的所有绘图都将丢失。处理此问题的正确方法是在 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;
}
我来晚了,这对有经验的 Win32 程序员来说可能很明显,但我想在 Remy Lebeau 示例中添加一个重要说明:
布尔值 isDown
和 4 个整数 x
、y
、last_x
和 last_y
都必须是静态的,否则整个解决方案将无法工作。