How can I create a form that doesn't have a caption, but can be re-sized?
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:
procedure TMainFrm.Panel1MouseDown(Sender: TObject; Button:
TMouseButton;
Shift: TShiftState; X, Y: Integer);
const
SC_DRAGMOVE = $F012;
begin
if Button = mbLeft then
begin
ReleaseCapture;
Perform(WM_SYSCOMMAND, SC_DRAGMOVE, 0);
end;
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! |
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:
unit NoCap;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, Buttons, BDE, DB;
type
TForm1 = class(TForm)
Button1 : TButton;
procedure Button1Click(Sender: TObject);
private
{Here's what we're overriding}
procedure CreateParams(VAR Params: TCreateParams); override;
procedure WMNCHitTest(VAR Msg: TWMNcHitTest); message WM_NCHITTEST;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.CreateParams(VAR Params: TCreateParams);
begin
Inherited CreateParams(Params);
WITH Params DO
Style := (Style OR WS_POPUP) AND (NOT WS_DLGFRAME);
{or... Style := Style + WS_POPUP - WS_DLGFRAME; which is the
equivalent to the above statement}
end;
procedure TForm1.WMNCHitTest(var msg: TWMNCHitTest);
begin
inherited;
if (msg.Result = htClient) then
msg.Result := htCaption;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end;
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.
|