[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_xlast_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 个整数 xylast_xlast_y 都必须是静态的,否则整个解决方案将无法工作。