Živě.cz o počítačích a internetu

Tipy a triky v Delphi, díl 130. - tlačítko do pruhu aplikace

Jan Šindelář - 17.3.2004

Jistě víte, že na titulkový pruh formuláře nelze umístit běžným způsobem žádné komponenty, tedy ani tlačítko. Máme k dispozici pouze tzv. client area, tedy tu část, kam umisťujeme komponenty a zobrazujeme data pro uživatele. Titulkový pruh však společně s okrajem okna, systémovým menu a tlačítky na změnu velikosti patří do non-client area. Sem tedy tlačítko umístit nemůžeme, ale můžeme na toto místo kreslit.

Postup bude následující. Nejprve potřebujeme funkci na vykreslení tlačítka. To bude v našem případě představováno malým obrázkem, který vykreslíme do předem připraveného tlačítka (s využitím knihovny Buttons). Na velikosti obrázku teoreticky nezáleží, protože ve finále budeme obrázek stejně vykreslovat tak, aby své rozměry přizpůsobil velikosti tlačítek na titulkovém pruhu formuláře. Příliš velká deformace obrázku způsobená špatně zvolenými rozměry však nebude vypadat pěkně, takže se snažte nakreslit si tlačítko (obrázek) tak velké, aby přibližně odpovídalo velikosti ostatních standardních tlačítek. I počet barev volte s rozmyslem. Ideální rozměr se bude pohybovat něco kolem 16x16 bodů při 16 až 256 barvách. Pomocí příslušných funkcí si zjistíme aktuální rozměry tlačítek na titulkovém pruhu a podle této velikosti pak budeme vykreslovat i naše tlačítko. Tím pádem by měl náš příklad dobře fungovat i při různých nastaveních systému (při jiném nastavení velikosti titulkového pruhu než je standardní, při použití vizuálních stylů atd..). V naší ukázce budeme obrázek načítat z malého BMP souboru metodou LoadFromFile, takže příslušný soubor musí být v aktuálním adresáři projektu. Pochopitelně si můžete bitmapu uložit třeba do resources a nebo místo kreslení obrázku vypisovat text (přesněji řečeno znak, víc se na tlačítko stejně nevejde).

Máme-li funkci na vykreslení tlačítka, musíme ještě zajistit jeho správné překreslování při změně rozměru okna nebo při jeho přesouvání po pracovní ploše. K tomu nám již tradičně pomůže systém zpráv Windows, kdy si příslušné zprávy o stavu okna budeme hlídat a při změně rozměru okna či přesunu překreslíme naše tlačítko.

Poslední věc, kterou musíme zajistit, je reakce na stisknutí tlačítka myší. I to provedeme pomocí zpráv systému.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Buttons;

type
  TForm1 = class(TForm)
    procedure FormResize(Sender: TObject);
  private
    { Private declarations }
    TitleButton: TRect;
    procedure DrawTitleButton;
    procedure WMSetText(var Msg: TWMSetText); message WM_SETTEXT;
    procedure WMNCPaint(var Msg: TWMNCPaint); message WM_NCPAINT;
    procedure WMNCActivate(var Msg: TWMNCActivate); message WM_NCACTIVATE;
    procedure WMNCHitTest(var Msg: TWMNCHitTest); message WM_NCHITTEST;
    procedure WMNCLButtonDown(var Msg: TWMNCLButtonDown); message WM_NCLBUTTONDOWN;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

const
  htTitleBtn = htSizeLast + 1;

implementation

{$R *.dfm}


procedure TForm1.DrawTitleButton;
var
  bmap: TBitmap;
  XFrame, XTtlBit, YTtlBit: Integer;
begin
  XFrame := GetSystemMetrics(SM_CXFRAME);
  XTtlBit := GetSystemMetrics(SM_CXSIZE);
  YTtlBit := GetSystemMetrics(SM_CYSIZE);
  TitleButton := Bounds(Width - XFrame - 4*XTtlBit + 2, XFrame + 2 , XTtlBit - 4 , YTtlBit - 4);
  Canvas.Handle := GetWindowDC(Self.Handle);
  try
    DrawButtonFace(Canvas, TitleButton, 1, bsAutoDetect, False, False, False);
    bmap := TBitmap.Create;
    bmap.LoadFromFile(`info.bmp`);
    TitleButton.Left := TitleButton.Left + 2;
    TitleButton.Top := TitleButton.Top + 2;
    TitleButton.Right := TitleButton.Right - 2;
    TitleButton.Bottom := TitleButton.Bottom - 2;
    Canvas.StretchDraw(TitleButton, bmap);
  finally
    ReleaseDC(Self.Handle, Canvas.Handle);
    bmap.Free;
    Canvas.Handle := 0;
  end;
end;

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

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

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

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

procedure TForm1.WMNCHitTest(var Msg: TWMNCHitTest);
begin
  Inherited;
  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(`Tlačítko bylo aktivováno`);
end;

end.

Na začátku procedury DrawTitleButton vidíme nejprve zjištění rozměrů tlačítek podle nastavení systému. Pak funkcí Bounds s příslušnými parametry určíme, kam bude naše budoucí tlačítko vykresleno.

V proceduře DrawTitleButton si rovněž všimněte části kódu v pasáži Try. Řádek DrawButtonFace vykreslí pomocí knihovny Buttons prázdné tlačítko. To ale nemusí být vždy potřeba, protože třeba chceme celou podobu tlačítka nakreslit obrázkem. Stačí tedy tento řádek odstranit. Poté dále v kódu načteme obrázek ze souboru a hned si o něco zmenšíme rozměry tlačítka (o 2 body), aby po vykreslení obrázku byly stále vidět okraje tlačítka. Tyto parametry si můžete rovněž přizpůsobit podle sebe. A na závěr dojde k samotnému vykreslení obrázku pomocí StretchDraw, takže budou přizpůsobeny rozměry. Pokud nechcete vykreslovat obrázek, ale například použít jen text či jednoduchou grafiku, stačí použít třeba metodu TextOut nebo cokoliv jiného, co je možné použít pro Canvas.

Tolik tedy tlačítko na titulkovém pruhu. I tentokrát si zájemci mohou stáhnout ukázkový projekt vytvořený v Delphi 7.