I have Delphi 7 and now installed Delphi XE2. I'm not really experienced with Design, VCL etc. but I would like to have a button (with Caption!) and a simple background image (PNG). I have 3 pictures of custom buttons (1 for click, 1 for mouseoff and 1 for mouseover). I have tried almost everything but I can't seem to find a way to have a simple button with caption in the middle and the images in the background. Please help.
PS.: The button should NOT visually go down on click (this is already in the png image.)


You might adapt this tiny component, no need to install for testing
procedure TForm1.MyOnClick( Sender: TObject );
begin
  ShowMessage( 'Hallo' );
end;
procedure TForm1.Button1Click( Sender: TObject );
begin
  with TImageButton.Create( self ) do
  begin
    Parent := self;
    Images := Imagelist1;
    Index := 0;
    HoverIndex := 1;
    DownIndex := 2;
    Caption := 'test';
    OnClick := MyOnClick;
    Width := Imagelist1.Width;
    Height := Imagelist1.Height;
    Font.Size := 12;
    Font.Style := [fsBold];
  end;
end;

unit ImageButton;
// 2013 bummi
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  ExtCtrls, StdCtrls,ImgList;
Type
  TState = (MouseIn, MouseOut, Pressed);
  TImageButton = class(TGraphicControl)
  private
    FChangeLink:TChangeLink;
    FImages: TCustomImageList;
    FDownIndex: Integer;
    FIndex: Integer;
    FHoverIndex: Integer;
    FState: TState;
    FCaption: String;
    FOwner: TComponent;
    FAutoWidth: Boolean;
    procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
    procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
    procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
    procedure WMLBUTTONDOWN(var Message: TMessage); message WM_LBUTTONDOWN;
    procedure WMLBUTTONUP(var Message: TMessage); message WM_LBUTTONUP;
    procedure SetDownIndex(const Value: Integer);
    procedure SetHoverIndex(const Value: Integer);
    procedure SetIndex(const Value: Integer);
    procedure SetImages(const Value: TCustomImageList);
    procedure SetCaption(const Value: String);
    procedure ImagelistChange(Sender: TObject);
    procedure SetAutoWidth(const Value: Boolean);
    procedure CheckAutoWidth;
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; Override;
  published
    property AutoWidth:Boolean read FAutoWidth Write SetAutoWidth;
    property Caption;
    property DownIndex: Integer read FDownIndex Write SetDownIndex;
    property Font;
    property HoverIndex: Integer read FHoverIndex Write SetHoverIndex;
    property Images: TCustomImageList read FImages write SetImages;
    property Index: Integer read FIndex Write SetIndex;
  End;
procedure Register;
implementation
procedure TImageButton.ImagelistChange(Sender:TObject);
begin
   invalidate;
   CheckAutoWidth;
end;
Constructor TImageButton.create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FOwner := AOwner;
  FState := MouseOut;
  Width := 200;
  Height := 200;
  FChangeLink:=TChangeLink.Create;
  FChangeLink.OnChange := ImagelistChange;
end;
Destructor TImageButton.Destroy;
begin
  if Assigned(FImages) then FImages.UnRegisterChanges(FChangeLink);
  FChangeLink.Free;
  inherited Destroy;
end;
procedure TImageButton.Paint;
var
  ico: TIcon;
  idx: Integer;
  DestRect: TRect;
  L_Caption: String;
begin
  inherited;
  idx := -1;
  if Assigned(FImages) then
  begin
    case FState of
      MouseIn:
        if FImages.Count > HoverIndex then
          idx := HoverIndex;
      MouseOut:
        if FImages.Count > Index then
          idx := Index;
      Pressed:
        if FImages.Count > DownIndex then
          idx := DownIndex;
    end;
    if idx > -1 then
      try
        ico := TIcon.create;
        FImages.GetIcon(idx, ico);
        Canvas.Draw(0, 0, ico);
      finally
        ico.Free;
      end;
  end
  else
  begin
    Canvas.Rectangle(ClientRect);
  end;
  Canvas.Brush.Style := bsClear;
  DestRect := ClientRect;
  L_Caption := Caption;
  Canvas.Font.Assign(Font);
  Canvas.TextRect(DestRect, L_Caption, [tfVerticalCenter, tfCenter, tfSingleLine]);
