Using TImageList Image Lists as Centralized Collections of Images

From RAD Studio
Jump to: navigation, search

Go Up to FireMonkey Application Design

FireMonkey TImageList image lists provide full featured tools for using centralized collections of small images by GUI elements (controls, menus, and others) in FireMonkey applications. The TImageList image lists in FireMonkey serve the same purpose as the Vcl.Controls.TImageList image lists in VCL, but provide more advanced features. The TImageList image lists in FireMonkey are used to efficiently manage large sets of icons or bitmaps and are convenient to use in multi-platform applications.

FireMonkey is a multi-platform framework (Windows, iOS, OS X, and Android), and therefore FireMonkey applications work on devices having different screen densities and resolutions. Using FireMonkey multi-resolution bitmaps means that your images and icons in FireMonkey applications appear correctly in different densities, resolutions, and platforms (Windows, iOS, OS X, and Android). A multi-resolution bitmap is a collection of bitmap items providing the same image but having different scales.

Why We Recommend To Use Image Lists

In a GUI application, many controls (such as buttons, menu items, items in different lists) can contain small pictures (icons, pictograms, or widgets). For example, TMenuItem has the Bitmap property. Using this property, you can load an image from a file. This is the most obvious and as it might seem - the most convenient way of loading images into your applications. In each new control you can load and show a specific image.

The other way is - store all images used in an application in a centralized collection and for each control store only a reference to an image in this collection. For example, you can use an image from a style. For example, in the Object Inspector for a button select the StyleLookup property, click the arrow to expand the drop-down list, and select searchtoolbutton. The 'magnifying glass' icon appears for the button. We keep this possibility, however it is convenient for a fixed collection of the most standard icons. However real applications often require you to load and store personalized icons. In such situation the TImageList component can be rather useful.

The schematic algorithm of the TImageList usage can look like the following:

  1. On a form (better on a specially created data module) place a TImageList image list (non-visible component).
  2. Load your pictures into this TImageList object.
  3. In the Object Inspector, select the Images property of your control, click the arrow to expand the drop-down list, and select the reference to the TImageList object.
  4. In the ImageIndex property select the number of the image - in the image list object - you want to display.

For each new control, you repeat steps 3 and 4. If the TImageList object does not contain a desired picture, repeat step 2. At first, this algorithm can look too complicated. To explain the advantages completing all these steps and using TImageList, instead of simply selecting a desired image from some folder, we discuss the following TImageList usage examples.

The first example: A collection of images of an experienced programmer can exceed 9000-10000. Searching for the particular desired image in such collections can be a difficult task. On the other hand, even rather complicated applications can use up to 100-200 images. Many controls in an application can use the same (or similar) image and it is desirable to support some consistency in using these images. For example, about 1 year ago, developing the 1st version of your program, your former colleague assigned to the Save button an icon with the 'blue 3 inch diskette'. Now developing the new version of your program you need to create a new form containing the similar Save button and assign to it the same icon. However finding this icon may be difficult. By using TImageList object you would not have this problem as you can simply select this icon in the drop-down list expanded for your TImageList object.

The second example: Suppose that your boss has decided that it is time to use an icon with the modern energy-saving lamp instead of an icon with the old style incandescent lamp. How many controls (and menu items) are using this icon? How much time do you need to change all these icons? If all your icons are stored in one centralized collection of images then such mass substitutions are trivial. You only need to change this icon in your centralized collection of images. Following this, you can also create a data module containing a collection of images (image list), and add this module to several applications that need to use images from this collection.

A short third example: Dynamical creation of many controls that show icons at run time will have lots of duplicated graphical data. If we use TImageList then each control keeps only an index of an icon to draw in the TImageList collection.

So we can say that usage of the common TImageList collection helps to solve similar tasks as 'reusing of code' but relative to graphical data.

How To Use Image Lists

You can view the video demonstrating typical steps required to create a new Image List in your FireMonkey project and the video demonstrating how to use the Image List Editor to edit Image Lists.

