TMenuItem *mi = new TMenuItem(this);
mi->OnDrawItem = &miThemesDrawItem;
results in error:
[bcc64 Error] _TForm1.cpp(280): assigning to 'Vcl::Menus::TMenuDrawItemEvent' (aka 'void ((__closure *))(System::TObject *, Vcl::Graphics::TCanvas *, const System::Types::TRect &, bool) __attribute__((fastcall))') from incompatible type 'void (__closure *)(System::TObject *, Vcl::Graphics::TCanvas *, System::Types::TRect &, bool)'
Function declaration is okay, in fact I can assign it at design-time to menu items that are available at design-time.
void __fastcall miThemesDrawItem(TObject *Sender, TCanvas *ACanvas, TRect &ARect, bool Selected);
I found a workaround. I assigned this handler at design-time on a separator item N1
, and then at run-time I just do mi->OnDrawItem = N1->OnDrawItem
, and it works fine since OnDrawItem
does not get called for a menu separator, but I don't like this!
What am I missing, how to assign this handler?
Function declaration is okay
Actually, it is not. Pay very close attention to what the error message is telling you:
assigning to 'Vcl::Menus::TMenuDrawItemEvent' ... from incompatible type ...'
So, why is miThemesDrawItem()
incompatible? Because it is has a different type than what TMenuDrawItemEvent
is expecting!
Let's look at TMenuDrawItemEvent
first. The error message says its type is:
void ((__closure *))(System::TObject *, Vcl::Graphics::TCanvas *, const System::Types::TRect &, bool) __attribute__((fastcall))
We can ignore the __attribute__
for now, thus the type is:
void (__closure *)(System::TObject *, Vcl::Graphics::TCanvas *, const System::Types::TRect &, bool)
That matches up with the actual declaration of TMenuDrawItemEvent
in Vcl.Menus.hpp
:
typedef void __fastcall (__closure *TMenuDrawItemEvent)(System::TObject* Sender, Vcl::Graphics::TCanvas* ACanvas, const System::Types::TRect &ARect, bool Selected);
Now, let's look at your miThemesDrawItem()
. The error message says its type is:
void (__closure *)(System::TObject *, Vcl::Graphics::TCanvas *, System::Types::TRect &, bool)
That matches up with your actual declaration:
void __fastcall miThemesDrawItem(TObject *Sender, TCanvas *ACanvas, TRect &ARect, bool Selected);
Notice any differences between the declared type of TMenuDrawItemEvent
vs the declared type of miThemesDrawItem()
? The TRect
parameter in miThemesDrawItem()
is not declared as const
!
You need to declare miThemesDrawItem()
to have the exact same signature as how TMenuDrawItemEvent
is declared, eg:
void __fastcall miThemesDrawItem(TObject* Sender, TCanvas* ACanvas, const TRect &ARect, bool Selected);
in fact I can assign it at design-time to menu items that are available at design-time
That by itself doesn't guarantee that miThemesDrawItem()
is 100% compatible with the TMenuItem::OnDrawItem
event. Although the IDE does validate an event handler's type when the user tries to assign it to an event at design-time, the IDE is a little lenient on this matter. The IDE is relying on RTTI when performing the validation, and the RTTI is based on Delphi information, not C++ information. Const-correctness works a little differently in Delphi than in C++. So, sometimes the IDE does allow less-than-compatible handlers through, especially for the sake of backwards compatibility (ie, the TRect
parameter of TMenuDrawItemEvent
wasn't always const
).
After validation, the IDE merely stores the name of the event handler into the DFM, but the actual function is not assigned to the event until run-time (since its memory address is not known at design-time). When the DFM is streamed in at run-time, the function's type will not be validated again when the function is assigned to the event, it is blindly taken as-is. So, even at run-time, there may be subtle issues if the handler is not 100% compatible with the event.
The compiler, on the other hand, requires the type of a function to be 100% compatible with a function pointer that it is being assigned to. There is no room for any differences. So, in this case, a difference in const
qualifiers on a parameter is a big no-no. That is why the compiler won't let you assign miThemesDrawItem()
to the TMenuItem::OnDrawItem
event in your code, until you fix the signature of miThemesDrawItem()
to match the type of TMenuDrawItemEvent
exactly.