end;
procedure TImageButton.CheckAutoWidth;
begin
  if FAutoWidth and Assigned(FImages) then
    begin
      Width := FImages.Width;
      Height := FImages.Height;
    end;
end;
procedure TImageButton.SetAutoWidth(const Value: Boolean);
begin
  FAutoWidth := Value;
  CheckAutoWidth;
end;
procedure TImageButton.SetCaption(const Value: String);
begin
  FCaption := Value;
  Invalidate;
end;
procedure TImageButton.SetDownIndex(const Value: Integer);
begin
  FDownIndex := Value;
  Invalidate;
end;
procedure TImageButton.SetHoverIndex(const Value: Integer);
begin
  FHoverIndex := Value;
  Invalidate;
end;
procedure TImageButton.SetImages(const Value: TCustomImageList);
begin
  if Assigned(FImages) then FImages.UnRegisterChanges(FChangeLink);
  FImages := Value;
  if Assigned(FImages) then
      begin
        FImages.RegisterChanges(FChangeLink);
        FImages.FreeNotification(FOwner);
        CheckAutoWidth;
      end;
  Invalidate;
end;
procedure TImageButton.SetIndex(const Value: Integer);
begin
  FIndex := Value;
  Invalidate;
end;
procedure TImageButton.WMLBUTTONDOWN(var Message: TMessage);
begin
  inherited;
  FState := Pressed;
  Invalidate;
end;
procedure TImageButton.WMLBUTTONUP(var Message: TMessage);
begin
  inherited;
  FState := MouseIn;
  Invalidate;
end;
procedure TImageButton.CMFontChanged(var Message: TMessage);
begin
  Invalidate;
end;
Procedure TImageButton.CMMouseEnter(var Message: TMessage);
Begin
  inherited;
  if (csDesigning in ComponentState) then
    Exit;
  if FState <> MouseIn then
  begin
    FState := MouseIn;
    Invalidate;
  end;
end;
Procedure TImageButton.CMMouseLeave(var Message: TMessage);
Begin
  inherited;
  if (csDesigning in ComponentState) then
    Exit;
  if FState <> MouseOut then
  begin
    FState := MouseOut;
    Invalidate;
  end;
end;
procedure TImageButton.CMTextChanged(var Message: TMessage);
begin
  invalidate;
end;
procedure Register;
begin
  RegisterComponents('Own', [TImageButton])
end;
end.
Will respect transparencies if use with PNG and Imagelist cd32Bit
You can inherit from TBitBtn and override CN_DRAWITEM message handler - this will create a fully normal button with focus,with any pictures you need as a background and with all window messages that buttons need (see BM_XXX messages). You can also implement a virtual method to do other kinds of buttons with just this method overriden.
Something like that:
TOwnerDrawBtn = class(TBitBtn)
private
  procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
  procedure CMFocusChanged(var Message: TMessage); message CM_FOCUSCHANGED;
protected
  procedure DrawButton(const DrawItemStruct: TDrawItemStruct); virtual;
end;
procedure TOwnerDrawBtn.CNDrawItem(var Message: TWMDrawItem);
begin
  DrawButton(Message.DrawItemStruct^);
  Message.Result := Integer(True);
end;
procedure TOwnerDrawBtn.CMFocusChanged(var Message: TMessage);
begin
  inherited;
  Invalidate;
end;
procedure TOwnerDrawBtn.DrawButton(const DrawItemStruct: TDrawItemStruct);
var 
  Canvas: TCanvas;
begin
  Canvas := TCanvas.Create;
  try
    Canvas.Handle := DrawItemStruct.hDC;
    //do any drawing here
  finally
    Canvas.Handle := 0;
    Canvas.Free;
  end;
end;
                        You Can Simply Use TJvTransparentButton from JEDI-Project JVCL . 
With this component you can use single imagelist for all events and all other buttons , more events with image state , more style, Caption , Glyph, PressOffset and ... .
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With