The more detailed algorithm of the TImageList usage can look like the following:

  1. Create a data module and place it before the main form in the list of modules in a project.
  2. Add the unit name of the data module into the uses section of the main module.
  3. Using the Tool Palette, place a TImageList object on the data module.
  4. Double-click the TImageList object.
    The empty Image List Editor (FireMonkey) opens.
  5. Click the Add button.
  6. In the Add Images dialog box, browse to a desired graphic file and click Open. For example, browse to the
    C:\Program Files (x86)\Embarcadero\Studio\18.0\Images\GlyFX\Icons\XP\PNG\16x16
    folder and select a graphical file in it.
  7. Previews of all opened images appear in the List of Images pane. You can change the order of images drawing them with the mouse.
    Note: The TImageList component itself does not have general Width and Height properties. Each image is scaled to best fit to the shape of a control that displays the image. Resize the List of Images pane and you see that images are resized accordingly.
    Note: Please notice that though you can add an image of any size, however TImageList is intended for keeping of many small images that can be shown in different GUI elements (buttons, menu items, lists, and others). By default, images are stored without any modifications. They are scaled on demand. Therefore, if you load some large images or photos, they may not appear as you expect in a 24*24 button, and an application on a mobile device can simply hang because of a lack of system resources.
  8. Now click OK to close the Image List editor.
  9. On the Tool Palette find the TGlyph graphical control. The TGlyph functionality is similar to TImage but does not contain any graphical data.
  10. In the Object Inspector locate the Images property of the Glyph control. The list in Images shows all TImageList collections known in the project.
  11. Select the desired TImageList collection and locate the ImageIndex property. ImageIndex is the index (number) of an image in the list of images. The list in ImageIndex shows all images existing in the selected TImageList collection. You can select a desired image or type an integer index (ImageIndex) of the image you want to display. If an image for the specified ImageIndex does not exist, then no image is selected.

TGlyph inherits TControl and can be used in styles to construct controls. TGlyph is included into many controls supporting usage of image lists.

TGlyph controls have the AutoHide property. AutoHide defines that the TGlyph control itself manages the value of the Visible property:

  • During a run time of the application, when AutoHide is True, then Visible is True if the control contains some graphics (BitmapExists is True). Otherwise, Visible is False. If the code of the application tries to set a new value to Visible, then this value is ignored. The Visible value has meaning for the Align property when Align is not None. Then if Visible is True then an empty visible control draws the empty space occupied by the control. If Visible is False then an empty invisible control does not draw the empty space.
  • In the Design mode, when AutoHide is True, then Visible is always True and inaccessible.

New properties Images and ImageIndex appear in many graphical controls (buttons, menus, lists, and others) and actions. Notice that for components providing lists of elements, the Object Inspector shows only the Images property. While for elements of these lists the Object Inspector shows only the ImageIndex property. Notice that the Images property of all elements of the list control has the same value as Images in the list control. That is, all menu items always use the same Images collection, that is set in the Images property of the menu.

Some controls having the Images and ImageIndex properties can have the Bitmap property, which also can reference an image. If the Images and ImageIndex properties point to some existing image, then this image is drawn. If ImageIndex does not point an existing image, then the control draws the image referenced by Bitmap. Notice that if the image referenced from Bitmap is not used, it is just ignored, it is not deleted automatically in order to avoid loss of data. For example, if you occasionally set ImageIndex to reference a non-existing image. You can delete this image reference manually, editing the Bitmap property.

Top Level Structure of TImageList

In general, a FireMonkey application does not know the required size of an image to draw. This depends upon a particular control, control style, and the scale of the scene (and this scale can be not only 1, 1.5, 2), which in turn depends upon the particular computer or mobile device. Therefore, images are often scaled in order to fit into desired boundaries. Therefore, we have introduced the TMultiResBitmap class. Multi-resolution bitmaps store several variants of the same image optimized for different sizes. This minimizes the quality loss when scaling images. As the next step, we have introduced the TImageList image lists. TImageList objects store collections of TMultiResBitmap images. Each one of these Multi-resolution bitmaps is a set of several variants of the same image with different sizes.

We need to review the TImageList structure of the component to understand meanings and functionality of elements in the Image List Editor.

Each TImageList component contains two collections of images:

  • The Source collection contains a source graphical data. This data is used to produce images in the Destination collection.
  • The Destination collection contains the data required to form the images which to draw as the images of the TImageList collection.

In the provided ImageList Sample in the Structure view you can see the ImageList1 structure like in the following figure:

Structure View in Image List Editor
Figure: Structure View

In this figure you see that each image in the Source collection has:

  • The unique case-insensitive name (in each TImageList collection). For example, image 0 has the name Stop, image 1 has the name TImageList, and image 2 has the name folder.
  • The MultiResBitmap property. Each element in a MultiResBitmap collection, in turn, represents a variant of the same image with a particular size (scale). For example, the MultiResBitmap collection of the folder image contains 5 bitmaps for 1.0, 2.0, 1.5, 3.0, and 16.0 scales.

