2011. június 13., hétfő

How to draw buttons on the title bar of a TForm


Problem/Question/Abstract:

How to draw buttons on the title bar of a TForm

Answer:

Solve 1:

Place an icon-sized TImage on a form and add the following code:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    procedure FormCreate(Sender: TObject);
  private
    {Private declarations}
    TitleBarCanvas: TCanvas;
    procedure WMNCPaint(var Msg: TWMNCPaint); message WM_NCPAINT;
    procedure WMNCActivate(var Msg: TWMNCActivate); message WM_NCACTIVATE;
    procedure DrawExtraStuff;
  public
    {Public declarations}
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  NonClientMetrics: TNonClientMetrics;
begin
  TitleBarCanvas := TCanvas.Create;
  TitleBarCanvas.Handle := GetWindowDC(Handle);
  NonClientMetrics.cbSize := SizeOf(NonClientMetrics);
  SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NonClientMetrics, 0);
  TitleBarCanvas.Font.Handle := CreateFontIndirect(NonClientMetrics.lfCaptionFont);
  TitleBarCanvas.Brush.Style := bsClear;
  Caption := '';
end;

procedure TForm1.WMNCPaint(var Msg: TWMNCPaint);
begin
  inherited;
  DrawExtraStuff;
end;

procedure TForm1.WMNCActivate(var Msg: TWMNCActivate);
begin
  inherited;
  if Msg.Active then
    TitleBarCanvas.Font.Color := clCaptionText
  else
    TitleBarCanvas.Font.Color := clInactiveCaptionText;
  DrawExtraStuff;
end;

procedure TForm1.DrawExtraStuff;
var
  X, Y, TransColor: Integer;
begin
  {set the transparent color to bottom left pixel}
  TransColor := Image1.Canvas.Pixels[0, Image1.Picture.Height - 1];
  with Image1 do
    for x := 0 to Picture.Width - 1 do
      for y := 0 to Picture.Height - 1 do
        if Canvas.Pixels[x, y] <> TransColor then
          TitleBarCanvas.Pixels[22 + x, 5 + y] := Canvas.Pixels[x, y];
  TitleBarCanvas.TextOut(40, 6, '<- Here is the other icon');
end;

end.


Solve 2:

I got my first clue into solving this problem when I wrote a previous tip that covered rolling up the client area of forms so that only the caption bar showed. In my research for that tip, I came across the WMSetText message that is used for drawing on a form's canvas. I wrote a little sample application to test drawing in the caption area. The only problem with my original code was that the button would disappear when I resized or moved the form.

I turned to well-known Delphi/Pascal guru, Neil Rubenking, for help. He pointed me in the direction of his book, "Delphi Programming Problem Solver," which had an example of doing this exact thing. The code you'll see below is an adaptation of the example in his book. The most fundamental difference between our examples is that I wanted to make a speedbutton with a bitmap glyph, and Neil actually drew a shape directly on the canvas. Neil also placed the button created in 16-bit Delphi on the left-hand side of the frame, and Win32 button placement was on the right. I wanted my buttons to be placed on the right for both versions, so I wrote appropriate code to handle that. The deficiency in my code was the lack of handlers for activation and painting in the non-client area of the form.

One thing that I'm continually discovering is that there is a very definitive structure in Windows - a definite hierarchy of functions. I've realized that the thing that makes Windows programming at the API level difficult is the sheer number of functions in the API set. For those who are reluctant to dive into the WinAPI, think in terms of categories first, then narrow your search. You'll find that doing it this way will make your life much easier.

What makes all of this work is Windows messages. The messages that we are interested in here are not the usual Windows messages handled by vanilla Windows apps, but are specific to an area of a window called the non-client area. The client area of a window is the part inside the border which is where most applications present information. The non-client area of a window consists of its borders, caption bar, system menu, and sizing buttons. The Windows messages that pertain to this area have the naming convention of WM_NCMessageType. Taking the name apart, 'WM' stands for Windows Message, 'NC' stands for Non-client area, and MessageType is the message type being trapped. For example, WM_NCPaint is the paint message for the non-client area. Taking into account the hierarchical and categorical nature of the Windows API, nomenclature is a very big part of it; especially with Windows messages. If you look in the help file under messages, peruse through the list of messages and you will see that the order that is followed.

