[c ++] WinApi-简单程序绘制线

时间:2018-11-12 22:22:56

标签: c++ winapi gdi

我是新手,我想用鼠标编写一个简单的程序来绘制线条。 我在绘制这些线条时遇到问题,因为它留下了痕迹。

这是我的问题的图片:

image

这是我的代码示例:

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来保存每条线的坐标。谢谢大家的帮助!

2 个答案:

答案 0 :(得分:1)

由于在绘制新线之前没有擦除旧图形,或者在每次移动中至少更新last_xlast_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个整数xylast_xlast_y都必须是静态的 ,否则整个解决方案将无法正常工作。