In this figure you also see that each element in the Destination collection can include several layers, which are included into the Layers collection. For example, the image 3 consists from 2 layers: the layer 0 containing the folder multi-resolution bitmap and the layer 1 containing the delete multi-resolution bitmap. Each layer contains the name of the source multi-resolution bitmap and coordinates of a section of this bitmap (always in the scale 1). This section of the bitmap is drawn generating the image. Generating an image, FireMonkey, at first, draws the multi-resolution bitmap from the 0 layer, the multi-resolution bitmap from the 1 layer is drawn over the obtained image, the multi-resolution bitmap from the 2 layer is drawn over the obtained image, and so on. We will demonstrate this in more details in the Selected Image Pane section. If a multi-resolution bitmap in a layer has bitmaps with several scales, then the bitmap with the most proper scale is used. The scale is selected according to the required size of the ready-made image and the scene scale.

Knowing the TImageList structure, one can form parts of the whole TImageList instance at run time. The ImageList Sample contains the example of such code.

To edit a TImageList collection in IDE, you can use the Structure view and the Object Inspector. However we provide the specialized Image List editor, which is much more convenient for managing a content of TImageList collections.

Differences with the TImageList Implementation in VCL

In VCL a Vcl.Controls.TImageList image list is a collection of images having the same fixed size. This leads to problems when your application attempts to draw images with different scales. To draw images with different scale in VCL applications, we need to create several TImageList objects. Each TImageList object contains a special collections of images for a particular size. In all such TImageList objects the same images should have the same numbers (Indexes). A VCL application can draw only images with these scales. In VCL applications, it is difficult to change some images during run time.

How To Use The Image List Editor

You can view the video demonstrating how to use the Image List Editor to create and edit Image Lists. This video demonstrates some advanced and not trivial features of the Image List Editor.

We describe properties of the Image List editor using the ImageList Sample, which provides the example of the image list usage. Load the ImageListDemo.dproj project and in the Project Manager double-click the UnitDataModule.dfm module. In the Form Designer, double-click the ImageList1 icon. The Image List editor opens with the ImageList1 component. The Image List editor contains three panes: List of images, Sources of Images, and Selected Image.

Image List Editor
Figure: Image List Editor

List of Images Pane

The List of images pane shows the horizontal list of images. Each image is a preview of a corresponding image in the Destination collection. These images are shown in the ImageIndex property of each control using this collection of images.

In the List of images pane you can drag images - using the mouse - to change their order in the ImageList1 collection of images.

The Add button adds an image from a file. Clicking Add opens the Add Images dialog box where you can browse for an image file. If the size of an image to add is multiple of the specified Width and Height, a prompt appears asking whether the Image List editor should divide the image into several images. This is useful for toolbar bitmaps, which are usually composed of several small images in a sequence and stored as one larger bitmap. The added image appears highlighted in the preview list of the List of Images pane and is shown in the Selected Image pane. Notice, that the image is added into the Source collection and is shown in the Sources of Images pane.

Using the Export button, you can save all images from the Destination collection in one graphical file. The image stored in this file consists from fragments containing all images in the List of Images pane. All fragments have the same size defined by the specified Width and Height.

The Delete button deletes the selected image from the Destination collection. (Notice, that Delete does not delete the correspondent image from the Source collection.)

The New.png (New Image) toolbar button (at the top left corner of the pane) adds a previously loaded image (having one layer). If the focus is in the Sources of Images pane then the selected image is added. Otherwise, an image in a selected layer in the Selected Image pane is added. The added image appears highlighted in the List of images pane.

Sources of Images Pane

The Sources of Images pane (see the Image List Editor figure above) displays all images in the Source collection of the current TImageList image list. These source images are used to construct images in the Destination collection (shown in the List of Images pane).

As we already have mentioned, clicking Delete in the List of Images pane deletes the selected image from the Destination collection. Notice that Delete does not delete the correspondent image from the Source collection shown in the Sources of Images pane. To delete an image from the Source collection use the Action delete.png Delete Source toolbar button.

The Arrow up.jpg and Arrow down.jpg arrow toolbar buttons move Up and Down source images changing their order. You can use this for convenience.

Click the New.png (New Source) toolbar button to add a new image into the Source collection. Clicking this button shows the prompt asking for the New source item name and then activates the MultiResBitmap Editor.

Using MultiResBitmap Editor

You can view the video demonstrating typical usage of the MultiResBitmap Editor.

Using the MultiResBitmap Editor you can add several images optimized for different scales:

