In Winforms, it's possible to create a region that is not a rectangle and invalidate it this way :
Region region = new Region(new Rectangle(...));
region.Union(new Rectangle(...));
Invalidate(region);
Then, in OnPaint()
event, only the region invalidated above will be drawn again:
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);
}
Inside OnPaint()
event, is there a way to check if a given rectangle intersect with the invalidated area ?
I could use rectangle.Intersect(e.ClipRectangle)
but this might gave false positives.
EDIT : it seems what I want is possible using the GetUpdateRgn Win32 function (AFAIK there is no direct Winforms equivalent of that function)
CodePudding user response:
private List<Rectangle> rectangleList;
When you invalidate, you set your rectangleList:
rectangleList = getRectangles(...);
Region region = new Region(new Rectangle(...));
foreach(Rectangle rect in rectangleList)
{
region.Union(rect);
}
Invalidate(region);
And in the paint method, you check if any Rectangles in your rectangleList intersect, then you clear it:
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();
}
}
CodePudding user response:
I answer my own question :
It's possible to get the update region by calling GetUpdateRgn()
function before BeginPaint
.
You need to call IsVisible()
to know if rectangle is inside region. Which means it will performs a GDI api call (unlike Rectangle.Intersect()
) and so it's just as slow as calling GDI drawing function directly and let it discard the result (eg : DrawText()
, FillRect()
).
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)
{
if (region != null && region.IsVisible(rectangle))
{
//...
}
}
Win32 native functions declaration:
[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;