Let's look at a list of things that we need to consider to add a button to the title bar of a form:

We need to have a function to draw the button
We'll have to trap drawing and painting events so that our button stays visible when the form activates, resizes, or moves
Since we're dropping a button on the title bar, we have to have some way of trapping for a mouse click on the button.

I'll now discuss these topics, in the above order.


Drawing a TRect as a Button

As I mentioned above, you can't drop VCL objects onto a non-client area of a window, but you can draw on it and essentially simulate the appearance of a button. In order to perform drawing in the title bar of a window, you have to do three very important things in order:

You have to get the current measurements of the window and the size of the frame bitmaps so you know what area to draw in and how big to draw the rectangle. 2.Then, you have to define a TRect structure with the proper size and position within the title bar. 3.Finally, you have to draw the TRect to appear as a button, then add any glyphs or text you might want to draw to the buttonface.

All this is accomplished in a single call. For this program we make a call to a procedure called DrawTitleButton, which is listed below:

procedure TTitleBtnForm.DrawTitleButton;
var
  bmap: TBitmap; {Bitmap to be drawn - 16 x 16 : 16 Colors}
  XFrame, {X and Y size of Sizeable area of Frame}
  YFrame,
    XTtlBit, {X and Y size of Bitmaps in caption}
  YTtlBit: Integer;
begin
  {Get size of form frame and bitmaps in title bar}
  XFrame := GetSystemMetrics(SM_CXFRAME);
  YFrame := GetSystemMetrics(SM_CYFRAME);
  XTtlBit := GetSystemMetrics(SM_CXSIZE);
  YTtlBit := GetSystemMetrics(SM_CYSIZE);
{$IFNDEF WIN32}
  TitleButton := Bounds(Width - (3 * XTtlBit) - ((XTtlBit div 2) - 2), YFrame - 1,
    XTtlBit + 2, YTtlBit + 2);
{$ELSE} {Delphi 2.0 positioning}
  if (GetVerInfo = VER_PLATFORM_WIN32_NT) then
    TitleButton := Bounds(Width - (3 * XTtlBit) - ((XTtlBit div 2) - 2), YFrame - 1,
      XTtlBit + 2, YTtlBit + 2)
  else
    TitleButton := Bounds(Width - XFrame - 4 * XTtlBit + 2, XFrame + 2, XTtlBit + 2,
      YTtlBit + 2);
{$ENDIF}
  Canvas.Handle := GetWindowDC(Self.Handle); {Get Device context for drawing}
  try
    {Draw a button face on the TRect}
    DrawButtonFace(Canvas, TitleButton, 1, bsAutoDetect, False, False, False);
    bmap := TBitmap.Create;
    bmap.LoadFromFile('help.bmp');
    with TitleButton do
{$IFNDEF WIN32}
      Canvas.Draw(Left + 2, Top + 2, bmap);
{$ELSE}
      if (GetVerInfo = VER_PLATFORM_WIN32_NT) then
        Canvas.Draw(Left + 2, Top + 2, bmap)
      else
        Canvas.StretchDraw(TitleButton, bmap);
{$ENDIF}
  finally
    ReleaseDC(Self.Handle, Canvas.Handle);
    bmap.Free;
    Canvas.Handle := 0;
  end;
end;

Step 1 above is accomplished by making four calls to the WinAPI function, GetSystemMetrics, asking the system for the width and height of the window that can be sized (SM_CXFRAME and SM_CYFRAME), and the size of the bitmaps contained on the title bar (SM_CXSIZE and SM_CYSIZE).

Step 2 is performed with the Bounds function which returns a TRect defined by the size and position parameters which are supplied to it. Notice that I used some conditional compiler directives here. This is because the size of the title bar buttons in Windows 95 and Windows 3.1 are different, so they have to be sized differently. And since I wanted to be able to compile this in either version of Windows, I used a test for the predefined symbol, WIN32, to see what version of Windows the program is compiled under. However, since the Windows NT UI is the same as Windows 3.1, it's necessary to grab further version information under the Win32 conditional to see if the Windows version is Windows NT. If it is, then we define the TRect to be just like the Windows 3.1 TRect.

