To take a previous question a bit further, namely, How do I draw the outline of a collection of rectangles?, I would like to know if it is possible to draw the outline of a region with a dashed line, not solid?
I have a class called Deck that contains a Region object. The constructor creates a large rectangle, supposed to represent the surface of a floor.
public Deck( double x, double y, double width, double height )
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
region = new Region( new RectangleF( (float)x, (float)y, (float)width, (float)height ) );
}
Then, I want the user to be able to either "cut" holes in the floor, or "fill" up the holes, but allowing that whatever rectangle shape gets cut out, perhaps only a portion of it can be put back in. For this I created methods to that basically wrap the Union and Exclude methods to either add or remove rectangles from the region.
public void RemoveA( double x, double y, double width, double height )
{
var rect = new RectangleF( (float)x, (float)y, (float)width, (float)height );
region.Exclude( rect );
}
public void AddA( double x, double y, double width, double height )
{
var rect = new RectangleF( (float)x, (float)y, (float)width, (float)height );
region.Union( rect );
}
Then to draw the outline of the Deck and the cutouts (the region basically) I use the following code that I got from Peter's answer to the aforementioned question
[DllImport( @"gdi32.dll" )]
static extern bool FrameRgn( IntPtr hDC, IntPtr hRgn, IntPtr hBrush, int nWidth, int nHeight );
public void Draw( Graphics g, Color c, float scale )
{
COLORREF colorref = new COLORREF( c );
IntPtr hdc = IntPtr.Zero, hbrush = IntPtr.Zero, hrgn = IntPtr.Zero;
try
{
hrgn = this.region.GetHrgn( g );
hdc = g.GetHdc();
hbrush = CreateSolidBrush( colorref.colorref );
FrameRgn( hdc, hrgn, hbrush, 2, 2 );
}
finally
{
if (hrgn != IntPtr.Zero)
{
region.ReleaseHrgn( hrgn );
}
if (hbrush != IntPtr.Zero)
{
DeleteObject( hbrush );
}
if (hdc != IntPtr.Zero)
{
g.ReleaseHdc( hdc );
}
}
}
This worked great, but I wanted to see if I could find a way to draw the outline with a dashed or broken line, went through the documentation on gdi32.dll and other forums, but couldn't find anything that allowed me to use a dashed line.
I then tried to use a GraphicsPath instead of a Region, but that started to create other problems trying to determine when a GraphicsPath sees a Rectangle as being added, or removed, depending on its FillMode. This is the test app I ran.
[DllImport( @"gdiplus.dll" )]
public static extern int GdipWindingModeOutline( HandleRef path, IntPtr matrix, float flatness );
GraphicsPath gPath = null;
public Form1()
{
InitializeComponent();
gPath = new GraphicsPath();
// draw the main rectangle (Deck) that the user can then cut recangular holes out of or add back in
gPath.AddRectangle( new RectangleF( 10, 10, 150, 150 ) );
// cut holes in the main rectangle, default FillMode (Alternate)
gPath.AddRectangle( new System.Drawing.Rectangle( 30, 30, 60, 90 ) );
// make this rectangle to overlap the one above, on purpose
gPath.AddRectangle( new System.Drawing.Rectangle( 80, 30, 60, 90 ) );
this.Paint += Form1_Paint;
}
private void Form1_Paint( object sender, PaintEventArgs e )
{
using (Pen outlinePen = new Pen( Color.Red, 2 ))
{
outlinePen.DashStyle = DashStyle.Dash;
e.Graphics.DrawPath( outlinePen, gPath );
}
}
The result I get is the outline of the larger rectangle, but the other 2 "hole" rectangles overlap, no good.
I can get rid of the overlap if I use the GpidWindingModeOutline approach in my Paint method
private void Form1_Paint( object sender, PaintEventArgs e )
{
HandleRef handle =
new HandleRef( gPath, (IntPtr)gPath.GetType().GetField( "nativePath", BindingFlags.NonPublic | BindingFlags.Instance ).GetValue( gPath ) );
//Change path so it only contains the outline
GdipWindingModeOutline( handle, IntPtr.Zero, 0.25F );
using (Pen outlinePen = new Pen( Color.FromArgb( 255, Color.Red ), 2 ))
{
outlinePen.DashStyle = DashStyle.Dash;
e.Graphics.DrawPath( outlinePen, gPath );
}
}
Still no good though, because all I see now is the main outer rectangle.
Then another complication arises when the rectangles share an edge, or portion of an edge. The GraphicsPath then draws a solid line on this shared edge, whereas a Region object wouldn't draw one at all, which is what I want.
I realize I could perform checks for overlapping Rectangles, merging ones that share and edge etc, it just becomes complicated when rectangles only share perhaps a portion of an edge or overlap on a corner. A Region takes care of all of that which is great, just a pity there doesn't appear to be a way to draw the outline with a dashed line.