Articles   Members Online:
-Article/Tip Search
-News Group Search over 21 Million news group articles.
Member Area
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Become a Member
-Why sign up!
-Chat Online!
-Indexes NEW!!
-Build your resume
-Find a job
-Post a job
-Resume Search
-Link to us
Visit Embarcadero
Embarcadero Community
How to Create weird shaped forms Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
Delphi 2.x
User Rating
No Votes
# Votes
DSP, Administrator
Reference URL:
			Author: Lou Adler

Is it possible to create forms with shapes other than the standard rectangular 
shape in Windows?


Sometimes it's just not enough to write applications that have the same boring 
rectangular forms over and over again. Sometimes you need a change. How about an 
elliptical form? Or maybe even a triangular form? Sound intriguing? It's not that 
hard to  do.

New in Win32 is something called a region. The Win32 API Programmer's Reference 
defines a region as follows:

...a rectangle, polygon or ellipse (or a combination of two or more of these 
shapes) that can be filled, painted, inverted, framed and used to perform hit 
testing (testing for the cursor location).

From the definition, the most notable thing about a region is that it can be 
manipulated in a variety of ways. For our purposes we want to define a region to 
create a specific shape.

I should point out that a region can be defined for just about any TWinControl 
descendant (not just forms), meaning you can apply a region to a TPanel or even a 
TEdit (though I strongly recommend against it). But to alter the shape of a 
TWinControl descendant, all you need to provide is a handle and employ some 
handy-dandy shape change functions.

To get a control to change its shape, follow this two-step process:

Define the boundaries of the region that represent a particular shape.
Apply the boundaries you've defined to a window.

This is pretty simple. However, it's very important to refer to the help file, and 
to have the source at hand. I wouldn't be able to accomplish many of my projects, 
let alone write many of the articles I write here, without those two resources at 
my disposal. Especially with the Windows API calls, having access to the Window.PAS 
file is essential so I know what to pass into the functions. Remember, the WinAPI 
calls are really wrapper calls into the appropriate Windows DLLs, and of course, 
the help file is essential to getting background information on the topic you're 
interested in.

With respect to this article, look up the SetWindowRgn topic in Win32 Developer's 
Help, and have it handy while you're putting together your program. Pay particular 
attention to the Group hyperlink because it will give you a run-down of all the 
procedures related to the region topic. Let's move on!

Defining a Region's Boundary

The first step to creating a form of a different shape is to define the shape 
itself. For our discussion, we'll use three WinAPI calls:

This function will create an elliptically-shaped region.
This will create a rectangular region with rounded corners.
This will create just about any multi-sided shape, as long as the lines form a 
closed solid.

