Non-Rectangular Form

I received some good feedback from my previous article (A Console IRC Bot) and here is the next one.

The question for this article was how to make nonsquare windows?.

It's actually very easy according to the MSDN site. You add this piece of code to your form and voila, non-square.

[csharp] protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { System.Drawing.Drawing2D.GraphicsPath shape = new System.Drawing.Drawing2D.GraphicsPath(); shape.AddEllipse(0, 0, this.Width, this.Height); this.Region = new System.Drawing.Region(shape); } [/csharp]

But that's too easy, right? Well, actually, the setting is easy, it's creating the Region that's difficult. You could go looking for tutorials on how to draw shapes, but it could take a long time before you create your Region with shapes.

So, what we'll do here, is create a class that will allow us to make an image in any graphics program, that will represent our visible area.

Let's start with drawing something we want to use as a form.

Create a WinForm app (NonSquareForm). And immediately add a class to it.

This is were we begin:

[csharp] using System; using System.Drawing; using System.Drawing.Drawing2D;

namespace System.Drawing.Drawing2D { public class RegionConvert {

  } /* RegionConvert */

} / System.Drawing.Drawing2D / [/csharp]

The first method we'll create is ConvertFromTransparentBitmap which will take a Bitmap and a Color. The Color indicates which area will be removed from the resulting Region.

To start, we begin with finding the dimensions of our image.

[csharp] // First we get the dimensions of our image GraphicsUnit aPixel = GraphicsUnit.Pixel; RectangleF imageBoundsF = imageRegion.GetBounds(ref aPixel); int imageWidth = Convert.ToInt32(imageBoundsF.Width); int imageHeight = Convert.ToInt32(imageBoundsF.Height); [/csharp]

As you can see from the above MSDN example, we need a GraphicsPath, where we will add all our non-transparent pixels to.

[csharp] // This will be the path for our Region GraphicsPath regionPath = new GraphicsPath(); [/csharp]

And for the logic of our method, we'll loop over the image pixel per pixel and determine if it's transparent or not.

[csharp] // We loop over every line in our image, and every pixel per line for (int intY = 0; intY < imageHeight; intY++) { for (int intX = 0; intX < imageWidth; intX++) { if (imageRegion.GetPixel(intX, intY) != transparentColor) { // We have to see this pixel! regionPath.AddRectangle(new Rectangle(intX, intY, 1, 1)); } } } [/csharp]

When we have all our pixels in our path, we'll create a Region out of it, and return it.

[csharp] Region formRegion = new Region(regionPath); regionPath.Dispose(); return formRegion; [/csharp]

Now we go to our Form, you'll have to set the FormBorderStyle property to None.

Add the following code to your Form constructor:

[csharp] // Read the background file FileStream imageStream = File.OpenRead("Glass.bmp"); Bitmap imageBackground = (Bitmap)Bitmap.FromStream(imageStream); Color transparentColor = Color.FromArgb(0xFF, 0x00, 0xFF);

// Make our form fit the background this.Width = imageBackground.Width; this.Height = imageBackground.Height;

// Apply custom region with FF00FF as transparent color this.Region = RegionConvert.ConvertFromTransparentBitmap(imageBackground, transparentColor); this.BackgroundImage = imageBackground; [/csharp]

Glass.bmp is a bitmap that lives next to your .exe file. Now you got a non-rectangular form!

Of course you will have to add your own methods of minimizing, closing and moving the form now.

I uploaded the project as an example. You can drag it around and press 'Q' to quit.

Stay tuned for more! ;)

Update: I wrote an article about how to include the bitmap in the .exe, check it out: Including Resources in .exe.