Link to home
Start Free TrialLog in
Avatar of TAZI
TAZIFlag for South Africa

asked on

Loading Images at Runtime

Hi,

I would like to load images into my program but this should happen only at runtime.  The reason being, I am loading a logo for different companies for eg) If I am working on a CompanyA's data, then a logo for (CompanyA) should be visible in the right hand corner or it I am working with COmpanyB's data then a logo for (CompanyB) must appear in place of the logo of CompanyA.  The image loading must be dynamic as new clients are loaded into the system all the time.  I have thought of loading the images from disk but this slows down the speed of the system as I have to use load from file all the time, I don't mind using load from file on start up (OnFormCreate).  Is there a way to load images into an imagelist at runtime then I can load the images into the imagelist at runtime and then change the logo as the client changes.

Thanks for your help in advance
Regards
TAZI
Avatar of Lischke
Lischke

Hi TAZI,

you have actually several options. You can store images onto disc or in the resouce of the executable. You can then load them into an image list (but this limits all images to the same size) or into a TBitmap.

For imagelists you can use TCustomImageList.FileLoad.

What do you prefer?

Ciao, Mike
Avatar of TAZI

ASKER

Hi Mike ...

I don't want to load images into the resource file of the executable because then I have to continually recompile my program if a new client is added.

If I store images on the disk and I use LoadFromFile each time .. it slows down the application.

SO, I think the 3rd option miight be the one.  If my images are stored on the local harddrive (c:\images) folder can you show me some code to load these images into a ImageList but this must happen at runtime only.

Regard
TAZI
Hi Tazi,

Image1.Picture.Loadfromfile('C:\windows\Red Blocks.bmp');

Asw
Avatar of TAZI

ASKER

Hi ..

I mentioned above that I don't want to load directly from the local hard drive with loadfromfile ... it works to slowly over a network.  I want to load theses into a string list on startup and then refer to these images from the image list as I make use of the program

I want to load images at startup

Regards
TAZI
Image1.Picture.LoadFromFile('file.ext');

For example, if pictures in folder \splash\ in program install folder:

in OnCreate:

var
 SpFile : String;
....
SpFile := ExtractFilePath(Application.ExeName);
If (SpFile[Length(SpFile)]<>'\') And
   (SpFile[Length(SpFile)]<>'/') Then
 SpFile := SpFile+'\';

// function SelectSplash() return a
// splash file name for current firm ID
SpFile := SpFile+'splash\'+SelectSplash;

// Load into SpImage - TImage with splash
SpImage.Picture.LoadFromFile(SpFile);
....
Avatar of TAZI

ASKER

Hi, lmikle

I donot want to load the image from a file ...... there are multiple images that need to be accessed.  Each time I select a client in the combobox, the image changes.  I want to access these images from a imagelist or something to that effect.  

Regards
TAZI
For loading Images into TImageList;

1st set a Width and Height properties if TImageList to max Width and Height of your images (it's in disign time recomended).

In loop:

2nd load a image into Bitmap (LoadFromFile)
3rd insert this bitmap into TImageList:

ImageList1.Add(Bitmap,nil);


Avatar of TAZI

ASKER

Hi, lmikle

I am having a problem with step 2 ..

Loading from local c drive into imagelist

give me some code please

Regards
TAZI
Oh, quite hectical conversation here :-) TAZI please use the FileLoad command I gave you already. Please all your images you want to have into one bitmap (each close to another, into one line) and use the resulting bitmap as parameter for FileLoad. I think you should set the size of the image list just before the call to FileLoad then it will automatically split the images and you can access them separately.

Ciao, Mike
Avatar of TAZI

ASKER

Hi Mike

I may come across as an idiot, but I am totally lost here.

I have 3 BMP's name

ImageNo1.bmp ImageNo2.bmp and ImageNo3.bmp they all reside in
the (c:\images) folder. I want to load all 3 images into the imagelist on click of a button.

I used
TCustomImageList.FileLoad('c:\Images\ImageNo1.bmp');

and I get the ffg error
Incompatible type TResType and String

Sorry ... but I am totally lost here

HELP

Regards
TAZI
No problem TAZI, we all have our dark days :-) Actually, the call should be:

FileLoad(rtBitmap, 'c:\Images\ImageNo1.bmp', clFuchsia);

where clFuchsia is a color which you should use in your bitmap for transparent parts (if you need this color for the image then use another one).