MultiResBitmap Editor ImageList
Figure: MultiResBitmap Editor

During the run time an application selects an image having the most appropriate scale.

As we have already pointed, the TImageList component is intended for storing lists of large number of small images. Images from TImageList component are intended to use as icons in controls, menus, lists, and so on.

If you have only files containing large images, then you can customize their sizes during addition of these images in the MultiResBitmap Editor. Before adding a large image from a file, click the SizeKind control and select Custom size in the opened list. The Width and Height edit boxes appear, if they were not shown before. Width and Height define the Width and Height properties for the image having the Scale = 1. For example, if you load an image for the Scale = 1.5, then the specified Width and Height are multiplied on 1.5. To load an image for a particular Scale, click the Fill All From File.png button in this scale entry. To load an image for all Scales, click the Fill All From File.png Fill All From File toolbar button in the toolbar of the MultiResBitmap Editor.

Notice that usage of the Custom size option, does not change the image in a source file. Usage of Custom size only changes the size of the image stored in the .fmx or .dfm file.

Notice that the design-time information used by the MultiResBitmap Editor includes the Transparent color, SizeKind, Width, Height, and the names of files with source images. These properties are not published and they are used only at design time. After you complete editing of the multi-resolution bitmap, these properties can be removed from the .fmx or .dfm file using the Clear design time info and close.png (Finish) toolbar button. Pressing Clear design time info and close.png in the MultiResBitmap Editor clears design-time information only in the multi-resolution bitmap being edited in the MultiResBitmap Editor. Pressing Clear design time info and close.png in the Image List Editor clears design-time information for all images in the current image list.

Selected Image Pane

The Selected Image pane shows (see the Image List Editor figure above) the detailed information about the image selected in the List of Images pane. As we know, List of images shows previews of images in the Destination collection.

As we have already discussed in the TImageList Structure section (see the Structure View figure above), each image in the Destination collection can contain several Layers. Each Layer shows a Name of one image from the Source collection and corresponding multi-resolution bitmap. Also each layer contains coordinates of a section of this multi-resolution bitmap (always in the scale 1 independently to the scale of the bitmap to be used). Only this section of the multi-resolution bitmap is drawn in the ready-made image.

Usually each image contains only one layer. However, in some cases you might want to use several layers to form a compound image. For example, it can be convenient to use a separate layer containing a 'strikethrough' or a 'magnifying glass' picture. This layer can then be placed over a layer containing another picture. For example, the image in the layer can be drawn over a layer containing a 'folder' or an 'envelope' picture. Then the two layers can form a combined picture, such as a 'strikethrough folder' or 'magnifying glass over envelope' image.

Generating an image, FireMonkey, at first, draws the multi-resolution bitmap from the 0 layer, the multi-resolution bitmap from the 1 layer is drawn over the obtained image, the multi-resolution bitmap from the 2 layer is drawn over the obtained combined image, and so on. If an image with the name specified in a layer does not exist or the specified sector is outside of the image, then the layer does not draw anything and no exception is raised. If a multi-resolution bitmap in a layer has bitmaps with several scales, then the bitmap with the most proper scale is used. The scale is selected according to the required size of the ready-made image and the scene scale.

Let us demonstrate these functionality using images from the ImageList Sample. In the List of Images pane (see How To Use The Image List Editor above) select the image having ImageIndex = 3. This is the 'strikethrough folder'. The Selected Image pane shows the following 2 layers:

Selected Image pane for strikethrough folder
Figure: Selected Image pane for strikethrough folder

Compare this with the representation of the Destination image having ImageIndex = 3 in the Structure view:

Structure view for strikethrough folder
Figure: Structure view for the 'strikethrough folder'

At this figure we see that the folder image is in the 0 layer and delete image is in the 1 layer. In the List of Images pane you see that these 2 layers produce the following 'strikethrough folder' destination image:

Strikethrough folder

You see that the delete image in the 1 layer is drawn over the folder image from the 0 layer.

The Left, Top, Width, and Height edit boxes in a layer pane specify the coordinates of a section of the multi-resolution bitmap stored in the layer. These coordinates you can see in the Selected Image pane for the strikethrough folder figure as (0,0,16,16) for the delete image. Let us set Left equal to 4 and Top equal to 4 for the delete image. Click Apply. Now in the Structure view you can see the (4,4,20,20) coordinates of the section for the delete image. In the List of Images pane the 'strikethrough folder' destination image is changed. The 'strikethrough' image is shifted to the top-left:

Strikethrough folder is moved

