Author: Lou Adler
How can I create a form that doesn't have a caption, but can be re-sized?
Answer:
Solve 1:
As they say, "There's more than one way to skin a cat," and I can't agree more as
far as programming is concerned. Let me share a little anecdote with you...
Being the "artistic dude" in my company, I'm always in search of new ways to
present information to users. I do this by creating non-standard user interfaces
(which I find rather boring), spicing them up with graphics and multimedia
features. My philosophy centers around this question: Why should information
retrieval be a boring task? Well, it shouldn't. And an extension to this question
could be: Why do business programs have to all look the same? Well, they don't. So
I choose to build "odd" business user interfaces.
My latest designs have followed game interfacess that use a plethora of high-
resolution graphics and captionless forms (this is where it all kicks in). In the
past, I didn't need my forms to move anywhere. But as my interfaces have become
more complex, I've had to start providing ways to move them. Unfortunately, the
method that I employed in the original article here, didn't account for clicking
only in a certain area on a form. You just click and hold the mouse button down
anywhere on the form, and the form will move. Unfortunately, that isn't always the
best solution.
For instance, with one of my forms, I created a "pseudo" caption by aligning a
TPanel at the top of the client area of my form. There's a bit more functionality
built into the panel, but I wanted it to act very much like a regular caption: a
click and drag would drag the form, and a double-click would maximize it. With that
in mind, I set about writing the panel's click and drag method using what I
originally wrote as a base. It didn't work. So doing a little research and asking a
couple of questions around the newsgroups, Kerstin Thaler, a very helpful person,
showed me a real cool method for implementing what I needed to do. Here it is:
1 procedure TMainFrm.Panel1MouseDown(Sender: TObject; Button:
2 TMouseButton;
3 Shift: TShiftState; X, Y: Integer);
4 const
5 SC_DRAGMOVE = $F012;
6 begin
7 if Button = mbLeft then
8 begin
9 ReleaseCapture;
10 Perform(WM_SYSCOMMAND, SC_DRAGMOVE, 0);
11 end;
12 end;
This is such incredibly easy code! Instead of overriding the default NC_HITTEST
message handler, I could accomplish form movement from the MouseDown of my panel!
Basically, all the method does is send a WM_SYSCOMMAND message to the form with the
SC_DRAGMOVE constant to perform a drag move. Kerstin did say, that the $F012 isn't
documented. But hey! the method works and it works well. So if you have a
captionless form and want to move it by dragging from one of its child components,
this is the way to do it!
Solve 2:
Many folks would say, "Just set the BorderStyle of the form to bsNone and you'll
remove the caption." However, there's a problem with that suggestion: Not only do
you lose the caption bar, you lose the entire border, which means you can't resize
the form. The only way to get around this is to go behind the scenes in Delphi.
Fortunately, it's a relatively simple process.
Delphi is not just ObjectPascal; it is also a very effective wrapper of the Windows
API (Don't worry, we won't get into the Windows API too much in this article). In
Windows, every window is created using one of two standard functions: CreateWindow
and CreateWindowEx. CreateWindow makes a window with standard window styles, while
CreateWindowEx is the same as CreateWindow, but you can add extended window styles
to the window you want to create. (I encourage you to read through the help file
for a thorough discussion of these two API calls since I won't be going into detail
with these topics.)
When a form is created in Delphi, a call is made to CreateWindowEx &mdash TForm's
Create method is the wrapper function for this call &mdash and Create passes a
record structure to CreateWindowsEx through a virtual method of TForm called
CreateParams.
CreateParams is a virtual method of TForm. This means you can override it which, in
turn, means you can change the default style of a window when it's created to suit
your particular needs. For our purposes, we want to eliminate the caption. That's
easily done by changing the style bits of the LongInt Style field of the
TCreateParams structure, the record that's passed to CreateWindowEx. Look at the
code; we'll discuss particulars below:
13 unit NoCap;
14
15 interface
16
17 uses
18 SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
19 Controls, Forms, Dialogs, StdCtrls, Buttons, BDE, DB;
20
21 type
22 TForm1 = class(TForm)
23 Button1: TButton;
24 procedure Button1Click(Sender: TObject);
25 private
26 {Here's what we're overriding}
27 procedure CreateParams(var Params: TCreateParams); override;
28 procedure WMNCHitTest(var Msg: TWMNcHitTest); message WM_NCHITTEST;
29 end;
30
31 var
32 Form1: TForm1;
33
34 implementation
35 {$R *.DFM}
36
37 procedure TForm1.CreateParams(var Params: TCreateParams);
38 begin
39 inherited CreateParams(Params);
40 with Params do
41 Style := (Style or WS_POPUP) and (not WS_DLGFRAME);
42 {or... Style := Style + WS_POPUP - WS_DLGFRAME; which is the
43 equivalent to the above statement}
44 end;
45
46 procedure TForm1.WMNCHitTest(var msg: TWMNCHitTest);
47 begin
48 inherited;
49 if (msg.Result = htClient) then
50 msg.Result := htCaption;
51 end;
52
53 procedure TForm1.Button1Click(Sender: TObject);
54 begin
55 Close;
56 end;
57
58 end.
Notice in the line in CreateParams where I set the Style for the form: Style :=
(Style OR WS_POPUP) AND (NOT WS_DLGFRAME);. My first bit manipulation is Style OR
WS_POPUP. This means give me the default style bits and make the window a regular
pop-up window with a resizeable border. The second portion says don't include a
dialog frame. With respect to this, the WS_DLGFRAME will produce a frame typical of
dialog boxes. By masking it out, you remove the title bar. WS_POPUP ensures you
have a resizeable border with which to work.
What about the WMNCHitTest message handler? Well, if you have a form with no title
bar, you have absolutely no way to move it, because by convention, forms are moved
by dragging the title bar. By trapping a mouse hit with the WM_NCHITTEST message
and changing the default behavior of the mouse hit, you can allow dragging of the
form from the client area.
Read through the Windows API help and look at all the style bits you can set. Play with different combinations to see what you get.
|