To perform Step 3, we make a call to the Buttons unit's DrawButtonFace to draw button features within the TRect that we defined. As added treat, I included code to draw a bitmap in the button. Again, you'll see that I used a conditional compiler directive to draw the bitmap under different versions of Windows. I did this purely for personal reasons because the bitmap that I used was 16 X 16 pixels in dimension, which might be too big for Win95 buttons. So I used StretchDraw under Win32 to stretch the bitmap to the size of the button.


Trapping the Drawing and Painting Events

You have to make sure that the button will stay visible every time the form repaints itself. Painting occurs in response to activation and resizing, which fire off paint and text setting messages that will redraw the form. If you don't have a facility to redraw your button, you'll lose it every time a repaint occurs. So what we have to do is write event handlers which will perform their default actions, but also redraw our button when they fire off. The following four procedures handle the paint triggering and painting events:

{Paint triggering events}

procedure TForm1.WMNCActivate(var Msg: TWMNCActivate);
begin
  inherited;
  DrawTitleButton;
end;

procedure TForm1.FormResize(Sender: TObject);
begin
  Perform(WM_NCACTIVATE, Word(Active), 0);
end;

{Painting events}

procedure TForm1.WMNCPaint(var Msg: TWMNCPaint);
begin
  inherited;
  DrawTitleButton;
end;

procedure TForm1.WMSetText(var Msg: TWMSetText);
begin
  inherited;
  DrawTitleButton;
end;

Every time one of these events fires off, it makes a call to the DrawTitleButton procedure. This will ensure that our button is always visible on the title bar. Notice that we use the default handler OnResize on the form to force it to perform a WM_NCACTIVATE.


Handling Mouse Clicks

Now that we've got code that draws our button and ensures that it's always visible, we have to handle mouse-clicks on the button. The way we do this is with two procedures. The first procedure tests to see if the mouse-click was in the area of our button, then the second procedure actually performs the code execution associated with our button. Let's look at the code below:

{Mouse-related procedures}

procedure TForm1.WMNCHitTest(var Msg: TWMNCHitTest);
begin
  inherited;
  {Check to see if the mouse was clicked in the area of the button}
  with Msg do
    if PtInRect(TitleButton, Point(XPos - Left, YPos - Top)) then
      Result := htTitleBtn;
end;

procedure TForm1.WMNCLButtonDown(var Msg: TWMNCLButtonDown);
begin
  inherited;
  if (Msg.HitTest = htTitleBtn) then
    ShowMessage('You pressed the new button');

end;

The first procedure WMNCHitTest(var Msg : TWMNCHitTest) is a hit tester message to determine where the mouse was clicked in the non-client area. In this procedure we test if the point defined by the message was within the bounds of our TRect by using the PtInRect function. If the mouse click was performed in the TRect, then the result of our message is set to htTitleBtn, which is a constant that was declared as htSizeLast + 1. htSizeLast is a hit test constant generated by hit test events to test where the last hit occurred.

The second procedure is a custom handler for a left mouse-click on a button in the non-client area. Here we test if the hit test result was equal to htTitleBtn. If it is, we show a message. This was purely for simplicity's sake, but you can make any call you choose to at this point.


Putting it All Together

Let's look at the entire code in the form to see how it all works together:

unit Capbtn;

interface

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

type
  TTitleBtnForm = class(TForm)
    procedure FormResize(Sender: TObject);
  private
    TitleButton: TRect;
    procedure DrawTitleButton;
    {Paint-related messages}
    procedure WMSetText(var Msg: TWMSetText); message WM_SETTEXT;
    procedure WMNCPaint(var Msg: TWMNCPaint); message WM_NCPAINT;
    procedure WMNCActivate(var Msg: TWMNCActivate); message WM_NCACTIVATE;
    {Mouse down-related messages}
    procedure WMNCHitTest(var Msg: TWMNCHitTest); message WM_NCHITTEST;
    procedure WMNCLButtonDown(var Msg: TWMNCLButtonDown);
      message WM_NCLBUTTONDOWN;
    function GetVerInfo: DWORD;
  end;

var
  TitleBtnForm: TTitleBtnForm;

const
  htTitleBtn = htSizeLast + 1;

implementation

{$R *.DFM}

