Microsoft & .NETVisual BasicCreating Odd Shaped Forms

Creating Odd Shaped Forms content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Odd shaped forms are a great novelty for splash screens and about boxes. They are fun
for the user, but are a little tricky to create. The whole idea is that odd shaped forms
are based around regions: an area, in the shape of a rectangle, ellipse, or polygon, which
can be combined with other regions to form very complex shapes.

The Windows API allows you to create these regions, returning
a handle to the region. This is effectively an ‘address’ to the region. If we want to know
about the region, pass Windows the address and Windows will return the information having
looked it up in its ‘address book’. All of the create region APIs return a handle to a
region. You can then use this handle to combine it with others,
and then finally set the window’s region to the new shape. These region creation APIs will
return 0 on failure.

Once you have finished with a region or combined it into a new region, you must tell
windows to remove the item from its address book, in order to free up resources.
This is done using the DeleteObject API.

In this short tutorial, I will show you how to create rectangular, elliptical, round
and other shaped regions, that you can combine together to create interesting effects such
as the happy face window featured in the figure seen above.

In order to create shaped Windows, you need to create some regions!

Create Rectangles

Public Declare Function CreateRectRgn _
   Lib "gdi32" _
   (ByVal X1 As Long, ByVal Y1 As Long, _
   ByVal X2 As _
   Long, ByVal Y2 As Long) As Long

This syntax creates a rectangular region with the upper left corner defined by X1, Y1 and the lower
right corner defined by X2, Y2.

Create Ellipses

Public Declare Function CreateEllipticRgn _
   Lib "gdi32" _
   (ByVal X1 As Long, ByVal _
   Y1 As Long, ByVal X2 As Long, _
   ByVal Y2 As Long) As Long

This syntax creates an elliptic region that fits within the bounds specified by X1, Y1 and X2, Y2.

Create Rounded Rectangles

Public Declare Function CreateRoundRectRgn _
   Lib "gdi32" _
   (ByVal X1 As Long, ByVal Y1 As Long, _
   ByVal X2 As Long, _
   ByVal Y2 As Long, ByVal X3 As Long, _
   ByVal Y3 As Long) As Long

This creates a rectangular region, like the CreateRectRgn. However, it allows you to
round the corners, using the X3 and Y3 parameters. If set to 0, they produce square
corners. If set at the width/height, they produce completely rounded corners.

Creating any Shape you Like

Public Declare Function CreatePolygonRgn _
   Lib "gdi32" _
   (lpPoint As POINTAPI, _
   ByVal nCount As Long, ByVal _
   nPolyFillMode As Long) As Long

This is function allows you to create any shaped region. The shape of it is limited
only by your patience to plug in numbers. To use this function, create an array of
POINTAPI structures. In the lpPoint parameter pass the first element in the array, and in
the nCount parameter, pass the number of points. The nPolyFillMode parameter determines
how the filled area of the region is selected. There are only subtle differences between
the two options (WINDING and ALTERNATE), but if you fiddle round, you may spot the
Windows automatically joins the first and last points to make a closed figure.

Creating Several Custom Shapes

Public Declare Function CreatePolyPolygonRgn _
   Lib "gdi32" _
   (lpPoint As POINTAPI, lpPolyCounts _
   As Long, ByVal nCount As _
   Long, ByVal nPolyFillMode As Long) As Long

As the name suggests, this function creates a region of several shapes. As before, the
lpPoint is the first element in an array of POINTAPIs, and nCount is the number of
elements. However, lpPolyCounts is the first element in an array of Longs. Each element in
this array defines how many points each polygon has. Therefore, the sum of all of the
elements of lpPolyCounts must equal nCount.
A similar effect can be achieved using several individual polygons, combined into one

Shifting Regions

Public Declare Function _ OffsetRgn _
   Lib "gdi32" _
   (ByVal hRgn As Long, _
   ByVal x As Long, _
   ByVal y As Long) As Long

This function allows you to shift the specified region by x, y pixels.
It can return the following constants:

  • COMPLEXREGION: if the region has borders that overlap each other
  • SIMPLEREGION: if the borders of the region do not overlap each other
  • NULLREGION: if the region is empty
  • ERRORAPI: if the combined region could not be created
  • Joining Regions

    Public Declare Function _
       CombineRgn Lib "gdi32" _
       (ByVal hDestRgn As Long, _
       ByVal hSrcRgn1 As Long, _
       ByVal hSrcRgn2 As Long, _
       ByVal nCombineMode As Long)_
       As Long

    This API provides the ability to join two regions using a variety of different methods.
    The source regions are specified in hSrcRgn1 and hSrcRgn2, and the combined region will be
    specified in hDestRgn. Note that the destination region must already be created as a
    region before this API is called. To get round this, create a rectangle, left, top, width
    and height dimensions of 0, then call the CombineRgn API with this region handle. The
    nCombineMode can be one of the following:

  • RGN_AND: hDestRgn is set to the intersection of the two source regions (the area common
    to both)
  • RGN_COPY: hDestRgn is set to a copy of hSrcRgn1
  • RGN_DIFF: hDestRgn is set to the area in hSrcRgn1 that is not present in hSrcRgn2.
  • RGN_OR: hDestRgn is set to the union of the two source regions (the area that appears in
    either source region)
  • RGN_XOR: hDestRgn is the exclusive OR of the two source regions (the area that appears in
    either the source region, but not both)
  • It returns the same values as above.

    Initializing the Window

    Public Declare Function SetWindowRgn _
       Lib "user32" _
       (ByVal hWnd As Long, ByVal hRgn _
       As Long, ByVal bRedraw As Boolean) _
       As Long

    Use this function to make the window into your custom shape. The hWnd is the window
    handle of the window on which to set the region. This can be anything that provides a
    hWnd, including Forms, Command Buttons, Picture Boxes, etc. Set bRedraw to True to
    automatically redraw the window when the new region has been applied.
    This will return 0 on failure.

    Tidying Up

    Public Declare Function DeleteObject _
       Lib "gdi32" _
       (ByVal hObject As Long)_
       As Long

    As with most GDI functions, the region handles must be "tidied up". This is
    done using the DeleteObject API. Once you have set the window’s region, you can delete all
    the regions, except for the one that you used in SetWindowRgn. You should return the form
    to its original shape by passing hRgn=0 to the SetWindowRgn function, then deleting the
    region handle.

    The sky is the limit to what you can do.
    You can create extremely complex regions fairly easily. Note however that complex regions
    take longer to redraw, so there is a performance hit. You can also use these API calls on
    any control that returns a window handle. You can make circular buttons and listboxes (if
    you so desire!) very easily.

    Get the Free Newsletter!

    Subscribe to Developer Insider for top news, trends & analysis

    Latest Posts

    Related Stories