Creating Forms with Custom Title Bars (Part I)

by Mark R. Johnson

The real power of Borland's Delphi is in its ability to combine high-level visual programming with low-level code that can get as detailed as needed. In this article, I will demonstrate how these two aspects may be combined to construct forms with customized title bars.

Before I get into the coding itself, I would like to take this opportunity to recognize Charlie Kindel, whose C code for windows with small title bars has served as a constant reference for this article. Kindel's original work can be found on the January '95 MSDN CD-ROM as (135K). Much of the code presented here is simply a Pascal port of Kindel's ItsyBits project.

Source Code

Accompanying this article is a PKZip file containing the source code for the project and a bitmap that can be used for the Browse Gallery when installing the form as a template. Click here to download (7K).

Getting Started

To begin, run Delphi and begin a new (blank) project. Delphi should create a new form for you to use by default. Click on the Code Editor and select the unit corresponding to this form (Unit1). From the File menu, select Save File As and save the unit as LILTITLE.PAS. The unit should then appear as follows:

unit LilTitle;


  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs;

  TForm1 = class(TForm)
    { Private declarations }
    { Public declarations }

  Form1: TForm1;


{$R *.DFM}


Custom Title Bars

In this article, the process of customizing the title bar of a Delphi form has been divided into five major steps:

  1. Responding to WM_NCxxxx Messages
  2. Creating a System Menu
  3. Setting the Minimum Window Size
  4. Compiling and Testing the Form
  5. Installing the Form as a Template

WARNING: It is recommended that from this point on, you do not run this program until instructed to do so. Executing the program prematurely may result in a program that no longer responds to the system or is otherwise improperly displayed.

A Note About Window Messages

In Microsoft Windows, every window has a procedure that receives messages from the environment. These messages are the basis for interaction between the user, the environment, and each of the programs and controls. Some typical window messages are WM_PAINT, which tells a window to repaint itself, and WM_KEYDOWN, which indicates when a key is pressed on the keyboard.

In Delphi, messages are passed to forms as a TMessage parameter to a virtual procedure called WndProc(). The message parameter type is as follows:

  TMessage = record
    Msg: Word;
    case Integer of
      0: (
        WParam: Word;
        LParam: Longint;
        Result: Longint);
      1: (
        WParamLo: Byte;
        WParamHi: Byte;
        LParamLo: Word;
        LParamHi: Word;
        ResultLo: Word;
        ResultHi: Word);

The Msg field holds a numeric value identifying the record, such as WM_PAINT. This value can be one of the predefined windows messages, or it can be a user defined value. (See the Windows API help file for more information about user defined messages. Search the index for WM_USER and select topic WM_USER message (WINAPI.HLP).)

The WParam and LParam hold additional information, dependent upon the message type. For example, a WM_PAINT does not use these values, while a WM_KEYDOWN places the virtual-key code in WParam and key data in LParam.

Finally, the Result field provides a mechanism for indicating a return value from the WndProc() procedure. Again, the meaning of this value depends on the message received.

When the WndProc() procedure receives a message, it then dispatches the message to a procedure declared with the appropriate message directive. In order to catch and respond to a window message in Delphi, we would declare a procedure in the protected section of a form with the message directive followed by the message we wish to capture.

For example:

    procedure WMPaint(var Message : TWMPaint); message WM_PAINT;

would catch any WM_PAINT messages sent to the form. You will notice, however, that the parameter to WMPaint is of type TWMPaint, not TMessage. In addition to being able to specify the procedure name and the window message, we can also specify the type of the parameter. Our only limitation is that there be exactly one var parameter to the procedure. In the Component Writer's Guide on-line help (cwg.hlp), the TWMPaint structure is declared as:

  TWMPaint = record
    Msg: Cardinal;
    DC: HDC;
    Unused: Longint;
    Result: Longint;

As you can see, it fits the same form as the TMessage declaration, except the internal values are typed according to the specific message. In the CWG on-line help, you will find a long list of TWMxxxx types already defined for your convenience.

MORE... Creating Forms with Custom Title Bars (Pt. II)

[ Home Page | What's New | About CITY ZOO | Borland Delphi | About the Authors | INDEX ]
Copyright © 1995 Mark R. Johnson. This is a CITY ZOO production.
Last revised June 14, 1995.
Enhanced version