To demonstrate how coordinates of the section of a multi-resolution bitmap can be used, double-click the 'strikethrough' image. The MultiResBitmap Editor opens:

Section to show in the MultiResBitmap Editor
Figure: Section to show in the MultiResBitmap Editor

This figure demonstrates, which section of the 'strikethrough' image is drawn in the layer. Click in the selection rectangle that specifies the 'section to show'. Try to resize the selection rectangle. Try to move the selection rectangle and see corresponding changes of the Left, Top, Width, and Height properties of the selection rectangle (in the layer in the Selected Image) and changes of the preview image in the List of Images pane.

Functionality of toolbar buttons in the Selected Image pane is rather obvious. New.png--adds a new layer into the Selected Image pane. Action delete.png--deletes the selected layer from the Selected Image pane. Arrow up.jpg and Arrow down.jpg arrows move Up and Down the selected layer changing the order of layers. This can be useful because the image in a layer with the higher number is drawn over images drawn by the previous layers.

Basic Classes of TImageList

The information about basic features and functionality of image list classes can be useful for that who want to develop custom components using image lists.

System.ImageList Unit

Beginning from the XE8 version, RAD Studio provides the new System.ImageList RTL unit. System.ImageList contains the common for both FireMonkey and VCL code implementing the most basic device-independent features of image lists. System.ImageList contains the code supporting interaction between images in an image list and using them components (like controls, menu items, and so on).

The System.ImageList unit defines the TBaseImageList and TImageLink classes.

TBaseImageList Class

The TBaseImageList class provides methods and properties handling interactions between an image list and all GUI components using images from this image list. Beginning from the RAD Studio XE8 version, both FMX.ImgList.TCustomImageList and Vcl.ImgList.TCustomImageList classes inherit this TBaseImageList class.

TImageLink Class

A TBaseImageList image list object uses TImageLink objects to send notifications to components using the image list object about changes have happened in the image list object.

When changes of the ImageIndex or Images properties happen, then the TImageLink object calls the virtual Change method. Remember that any change in the TBaseImageList leads to change of Images.

In order to your component to be able to provide an adequate reaction on changes in an image list, your component should contain an instance of TImageLink descendant. This TImageLink descendant should have correct Images and ImageIndex properties and should override the Change method or the OnChange event handler should be defined.

TBaseImageList contains the Links array property that contains TImageLink instances. When you set the TImageLink.Images property to a component, then this image list is added into this Links array. Descendants of TBaseImageList after any changes of Images or ImageIndex, sequentially take each image link object from the Links array and calls Change for all (or only for some) these image links (components).

TImageLink.IgnoreIndex Property

If the IgnoreIndex property is True, then Change is not executed when any image or an order of images has been changed in a TBaseImageList image list. Usually notifications arrive when an image with some index changes. Setting IgnoreIndex to True, suppress such notifications. This property can be used when an application needs to display the whole image list, for example, in an editor window.

TImageLink.IgnoreImages Property

If the IgnoreImages property is True, then Change is not executed when the Images value has been changed. This property is used for compatibility with VCL classes inheriting classes from System.ImageList.

FMX.ImgList Unit

The FMX.ImgList unit defines the basic FireMonkey classes implementing FireMonkey image lists.

FMX.ImgList.TCustomImageList Class

The FMX.ImgList.TCustomImageList component implements the core functionality of FireMonkey image lists. Traditionally FMX.ImgList.TImageList differs from the FMX.ImgList.TCustomImageList class only by redeclaration of some properties as Published. In the FMX.ImgList.TImageList case these are the following Published properties: Source, Destination, OnChanged, and OnChange.

TCustomImageList.Dormant Property

The Dormant property is True if all multi-resolution bitmaps in the Source collection have their Dormant properties True.

TCustomImageList.CacheSize Property

The CacheSize property indicates the maximum number of images that can be stored in the internal cache of the current TCustomImageList object.

Drawing of each image consists from several rather resource expensive steps. In order to avoid scaling and drawing of several layers for showing the same image in each of several controls, the generated image is placed into the array in the internal cache and then the ready image is taken from this cache array. New generated image is appended into the end of this cache array shifting all existing images to the beginning of the array. If the number of images to store in the cache array exceeds the specified CacheSize number of images, then the oldest image is deleted from the cache. It is obvious that the bigger CacheSize number, increases the processing speed, but also increases the used memory. The default maximum number of images to be stored in the cache array is 8; the minimal number is 1.

TCustomImageList.ClearCache Method