These functions return a HRGN type, which will then be used by a function called 
SetWindowRgn whose sole purpose in life it is to set the parameters defined by a 
particular region variable. I've encapsulated these functions in methods that are 
part of a demonstration form. The functions are coded as follows:
2   {===========================================================================
3     Notice that all the functions are used in an assignment
4     operation to a variable called rgn. This is a
5     private var that I declared for the form. The private var is
6     accessible to all functions; I did this so that I could change the shape of
7     the form or a control on the form, and use the same region.
8    ===========================================================================}
10  procedure TForm1.DrawEllipticRegion(wnd: HWND; rect: TRect);
11  begin
12    rgn := CreateEllipticRgn(rect.left,, rect.right, rect.bottom);
13    SetWindowRgn(wnd, rgn, TRUE);
14  end;
16  procedure TForm1.DrawRndRectRegion(wnd: HWND; rect: TRect);
17  begin
18    rgn := CreateRoundRectRgn(rect.left,, rect.right, rect.bottom, 30, 30);
19    SetWindowRgn(wnd, rgn, TRUE);
20  end;
22  procedure TForm1.DrawPolygonRegion(wnd: HWND; rect: TRect; NumPoints: Integer;
23    DoStarShape: Boolean);
24  const
25    RadConvert = PI / 180;
26    Degrees = 360;
27    MaxLines = 100;
28  var
29    x, y,
30      xCenter,
31      yCenter,
32      radius,
33      pts,
34      I: Integer;
35    angle,
36      rotation: Extended;
37    arPts: array[0..MaxLines] of TPoint;
38  begin
40    xCenter := (rect.Right - rect.Left) div 2;
41    yCenter := (rect.Bottom - rect.Top) div 2;
42    if DoStarShape then
43    begin
44      rotation := Degrees / (2 * NumPoints);
45      pts := 2 * NumPoints;
46    end
47    else
48    begin
49      rotation := Degrees / NumPoints; //get number of degrees to turn per point
50      pts := NumPoints
51    end;
52    radius := yCenter;
54    {This loop defines the Cartesian points of the shape. Notice
55     I've added 90 degrees to the rotation angle. This is so that shapes will
56     stand up; otherwise they'll lie on their sides. I had to
57     brush up on my trigonometry to accomplish this (forgot all those sin and cos
58     thingies. Many thanks to Terry Smithwick and David Ullrich for their
59     assistance on CompuServe!}
60    for I := 0 to pts - 1 do
61    begin
62      if DoStarShape then
63        if (I mod 2) = 0 then //which means that
64          radius := Round(radius / 2)
65        else
66          radius := yCenter;
68      angle := ((I * rotation) + 90) * RadConvert;
69      x := xCenter + Round(cos(angle) * radius);
70      y := yCenter - Round(sin(angle) * radius);
71      arPts[I].X := x;
72      arPts[I].Y := y;
73    end;
75    rgn := CreatePolygonRgn(arPts, pts, WINDING);
76    SetWindowRgn(wnd, rgn, TRUE);
77  end;

The first two functions are pretty simple, just two-liners. All that's needed to 
create the appropriate shapes is a handle and a TRect structure. For forms, that 
structure would be taken from the ClientRect property; for other controls, use the 
BoundsRect property.

The DrawPolygonRegion method, however, is much more complex. This is due in part to 
the fact that CreatePolygonRgn requires the vertices of the corners of the polygon 
to be passed as an array of TPoints, and partly because I wanted to draw 
equilateral polygons based off points rotated around a common center point. For 
that I had to use some trigonometry.

I wanted to not only draw polygon regions, but stars as well. Using rotational trig 
allowed me to do it. The way the function works if the DrawStarShape parameter is 
set to True is that for every even value of I in the loop, the radius of the circle 
is set to half its length, and to maintain the number of points of the polygon I 
want to draw, I double the number of points to accomodate the contraction of the 

At the very end of each function is a call to SetWindowRgn. This function takes as 
parameters a window handle, a rgn var, and a Boolean value that specifies whether 
the window should be re-drawn. In all cases, if you want to see the shape you've 
made, this must be always be set to True.

Below is the listing for the entire source code of my test form. On the form I've 
dropped four TButtons (one for each of the shapes: ellipse, round rectangle, 
polygon and star); a TPanel to demonstrate the ability to set regions for 
TWinControl descendants other than TForm; and a SpinEdit used in conjunction with 
the Polygon and Star region buttons to define the number of points that'll be 
defining the shape. Here's the code:

78  unit regmain;
80  interface
82  uses
83    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
84    ExtCtrls, StdCtrls, Spin;
86  type
87    TForm1 = class(TForm)
88      Button1: TButton;
89      Button2: TButton;
90      Button3: TButton;
91      SpinEdit1: TSpinEdit;
92      Button4: TButton;
93      Panel1: TPanel;
94      Edit1: TEdit;
95      procedure DrawRndRectRegion(wnd: HWND; rect: TRect);
96      procedure DrawEllipticRegion(wnd: HWND; rect: TRect);
97      procedure DrawPolygonRegion(wnd: HWND; rect: TRect; NumPoints: Integer;
98        DoStarShape: Boolean);
99      procedure Button1Click(Sender: TObject);
100     procedure Button2Click(Sender: TObject);
101     procedure Button3Click(Sender: TObject);
102     procedure Button4Click(Sender: TObject);
103   private
104     { Private declarations }
105     rgn: HRGN;
106     rect: TRect;
107   public
108     { Public declarations }
109   end;
111 var
112   Form1: TForm1;
114 implementation
116 {$R *.DFM}
118 procedure TForm1.DrawRndRectRegion(wnd: HWND; rect: TRect);
119 begin
120   rgn := CreateRoundRectRgn(rect.left,, rect.right, rect.bottom, 30, 30);
121   SetWindowRgn(wnd, rgn, TRUE);
122 end;
124 procedure TForm1.DrawEllipticRegion(wnd: HWND; rect: TRect);
125 begin
126   rgn := CreateEllipticRgn(rect.left,, rect.right, rect.bottom);
127   SetWindowRgn(wnd, rgn, TRUE);
128 end;
130 procedure TForm1.DrawPolygonRegion(wnd: HWND; rect: TRect; NumPoints: Integer;
131   DoStarShape: Boolean);
132 const
133   RadConvert = PI / 180;
134   Degrees = 360;
135   MaxLines = 100;
136 var
137   x, y,
138     xCenter,
139     yCenter,
140     radius,
141     pts,
142     I: Integer;
143   angle,
144     rotation: Extended;
145   arPts: array[0..MaxLines] of TPoint;
146 begin
148   xCenter := (rect.Right - rect.Left) div 2;
149   yCenter := (rect.Bottom - rect.Top) div 2;
150   if DoStarShape then
151   begin
152     rotation := Degrees / (2 * NumPoints);
153     pts := 2 * NumPoints;
154   end
155   else
156   begin
157     rotation := Degrees / NumPoints; //get number of degrees to turn per point
158     pts := NumPoints
159   end;
160   radius := yCenter;
162   {This loop defines the Cartesian points of the shape. Again,
163    I've added 90 degrees to the rotation angle so the shapes will
164    stand up rather than lie on their sides. Thanks again to Terry Smithwick and
165    David Ullrich for their trig help on CompuServe.}
166   for I := 0 to pts - 1 do
167   begin
168     if DoStarShape then
169       if (I mod 2) = 0 then //which means that
170         radius := Round(radius / 2)
171       else
172         radius := yCenter;
174     angle := ((I * rotation) + 90) * RadConvert;
175     x := xCenter + Round(cos(angle) * radius);
176     y := yCenter - Round(sin(angle) * radius);
177     arPts[I].X := x;
178     arPts[I].Y := y;
179   end;
181   rgn := CreatePolygonRgn(arPts, pts, WINDING);
182   SetWindowRgn(wnd, rgn, TRUE);
183 end;
185 procedure TForm1.Button1Click(Sender: TObject);
186 begin
187   DrawEllipticRegion(Form1.Handle, Form1.ClientRect);
188 end;
190 procedure TForm1.Button2Click(Sender: TObject);
191 begin
192   DrawPolygonRegion(Panel1.Handle, Panel1.BoundsRect, SpinEdit1.Value, False);
193 end;
195 procedure TForm1.Button3Click(Sender: TObject);
196 begin
197   DrawRndRectRegion(Form1.Handle, Form1.ClientRect);
198 end;
200 procedure TForm1.Button4Click(Sender: TObject);
201 begin
202   DrawPolygonRegion(Panel1.Handle, Panel1.BoundsRect, SpinEdit1.Value, True);
203 end;
205 end.

As you can see, defining and setting regions is pretty easy. Look in the help file for in-depth discussions. If you belong to the MS Developer's Network, the library CDs discuss this topic comprehensively.

Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10


Share this page
Download from Google

Copyright © Mendozi Enterprises LLC