TIcon only loads one image from the icon file

490 views Asked by At

I have an icon file that contains a 24x24, 32x32, 48x48, 64x64, and 256x256 icon. However, when I load it like this:

Application->Icon->LoadFromFile("filename.ico");

it appears to only load the one image from the file, despite the fact that my icon contains multiple resolutions of the icon. The result is that any Forms use the single icon re-scaled for both the taskbar icon, and the corner icon, which looks bad.

However, if I set filename.ico into a compiled resource, or if I set the icon in Project Properties > Application > Icon, then my Forms use the 24x24 icon for the corner icon, and the 48x48 icon for the taskbar.

My question is: how can I have my Forms use the icons from filename.ico where the filename is not known until runtime; but still use the 24x24 icon for the corner and use the 48x48 icon for the taskbar?

NB. I prefer not to hardcode those sizes 24x24, and 48x48, because other versions of Windows (or if the person uses the windows font scaling option) may then call for a different size icon.

1

There are 1 answers

8
Remy Lebeau On BEST ANSWER

When you call TIcon.LoadFrom...(), it stores a copy of the raw icon data into an internal memory block and then exits. That block is not processed until the next time that TIcon.HandleNeeded() is called, such as when the TIcon.Handle property is used.

If the icon data represents an icon of type RC3_STOCKICON (not usually encountered), the IDI_APPLICATION icon from LoadIcon() is used. Otherwise, if the icon data represents an icon of type RC3_ICON (the usual case), the data is parsed and the image that most closely matches the current TIcon.Width and TIcon.Height property values (or the SM_CXICON and SM_CYICON metrics via GetSystemMetrics() if the TIcon dimensions have not been assigned yet) are passed to CreateIcon().

From that point on, the HICON returned by LoadIcon() or CreateIcon() is the image used for the remainder of the TIcon's lifetime, or at least until the HICON is freed/released via TIcon.ReleaseHandle(), TIcon.Assign(), TIcon.LoadFrom...(), TIcon.SetHandle(), etc.

The memory block itself is only freed when the TIcon is freed, TIcon.Assign() is called, or a new image source is loaded. So it should be possible, for instance, to call TIcon.ReleaseHandle() to release the current HICON (you will then have to free it manually via DestroyIcon()), then resize the TIcon's dimensions, and then call TIcon.HandleNeeded() to re-parse the memory block to load the next closest matching image.

Update: TIcon cannot have multiple images of different resolutions loaded at the same time. A Form's corner icon and its Taskbar icon (and remember, when Application->MainFormOnTaskbar is false, the Taskbar button is controlled by the hidden Application window, not the MainForm window, unless you override that behavior manually) are actually separate icons at the OS layer, assigned via the WM_SETICON message using different input parameters (wParam=ICON_SMALL and wParam=ICON_BIG, respectively). However, the VCL only ever uses WM_SETICON to set a window's BIG icon, never its SMALL icon. So a Form's corner icon is just a scaled down version of its Taskbar icon (when MainFormOnTaskbar is true) or the Application's Taskbar button (when MainFormOnTaskbar is false). When the VCL issues WM_SETICON for a Form, it used the Form's own Icon if assigned, otherwise it uses the Application's Icon if assigned, otherwise it uses LoadIcon() to load the default IDI_APPLICATION icon.

So, if you really want different icons of different resolutions for the Form's corner icon and Taskbar icon, you will have to use separate TIcon objects to load the desired resolution images, as described above, and then issue your own WM_SETICON messages accordingly.