ClearCache removes the image specified by the Index number from the internal cache array. Cache can store several images with the same number but having different sizes. If Index is -1 (the default), then all images are removed from the cache (the cache is cleared). Usually ClearCache is automatically called when the TImageList object is changed.

TCustomImageList.BitmapExists Method

The BitmapExists method returns True, if an image with the specified number exists and contains some actual bitmap. That is, if the specified image contains at least one layer containing a Name of existing source image having rectangle to draw that has non-zero intersection with the rectangle of the image.

Non-existing image is shown as an empty dashed rectangle in the Image List Editor. See the image number 9 in the List of Images pane in the Image List Editor figure above. If the rectangle to draw contains graphical data, but all these graphics are transparent (see Transparent color), then BitmapExists method returns True, but this layer does not draw any graphics. BitmapExists is used in the TGlyph class to set the Visible property when AutoHide is True.

TCustomImageList.Bitmap Method

The Bitmap method returns the TBitmap object stored in the cache corresponding to the image specified by Index and having the specified Size size.

If the image specified by Index and having Size does not exist in the cache, then Bitmap tries to create an appropriate image and add it into the cache. If an Index image does not exist in the Source collection or Bitmap cannot create a bitmap having the specified Size or cannot add it into the cache, then Bitmap returns nil.

Since images in the cache are created and destroyed by internal TCustomImageList features, therefore you should not explicitly destroy the returned TBitmap object, stored in the cache, or try to store a reference to this object.

If you simply need to draw some image from the TCustomImageList list, use the Draw method. If you really need to store the obtained TBitmap object for the future usage, you should create another TBitmap object and use Assign to create a copy of the obtained TBitmap object.

TCustomImageList.BitmapItemByName Method

The BitmapItemByName method returns True if it finds an image having the specified Name in the Source collection.

If BitmapItemByName returns True, then Item returns the found bitmap for a scale most appropriate of 1, and Size returns the size of the returned bitmap. If BitmapItemByName returns False, then Item and Size are not changed.

TCustomImageList.UpdateImmediately Method

The UpdateImmediately method initiates immediate redrawing of all components using this image list.

When an image is changed, then all components using this image should receive notifications that they need to update their image. By default, to avoid multiple redrawing during reforming of an image list, such notifications are sent shortly after the changes. If there are several changes during this period of time, only one notification is sent and controls are redrawn only once.

TCustomImageList.Source Property

The Source property keeps the reference to the TSourceCollection collection containing TCustomSourceItem source multi-resolution bitmaps. Source collections are created by the CreateSource method. You can override this method to create collections of your own type. The important method of TSourceCollection source collections is IndexOf returning the index of the image in the source collections corresponding to the specified Name. If an image is not found, then nil is returned.

TCustomSourceItem Class

The TCustomSourceItem class defines elements of TSourceCollection source collections. Each TCustomSourceItem object has a unique case-insensitive Name of the image item in the Source collection and a MultiResBitmap property containing a multi-resolution bitmap keeping the set of the same shape images for different scales.

TCustomImageList.Destination Property

The Destination property keeps the reference to the TDestinationCollection collection containing TCustomDestinationItem items. Destination items contain information required to prepare images to be drawn. Destination collections are created by CreateDestination.

TCustomDestinationItem Class

TCustomDestinationItem objects are elements of TDestinationCollection collections. Each TCustomDestinationItem element contains the Layers collection of TLayer elements. Layers is created with CreateLayers. The LayersCount function returns the number of not-empty layers. Notice the difference with the TLayers.Count property, which keeps the total number of layers in the layer collection.

TLayers Class

The TLayers collection contains TLayer layers. During generation of ready-made images, bitmaps with the most appropriate scales from all visible layers are sequentially drawn beginning from the 0 layer and till the last Count layer.

TLayer Class

The TLayer layer contains information about a source image, which is used during drawing of a ready-made image.

TLayer.MultiResBitmap Property

The MultiResBitmap property keeps a multi-resolution bitmap from the Source collection.

TLayer.Name Property

The Name property contains the case-insensitive name of an image from the Source collection. The BitmapItemByName function returns the multi-resolution bitmap having the Name in the Source collection.

TLayer.SourceRect Property

The SourceRect property keeps coordinates of a section of this multi-resolution bitmap (always in the scale 1 independently to the scale of the bitmap to be used). Only this section of the multi-resolution bitmap is drawn in the ready-made image.

FMX.TImageList Code Sample

You can view the video demonstrating features programmed in the 'FMX.ImageList Sample'.

