unit Unit1;

{ DEMO Unit for owner-draw listbox with bitmaps. }
{ }
{ Written by Jeff Atwood, JAtwood159@AOL.COM, or on CIS as 76743,3276. }
{}
{ Note: for this code to work when cut n' pasted, you must set the listbox }
{ style to lbOwnerDrawVariable or lbOwnerDrawFixed as necessary. Also      }
{ be sure to set the OnDrawItem and OnMeasureItem events to the procedures }
{ below. }

interface

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

const
  BORDER = 4; { this is the spacing between objects above, below }
              { and between the text and the picture }

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    btnAdd: TButton;
    GroupBox1: TGroupBox;
    btnLoad: TButton;
    Edit1: TEdit;
    Label2: TLabel;
    OpenDialog1: TOpenDialog;
    Label1: TLabel;
    ScrollBox1: TScrollBox;
    Image1: TImage;
    Label3: TLabel;
    procedure btnAddClick(Sender: TObject);
    procedure btnLoadClick(Sender: TObject);
    procedure ListBox1DrawItem(Control: TWinControl; Index: Integer;
      Rect: TRect; State: TOwnerDrawState);
    procedure ListBox1MeasureItem(Control: TWinControl; Index: Integer;
      var Height: Integer);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  bitmaps: Array [1..100] of TBitmap;
  bitIndex: Integer;
  loadedHt: Integer;

implementation

{$R *.DFM}

procedure TForm1.btnAddClick(Sender: TObject);
begin
  { create a new bitmap in the array (up to 100)    }
  { and make the object point to this array element }
  { I got burned BAD when I first tried to do this  }
  { by having all the objects point to              }
  { an image control. Change the image control pic  }
  { and it caused all the previously loaded images  }
  { to GPF.. they were still pointing to the OLD    }
  { image! }
  { Also, this is kind of inefficient since I       }
  { assume that 100 DIFFERENT bitmaps could be used }
  { if you are using one or two bitmaps, by all     }
  { means have multiple references to the same pic  }
  { to save memory. }
  bitIndex := bitIndex + 1;
  bitmaps[bitIndex] := TBitmap.Create;
  bitmaps[bitIndex].LoadFromFile(Label3.Caption);
  { here's where we add the next item text and bitmap }
  ListBox1.Items.AddObject(Edit1.Text, bitmaps[bitIndex]);
end;

procedure TForm1.btnLoadClick(Sender: TObject);
begin
  { load the bitmap into the image control for viewing }
  { get the filename, which we will use later to load a copy }
  { of the bitmap into memory again }
  { we must have the height of the most recently loaded bitmap }
  { it's stored in the form-level variable loadedHt }
  if OpenDialog1.Execute then begin
    label3.Caption := OpenDialog1.FileName;
    Image1.Picture.LoadFromFile(OpenDialog1.FileName);
    Label1.Caption := IntToStr(Image1.Picture.Height) + 'x' + IntToStr(Image1.Picture.Width);
    loadedHt := Image1.Picture.Height;
    btnAdd.Enabled := True;
  end;
end;

procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
  Rect: TRect; State: TOwnerDrawState);
var
  bitmap : TBitmap;
  alist: TListBox;
begin
  { type-cast the Control object }
  alist := Control as TListBox;
  { get the bitmap from the TStringList object storage }
  bitmap := TBitmap(alist.Items.Objects[Index]);
  { erase the current rectangle -- this MUST be done or you get garbage}
  { from previous drawing, trust me...}
  alist.Canvas.FillRect(Rect);
  { draw the bitmap }
  alist.Canvas.Draw(Rect.Left, Rect.Top + BORDER, bitmap);
  { draw the text }
  alist.Canvas.TextOut(Rect.Left + bitmap.Width + BORDER, Rect.Top + BORDER,
                       alist.Items[Index]);
end;
 

procedure TForm1.ListBox1MeasureItem(Control: TWinControl; Index: Integer;
  var Height: Integer);
var
  bitmap : TBitmap;
  alist: TListBox;
begin
  { type-cast the Control object }
  alist := Control as TListBox;
  { get the bitmap from the TStringList object storage }
  bitmap := TBitmap(alist.Items.Objects[Index]);
  { If there is no bitmap, it's because Windows is sizing  }
  { the area before we've finished loading the bitmap..    }
  { in that case, we know the size of the currently loaded }
  { bitmap which must be correct. Otherwise, use the       }
  { bitmap info from the object itself }
  { NOTE: I have noticed that this routine is NEVER called }
  { by Delphi after the initial load, so technically the }
  { ELSE clause never occurs! }
  if bitmap = nil then begin
    if loadedHt >= Height then Height := loadedHt + (BORDER * 2);
    end
  else begin
    if bitmap.Height >= Height then Height := bitmap.Height + (BORDER * 2);
  end;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  { just add the horizontal scroll bar so we can see stuff }
  { you might want to size this based on the WIDTH of the }
  { images inserted into the list, I made it an arbitrary fixed size }
  SendMessage(Listbox1.Handle, LB_SETHORIZONTALEXTENT, 1000, Longint(0));
end;

end.