Reputation: 20464
I started to create a generic usage/reusable method that will help to set the region of a Form to the bounds of its child controls.
But I found a problem when the rectangle of a control intersects with another, I mean the Z-Order, when a control is in front of other and covers a part of the other control in the background, in these circunstances the rectangle of the control in the front is not properly drawn...
See:
...where Button2 is in front of Button1.
Probably is my fault with the usage of the GraphicsPath
class to draw the region path, because I'm not experienced using GDI+ in this way, and maybe I'm writting bad the path...
How can I fix this code to set the expected region?.
Here is the code. Before use it, set the FormBorderStyle
property to None
(a borderless form).
VB.NET:
Public Shared Sub LockFormRegionToControls(ByVal f As Form)
LockFormRegionToControls(Of Control)(f)
End Sub
Public Shared Sub LokckFormRegionToControls(Of T As Control)(ByVal f As Form)
Dim rects As Rectangle() =
(From ctrl As T In f.Controls.OfType(Of T)
Order By f.Controls.GetChildIndex(ctrl) Ascending
Select ctrl.Bounds).ToArray()
Using path As New GraphicsPath()
path.AddRectangles(rects)
f.Region = New Region(path)
End Using
End Sub
C#:
public static void LockFormRegionToControls(Form f) {
LockFormRegionToControls<Control>(f);
}
public static void LockFormRegionToControls<T>(Form f) where T : Control {
Rectangle[] rects = (
from T ctrl in f.Controls.OfType<T>()
orderby f.Controls.GetChildIndex(ctrl) ascending
select ctrl.Bounds).ToArray();
using (GraphicsPath path = new GraphicsPath()) {
path.AddRectangles(rects);
f.Region = new Region(path);
}
}
Upvotes: 0
Views: 789
Reputation: 32223
This is just a C# implementation of the method TnTinMn coded.
(Since both C# and VB.Net tags are shown here, it may be useful).
Call LockFormRegionToControls(TestForm, [IsBorderless]);
with true or false.
//The Form is not Borderless
LockFormRegionToControls(TestForm, false);
public static void LockFormRegionToControls(Form f, bool IsBorderless) {
LockBLFormRegionToControls<Control>(f, IsBorderless);
}
public static void LockBLFormRegionToControls<T>(Form f, bool Borderless) where T : Control
{
Region NewRegion;
Point OffSet = Point.Empty;
if (Borderless)
{
NewRegion = new Region();
} else {
OffSet = new Point((f.Bounds.Width - f.ClientSize.Width) / 2, f.Bounds.Height - f.ClientSize.Height);
NewRegion = new Region(f.Bounds);
}
foreach (T ctrl in f.Controls.OfType<T>()) {
Point p = new Point(ctrl.Bounds.Left + OffSet.X, ctrl.Bounds.Y + (OffSet.Y - OffSet.X));
Size s = new Size(ctrl.Bounds.Width, ctrl.Bounds.Height);
NewRegion.Union(new Region(new Rectangle(p, s)));
}
f.Region = NewRegion;
}
Upvotes: 1
Reputation: 11791
For a border-less form it is pretty simple (no offset to client area). Just start with an empty region and union in the control bounds.
Public Shared Sub LockFormRegionToControls(ByVal f As Form)
Dim r As New Region()
r.MakeEmpty()
For Each c As Control In f.Controls
Using r2 As New Region(c.Bounds)
r.Union(r2)
End Using
Next
Dim oldRegion As Region = f.Region
f.Region = r
If oldRegion IsNot Nothing Then oldRegion.Dispose()
End Sub
Edit: Method to handle non-client areas of form with border.
Public Shared Sub LockFormRegionToControls(ByVal f As Form)
' determine offset to client rectangle
Dim zero As Point = f.PointToScreen(Point.Empty) ' top-left of client rectangle in screen coordinates
Dim offsetX As Int32 = zero.X - f.Location.X
Dim offsetY As Int32 = zero.Y - f.Location.Y
' region for entire form including non-client
Dim r As New Region(New Rectangle(0, 0, f.Width, f.Height))
Dim clientRect As Rectangle = f.ClientRectangle
' this rect is located at 0,0 so apply the offset
clientRect.Offset(offsetX, offsetY)
' subtract the client rectangle
r.Exclude(clientRect)
' now add in the control bounds
For Each c As Control In f.Controls
Dim b As Rectangle = c.Bounds
' controlBounds are relative to the client rectangle, so need to offset
b.Offset(offsetX, offsetY)
Using r2 As New Region(b)
r.Union(r2)
End Using
Next
Dim oldRegion As Region = f.Region
f.Region = r
If oldRegion IsNot Nothing Then oldRegion.Dispose()
End Sub
Edit 2: Thin border adjustment.
Public Shared Sub LockFormRegionToControls(ByVal f As Form)
' determine offset to client rectangle
Dim zero As Point = f.PointToScreen(Point.Empty) ' top-left of client rectangle in screen coordinates
Dim offsetX As Int32 = zero.X - f.Location.X
Dim offsetY As Int32 = zero.Y - f.Location.Y
' simulate thin border
Dim occludedBorderOffset As Int32 = Math.Max(offsetX - 2, 0)
Dim whAdjustment As Int32 = 2 * occludedBorderOffset
' region for entire form including non-client
Dim mainRect As New Rectangle(occludedBorderOffset, occludedBorderOffset, f.Width - whAdjustment, f.Height - whAdjustment)
Dim r As New Region(mainRect)
Dim clientRect As Rectangle = f.ClientRectangle
' this rect is located at 0,0 so apply the offset
clientRect.Offset(offsetX, offsetY)
' subtract the client rectangle
r.Exclude(clientRect)
' now add in the control bounds
For Each c As Control In f.Controls
Dim b As Rectangle = c.Bounds
' ontrolBounds are relative to the client rectangle, so need to offset
b.Offset(offsetX, offsetY)
Using r2 As New Region(b)
r.Union(r2)
End Using
Next
Dim oldRegion As Region = f.Region
f.Region = r
If oldRegion IsNot Nothing Then oldRegion.Dispose()
End Sub
Upvotes: 1