c++ - 如何在 Win32 应用程序中拖动纯色矩形而不会出现白色闪烁且不会干扰屏幕上的其他对象?
问题描述
我一直在玩弄一个小玩具 Win32 应用程序来尝试掌握基本绘图和处理鼠标和键盘消息的窍门。基本上,目标是在客户区域周围单击以放置矩形,按 alt 单击它们以在 4 种颜色之间循环,右键单击它们以直接使用 f 键之一重新分配颜色,或者右键单击并按住以通过拖放重新定位,当前选定的矩形具有虚线轮廓而不是实线。
事实证明,正确拖动是很困难的。拖动时,被拖动的选定矩形闪烁白色/闪烁,并暂时擦除其他矩形的笔绘制边框。克服这两个问题是我需要帮助的。
一个 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:您应该只在 WM_PAINT 处理程序中进行绘画。
而不是直接在 WM_MOUSEMOVE 处理程序中的屏幕表面上绘画,您应该执行以下操作:
WM_MOUSEMOVE
只需调用以InvalidateRect(,prcSelected)
使拖动矩形占用的区域无效。在你的
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); }
这样,您将仅以正确的顺序在 WM_PAINT 中进行绘图。
即使在这种情况下,您可能仍然有闪烁。如果是这样,通过向窗口添加WS_EX_COMPOSITED标志来 启用双缓冲。
推荐阅读
- azure - 我们可以为 azure 广告创建自定义用户定义声明吗?
- javascript - js:如何截取域名后缀
- java - 如何在Java中获取两个不为空的字符串中的任何一个并连接
- c - C中的3x3框模糊:最后一行
- oracle - 我存储了不同时间格式的 Oracle 数据库数据需要更改为另一种时间格式
- python - N皇后问题,我怎样才能解决这个变量
- javascript - PluginManager addCommand tinymce
- re2 - 如何匹配字符串排除子字符串使用re2
- azure - 如何使用 power bi 或 Azure 数据工厂解压缩 .tar.gz 文件 blob 存储
- prometheus - Grafana 从 Prometheus 获取当前小时的指标