Here we discuss typical code fragments - from the FMX.ImageList Sample providing the example of the image list usage - used to program standard operations with image lists.

Open the ImageListDemo.dproj project, as it is described in the FMX.ImageList Sample. Activate the Form Designer with the UnitMain module and open the Bitmap and Image tab. This tab contains several controls using images from the images list. Here we discuss the code implementing functionality of the Add New Source and Update Text buttons and of the Right and Left arrow buttons drawing the next or previous image - from the ImageList1 image list - over the None caption of the None tab. Code handling the Right and Left arrow buttons demonstrates how to draw an image from an image list on the surface of your custom control.

To open the code handling a control in the Code Editor, simply double-click this control in the Form Designer.

'Add New Source' Button's Event Handler

Double-click the Add New Source button in the Form Designer. The Code Editor shows the ActnAddSourceExecute implementation of the method. ActnAddSourceExecute is the OnClick event handler of the Add New Source button. ActnAddSourceExecute calls AddSourceToItem(9). The core of ActnAddSourceExecute is the internal

DrawPicture(Canvas: TCanvas; R: TRectF; Scale: Single)

method. This method simply draws some image in the specified Canvas. Other code of AddSourceToItem(9) prepares parameters to call DrawPicture. The AddSourceToItem(9) method assigns the specified parameter value 9 to the ImageIndex of the new image added into ImageList1. That is, ImageIndex=9. At first, AddSourceToItem checks whether the image - in the Destination collection - having ImageIndex=9 has, at least, one Layer. If not, create the 0 Layer. If the 0 Layer contains an image, then this image is stored in the SourceName variable:

SourceName := Layer.Name;

BitmapItemByName is called

MainDataModule.ImageList1.BitmapItemByName(SourceName, Item, Size)

to retrieve an element corresponding to the specified SourceName name from the Source collection. If succeed, then we consider that the image is already added and do nothing. Otherwise, add a new image into the Source collection. If the Layer exists and it contains the SourceName name, then the SourceName name is assigned to the added image. Otherwise, the default name is used.

Now cyclically create several images for different scales. Add a new element into TMultiResBitmap. Gets its default scale:

S := Item.Scale;

Using S and SourceRect of the Layer, calculate the size of the Source image:

Size.cx := Round(Layer.SourceRect.Width * S);

Then fill this image using the transparent color

Item.Bitmap.Clear(TAlphaColorRec.Null);

and draw a picture:

DrawPicture(Item.Bitmap.Canvas, R, S);
Note! Open the UnitDataModule unit in the Form Designer and double-click the ImageList1 image. In the List of Images pane you see that the image having ImageIndex=9 is empty.

So with the current implementation, clicking Add New Source adds the new image having ImageIndex=9 into the ImageList1 image list.

How to watch the functionality of the 'Add New Source' button:

  1. Read how to use the FMX.ImageList Sample.
  2. In the Demo of images application, select the Bitmap and Image tab.
  3. Repeatedly click the Right arrow button.
    You see images from the ImageList1 image list sequentially drawn over the None caption of the None tab.
  4. When you click the Right arrow button the ninth time, the text in the Label1 label becomes:
Images: 'ImageList1': Counts 11
ImageIndex: 9; Width 16; Height 16
and the empty image is drawn over the None caption (no images is drawn).
  1. In the Bitmap and Image tab, click Add New Source. The image having ImageIndex=9 is added into ImageList1.
  2. You see that the added image appears over the None caption.

'Update Text' Button's Event Handler

Double-click the Update Text button in the Form Designer. The Code Editor shows the ActnUpdateTextExecute implementation of the method. ActnUpdateTextExecute is the OnClick event handler of the Update Text button.

The text to draw is obtained from the FNumber integer field by converting into the string representation FNumber.ToString. Each click Update Text increases Inc(FNumber) the value of this field.

ActnUpdateTextExecute calls

DrawTextOnLayer(8,FNumber.ToString)

Here the first parameter 8 specifies the index of the image in the Destination collection of the ImageList1 image list that should be modified.

Note! Open the UnitDataModule unit in the Form Designer and double-click the ImageList1 image. In the List of Images pane click the image having ImageIndex=8. In the Selected Image pane you see that this image contains two Layers. The 0 layer is empty, and The 1 layer contains the TMultiResBitmap identified by the Name mail and showing an envelope picture:
Image 8
Figure: Image with ImageIndex=8

The second parameter FNumber.ToString specifies the text to draw.