procedure TTitleBtnForm.DrawTitleButton;
var
  bmap: TBitmap; {Bitmap to be drawn - 16 X 16 : 16 Colors}
  XFrame, {X and Y size of Sizeable area of Frame}
  YFrame,
    XTtlBit, {X and Y size of Bitmaps in caption}
  YTtlBit: Integer;
begin
  {Get size of form frame and bitmaps in title bar}
  XFrame := GetSystemMetrics(SM_CXFRAME);
  YFrame := GetSystemMetrics(SM_CYFRAME);
  XTtlBit := GetSystemMetrics(SM_CXSIZE);
  YTtlBit := GetSystemMetrics(SM_CYSIZE);
{$IFNDEF WIN32}
  TitleButton := Bounds(Width - (3 * XTtlBit) - ((XTtlBit div 2) - 2), YFrame - 1,
    XTtlBit + 2, YTtlBit + 2);
{$ELSE} {Delphi 2.0 positioning}
  if (GetVerInfo = VER_PLATFORM_WIN32_NT) then
    TitleButton := Bounds(Width - (3 * XTtlBit) - ((XTtlBit div 2) - 2), YFrame - 1,
      XTtlBit + 2, YTtlBit + 2)
  else
    TitleButton := Bounds(Width - XFrame - 4 * XTtlBit + 2, XFrame + 2, XTtlBit + 2,
      YTtlBit + 2);
{$ENDIF}
  Canvas.Handle := GetWindowDC(Self.Handle); {Get Device context for drawing}
  try
    {Draw a button face on the TRect}
    DrawButtonFace(Canvas, TitleButton, 1, bsAutoDetect, False, False, False);
    bmap := TBitmap.Create;
    bmap.LoadFromFile('help.bmp');
    with TitleButton do
{$IFNDEF WIN32}
      Canvas.Draw(Left + 2, Top + 2, bmap);
{$ELSE}
      if (GetVerInfo = VER_PLATFORM_WIN32_NT) then
        Canvas.Draw(Left + 2, Top + 2, bmap)
      else
        Canvas.StretchDraw(TitleButton, bmap);
{$ENDIF}
  finally
    ReleaseDC(Self.Handle, Canvas.Handle);
    bmap.Free;
    Canvas.Handle := 0;
  end;
end;

{Paint triggering events}

procedure TTitleBtnForm.WMNCActivate(var Msg: TWMNCActivate);
begin
  inherited;
  DrawTitleButton;
end;

procedure TTitleBtnForm.FormResize(Sender: TObject);
begin
  Perform(WM_NCACTIVATE, Word(Active), 0);
end;

{Painting events}

procedure TTitleBtnForm.WMNCPaint(var Msg: TWMNCPaint);
begin
  inherited;
  DrawTitleButton;
end;

procedure TTitleBtnForm.WMSetText(var Msg: TWMSetText);
begin
  inherited;
  DrawTitleButton;
end;

{Mouse-related procedures}

procedure TTitleBtnForm.WMNCHitTest(var Msg: TWMNCHitTest);
begin
  inherited;
  {Check to see if the mouse was clicked in the area of the button}
  with Msg do
    if PtInRect(TitleButton, Point(XPos - Left, YPos - Top)) then
      Result := htTitleBtn;
end;

procedure TTitleBtnForm.WMNCLButtonDown(var Msg: TWMNCLButtonDown);
begin
  inherited;
  if (Msg.HitTest = htTitleBtn) then
    ShowMessage('You pressed the new button');
end;

function TTitleBtnForm.GetVerInfo: DWORD;
var
  verInfo: TOSVERSIONINFO;
begin
  verInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
  if GetVersionEx(verInfo) then
    Result := verInfo.dwPlatformID;
  {Returns:
  VER_PLATFORM_WIN32s -- Win32s on Windows 3.1
  VER_PLATFORM_WIN32_WINDOWS -- Win32 on Windows 95
  VER_PLATFORM_WIN32_NT -- Windows NT }
end;

end.

