Reputation: 811
I am using Delphi XE3 and want to implement Windows Thumbnail style to show a list of images via TListView control.
What I need is like below:
The images are displayed as thumbnail style, there is a caption below each image. And when I click the image, the image together with the caption will be shown as selected...
To improve the performance, I do not want to load all the images into an image list beforehand, instead, I want to load the image when it is to be displayed. Therefore, I am thinking of using OnCustomDrawItem and OnAdvancedCustomDrawItem.
Below is a very simple version of my plan(I set the style of the list view to vsIcon):
procedure TForm1.FormCreate(Sender: TObject);
var
ListItem1: TListItem;
begin
ListItem1 := ListView1.Items.Add;
ListItem1.Caption := 'Chrysanthemum';
end;
procedure TForm1.ListView1AdvancedCustomDrawItem(Sender: TCustomListView;
Item: TListItem; State: TCustomDrawState; Stage: TCustomDrawStage;
var DefaultDraw: Boolean);
var
JPEG: TJPEGImage;
R: TRect;
begin
{
R := Item.DisplayRect(drBounds);
JPEG := TJPEGImage.Create;
JPEG.LoadFromFile('C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum (2).jpg');
Sender.Canvas.StretchDraw(R, JPEG);
}
end;
procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView;
Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
var
JPEG: TJPEGImage;
R: TRect;
begin
R := Item.DisplayRect(drBounds);
JPEG := TJPEGImage.Create;
JPEG.LoadFromFile('C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum (2).jpg');
Sender.Canvas.StretchDraw(R, JPEG);
end;
But the result is not satisfactory, as follows:
I cannot find a way to set the size of each icon. (All icon will have the same size).
I try to put the codes in OnCustomDrawItem and OnAdvancedCustomDrawItem. I cannot figure out much differences between these twos. The only main difference that in Advancedxxx version, the caption is editable. I cannot understand why.
The caption is not displayed under the image, instead, it is in the middle of the image, that is not desired. How to fix that?
Thanks
Upvotes: 1
Views: 1102
Reputation: 1483
The attached code loads the images (in this case icons) into the TImageList that is assigned to the LargeImages
property of the TListView only when the associated icon actually gets displayed in the listview. The main thing is to set the OwnerData
property of the listview to TRUE and to create an event handler for OnData
events. Parallel to the items in the listview the program maintains a list of the items in the listview that is in sync with the actual list in the listview, in this case a TStringList. In its Objects
property I store the index of the associated icon resource if that has already been loaded and added to the TImageList. If the icon resource has not been loaded, this happens in the LoadIconFromFile
function and the index of the icon in the TImageList gets stored in the TStringList.
The actual drawing of the icons and the text in the TListView is fully handled by the control itself, the code does neither handle OnDraw
nor any OnCustomDraw*
events. Just set the image size in the TImageList to the size of the bitmaps you want to display and create them accordingly.
Older Delphi versions contain a sample project called "VirtualListView.dpr" that is quite helpful to understand when the various OnData*
events get fired and how to properly use them.
unit MainFormU;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ImgList, StdCtrls, ComCtrls;
type
TForm1 = class(TForm)
Icons_LV: TListView;
Label1: TLabel;
Large_IL: TImageList;
procedure Icons_LVData(Sender: TObject; Item: TListItem);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FileList : TStringList;
procedure FillListView;
function LoadIconFromFile (const sFileName: String;
out iIndex: Integer) : Boolean;
end;
var Form1 : TForm1;
implementation
{$R *.dfm}
uses ShellApi;
const
cWinSysDir = 'c:\windows\system32\';
procedure TForm1.FormCreate (Sender: TObject);
begin
FileList := TStringList.Create;
FillListView;
end;
procedure TForm1.FormDestroy (Sender: TObject);
begin
FileList.Free;
end;
procedure TForm1.Icons_LVData (Sender: TObject; Item: TListItem);
var iIndex : Integer;
begin
if (Item.Index >= FileList.Count) then
exit;
Item.Caption := FileList [Item.Index];
if (FileList.Objects [Item.Index] = TObject (-1)) then
begin
if not (LoadIconFromFile (cWinSysDir + Item.Caption, iIndex)) then
iIndex := 0;
FileList.Objects [Item.Index] := TObject (iIndex);
end { if }
else iIndex := Integer (FileList.Objects [Item.Index]);
Item.ImageIndex := iIndex
end;
procedure TForm1.FillListView;
var SR : TSearchRec;
begin
FillChar (SR, SizeOf (TSearchRec), #0);
if (FindFirst (cWinSysDir + '*.exe', faAnyFile, SR) = 0) then
repeat
FileList.AddObject (SR.Name, TObject ((-1)));
until (FindNext (SR) <> 0);
FindClose (SR);
Icons_LV.Items.Count := FileList.Count;
end;
function TForm1.LoadIconFromFile (const sFileName: String;
out iIndex: Integer) : Boolean;
var
hIcon : Windows.HICON;
Icon : TIcon;
begin
Result := false;
if (ExtractIcon (MainInstance, PChar (sFileName), UInt ((-1))) > 0) then
begin
{$IFDEF DEBUG}
OutputDebugString (PChar (Format ('LoadIconFromFile "%s"', [sFileName])));
{$ENDIF}
hIcon := ExtractIcon (MainInstance, PChar (sFileName), 0);
if (hIcon <> 0) then
begin
Icon := TIcon.Create;
Icon.Handle := hIcon;
iIndex := Large_IL.AddIcon (Icon);
Icon.Free;
Result := true;
end; { if }
end { if }
end;
end.
The full example is available for download here.
Upvotes: 5