DrawTextOnLayer is rather similar to DrawPicture. At first, DrawTextOnLayer checks whether the image - in the Destination collection - having ImageIndex=8 has, at least, one Layer. Then checks whether the most top Layer contains an image, then this image is stored in the SourceName variable:

SourceName := Layer.Name;

BitmapItemByName is called

MainDataModule.ImageList1.BitmapItemByName(SourceName, Item, Size)

to retrieve an element corresponding to the specified SourceName name from the Source collection.

If BitmapItemByName does not retrieve an element corresponding to the SourceName name, then we add a new Source element:

NewSource := MainDataModule.ImageList1.Source.Add;

Assign the SourceName name to it:

NewSource.Name := SourceName;

Tries to get Item:

Item := NewSource.MultiResBitmap.ItemByScale(1, False, True);

If Item does not exist, then add a new item to the MultiResBitmap collection:

Item := NewSource.MultiResBitmap.Add;

and set its size:

Item.Bitmap.SetSize(Size.cx, Size.cy);

According to

Size.cx := Round(Layer.SourceRect.Rect.Width);
Size.cy := Round(Layer.SourceRect.Rect.Height);

After this or if BitmapItemByName succeed, DrawTextOnLayer draws the Text (equal to FNumber.ToString) over the obtained image:

Item.Bitmap.Canvas.FillText(
TRectF.Create(1,0,Size.cx - 1,Size.cy div 2),
Text, False, 1, [], TTextAlign.Center, TTextAlign.Center);

So with the current implementation, clicking Update Text draws a number over the image having ImageIndex=8 into the ImageList1 image list. Each new click increases on 1 the drawn number.

How to watch the 'Update Text' button's functionality:

  1. In the Demo of images application, select the Bitmap and Image tab.
  2. Repeatedly click the Right arrow button.
    You see images from the ImageList1 image list sequentially drawn over the None caption of the None tab and in the Glyph1 control.
  3. When you click the Right arrow button the eighth time, the envelope image having ImageIndex=8 is drawn.
  4. In the following picture you see that the string containing 4 is drawn over the image in the None caption and in the Glyph1 control.
Envelope with 4 drawn over it
Figure: Envelope image with 4 drawn over it

Using Image Lists in Your Controls

Here we discuss how to draw images from an image list on the surface of your own controls. Similar tricks you can use creating your own controls using images from image lists.

This sample draws images from the ImageList1 image list on the surface of the fourth tab control over the None caption. To see this, repeatedly click the Right or Left arrow buttons. You see images from the ImageList1 image list sequentially drawn over the None caption.

How the images drawing works

In the TMainForm.Create constructor of the form, we create theFImageLink field of the TImageLink type. Then

FImageLink.Images := MainDataModule.ImageList1;

assigns the ImageList1 image list to this field. Then assigns the OnImagesChange event handler for the OnChange event. OnImagesChange is executed when the program needs to redraw an image on a control using the ImageList1 image list. OnImagesChange can look like this:

... TabItem4.Repaint; ...

Drawing of an image is made from the TabItem4Paint event handler of the OnPaint event handler of the TabItem4 tab control:

procedure TMainForm.TabItem4Paint(Sender: TObject; Canvas: TCanvas;
                                  const ARect: TRectF);
var
  R: TRectF;
begin
  R := TRectF.Create(ARect.Right - 22, 4, ARect.Right - 6, 20);
  MainDataModule.ImageList1.Draw(TabItem4.Canvas, R,
                                 Glyph1.ImageIndex);
end;

Here R is the rectangle - in the TabItem4 tab control - in which an image is drawn. Draw draws the Glyph1.ImageIndex image in R on the specified TabItem4.Canvas canvas.

You can also create TImageLink's descendant in which to override the Change method instead of creation of the OnChange event handler.

Note! Do not forget to free the created FImageLink field in the destructor. Please use the DisposeOf method.

If your control supports using of actions, then this control, probably, supports the IGlyph interface. Then it is preferable to create a TGlyphImageLink object, which will call ImagesChanged method, then you need not to assign the OnChange event handler.

Properties ImageIndex and Images of your control must have the correct values. Usually are used properties of the following style:

property ImageIndex: TImageIndex read GetImageIndex write SetImageIndex
                                  stored ImageIndexStored;

function TMyControl.GetImageIndex: TImageIndex;
begin
  Result := FImageLink.ImageIndex;
end;

procedure TGlyph.SetImageIndex(const Value: TImageIndex);
begin
  FImageLink.ImageIndex := Value;
end;

See Also