简体   繁体   English

如何在 Win32 应用程序中拖动纯色矩形而不会出现白色闪烁且不干扰屏幕上的其他对象?

[英]How to drag a solid color rectangle in a Win32 app without white flickering and without interfering with other on screen objects?

I've been messing around with a little toy Win32 app to try and get the hang of basic drawing and processing mouse and keyboard messages.我一直在使用一个小玩具 Win32 应用程序来尝试掌握基本的绘图和处理鼠标和键盘消息的窍门。 Basically, the goal is to click around the client area to place rectangles, alt click them to cycle through 4 colors, right click them to reassign a color directly with one of the f keys, or right click and hold to reposition with drag and drop, with the currently selected rectangle having a dotted outline instead of solid.基本上,目标是在客户区周围单击以放置矩形,alt 单击它们以循环显示 4 种颜色,右键单击它们以直接使用 f 键之一重新分配颜色,或右键单击并按住以通过拖放重新定位,当前选定的矩形具有虚线轮廓而不是实心轮廓。

Dragging correctly is proving to be difficult.事实证明,正确拖动很困难。 While dragging, the selected rectangle being dragged flashes white / flickers, and also temporarily erases the pen drawn borders of the other rectangles.拖动时,被拖动的选定矩形会闪烁白色/闪烁,并且还会暂时擦除其他矩形的笔绘制边框。 Overcoming these two issues is what I need help with.克服这两个问题是我需要帮助的。

A c++ vector is being used to collect and process dynamically allocated CRect objects, in case it matters. C++ 向量用于收集和处理动态分配的 CRect 对象,以防万一。


    //relevant code from WndProc

        case WM_RBUTTONDOWN:
        {
            ClipCursor(&rcClip);
            if (prcSelected)
            {
                prcSelected->deselect();
                prcSelected = nullptr;
                InvalidateRect(hWnd, NULL, TRUE);
            }
            int x{ LOWORD(lParam) }, y{ HIWORD(lParam) };
            for (auto rc : vRect)
            {
                if (rc->IsClicked(x, y))
                {
                    rc->select();
                    prcSelected = rc;
                    InvalidateRect(hWnd, NULL, TRUE);
                    break;
                }
            }
        }
            break;

        case WM_RBUTTONUP:
            ClipCursor(&rcOldClip);
            InvalidateRect(hWnd, NULL, TRUE);
            break;

        case WM_MOUSEMOVE:
        {
            if (wParam & MK_RBUTTON && prcSelected)
            {
                CRect rcPrev{ *prcSelected };
                CRect crIsect{};
                int x{ LOWORD(lParam) }, y{ HIWORD(lParam) };
                int xShift{ x - prcSelected->r.left }, yShift{ y - prcSelected->r.top };
                prcSelected->shift(xShift, yShift);     
                rcPrev.SetFill(CR_WHITE);
                rcPrev.SetOutline(CR_WHITE);
                rcPrev.draw();

                for (auto rc : vRect)
                    if (IntersectRect(&crIsect.r, &(rc->r), &(rcPrev.r)))
                    {
                        crIsect.SetFill(rc->GetFill());
                        crIsect.SetOutline(rc->GetFill());
                        crIsect.draw();
                    }

                prcSelected->draw();
            }
        }
            break;

        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);

            for (auto rc : vRect)
                rc->draw(hdc);

            EndPaint(hWnd, &ps);
        }
            break;


    //Rectangle drawing code

    void CRect::draw()
    {
        HDC hdc{ GetDC(hWnd) };
        HPEN hpenDot;
        SelectObject(hdc, GetStockObject(DC_BRUSH));
        SetDCBrushColor(hdc, crBrush);
        if (!fSelected)
        {
            SelectObject(hdc, GetStockObject(DC_PEN)); // Can be set to any color. No need to release.
            SetDCPenColor(hdc, crPen);
            Rectangle(hdc, r.left, r.top, r.right, r.bottom);
        }
        else
        {
            hpenDot = CreatePen(PS_DOT, 1, crPen);
            SelectObject(hdc, hpenDot);
            Rectangle(hdc, r.left, r.top, r.right, r.bottom);
            DeleteObject(hpenDot);
        }
        ReleaseDC(hWnd, hdc);
    }

    void CRect::draw(HDC& hdc)
    {
        HPEN hpenDot;
        SelectObject(hdc, GetStockObject(DC_BRUSH));
        SetDCBrushColor(hdc, crBrush);
        if (!fSelected)
        {
            SelectObject(hdc, GetStockObject(DC_PEN)); 
            SetDCPenColor(hdc, crPen);
            Rectangle(hdc, r.left, r.top, r.right, r.bottom);
        }
        else
        {
            hpenDot = CreatePen(PS_DOT, 1, crPen);
            SelectObject(hdc, hpenDot);
            Rectangle(hdc, r.left, r.top, r.right, r.bottom);
            DeleteObject(hpenDot);
        }
    }


    void CRect::shift(int x, int y)
    {
        r.left   += x;
        r.top    += y;
        r.right  += x;
        r.bottom += y;
    }

    bool CRect::IsClicked(int x, int y)
    {
        POINT pt{ x, y };
        return (bool)PtInRect(&r, pt);

    }


TL;DR: you should do painting only in WM_PAINT handler. TL;DR:你应该只在 WM_PAINT 处理程序中进行绘画。

Instead of painting directly on screen surface in WM_MOUSEMOVE handler you should do the following:您应该执行以下操作,而不是在 WM_MOUSEMOVE 处理程序中直接在屏幕表面上绘画:

  1. In WM_MOUSEMOVE just call InvalidateRect(,prcSelected) to invalidate area occupied by your drag rectangle.WM_MOUSEMOVE只需调用InvalidateRect(,prcSelected)以使拖动矩形占用的区域无效。

  2. In your WM_PAINT handler在您的WM_PAINT处理程序中

    case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // clear surface FillRect(hdc, ps.rcPaint, COLORREF(0xFFFFFF)); for (auto rc : vRect) rc->draw(hdc); if(prcSelected) DrawDragRectangle(...); // drawing code from your WM_MOUSEMOVE EndPaint(hWnd, &ps); }

This way you will do drawing in WM_PAINT only and in correct order.这样,您将仅以正确的顺序在 WM_PAINT 中进行绘图。

You may still have flickering even in this case.即使在这种情况下,您可能仍然有闪烁。 If so enable double buffering on your window by adding WS_EX_COMPOSITED flag to it.如果是这样,请通过向其添加WS_EX_COMPOSITED标志来在您的窗口上启用双缓冲。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM