Board index » delphi » newbie - owner listbox help

newbie - owner listbox help

I hope this doesn't sound dumb but I have no formal programming
education, I just got delphi a while back and have done ok - but I've
never done anything with 'ownerdraw' before - not even sure when it
should be used - is it correct to say that if you want bitmaps by items,
then you _must_ use owner draw?

Anyway, I want a listbox which displays the drives - complete with
bitmaps - like the TDriveCombobox with the drop-down list always
showing.  So I made a test app with a listox and a button.  I ripped the
code, below my sig, from the filectrl.pas unit for the TDriveCombobox.
Understand, I don't know what all the code does (but I recognize that
the real work is done by some api calls), I just tried to figure it out
by experimenting.  Problems:

1.  when I click the button, the bitmaps load into the listbox, but the
descriptions make a white area on the upper left of the form, and go
there.
  if I unhook the OnDrawItem code, the descriptions load into the
listbox, but without bitmaps.
  So, I think the code needs to be 'combined' somehow.

2.  You can see, I create bitmaps, but don't free them - but I don't
know where to free them.

3.  It's much too slow.  About 5 seconds on my pc, for 6 drives
  Maybe I should get the bitmaps from my pc, store them in an
Imagelist?  Do different pc's (different windows versions) use different
bitmaps?  How would I get them from an imagelist?  or a resource file;
would that be better? How would I do that?

But the TDriveCombobox is almost instantaneous, so I don't think getting
the bitmaps is why it's slow.!

Well, tia for any help.

Max C.

****************

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    lb: TListBox;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure lbDrawItem(Control: TWinControl; Index: Integer; Rect:
TRect;
      State: TOwnerDrawState);

  private

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

function VolumeID(DriveChar: Char): string;
var
  OldErrorMode: Integer;
  NotUsed, VolFlags: Integer;
  Buf: array [0..MAX_PATH] of Char;
begin
  OldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
  try
    Buf[0] := #$00;
    if GetVolumeInformation(PChar(DriveChar + ':\'), Buf, sizeof(Buf),
                            nil, NotUsed, VolFlags, nil, 0)
      then SetString(Result, Buf, StrLen(Buf))
      else Result := '';
    Result := Format('[%s]',[Result]);
  finally
    SetErrorMode(OldErrorMode);
  end;
end;

function NetworkVolume(DriveChar: Char): string;
var
  Buf: Array [0..MAX_PATH] of Char;
  DriveStr: array [0..3] of Char;
  BufferSize: Integer;
begin
  BufferSize := sizeof(Buf);
  DriveStr[0] := UpCase(DriveChar);
  DriveStr[1] := ':';
  DriveStr[2] := #0;
  if WNetGetConnection(DriveStr, Buf, BufferSize) = WN_SUCCESS
    then SetString(Result, Buf, BufferSize)
    else Result := VolumeID(DriveChar);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  FloppyBmp, FixedBmp, NetworkBmp, CdromBmp, RamBmp : TBitmap;
  DriveNum : Integer;
  DriveChar: Char;
  DriveBits: set of 0..25;
begin
  FloppyBMP := TBitmap.Create;
  FloppyBMP.Handle := LoadBitmap(HInstance, 'FLOPPY');
  FixedBMP := TBitmap.Create;
  FixedBMP.Handle := LoadBitmap(HInstance, 'HARD');
  NetworkBMP := TBitmap.Create;
  NetworkBMP.Handle := LoadBitmap(HInstance, 'NETWORK');
  CDROMBMP := TBitmap.Create;
  CDROMBMP.Handle := LoadBitmap(HInstance, 'CDROM');
  RAMBMP := TBitmap.Create;
  RAMBMP.Handle := LoadBitmap(HInstance, 'RAM');

  { fill list }
  Integer(DriveBits) := GetLogicalDrives;
  for DriveNum := 0 to 25 do
  begin
    if not (DriveNum in DriveBits) then Continue;
    DriveChar := Char(DriveNum + Ord('a'));
 //   DriveChar := Upcase(DriveChar);

    case GetDriveType(PChar(DriveChar + ':\')) of
      Drive_REMOVABLE:
        lb.Items.AddObject(DriveChar + ':', FloppyBMP);
      Drive_FIXED:
        lb.Items.AddObject(DriveChar + ':  ' + VolumeID(DriveChar),
FixedBMP);
      Drive_REMOTE:
        lb.Items.AddObject(DriveChar + ':  ' + NetworkVolume(DriveChar),
NetworkBMP);
      Drive_CDROM:
        lb.Items.AddObject(DriveChar + ':  ' + VolumeID(DriveChar),
CDROMBMP);
      Drive_RAMDISK:
        lb.Items.AddObject(DriveChar + ':  ' + VolumeID(DriveChar),
RAMBMP);
    end;
  end;
end;

procedure TForm1.lbDrawItem(Control: TWinControl; Index: Integer;
  Rect: TRect; State: TOwnerDrawState);
var
  Bitmap: TBitmap;
  bmpWidth: Integer;
begin
  with lb.Canvas do
  begin
    FillRect(Rect);
    bmpWidth  := 16;
    Bitmap := TBitmap(lb.Items.Objects[Index]);
    if Bitmap <> nil then
    begin
      bmpWidth := Bitmap.Width;
      BrushCopy(Bounds(Rect.Left + 2,
               (Rect.Top + Rect.Bottom - Bitmap.Height) div 2,
               Bitmap.Width, Bitmap.Height),
               Bitmap, Bounds(0, 0, Bitmap.Width, Bitmap.Height),
               Bitmap.Canvas.Pixels[0, Bitmap.Height - 1]);
    end;
     { uses DrawText instead of TextOut in order to get clipping against
       the combo box button   }
    Rect.Left := Rect.Left + bmpWidth + 6;
    DrawText(Canvas.Handle, PChar(lb.Items[Index]), -1, Rect,
             DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
  end;
end;

end.

 

Re:newbie - owner listbox help


You are in a 'with'.

  use DrawText(Handle,   not Canvas.handle which is referring to the form.
HTH

Quote
mike...@mindspring.com wrote:

>   with lb.Canvas do
>   begin
>     FillRect(Rect);
>     bmpWidth  := 16;
>     Bitmap := TBitmap(lb.Items.Objects[Index]);
>     if Bitmap <> nil then
>     begin
>       bmpWidth := Bitmap.Width;
>       BrushCopy(Bounds(Rect.Left + 2,
>                (Rect.Top + Rect.Bottom - Bitmap.Height) div 2,
>                Bitmap.Width, Bitmap.Height),
>                Bitmap, Bounds(0, 0, Bitmap.Width, Bitmap.Height),
>                Bitmap.Canvas.Pixels[0, Bitmap.Height - 1]);
>     end;
>      { uses DrawText instead of TextOut in order to get clipping against
>        the combo box button   }
>     Rect.Left := Rect.Left + bmpWidth + 6;
>     DrawText(Canvas.Handle, PChar(lb.Items[Index]), -1, Rect,
>              DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
>   end;
> end;

> end.

Re:newbie - owner listbox help


Quote
Al Kirk wrote:

> You are in a 'with'.

>   use DrawText(Handle,   not Canvas.handle which is referring to the form.
> HTH

Great!  It works, and is much faster.

Now, do you know how to free the bitmaps I create?  You can see I create
a bitmap for each type of drive in the Button click.  But I can't Free
them at the end of that procedure, else they are not available for the
OnDrawItem procedure.  Do you know how I can modify things to handle
this?

Max

Other Threads