Ciao, Mike
Avatar of TAZI

ASKER

Hi Mike

I USED the statement as ffg

TCustomImageList.FileLoad(rtBitmap, 'c:\Images\ImageNo1.bmp', clFuchsia);

and I get the ffg error

This form of method call only allowed for class methods.

More Help Please

Regards
TAZI
TAZI, I assumed you are aware of the fact that you cannot call it directly this way. Replace TCustomImageList in the above example with your actual image list instance:

var
  IL: TImageList;

begin
  IL := TImageList.Create;
  IL.Width := 32;
  IL.Height := 32;
  IL.FileLoad(rtBitmap, 'c:\Images\ImageNo1.bmp', clFuchsia);
  ........
end;

Ciao, Mike
Avatar of TAZI

ASKER

Hi, Mike

Thanks ... I got that part working

Now, what about loading there 3 images into a TIMAGE component

Regards
TAZI
Don't use a TImage component for this special case. If I understand you correctly then you just want to display a logo out of serveral stored in the image list, right? So instead place a TPaintBox onto the place where the image should appear and in its OnPaint handler you just do an IL.Draw(Canvas, X, Y, Index, True); where Canvas is the canvas of the parent (usually the form or if you have placed the paint box onto a panel this panel's canvas), X and Y the position where to draw (if the paint box is already correctly located and sized then use 0, 0 there) and finally the index of the image you want to draw.

Ciao, Mike
Avatar of TAZI

ASKER

Hi Mike

You're correct I only want to display the company logo which is stored in the Image List.  So by placing a Tpaintbox component ... how do I load the image

what statement to use

Regards
TAZI
TAZI, you already have loaded the images into the image list. Now you want to display it. Since you have the image in the image list you just have to draw it (without loading it into another container like TImage). So place a TPaintBox component on the place where the logo should appear and make it the correct size. Assign an OnPaint handler and write:

procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
  IL.Draw(Canvas, 0, 0, 0, True);
end;
 
where IL is the image list into which you have loaded the company logos.

Ciao, Mike
Avatar of TAZI

ASKER

Hi Mike

I have done as you say but no logos seem to appear

Regards
TAZI
Tazi,

When using TImageList

1.) The bitmaps must all be the same size,

for example: if you have 3 bitmaps, they all must be same size.

example: 25 X 25

Then they must be combined to form a single bitmap.  example: 75 X 25

The TImageList will break them apart by using its Height & Width properties

In this example you would set your height & width to 25 & 25 respectively

To load the image you would call
the

TimageList's FileLoad method

Example:ImageList1.FileLoad(rtBitmap, 'C:\Text.bmp',clBlack);

The image list is capable of holding a large number of same sized images and retrieving them via their index within the range 0 to n - 1. The image list also has methods to facilitate storing, retrieving, and drawing of the stored images.

Hope this helps!

Shane

TAZI, send me your sample project (public@lischke-online.de) and I will have a look at it.

Ciao, Mike
just read all this thread,
nice work,
nice end ;-)


 Igor
Avatar of Wim ten Brink
Why use an ImageList? Why not use a stringlist and add objects to the string? Here, an example:
Create a new application with one form. Add a TListBox and a TImage to it. Then use the following code:

<code>
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Image1: TImage;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
  private
    List: TStringList;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
const
  Path = 'C:\WinNT\';
var
  Search: TSearchRec;
  Error: Integer;
  Bmp: TBitmap;
begin
  List := TStringList.Create;
  Error := FindFirst(Path + '*.bmp', faAnyFile - faDirectory, Search);
  if (Error = 0) then begin
    while (Error = 0) do begin
      Bmp := TBitmap.Create;
      Bmp.LoadFromFile(Path + Search.Name);
      List.AddObject(Search.Name, Bmp);
      Error := FindNext(Search);
    end;
    FindClose(Search);
  end;
  ListBox1.Items.AddStrings(List);
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to List.Count - 1 do begin
    if assigned(List.Objects[I]) and (List.Objects[I] is TBitmap) then begin
      (List.Objects[I] as TBitmap).Free;
    end;
  end;
end;

procedure TForm1.ListBox1Click(Sender: TObject);
var
  I: Integer;
begin
  I := List.IndexOf(ListBox1.Items[ListBox1.ItemIndex]);
  if (I >= 0) then begin
    if assigned(List.Objects[I]) and (List.Objects[I] is TBitmap) then begin
      Image1.Picture.Bitmap.Assign(List.Objects[I] as TBitmap);
    end;
  end;
end;

end.
</code>

Okay, on the OnCreate, a list of all bitmaps in my WinNT folder is created and all images are read into this list. A copy of these names are stored in th TListBox.
On the OnDestroy I delete all images again.
Finally, in the OnClick event of the listbox, the right bitmap is shown in the TImage.
It doesn't matter which method you use to fill the list. But just by using the Objects property of a stringlist you can store your logo's at the start of the application and you won't have to reload them afterwards.
And even better: the logo's can differ in size.
And to create the list, you can use above method and search for the files, or store the filenames in an INI file and let the application read these names. Or whatever method you prefer.

Keep It Simple, S.....!
Avatar of TAZI

ASKER

Hi All,

I make use of the ffg Code to Insert images into the ImageList

ImageList1.Width := 165;
ImageList1.Height := 118;
ImageList1.FileLoad(rtBitmap, 'c:\ImageLists\Logo1.bmp', clFuchsia);

I don't think the image is loading into the Image List and thus it cannot be displayed with the following code.

ImageList1.Draw(Canvas, 0, 0, 0, True);

I tested it by physically adding an image to the imagelist at design time and then I execute    
ImageList1.Draw(Canvas, 0, 0, 0, True);
and the image loads at the top left corner of the form.

I also would appreciate it if anyone can assist me in loading this image into a TIMAGE Component.

Thanks in advance for your help, it is greatly appreciated

Regards
TAZI
TAZI,

I got your eMails regarding this problem here, but I must say you are yourself not quite clear what you really want, do you? Or is it that you have difficulties to describe the problem?

Let me try to pool what we have so far:

1) You have logo files. One separate file for each company.
2) You want to display the logo whenever the client works with data of a certain company.
3) You don't want to make the output to complicated.
4) Switching the company in the program will not happen very often.

So my advice is just to load the image from disk when ever it is needed. Btw. you should use the same name for the disk file and the parameter to load the image. I modified your code to:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ImgList, jpeg, ExtCtrls, Buttons;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Image1: TImage;
    BitBtn1: TBitBtn;
    Panel1: TPanel;
    procedure Button1Click(Sender: TObject);
  private
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}



procedure TForm1.Button1Click(Sender: TObject);

begin
  Image1.Picture.LoadFromFile(ExtractFilePath(Application.ExeName) + 'NPC.bmp');
end;

end.


object Form1: TForm1
  Left = 423
  Top = 390
  Width = 696
  Height = 480
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Image1: TImage
    Left = 176
    Top = 176
    Width = 176
    Height = 120
    AutoSize = True
  end
  object Button1: TButton
    Left = 152
    Top = 96
    Width = 225
    Height = 25
    Caption = 'LOAD INTO IMAGE LIST'
    TabOrder = 0
    OnClick = Button1Click
  end
  object BitBtn1: TBitBtn
    Left = 152
    Top = 128
    Width = 225
    Height = 25
    Caption = 'LOAD INTO IMAGE'
    TabOrder = 1
  end
  object Panel1: TPanel
    Left = 424
    Top = 40
    Width = 225
    Height = 185
    Caption = 'Panel1'
    TabOrder = 2
  end
end

Ciao, Mike
Avatar of TAZI

ASKER

Hi Mike

I may be confusing you and at the same time driving you crazy.  Sorry about that.

OKAY ... LET ME TRY AND BE MORE PRECISE.

I have large volumes of data.  Certain people belonging to certian clients.  Eg).  
200 people for "CLIENTA",  300 people for "CLIENT B", 500 people for "CLIENT C" and so on.  There are approximately 30 Clients and it may increase at a later stage.  so in actual fact there are some 30 logo's that I am working with.

1) You have logo files. One separate file for each company.  - THIS IS CORRECT -

2) You want to display the logo whenever the client works with data of a certain company. - THIS IS CORRECT -

The user can search through the search engine for all surnames - "MAHOMED"  This will bring back data for all Clients in alphabetical order .. Surname and Initial and Account Number.

So I may find 2 Client A's, then 4 Client B's than 1 Client C and so on with the Surname Mahomed AND Initial A.

Now, as I browze through this list the logo must change depending on which Client I am working with.

The system is going to function on a network.  The exe is placed on the server and there are shortcuts to some 60 machines all sharing the application.  

I do not want to make use of a LoadFromFile.  I want to use the Image List.  I want to load these logo's on Startup of the program into the ImageList and then access them from the ImageList as the client changes when I select the next record from the combobox.

The Client can change often so loading from disk is not a good idea.

Hope I have made myself clear.

SORRY FOR THE INCONVENIENCE AND I APPRECIATE ALL YOUR TIME AND EFFORT

Regards
TAZI
Well, this makes it a lot clearer to me :-) Since I saw of what size the logos are I still recommend using TImage instead of TImageList. If I were you I would create an array of TImage (TBitmap would be enough too) and load the images either from a network drive, a database or the application's resouce.

This would then be quite similar to what Workshop_alex wrote. Selecting the appropriate image is then simply indexing into the list. Use TImage.Picture.Graphic := TBitmap(FList.Objects[Index]); to actually show the image.

Ciao, Mike
Avatar of TAZI

ASKER

Hi  Workshop_Alex,

Just wanted to ask you if the same code can be used for JPEG images and not Bitmaps.

Regards
TAZI
No, the code does not work for jpeg. I only works for bmps. Some extra steps are needed to load images which are not bmps. You can download my GraphicEx library from www.lischke-online.de/Graphics.html which contains a sample to load various image formats (gif, rle, dib, psp, psd, tif, jpeg, tga....).

Ciao, Mike
My version could be used for all kinds of objects. Bitmaps, JPeG's, GIF's, sounds, buttons, whatever you want. That's just the strength behind the TStrings Object property. You can add objects to a stringlist. Use the list as an index for the Objects in the list.
One drawback: you must create and free the objects yourself. In my code-sample I've added TBitmap objects. With Delphi 4 and 5 you can also find a unit called JPEG.pas. (and actually a complete JPEG package.) You could use this unit and create TJPEGImage objects in stead of TBitmap objects.
You could even mix TBitmaps and TJPEGImage. Since all you need to do to free an object is calling the Free method, freeing the objects in the list is done like this:
<code>
procedure TForm1.FormDestroy(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to List.Count - 1 do begin
    if assigned(List.Objects[I]) and (List.Objects[I] is TObject) then begin
      (List.Objects[I] as TObject).Free;
    end;
  end;
end;
</code>
I've only used TBitmap before in this code-part to keep the code more clear... But replace every TBitmap by TJPEGImage and you're creating a list of JPeG files. And assigning them to the TImage can be done with the same method as the TBitmap.
Better is to use TPicture then you don't need to care about TBitmap, TJPEGImage, TIcon, whatever.

Ciao, Mike
<code>
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Image1: TImage;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
  private
    List: TStringList;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
const
  Path = 'C:\Documents and Settings\Administrator.WIM\Mijn documenten\Mijn afbeeldingen\';
var
  Search: TSearchRec;
  Error: Integer;
  Bmp: TJPEGImage;
begin
  List := TStringList.Create;
  Error := FindFirst(Path + '*.jpg', faAnyFile - faDirectory, Search);
  if (Error = 0) then begin
    while (Error = 0) do begin
      Bmp := TJPEGImage.Create;
      Bmp.LoadFromFile(Path + Search.Name);
      List.AddObject(Search.Name, Bmp);
      Error := FindNext(Search);
    end;
    FindClose(Search);
  end;
  ListBox1.Items.AddStrings(List);
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to List.Count - 1 do begin
    if assigned(List.Objects[I]) and (List.Objects[I] is TObject) then begin
      (List.Objects[I] as TObject).Free;
    end;
  end;
end;

procedure TForm1.ListBox1Click(Sender: TObject);
var
  I: Integer;
begin
  I := List.IndexOf(ListBox1.Items[ListBox1.ItemIndex]);
  if (I >= 0) then begin
    if assigned(List.Objects[I]) and (List.Objects[I] is TJPEGImage) then begin
      Image1.Picture.Bitmap.Assign(List.Objects[I] as TJPEGImage);
    end;
  end;
end;

end.
</code>
Above code for D5 proved to me that it also works with JPeG's... But remember: storing large images in memory requires lots of (virtual) memory. And if Virtual memory is used, then it will slow down a bit. But not as slow as loading files from the network...

In this code I've used descendants of TGraphic. This means that icons, bitmaps, metafiles and, if you have the JPEG unit, JPeG files.
You have used a descentant of TGraphic, correct, it is TJPEGImage, but this will not allow to load icons, metafiles etc. Again, try TPicture. Btw: you can shorten the lines:

if assigned(List.Objects[I]) and (List.Objects[I] is TJPEGImage) then
  Image1.Picture.Bitmap.Assign(List.Objects[I] as TJPEGImage);


to

if Assigned(List.Objects[I]) then
  Image1.Picture.Graphic := TGraphic(List.Objects[I]);

This will free you from checking all the possible image types...


Ciao, Mike
Lischke, I prefer the TGraphic as base component. But since I know which type the image is going to be, it's easier to just use TBitmap to TJPEGImage. Especially if you're going to create a list of objects, then you might prefer the ones that take the least size. TPicture has more overhead than just TBitmap or TJPEGImage...
Avatar of TAZI

ASKER

Hi Workshop_Alex

I have adjusted and modified your code.
Instead of using a TListbox, I declared an array of type TJPEGImage and I make use of this array to load the appropriate logo.

Is it more Resource intensive, will the program use more memory if I allocate these images to an Array.  

You see....Some Clients make use of the same logo so I have to load the appropriate logo as the change is made.  On Change of a the Combobox ....

Please advise accordingly

Thanks for your efforts.  
Both you and Lischke have provided me with tons of information and I am very greatful.

Regards
TAZI

And about using begin-end or not... I prefer to use begin-end to make the code a bit more readable...
I could also have used this:

<code>
if assigned(List.Objects[I]) and (List.Objects[I] is TGraphic) then begin
  Image1.Picture.Bitmap.Assign(List.Objects[I] as TGraphic);
end;    
</code>

And I prefer to check the type of the object in the stringlist. This allows me to store more in the list than just graphics...

And using TPicture in stead of TGraphic descendants? It's a matter of taste and preferences. The TGraphic descendants are smaller and slightly faster than TPicture.
If you know that all images will be of one graphic type, then just use the right TGraphic descendant. Else use the TPicture object.
ASKER CERTIFIED SOLUTION
Avatar of Wim ten Brink
Wim ten Brink
Flag of Netherlands image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
TAZI, using an array takes even less resources but requires you to maintain the length of the array. You might consider using a TList instead.
<code>
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, JPeG, Psock, NMTime;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Image1: TImage;
    NMTime1: TNMTime;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
  private
    //List: TStringList;
    List: TList;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
const
  Path = 'C:\Documents and Settings\Administrator.WIM\Mijn documenten\Mijn afbeeldingen\';
var
  Search: TSearchRec;
  Error: Integer;
  Bmp: TJPEGImage;
begin
  //List := TStringList.Create;
  List := TList.Create;
  Error := FindFirst(Path + '*.jpg', faAnyFile - faDirectory, Search);
  if (Error = 0) then begin
    while (Error = 0) do begin
      Bmp := TJPEGImage.Create;
      Bmp.LoadFromFile(Path + Search.Name);
      //List.AddObject(Search.Name, Bmp);
      Index := List.Add(Bmp);
      // Do something with Index...
      ListBox1.Items.Add(Search.Name);
      Error := FindNext(Search);
    end;
    FindClose(Search);
  end;
  //ListBox1.Items.AddStrings(List);
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to List.Count - 1 do begin
    //if assigned(List.Objects[I]) and (List.Objects[I] is TObject) then begin
      //(List.Objects[I] as TObject).Free;
    //end;
    if assigned(List[I]) then TObject(List[I]).Free;
  end;
end;

{procedure TForm1.ListBox1Click(Sender: TObject);
var
  I: Integer;
begin
  I := List.IndexOf(ListBox1.Items[ListBox1.ItemIndex]);
  if (I >= 0) then begin
    if assigned(List.Objects[I]) and (List.Objects[I] is TJPEGImage) then begin
      Image1.Picture.Bitmap.Assign(List.Objects[I] as TJPEGImage);
    end;
  end;
end;
  }

procedure TForm1.ListBox1Click(Sender: TObject);
begin
  if (ListBox1.ItemIndex < List.Count) then Image1.Picture.Bitmap.Assign(TGraphic(List[ListBox1.ItemIndex]));
end;

end.
</code>
This sample demonstrates uning a TList. I use the TListbox just as a simple method to select an image. You might prefer to remember the index-number that the List.Add method return to you. Link that number to the associated compagny.

And about memory-size... This depends on the size of the images. I myself hardly notice any loss of speed, but I have 256 MB RDRAM which is quite a lot and very fast. (And a swapfile of around 700 MB...)
On a small 233 MHz PC with 64 MB RAM, you could end up with the images in your virtual memory, and thus some loss of speed. Increase the swapfile if required...
Still, you will only notice this if you have more megabytes of images than around a third of RAM. (So with 64 MB, less than 20 MB worth of images shouldn't be a big problem...)
@#$% Why does my answer appear twice??? :-(
Avatar of TAZI

ASKER

Hi  Workshop_Alex,

This is what I have done.  
//****************************//

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, Db, DBTables, jpeg;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Button1: TButton;
    Edit1: TEdit;
    qrySystem1: TQuery;
    Database1: TDatabase;
    Button2: TButton;
    ComboBox1: TComboBox;
    Edit2: TEdit;
    Edit3: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure UpdateArray;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Reconnect;
    procedure ComboBox1Change(Sender: TObject);
   
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  BmpArray : array[1..20] of TJPegImage;
  NewArray : array[1..40] of String;
  ListArray : array[1..40,1..3] of String;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
const
  Path = 'C:\Logos\bmp\';
var
  Search: TSearchRec;
  Error: Integer;
  Bmp: TJPEGImage;
  i : SmallInt;
begin
 Reconnect; //Proc to Connect to DBASE
  Error := FindFirst(Path + '*.jpg', faAnyFile - faDirectory, Search);
  if (Error = 0) then
     begin
      i := 1;
      while (Error = 0) do
        begin
         Bmp := TJPEGImage.Create;
         Bmp.LoadFromFile(Path + Search.Name);
         BmpArray[i] := Bmp;
         NewArray[i] := Search.Name;
         Error := FindNext(Search);
         inc(i)
       end;
    FindClose(Search);
    end;
end;

//Button 2 gets the data into the Combobox - (depending on the search)
procedure TForm1.Button2Click(Sender: TObject);
var
 i,S : smallInt;
begin
qrySystem1.Close;
qrySystem1.SQL.Clear;
qrySystem1.SQL.Add('Select ClientName,ClientCode, LogoName from Client');
qrySystem1.Open;
qrySystem1.First;
ComboBox1.Clear;
for i := 1 to qrySystem1.RecordCount do
     begin
     ComboBox1.Items.Add(qrySystem1.Fieldbyname('ClientName').asString + ' ' + qrySystem1.Fieldbyname('LogoName').asString);
     ListArray[i][1] := qrySystem1.Fieldbyname('LogoName').asString;
     qrySystem1.Next;
     end;
UpdateArray;    
end;

procedure TForm1.UpdateArray;
var
 i, s : integer;
begin
 for i := 1 to 40 do
     begin
        for s := 1 to 40 do
            begin
             if LowerCase(ListArray[s][1]) = NewArray[i] then
                ListArray[s][2] := IntToStr(i)
            end;
     end;      
end;

procedure TForm1.ComboBox1Change(Sender: TObject);
var
 Value : Integer;
 Value1 : String;
begin
try
edit2.Text := ListArray[ComboBox1.ItemIndex+1][1];
Value := StrToInt(ListArray[ComboBox1.ItemIndex+1][2]);
Image1.Picture.Assign(BmpArray[Value] as TJPegImage);
except
Showmessage('No Logo');
end;

end;

regards
TAZI

Avatar of TAZI

ASKER

Hi,

In your example above, you've got a statement as ffg.

Index := List.Add(Bmp);

Index is declared as what type ??


Regards
TAZI
Avatar of TAZI

ASKER

Hi,

I have modified my code and I declare the arrays as

var
 BMPArray : array of TJpegImages;
 

Laster, I declare the length of this array depending on the No of Images on my drive

SetLength(BMPArray,Value)

This dynamically creates the size of the array.  

Regards
TAZI
Index is defined as an Integer in the TForm1.FormCreate procedure. List.Add returns an Integer, so Index is an Integer.
You get a warning in my code, since I don't use Index after the assignment. For example, I don't link Index with some other data. You want to show a certain logo based on some condition, so you will need to know it's indexnumber in the list.
The advantage of using a TList in stead of an array is that the TList can hold any number of images, while the array has a fixed size. That's why I prefer a TList.

And in your case, where each logo has a name, I would prefer using a stringlist since this makes finding the right logo a bit easier...

But let's create another sample based on your code and let's use the database you've supplied... (For this, I've created a dummy table with some random data...) This sample will handle multiple image file formats!...
I also assume that the logonames in the database are the filenames of the logo files excluding the file extension! If not, then what relation does exists?

<DFM>
object Form1: TForm1
  Left = 317
  Top = 107
  Width = 869
  Height = 640
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object Splitter1: TSplitter
    Left = 0
    Top = 121
    Width = 861
    Height = 3
    Cursor = crVSplit
    Align = alTop
  end
  object ScrollBox1: TScrollBox
    Left = 0
    Top = 124
    Width = 861
    Height = 489
    Align = alClient
    BorderStyle = bsNone
    TabOrder = 0
    object Image1: TImage
      Left = 0
      Top = 0
      Width = 100
      Height = 100
      AutoSize = True
    end
  end
  object Panel1: TPanel
    Left = 0
    Top = 0
    Width = 861
    Height = 121
    Align = alTop
    BevelInner = bvLowered
    BorderWidth = 4
    Caption = ' '
    TabOrder = 1
    object DBGrid1: TDBGrid
      Left = 6
      Top = 6
      Width = 849
      Height = 109
      Align = alClient
      DataSource = DataSource1
      TabOrder = 0
      TitleFont.Charset = DEFAULT_CHARSET
      TitleFont.Color = clWindowText
      TitleFont.Height = -11
      TitleFont.Name = 'MS Sans Serif'
      TitleFont.Style = []
      Columns = <
        item
          Expanded = False
          FieldName = 'ClientName'
          Visible = True
        end
        item
          Expanded = False
          FieldName = 'ClientCode'
          Visible = True
        end>
    end
  end
  object QrySystem1: TQuery
    AfterScroll = QrySystem1AfterScroll
    DatabaseName = 'Logos'
    SQL.Strings = (
      'SELECT '
      '  ClientName,'
      '  ClientCode,'
      '  LogoName'
      'FROM'
      '  Client'
      'ORDER BY'
      '  ClientName')
    Left = 264
    Top = 40
    object QrySystem1ClientName: TStringField
      FieldName = 'ClientName'
      Origin = 'LOGOS."Client.DB".ClientName'
    end
    object QrySystem1ClientCode: TStringField
      FieldName = 'ClientCode'
      Origin = 'LOGOS."Client.DB".ClientCode'
      Size = 4
    end
    object QrySystem1LogoName: TStringField
      FieldName = 'LogoName'
      Origin = 'LOGOS."Client.DB".LogoName'
      Size = 255
    end
  end
  object Database1: TDatabase
    AliasName = 'BCDEMOS'
    Connected = True
    DatabaseName = 'Logos'
    LoginPrompt = False
    Params.Strings = (
      'PATH=C:\Logos\Database'
      'DEFAULT DRIVER=PARADOX'
      'ENABLE BCD=FALSE')
    SessionName = 'Default'
    Left = 360
    Top = 40
  end
  object DataSource1: TDataSource
    DataSet = QrySystem1
    Left = 152
    Top = 40
  end
end
</DFM>

<code>
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, JPeG, Db, DBTables, Grids, DBGrids;

type
  TForm1 = class(TForm)
    QrySystem1: TQuery;
    Database1: TDatabase;
    ScrollBox1: TScrollBox;
    Image1: TImage;
    Panel1: TPanel;
    DBGrid1: TDBGrid;
    DataSource1: TDataSource;
    Splitter1: TSplitter;
    QrySystem1ClientName: TStringField;
    QrySystem1ClientCode: TStringField;
    QrySystem1LogoName: TStringField;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure QrySystem1AfterScroll(DataSet: TDataSet);
  private
    List: TStringList;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
const
  Path = 'C:\Logos\bmp\';
var
  Search: TSearchRec;
  Error: Integer;
  Bmp: TGraphic;
begin
  List := TStringList.Create;
  List.Sorted := True;
  List.Duplicates := dupIgnore;
  Error := FindFirst(Path + '*.*', faAnyFile - faDirectory, Search);
  if (Error = 0) then begin
    while (Error = 0) do begin
      if (UpperCase(ExtractFileExt(Search.Name)) = '.JPG') then begin
        Bmp := TJPEGImage.Create;
      end
      else if (UpperCase(ExtractFileExt(Search.Name)) = '.BMP') then begin
        Bmp := TBitmap.Create;
      end
      else if (UpperCase(ExtractFileExt(Search.Name)) = '.ICO') then begin
        Bmp := TIcon.Create;
      end
      else if (UpperCase(ExtractFileExt(Search.Name)) = '.WMF') then begin
        Bmp := TMetafile.Create;
      end
      else begin
        Bmp := nil;
      end;
      if (Bmp <> nil) then begin
        Bmp.LoadFromFile(Path + Search.Name);
        List.AddObject(ChangeFileExt(Search.Name, ''), Bmp);
      end;
      Error := FindNext(Search);
    end;
    FindClose(Search);
  end;
  QrySystem1.Active := True;
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  I: Integer;
begin
  QrySystem1.Active := False;
  for I := 0 to List.Count - 1 do begin
    if assigned(List.Objects[I]) and (List.Objects[I] is TObject) then begin
      (List.Objects[I] as TObject).Free;
    end;
  end;
end;

procedure TForm1.QrySystem1AfterScroll(DataSet: TDataSet);
var
  I: Integer;
begin
  I := List.IndexOf(QrySystem1.FieldByName('LogoName').AsString);
  if (I >= 0) and assigned(List.Objects[I]) and (List.Objects[I] is TGraphic) then begin
    Image1.Picture.Graphic := (List.Objects[I] as TGraphic);
  end
  else begin
    Image1.Picture.Graphic := Application.Icon;
  end;
end;

end.
</code>

Some explanation...
In TForm1.FormCreate I create the imagelist as a stringlist. Since we need to remember the names of the logos, the stringlist is the most compact option. I sort the stringlist since the 'IndexOf' method of the stringlist is much faster if it's a sorted list. I'm going to ignore duplicate names. (This could happen if a logo exists as a JPeG and as a bitmap, both with the same name but different extensions.)
And since logos are added to the list by name only, I remove the file extension. By the way, the IndexOf routine is case insensitive! No need to worry about that!
At the end of this call, the query is opened. You don't want to do this before the list is created...
Another option would be to assign the AfterScroll event to the query AFTER the list is created. "QrySystem1.AfterScroll := QrySystem1AfterScroll;" This way, you can keep the query open in design-time. But then don't set the AfterScroll event in designtime.

In TForm1.FormDestroy I remove the complete list of images. No true rocket-science. Just free all objects in the list, then free the list. And beware, make sure the qquery is closed.

TForm1.QrySystem1AfterScroll is an event that's connected to the AfterScroll event of the Query. This will always show the right logo for the current record. No need to do some complex stuff... If you go to a new record, the image will adjust itself... No image available? Then the application icon will be shown. A bit nicer than an exception. You could also show some other bitmap, just to show that no logo is available. Or assign nil to Image1.Picture.Graphic. Then it will be empty...

On my PC, this code is lightning fast. But that's because it's a extremely fast machine... ;-)
First you insert a form a filelistbox and image. and you should write this codes :

{event of the filelistbox click}
procedure TForm1.FileListBox1Change(Sender: TObject);
begin
     image1.Picture.Bitmap.LoadFromFile(filelistbox1.filename);
end;
undefinity, didn't you read the question correctly? The problem is that loading the image when it's required is too slow. The user is browsing through multiple records, and thus multiple logo's. The logo's aren't part of the database, but loose images in a folder on a network-drive. Every time they're loaded it takes some time. This slows down the browsing to an unacceptable level.

So, the solution so far is to load the images at startup and then access them from memory. This costs a lot of internal memory (RAM & swapfile) but it's quicker to browse through than reloading the different images every time.

Speed is important in this case...
Additionally, it is very unfriendly to propose an answer instead of a comment when we are already discussing the problem quite a while.

Ciao, Mike
Avatar of TAZI

ASKER

Hi,
Lischke, Workshop_Alex  & undefinity

To - undefinity : Workshop_Alex explains the problem above.

Guys, thanks for your efforts.  Give me a couple of days to get the entire code tested and functional

Many thanks for all your efforts ... it is greatly appreciated



Avatar of TAZI

ASKER

Hi

Sorry about the long delay in responding to this question.  I had actually forgotten about it until receiving an autodelete warning this morning.

Sorry about that ! !

I haven't yet implemented it into my program, but I have tried and tested it on a seperate application.

Thanks for all your efforts.  It is greatly appreciated

Regards
TAZI

Doesn't matter... I've forgotten about it too... ;-)

Thanx for the points!