我应该如何修改我的代码,以便在窗口滚动期间鼠标绘制的线条保留在窗口的画布上

问题描述 投票:0回答:1

我的英语不好,所以,如果你不能理解我的问题,我很抱歉。

描述: 我正在使用win32 api制作一个绘画程序,当我尝试解决使用鼠标画线时,将线保留在画布中,它不能很好地工作。

代码: 这是Win进程函数

LRESULT MainWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static int canvasWidth = 1200;              //初始的画布大小, initialize canvas width
    static int canvasHeight = 700;              //初始的画布大小, initialize canvas height
    static int workWidth = canvasWidth + 60;    //设定的工作区大小, set work area width
    static int workHeight = canvasHeight + 60;  //设定的工作区大小, set work area height
    static std::vector<Scroll*> scrolls;        //滚动条容器, Scroll container
    static RECT clientRC;                       //窗口客户区矩形, window client rectangle
    static HDC hMemDC;                          //内存设备上下文, memory device context
    static HBITMAP hMemBM;                      //内存位图, memory bitmap
    static POINT ptPreviouse;                   //鼠标上一次位置, previous mouse position
    static POINT ptCurrent;                     //鼠标当前位置, current mouse position
    static bool bDraw = false;                  //是否开始绘制, whether to start drawing
    static HDC hDC;                             //绘图设备上下文, drawing device context
    static RECT canvasRect;                     //画布区域, canvas area
    TRACKMOUSEEVENT mouseEvent;                 //鼠标追踪事件, mouse track event
    static int CanvasLTop_X = 0;                //画布的左上x坐标, canvas left top x
    static int CanvasLTop_Y = 0;                //画布的左上y坐标, canvas left top y
    static int OldCanvasLTop_X = 0;             //上一个画布的左上x坐标, previous canvas left top x
    static int OldCanvasLTop_Y = 0;             //上一个画布的左上y坐标, previous canvas left top y
    static int VscrollPos = 0;                  //垂直滚动条位置, vertical scroll bar position
    static int HscrollPos = 0;                  //水平滚动条位置, horizontal scroll bar position

    switch (message)
    {
    case WM_COMMAND:
    {
        // 分析菜单选择:
        //analysis menu selection
        switch (LOWORD(wParam))
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, MainWindow::About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        case IDC_BUTTON1:
            // TODO: 在此处添加按钮事件处理代码...
            //TODO: add button event handling code here...

            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }
    break;

    case WM_CREATE:
    {
        // TODO: 在此处添加控件创建代码...
        //TODO: add control creation code here...
        GetClientRect(hWnd, &clientRC);
        hDC = GetDC(hWnd);

        //创建垂直滚动条
        //create vertical scroll bar
        Scroll* VerticalScroll = new Scroll(hWnd, SB_VERT, 0, workHeight, clientRC.bottom);
        scrolls.push_back(VerticalScroll);

        //创建水平滚动条
        //create horizontal scroll bar
        Scroll* HorizontalScroll = new Scroll(hWnd, SB_HORZ, 0, workWidth, clientRC.right);
        scrolls.push_back(HorizontalScroll);

        //创建内存设备上下文
        //create memory device context
        hMemDC = CreateCompatibleDC(hDC);
        hMemBM= CreateCompatibleBitmap(hDC, canvasWidth, canvasHeight);
        SelectObject(hMemDC, hMemBM);
        PatBlt(hMemDC, 0, 0, clientRC.right, clientRC.bottom, WHITENESS);

        ReleaseDC(hWnd, hDC);

    }

    break;

    case WM_DRAWITEM:
    {
        DRAWITEMSTRUCT* ptrDIS = (DRAWITEMSTRUCT*)lParam; // 转换为DRAWITEMSTRUCT结构, convert to DRAWITEMSTRUCT structure

        // TODO: 在此处添加绘制控件代码...
        //TODO: add control drawing code here...
    }
    break;

    case WM_SIZE:
    {
        // TODO: 在此处添加大小变化事件处理代码...
        //TODO: add size change event handling code here...

        hDC = GetDC(hWnd);

        //将内容保存至临时位图
        //copy content to temporary bitmap
        HDC tempDC = CreateCompatibleDC(hDC);
        HBITMAP tempBM = CreateCompatibleBitmap(hDC, clientRC.right, clientRC.bottom);
        SelectObject(tempDC, tempBM);
        BitBlt(tempDC, 0, 0, clientRC.right, clientRC.bottom, hMemDC, 0, 0, SRCCOPY);

        GetClientRect(hWnd, &clientRC);

        //更新滚动条信息
        //update scroll bar information
        for (Scroll* scroll : scrolls)
        {
            if (scroll->widgeType == SB_VERT)
            {
                SCROLLINFO newVSI = scroll->GetScrollStruct();
                newVSI.cbSize = sizeof(SCROLLINFO);
                newVSI.fMask = SIF_ALL;
                newVSI.nMax = workHeight;
                newVSI.nPage = clientRC.bottom;
                scroll->UpdateScrollInfo(hWnd, newVSI);
            }

            if (scroll->widgeType == SB_HORZ)
            {
                SCROLLINFO newHSI = scroll->GetScrollStruct();
                newHSI.cbSize = sizeof(SCROLLINFO);
                newHSI.fMask = SIF_ALL;
                newHSI.nMax = workWidth;
                newHSI.nPage = clientRC.right;
                scroll->UpdateScrollInfo(hWnd, newHSI);
            }

        }

        //计算画布的新位置
        //calculate new canvas position
        if (clientRC.right - 45 < canvasWidth) CanvasLTop_X = 30;
        else CanvasLTop_X = (clientRC.right - canvasWidth) / 2;

        if (clientRC.bottom - 45 < canvasHeight) CanvasLTop_Y = 30;
        else CanvasLTop_Y = (clientRC.bottom - canvasHeight) / 2;

        //将临时位图内容复制到内存设备上下文
        //copy temporary bitmap content to memory device context
        hMemDC= CreateCompatibleDC(hDC);
        hMemBM = CreateCompatibleBitmap(hDC, clientRC.right, clientRC.bottom);
        SelectObject(hMemDC, hMemBM);
        PatBlt(hMemDC, 0, 0, clientRC.right, clientRC.bottom, WHITENESS);
        BitBlt(hMemDC, CanvasLTop_X, CanvasLTop_Y, canvasWidth, canvasHeight, tempDC, OldCanvasLTop_X, OldCanvasLTop_Y, SRCCOPY);

        //更新画布坐标
        //update canvas coordinates
        OldCanvasLTop_X = CanvasLTop_X;
        OldCanvasLTop_Y = CanvasLTop_Y;

        DeleteObject(tempBM);
        DeleteDC(tempDC);

    }
    break;

    case WM_PAINT:
    {
        // TODO: 在此处添加使用 hdc 的任何绘图代码...
        //TODO: add any drawing code using hdc here...

        PAINTSTRUCT ps;
        hDC = BeginPaint(hWnd, &ps);
        GetClientRect(hWnd, &clientRC);

        //获取滚动条位置
        //get scroll bar position
        for (Scroll* scroll : scrolls)
        {
            if (scroll->widgeType == SB_VERT)
            {
                VscrollPos = scroll->GetScrollPos(hWnd);
            }
            if (scroll->widgeType == SB_HORZ)
            {
                HscrollPos = scroll->GetScrollPos(hWnd);
            }
        }

        //跟新画布区域
        //update canvas area
        canvasRect = { CanvasLTop_X - HscrollPos, CanvasLTop_Y - VscrollPos , CanvasLTop_X + canvasWidth - HscrollPos, CanvasLTop_Y + canvasHeight - VscrollPos };
        
        //将内存设备上下文内容复制到窗口设备上下文
        //copy memory device context content to drawing device context
        BitBlt(hDC, canvasRect.left, canvasRect.top, canvasWidth, canvasHeight, hMemDC, canvasRect.left, canvasRect.top, SRCCOPY);

        EndPaint(hWnd, &ps);
        ReleaseDC(hWnd, hDC);

    }
    break;

    case WM_VSCROLL:
    {
        // TODO: 在此处添加垂直滚动条事件处理代码...
        //TODO: add vertical scroll bar event handling code here...

        SCROLLINFO verticalSI;

        for (Scroll* scroll : scrolls)
        {
            if (scroll->widgeType == SB_VERT)
            {
                verticalSI = scroll->GetScrollStruct();
                int newPos = scroll->GetScrollPos(hWnd);
                switch (LOWORD(wParam))
                {
                case SB_LINEUP:
                    newPos -= 10;
                    break;
                case SB_LINEDOWN:
                    newPos += 10;
                    break;
                case SB_PAGEUP:
                    newPos -= verticalSI.nPage;
                    break;
                case SB_PAGEDOWN:
                    newPos += verticalSI.nPage;
                    break;
                case SB_THUMBTRACK:
                    newPos = verticalSI.nTrackPos;
                    break;
                }

                if (newPos < 0) newPos = 0;
                if (newPos > verticalSI.nMax - verticalSI.nPage) newPos = verticalSI.nMax - verticalSI.nPage;

                ScrollWindowEx(hWnd, 0, scroll->GetScrollPos(hWnd) - newPos, nullptr, nullptr, nullptr, nullptr, SW_INVALIDATE | SW_ERASE);
                scroll->UpdateScrollInfo(hWnd, newPos);
                UpdateWindow(hWnd);
                break;
            }
        }
    }
    break;

    case WM_HSCROLL:
    {
        // TODO: 在此处添加水平滚动条事件处理代码...
        //TODO: add horizontal scroll bar event handling code here...

        for (Scroll* scroll : scrolls)
        {
            if (scroll->widgeType == SB_HORZ)
            {
                SCROLLINFO horizontalSI = scroll->GetScrollStruct();
                int newPos = scroll->GetScrollPos(hWnd);
                switch (LOWORD(wParam))
                {
                case SB_LINEUP:
                    newPos -= 10;
                    break;
                case SB_LINEDOWN:
                    newPos += 10;
                    break;
                case SB_PAGEUP:
                    newPos -= horizontalSI.nPage;
                    break;
                case SB_PAGEDOWN:
                    newPos += horizontalSI.nPage;
                    break;
                case SB_THUMBTRACK:
                    newPos = horizontalSI.nTrackPos;
                    break;
                }

                if (newPos < 0) newPos = 0;
                if (newPos > horizontalSI.nMax - horizontalSI.nPage) newPos = horizontalSI.nMax - horizontalSI.nPage;

                ScrollWindowEx(hWnd, scroll->GetScrollPos(hWnd) - newPos, 0, nullptr, nullptr, nullptr, nullptr, SW_INVALIDATE | SW_ERASE);
                scroll->UpdateScrollInfo(hWnd, newPos);
                UpdateWindow(hWnd);
                break;
            }
        }
    }

    break;

    case WM_LBUTTONDOWN:
    {
        hDC = GetDC(hWnd);
        bDraw = true;
        ptPreviouse.x = LOWORD(lParam);
        ptPreviouse.y = HIWORD(lParam);
    }
    break;

    case WM_MOUSEMOVE:
    {
        if (bDraw)
        {
            MoveToEx(hMemDC, ptPreviouse.x, ptPreviouse.y, nullptr);
            LineTo(hMemDC, LOWORD(lParam), HIWORD(lParam));
            BitBlt(hDC, canvasRect.left, canvasRect.top, canvasWidth, canvasHeight, hMemDC, canvasRect.left, canvasRect.top,  SRCCOPY);
            ptPreviouse.x = LOWORD(lParam);
            ptPreviouse.y = HIWORD(lParam);

            mouseEvent.cbSize = sizeof(TRACKMOUSEEVENT);
            mouseEvent.dwFlags = TME_LEAVE;
            mouseEvent.hwndTrack = hWnd;
            TrackMouseEvent(&mouseEvent);
        }
    }
    break;

    case WM_LBUTTONUP:
    {
        bDraw = false;
        ReleaseDC(hWnd, hDC);
    }
    break;
    case WM_MOUSELEAVE:
    {
        bDraw = false;
        ReleaseDC(hWnd, hDC);
    }
    break;

    case WM_DESTROY:
    {
        //释放scrolls容器中的对象
        //release objects in scrolls container
        for (Scroll* scroll : scrolls)
        {
            delete scroll;
        }
        scrolls.clear();
        PostQuitMessage(0);
    }
    break;


    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }

    return 0;
}

效果: 第一次抽奖 首先画一个“?” 向下滚动窗口: 滚动至底部 向后滚动: 滚动到顶部 第二次抽奖 绘制一个新对象

我的技术很差。不知道怎么解决。

c++ winapi
1个回答
0
投票

请勿在

WM_PAINT
消息处理程序之外直接在窗口上绘制。 下次重新绘制窗口时,您绘制的任何内容都将被删除。

您有几个选择:

  • 让你的鼠标处理程序将坐标保存在丢失的地方,然后仅响应

    WM_PAINT
    绘制所有线条。

  • 创建一个与窗口大小相同的内存位图,并让鼠标处理程序在该位图上绘制线条。然后使用

    WM_PAINT
    将当前位图绘制到窗口上。

© www.soinside.com 2019 - 2024. All rights reserved.