I'm using Delphi 7. Testing this on Windows 7.
Drop a TMainMenu
and a TImageList
on a form. Add some menus to the TMainMenu
and some images to the TImageList
. When the TImageList
is NOT assigned to the TMainMenu
's Images
property, the application looks like this:
But once the TImageList
is assigned to the TMainMenu
's Images
property, the application looks like this:
Further more, if the Images
property is changed (assigned or unassigned) at run-time, only the submenu items change, the root menu items (File, Edit, Tools, Settings, and Help in my example application) never change -- they always stay themed if the Images
property was not assigned at design time, or they always stay non-themed if the Images
property was assigned at design time.
And finally, all of this is happening whether or not XPManifest
is used.
So, my questions are:
1. Why is the theming disappearing when icons are used? I would guess that icons are drawn internally using something like Owner Drawing, which breaks the theming, but that's just a guess.
2. Why is the main menu themed, even when XPManifest
is not used?
3. And most importantly, how can I have a themed menu with icons?
I hope this answer does not come across as too much of a rant, but this is an area where Embarcadero have a long history of mis-steps. I have submitted a large number of QC reports in this area so perhaps I am a little bitter. That said, the most recent releases of Delphi seem to implement menus in an acceptable way. I wasn't able to trip up XE6 menus when I took them for a spin recently. But it has taken them a long time to catch up.
Your Delphi pre-dates Vista. And Vista was the great water-shed for Windows menus. Although the theme API was introduced in XP, it had no real impact on menus. That changed in Vista. But Delphi 7 was before all that and was coded with XP in mind.
In XP, drawing menus with glyphs was not easy. The
MENUITEMINFO
struct has a bitmap field,hbmpItem
. But in XP it is of limited use. A system drawn XP menu will not draw a clean alpha bitmap on a menu. Such menus require owner drawing. And so in the Delphi 7 code, if your menu has any glyphs then it will be owner drawn. And owner drawn using the XP APIs.That explains the difference between the two screenshots in your question. The themed screenshot is a menu with no glyphs. The Delphi 7 menus code asks the system to draw the menu. And it draws themed menus. With or without the comctl32 manifest. That's the standard menu on Vista and later.
And when you add glyphs, the VCL code which only knows about XP, decides to owner draw the menus. And does so using XP functionality. After all, it cannot be expected to use the Vista themed menu APIs. The code pre-dates those.
Modern versions of Delphi have gradually added support for Vista themed menus. The original implementations in the
Menus
unit were, in all honesty, pitiful. The Embarcadero designers elected to draw the menus using the theme API. An API that is, to all intents and purposes, undocumented. Probably the best source of information on that API is the Delphi source code (!), and the Wine source code. It is pointless looking to MSDN for help here. So, I do have sympathy for Embarcadero here, for the poor engineer who had to work this out. And take 5 releases of the software to flush out the bugs.However, Embarcadero do also deserve a smattering of opprobrium. For it is possible to get the system to draw themed menus on Vista and up that contain glyphs. The secret is the
hbmpItem
field. Although it was of limited use on XP, it comes into its own on Vista. You won't find documentation of this anywhere. The only good source of documentation, a blog article published by an MS staffer on the Shell Revealed blog, has for some reason been removed from the internet (but captured by archive.org). But the details are simple enough. Put a PARGB32 bitmap intohbmpItem
, and let the system draw the menu. And then it's all good.Of course the Delphi
Menus
unit doesn't make this easy to achieve. In fact it is not possible with that unit in vanilla form. In order to make this happen you need to modify the code in that unit. You need to change the code which elects to custom draw the menu. And instead create PARGB32 bitmaps to be placed inhbmpItem
, and ask the system to paint them. This takes a degree of skill, not least because you need to manage the lifetime of the PARGB32 bitmaps to avoid resource leaks.So, that's how you achieve a themed menu with icons in Delphi 7. I actually implemented this for Delphi 6 at the time, but the code is the same. And even in my current codebase which is in XE3, I still use the same approach. Why? Because I trust the system to draw the menus more than I trust the VCL code.
I cannot share the code easily because it involves modifications to the
Menus
unit in a handful of places. And theMenus
code is not mine to share. But the essentials are:hbmpItem
and let the system do the rest.A good place to look for ideas on this is the Tortoise SVN source code. That uses this undocumented technique to paint its themed glyph heavy menus.
Some links:
I dug out some of my code from the Delphi 6 time frame. I'm sure it is still applicable.
Right at the top of the interface section of my modified version of the
Menus
unit I declared this interface:This is implemented by an image list class and is used to provide PARGB32 bitmaps. Then in
TMenuItem.AppendTo
, if the version is Vista or up, and if the VCL code is planning to owner draw, I setIsOwnerDraw
toFalse
. And then useIImageListConvertIconToPARGB32Bitmap
to get aPARGB32
bitmap.The implementation of the image list looks like this: