Recently, while working on a project, I encountered the situation of saving a series of images in an image list (TImageList) to a specified file or binary stream so that they can be dynamically restored when needed. So I searched for the properties and methods related to the TImageList class in Delphi's help. Unfortunately, Delphi did not provide the SaveToFile and SaveToStream methods in TImageList. Therefore, in view of the current limitations of TImageList, other methods must be adopted to extend the functions of TImageList to meet the needs of actual project needs. |
Solution |
Method one: |
Use API functions ImageList_Write and ImageList_Read. Both need to specify a parameter of type IStream. The function of the former is to save the image list of the specified handle into a binary stream of type IStream; the latter is to read the originally saved image list from the binary stream of type IStream. , and returns a handle to this image list. IStream is an OLE object, and its declaration in Delphi is TStreamAdapter = class(TInterfacedObject, IStream), which means that TStreamAdapter is an object inherited from TInterfacedObject that manipulates the IStream interface. Through the TStreamAdapter object, the manipulation of the ISTream interface object by the Delphi internal TStream object can be realized. |
Method two: |
Inherit a subclass TImageListEx from TImageList and implement custom SaveToFileEx and SaveToStreamEx methods. By default, the images saved in TImageList are composed of ordinary images and their mask images, so the GetImages(Index: Integer; Image, Mask: provided by the PRotected part of its base class TCustomImageList must be called) TBitmap) method to obtain the bitmap with the specified index number in the image list and its mask bitmap, and then save them to a custom file or binary stream respectively. In addition, it is necessary to provide the LoadFromFileEx and LoadFromStreamEx methods to obtain the bitmap from the custom file or binary stream. Restore image collection from stream. |
Implementation steps |
The custom TImageListEx control encapsulates the above two methods in the Public part. |
The source code of the TImageListEx class is as follows: |
unit ImageListEx; |
interface |
uses Windows, SysUtils, Classes, Graphics, Controls, Commctrl, ImgList, Consts; |
type |
TImageListEx = class(TImageList) |
public |
procedure LoadFromFile(const FileName: string);//Implement API method to save |
procedure LoadFromStream(Stream: TStream); |
procedure SaveToFile(const FileName: string); |
procedure SaveToStream(Stream: TStream); |
procedure LoadFromFileEx(const FileName: string);//Achieve custom saving method |
procedure LoadFromStreamEx(Stream: TStream); |
procedure SaveToFileEx(const FileName: string); |
procedure SaveToStreamEx(Stream: TStream); |
end; |
procedure Register; |
implementation |
procedure Register; |
begin |
RegisterComponents('ImageListEx', [TImageListEx]); |
end; |
{TImageListEx} |
procedure TImageListEx.LoadFromFile(const FileName: string); |
var |
Stream: TStream; |
begin |
Stream := TFileStream.Create(FileName, fmOpenRead); |
try |
LoadFromStream(Stream); |
finally |
Stream.Free; |
end; |
end; |
procedure TImageListEx.LoadFromFileEx(const FileName: string); |
var |
Stream: TStream; |
begin |
Stream := TFileStream.Create(FileName, fmOpenRead); |
try |
LoadFromStreamEx(Stream); |
finally |
Stream.Free; |
end; |
end; |
procedure TImageListEx.LoadFromStream(Stream: TStream); |
var |
SA: TStreamAdapter; |
begin |
SA := TStreamAdapter.Create(Stream); |
try |
Handle := ImageList_Read(SA); //Point the handle of the current image list to the handle obtained from the binary stream |
if Handle = 0 then |
raise EReadError.CreateRes(@SImageReadFail); |
finally |
SA.Free; |
end; |
end; |
procedure TImageListEx.LoadFromStreamEx(Stream: TStream); |
var |
Width, Height: Integer; |
Bitmap, Mask: TBitmap; |
BinStream: TMemoryStream; |
procedure LoadImageFromStream(Image: TBitmap); |
var |
Count: DWord; |
begin |
Image.Assign(nil); |
Stream.ReadBuffer(Count, SizeOf(Count));//First read the size of the bitmap |
BinStream.Clear; |
BinStream.CopyFrom(Stream, Count);//Then read the bitmap |
BinStream.Position := 0; //Stream pointer reset |
Image.LoadFromStream(BinStream); |
end; |
begin |
Stream.ReadBuffer(Height, SizeOf(Height)); |
Stream.ReadBuffer(Width, SizeOf(Width)); |
Self.Height := Height; |
Self.Width := Width;//Restore the original height and width of the image list |
Bitmap := TBitmap.Create; |
Mask := TBitmap.Create; |
BinStream := TMemoryStream.Create; |
try |
while Stream.Position <> Stream.Size do |
begin |
LoadImageFromStream(Bitmap);//Read bitmap from binary stream |
LoadImageFromStream(Mask);//Read the mask bitmap from the binary stream |
Add(Bitmap, Mask);//Add the bitmap and its mask bitmap to the image list |
end; |
finally |
Bitmap.Free; |
Mask.Free; |
BinStream.Free; |
end; |
end; |
procedure TImageListEx.SaveToFile(const FileName: string); |
var |
Stream: TStream; |
begin |
Stream := TFileStream.Create(FileName, fmCreate); |
try |
SaveToStream(Stream); |
finally |
Stream.Free; |
end; |
end; |
procedure TImageListEx.SaveToFileEx(const FileName: string); |
var |
Stream: TStream; |
begin |
Stream := TFileStream.Create(FileName, fmCreate); |
try |
SaveToStreamEx(Stream); |
finally |
Stream.Free; |
end; |
end; |
procedure TImageListEx.SaveToStream(Stream: TStream); |
var |
SA: TStreamAdapter; |
begin |
SA := TStreamAdapter.Create(Stream); |
try |
if not ImageList_Write(Handle, SA) then//Save the current image list to the binary stream |
raise EWriteError.CreateRes(@SImageWriteFail); |
finally |
SA.Free; |
end; |
end; |
procedure TImageListEx.SaveToStreamEx(Stream: TStream); |
var |
I: Integer; |
Width, Height: Integer; |
Bitmap, Mask: TBitmap; |
BinStream: TMemoryStream; |
procedure SetImage(Image: TBitmap; IsMask: Boolean); |
begin |
Image.Assign(nil);//Clear the last saved image to avoid image overlap |
with Image do |
begin |
if IsMask then MonoChrome := True;//The mask bitmap must use monochrome |
Height := Self.Height; |
Width := Self.Width; |
end; |
end; |
procedure SaveImageToStream(Image: TBitmap); |
var |
Count: DWORD; |
begin |
BinStream.Clear; |
Image.SaveToStream(BinStream); |
Count := BinStream.Size; |
Stream.WriteBuffer(Count, SizeOf(Count));//First save the size of the bitmap |
Stream.CopyFrom(BinStream, 0);//Then save the bitmap |
end; |
begin |
Height := Self.Height; |
Width := Self.Width; |
Stream.WriteBuffer(Height, SizeOf(Height));//Save the height of the original image list |
Stream.WriteBuffer(Width, SizeOf(Width));//Save the width of the original image list |
Bitmap := TBitmap.Create; |
Mask := TBitmap.Create; |
BinStream := TMemoryStream.Create; |
try |
for I := 0 to Count - 1 do//Save the image in the image list |
begin |
SetImage(Bitmap, False); |
SetImage(Mask, True); |
GetImages(I, Bitmap, Mask);//Get the bitmap with the specified index number and its mask bitmap |
SaveImageToStream(Bitmap);//Save bitmap to binary stream |
SaveImageToStream(Mask);//Save the mask bitmap to the binary stream |
end; |
finally |
Bitmap.Free; |
Mask.Free; |
BinStream.Free; |
end; |
end; |
end. |
The following demonstrates how to use it in Delphi: |
First create a new project in Delphi, and then place an ImageListEx control, a TreeView control and four Button controls on Form1. Associate the Images property of the TreeView control with ImageListEx, add any number of images to ImageListEx, and add a corresponding number of items to TreeView. The ImageIndex properties of the items correspond to the index numbers of the images in ImageListEx. Now the corresponding icon can be displayed before each item in TreeView. |
Finally, write in the OnClick event of Button1: |
ImageListEx1.SaveToFile('C:CJ.dat'); |
ImageListEx1.SaveToFileEx('C:CJEx.dat'); |
In the OnClick event of Button2 write: ImageListEx1.Clear; |
Write in the OnClick event of Button3: ImageListEx1.LoadFromFile('C:CJ.dat'); |
Write in the OnClick event of Button4: ImageListEx1.LoadFromFileEx('C:CJEx.dat'); |
Run the program, first click Button1, then click Button2, and finally click Button3 or Button4. You can see that the program can save the images in the image list to the specified file, and can correctly restore and display them from the specified file. . |
Conclusion |
The content introduced in this article has been used to solve the situation I encountered in actual projects. I also hope that programmers who also encounter this problem can find the answer from it. The above code has passed debugging and running in Delphi5.0 and Windows2000 Server. |