You might want to play around with this code a bit to customize it to your own needs. For instance, if you want to add a bigger button, add pixels to the XTtlBit var. You might also want to mess around with creating a floating toolbar that is purely on the title bar. Also, now that you have a means of interrogating what's going on in the non-client area of the form, you might want to play around with the default actions taken with the other buttons like the System Menu button to perhaps display your own custom menu. Take heed though, playing around with Windows messages can be dangerous. Save your work constantly, and be prepared for some system crashes while you mess around with them.


Solve 3:

unit TitleBtn;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Forms, Dialogs,
  Buttons, Controls, StdCtrls, ExtCtrls;

type
  TTitleBtnForm = class(TForm)
    procedure FormResize(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    function GetSystemTitleBtnCount: integer;
    procedure KillHint;
  private
    TitleButton: TRect;
    FActive: boolean;
    FHint: THintWindow;
    Timer2: TTimer;
    procedure DrawTitleButton(i: integer);
    {Paint-related messages}
    procedure WMSetText(var Msg: TWMSetText); message WM_SETTEXT;
    procedure WMNCPaint(var Msg: TWMNCPaint); message WM_NCPAINT;
    procedure WMNCActivate(var Msg: TWMNCActivate); message WM_NCACTIVATE;
    {Mouse-related messages}
    procedure WMNCHitTest(var Msg: TWMNCHitTest); message WM_NCHitTest;
    procedure WMNCLButtonDown(var Msg: TWMNCLButtonDown);
      message WM_NCLBUTTONDOWN;
    procedure WMNCLButtonUp(var Msg: TWMNCLButtonUp); message WM_NCLBUTTONUP;
    procedure WMNCMouseMove(var Msg: TWMNCMouseMove); message WM_NCMouseMove;
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    {-}
    function GetVerInfo: DWORD;
    {-}
    procedure ShowHint;
    procedure Timer2Timer(Sender: TObject);
  public
  end;

const
  htTitleBtn = htSizeLast + 1;

implementation

uses
  PauLitaData, About, SpoolMessages;

procedure TTitleBtnForm.FormResize(Sender: TObject);
begin
  Perform(WM_NCACTIVATE, Word(Active), 0);
end;

procedure TTitleBtnForm.DrawTitleButton(i: integer);
var
  bmap: TBitmap; {Bitmap to be drawn - 16x16: 16 Colors}
  XFrame, {X and Y size of Sizeable area of Frame}
  YFrame,
    XTtlBit, {X and Y size of Bitmaps in caption}
  YTtlBit: integer;
  n: integer;
begin
  {Get size of form frame and bitmaps in title bar}
  XFrame := GetSystemMetrics(SM_CXFRAME);
  YFrame := GetSystemMetrics(SM_CYFRAME);
  XTtlBit := GetSystemMetrics(SM_CXSIZE);
  YTtlBit := GetSystemMetrics(SM_CYSIZE);
  n := GetSystemTitleBtnCount;
  if GetVerInfo = VER_PLATFORM_WIN32_NT then
    TitleButton := Bounds(Width - XFrame - (n + 1) * XTtlBit + 1 - 3, YFrame + 1 - 3,
      XTtlBit - 2, YTtlBit - 4)
  else
    TitleButton := Bounds(Width - XFrame - (n + 1) * XTtlBit + 1, YFrame + 1, XTtlBit
      - 2, YTtlBit - 4);
  Canvas.Handle := GetWindowDC(Self.Handle);
  try
    {Draw a button face on the TRect}
    DrawButtonFace(Canvas, TitleButton, 1, bsAutoDetect, FALSE, FALSE, FALSE);
    bmap := TBitmap.Create;
    DataModule1.ImageList1.GetBitmap(i, bmap);
    with TitleButton do
      if GetVerInfo = VER_PLATFORM_WIN32_NT then
        Canvas.Draw(Left + 2, Top + 2, bmap)
      else
        Canvas.StretchDraw(TitleButton, bmap);
  finally
    ReleaseDC(Self.Handle, Canvas.Handle);
    bmap.Free;
    Canvas.Handle := 0;
  end;
end;

procedure TTitleBtnForm.WMSetText(var Msg: TWMSetText);
begin
  inherited;
  DrawTitleButton(0);
end;

procedure TTitleBtnForm.WMNCPaint(var Msg: TWMNCPaint);
begin
  inherited;
  DrawTitleButton(0);
end;

procedure TTitleBtnForm.WMNCActivate(var Msg: TWMNCActivate);
begin
  inherited;
  DrawTitleButton(0);
end;

procedure TTitleBtnForm.WMNCLButtonDown(var Msg: TWMNCLButtonDown);
begin
  inherited;
  if (Msg.HitTest = htTitleBtn) then
    DrawTitleButton(1);
end;

procedure TTitleBtnForm.WMNCLButtonUp(var Msg: TWMNCLButtonUp);
begin
  inherited;
  if (Msg.HitTest = htTitleBtn) then
  begin
    KillHint;
    ShowAboutBox;
  end;
end;

procedure TTitleBtnForm.WMNCMouseMove(var Msg: TWMNCMouseMove);
begin
  inherited;
  if (Msg.HitTest = htTitleBtn) and PtinRect(TitleButton, Point(Msg.XCursor - Left,
    Msg.YCursor - Top)) then
    ShowHint
  else
    KillHint;
end;

function TTitleBtnForm.GetVerInfo: DWORD;
var
  verinfo: TOSVERSIONINFO;
begin
  verinfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
  if GetVersionEx(verinfo) then
    Result := verinfo.dwPlatformID;
end;

procedure TTitleBtnForm.WMNCHitTest(var Msg: TWMNCHitTest);
begin
  inherited;
  with Msg do
  begin
    if PtinRect(TitleButton, Point(XPos - Left, YPos - Top)) then
      Result := htTitleBtn;
  end;
end;

function TTitleBtnForm.GetSystemTitleBtnCount: integer;
var
  Menu: HMenu;
  i, n, m, l: integer;
begin
  l := 0;
  Menu := GetSystemMenu(Handle, FALSE);
  n := GetMenuItemCount(Menu);
  for i := 0 to n - 1 do
  begin
    m := GetMenuItemID(Menu, i);
    if (m = SC_RESTORE) or (m = SC_MAXIMIZE) or (m = SC_CLOSE) then
      Inc(l)
    else if (m = SC_MINIMIZE) then
      Inc(l, 2);
  end;
  Result := l;
end;

procedure TTitleBtnForm.KillHint;
begin
  if Assigned(Timer2) then
  begin
    Timer2.Enabled := FALSE;
    Timer2.Free;
    Timer2 := nil;
  end;
  if Assigned(FHint) then
  begin
    FHint.ReleaseHandle;
    FHint.Free;
    FHint := nil;
  end;
  FActive := FALSE;
end;

procedure TTitleBtnForm.Timer2Timer(Sender: TObject);
var
  thePoint: TPoint;
  theRect: TRect;
  Count: DWORD;
  i: integer;
begin
  Timer2.Enabled := FALSE;
  Timer2.Free;
  Timer2 := nil;
  thePoint.X := TitleButton.Left;
  thePoint.Y := TitleButton.Bottom - 25;
  with theRect do
  begin
    topLeft := ClientToScreen(thePoint);
    Right := Left + Canvas.TextWidth(MsgAbout) + 10;
    Bottom := Top + 14;
  end;
  FHint := THintWindow.Create(Self);
  FHint.Color := clInfoBk;
  FHint.ActivateHint(theRect, MsgAbout);
  for i := 1 to 7 do
  begin
    Count := GetTickCount;
    repeat
      {Application.ProcessMessages;}
    until
      (GetTickCount - Count >= 18);
    with theRect do
    begin
      Inc(Top);
      Inc(Bottom);
      FHint.SetBounds(Left, Top, FHint.Width, FHint.Height);
      FHint.Update;
    end;
  end; { i }
  FActive := TRUE;
end;

procedure TTitleBtnForm.ShowHint;
begin
  if FActive then
    Exit;
  if Assigned(Timer2) then
    Exit;
  Timer2 := TTimer.Create(Self);
  Timer2.Interval := 500;
  Timer2.OnTimer := Timer2Timer;
  Timer2.Enabled := TRUE;
end;

procedure TTitleBtnForm.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y:
  Integer);
begin
  inherited;
  KillHint;
end;

procedure TTitleBtnForm.FormCreate(Sender: TObject);
begin
  OnMouseMove := FormMouseMove;
end;

end.

Nincsenek megjegyzések:

Megjegyzés küldése