在 OnPaint() 事件中,如何检查给定的矩形是不是与无效区域相交?
Posted
技术标签:
【中文标题】在 OnPaint() 事件中,如何检查给定的矩形是不是与无效区域相交?【英文标题】:In OnPaint() event, how to check if a given rectangle intersects with the invalidated region?在 OnPaint() 事件中,如何检查给定的矩形是否与无效区域相交? 【发布时间】:2022-01-06 06:38:39 【问题描述】:在 Winforms 中,可以创建一个不是矩形的区域并以这种方式使其无效:
Region region = new Region(new Rectangle(...));
region.Union(new Rectangle(...));
Invalidate(region);
那么在OnPaint()
事件中,只会再次绘制上面失效的区域:
protected override void OnPaint(PaintEventArgs e)
//will only repaint the region invalidated above
//even if ClipRectangle area is bigger than that
e.Graphics.FillRectangle(Brushes.Blue, e.ClipRectangle);
在OnPaint()
事件中,有没有办法检查给定的矩形是否与无效区域相交?
我可以使用rectangle.Intersect(e.ClipRectangle)
,但这可能会产生误报。
编辑:似乎我想要的可以使用GetUpdateRgn Win32 函数(AFAIK 没有直接等效于该函数的 Winforms)
【问题讨论】:
在阅读了关于 Control.Invalidate 和 Control.Paint Event 的页面后,我不清楚实际上只有 Invalidate 中指定的区域被重新绘制。似乎有可能在多次调用 Invalidate 之后,重新绘制一个包含所有指定区域的矩形。这可以解释为什么 PaintEventArgs 包含一个矩形而不是一个更通用的区域。你对此有更深入的了解吗? 在 OnPaint() 中使用随机颜色绘制清楚地表明只有无效区域被重新绘制(而不是 FillRectangle 或其他绘制函数参数中指定的区域)。 我明白了,这很有趣。在这种情况下,您可能必须跟踪您无效的所有矩形。如果在引发 Paint 事件时设置了它们,则必须单独检查与所有这些矩形的交集。如果有任何相交,则重新绘制图形。 【参考方案1】:private List<Rectangle> rectangleList;
当你无效时,你设置你的 rectangleList:
rectangleList = getRectangles(...);
Region region = new Region(new Rectangle(...));
foreach(Rectangle rect in rectangleList)
region.Union(rect);
Invalidate(region);
在paint方法中,检查矩形列表中是否有任何矩形相交,然后将其清除:
protected override void OnPaint(PaintEventArgs e)
bool intersection = false;
foreach(Rectangle rect in rectangleList)
if(e.ClipRectangle.Intersect(rect)
intersection = true;
break;
if(intersection)
rectangleList.Clear();
DoIntersectionStuff();
else
DoNonIntersectionStuff();
【讨论】:
如果 Windows 向 Winforms 应用程序发送 WM_PAINT 消息怎么办?某些区域不会被绘制,因为 rectangleList 将为空。 在相交的情况下你想实现什么,如果不相交怎么办? 我想避免/跳过对 DrawText() 的一些不需要的调用(ClipRectangle 很大,但只需要重新绘制几个区域) 在什么情况下?我相信我给了你一个好主意,如何确定你的特定无效区域是否与重绘的矩形相交,所以我相信我回答了这个问题。在确定这些区域是否相交之后,您所做的事情超出了问题的范围。我相应地调整了代码。【参考方案2】:我回答我自己的问题:
在调用BeginPaint()
之前,可以通过在WM_PAINT 事件中调用GetUpdateRgn()
函数来获取更新区域。
要知道矩形是否在区域内,使用IsVisible()
方法。这是一个 GDI api 调用(与 Rectangle.Intersect()
不同),因此它通常与直接调用 GDI 绘图函数一样慢(例如:DrawText()
)并在必要时让 GDI 完成丢弃工作。
private Region region;
protected override void WndProc(ref Message m)
switch (m.Msg)
case WM_PAINT:
region = null;
IntPtr hrgn = CreateRectRgn(0, 0, 0, 0);
try
int result = GetUpdateRgn(Handle, hrgn, false);
if (result == SIMPLEREGION || region == COMPLEXREGION)
region = Region.FromHrgn(hrgn);
finally
DeleteObject(hrgn);
break;
base.WndProc(ref m);
protected override void OnPaint(PaintEventArgs e)
var rectangle = ...
if (region != null && region.IsVisible(rectangle))
//...
这是原生的 win32 函数声明:
[DllImport("gdi32.dll")]
static extern IntPtr CreateRectRgn(int left, int top, int right, int bottom);
[DllImport("user32.dll")]
static extern int GetUpdateRgn(IntPtr hWnd, IntPtr hRgn, bool bErase);
[DllImport("gdi32.dll")]
static extern bool DeleteObject(IntPtr hObject);
const int WM_PAINT = 0x000F;
const int SIMPLEREGION = 2;
const int COMPLEXREGION = 3;
【讨论】:
以上是关于在 OnPaint() 事件中,如何检查给定的矩形是不是与无效区域相交?的主要内容,如果未能解决你的问题,请参考以下文章