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.

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);

}


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:

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

 

namespace System.Drawing.Drawing2D {

      public class RegionConvert {

 

      } /* RegionConvert */

} /* System.Drawing.Drawing2D  */


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.

// 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);


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

// This will be the path for our Region

GraphicsPath regionPath = new GraphicsPath();


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

// 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));

            }

      }

}


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

Region formRegion = new Region(regionPath);

regionPath.Dispose();

return formRegion;


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

Add the following code to your Form constructor:

// 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;

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

Ofcourse 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.

11 Comments

  • cool! it's kinda funny, and strange :s ;)

  • Nice, thx voor the answer...

    will test is someday :)

  • nice, verry nice :)



    but can't the picture be included in the exe-file?



    -bert-

  • I tested the project you uploaded under Windows2000 but it works in a strange way. The region is correct but the backgroung image of the form isn't align with the region, so i can see the purple pixel around the bitmap.

  • ehmm no :)

    the problem isn't about few pixels around the image, but the background is shifted down about 50 px, i think this is the vertical space of the caption or not?

  • after this line:

    this.Region = RegionConvert.ConvertFromTransparentBitmap(imageBackground, transparentColor);



    I must add this one:



    this.Region.Translate(SystemInformation.FrameBorderSize.Width - 1,SystemInformation.CaptionHeight + SystemInformation.FrameBorderSize.Height - 1 );



    Dont ask me why, but it works fine now :) :)

  • I used your uploaded project and it has the FormBorderStyle property already set to None.

  • what about the ClientRectangle? I'd like to use a shaped form like this, but inside of it i want to be able to use the standard Dock Fill method to have a child control resize to an inset position... e.g. i want to draw my own title bar and resize edges. when i just shape the window, the ClientRectangle is always the max size, so components which Dock Fill stretch out over my edges!



    Am I making sense? I tried overriding WndProc for WM_NCCALCSIZE, but even though I inset the ClientRectangle, it seems to have no effect.

  • For InstaPix; Just put a panel or something transparent in the form then have the control Dock Fill the panel.

  • Thanks alot, Im going to use this for my keith knutsson applications. thanks alot.

    Keith Knutsson

  • Nice article. However, in most cases a believe intersecting regions might be even easier. Thanks a lot!

Comments have been disabled for this content.