在 C++Builder 中将 OnDrawItem 动态分配给 TMenuItem
Assigning OnDrawItem to TMenuItem dynamically in C++Builder
TMenuItem *mi = new TMenuItem(this);
mi->OnDrawItem = &miThemesDrawItem;
导致错误:
[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)'
函数声明没问题,事实上我可以在设计时将其分配给设计时可用的菜单项。
void __fastcall miThemesDrawItem(TObject *Sender, TCanvas *ACanvas, TRect &ARect, bool Selected);
我找到了解决方法。我在设计时在分隔项 N1
上分配了这个处理程序,然后在 运行 时我只是做 mi->OnDrawItem = N1->OnDrawItem
,它工作正常,因为 OnDrawItem
没有得到需要菜单分隔符,但我不喜欢这个!
我错过了什么,如何分配这个处理程序?
Function declaration is okay
其实不然。 非常接近注意错误消息告诉您的内容:
assigning to 'Vcl::Menus::TMenuDrawItemEvent' ... from incompatible type ...'
那么,为什么 miThemesDrawItem()
不兼容?因为它的类型与 TMenuDrawItemEvent
预期的类型不同!
我们先来看TMenuDrawItemEvent
。错误信息说它的类型是:
void ((__closure *))(System::TObject *, Vcl::Graphics::TCanvas *, const System::Types::TRect &, bool) __attribute__((fastcall))
我们暂时可以忽略__attribute__
,因此类型是:
void (__closure *)(System::TObject *, Vcl::Graphics::TCanvas *, const System::Types::TRect &, bool)
这与 Vcl.Menus.hpp
中 TMenuDrawItemEvent
的 实际 声明相匹配:
typedef void __fastcall (__closure *TMenuDrawItemEvent)(System::TObject* Sender, Vcl::Graphics::TCanvas* ACanvas, const System::Types::TRect &ARect, bool Selected);
现在,让我们看看您的 miThemesDrawItem()
。错误信息说它的类型是:
void (__closure *)(System::TObject *, Vcl::Graphics::TCanvas *, System::Types::TRect &, bool)
这与您的实际声明相匹配:
void __fastcall miThemesDrawItem(TObject *Sender, TCanvas *ACanvas, TRect &ARect, bool Selected);
请注意 TMenuDrawItemEvent
的声明类型与 miThemesDrawItem()
的声明类型之间的任何差异? miThemesDrawItem()
中的TRect
参数是not声明为const
!
您需要声明 miThemesDrawItem()
以具有 与 TMenuDrawItemEvent
声明方式完全相同的签名,例如:
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
这本身并不能保证 miThemesDrawItem()
与 TMenuItem::OnDrawItem
事件 100% 兼容。虽然 IDE 确实在用户尝试将其分配给 design-time 处的事件时验证事件处理程序的类型,但 IDE 是一个 小 宽松在这个问题上。 IDE 在执行验证时依赖于 RTTI,而 RTTI 是基于 Delphi 信息,而不是 C++ 信息。 Const-correctness 在 Delphi 中的工作方式与在 C++ 中略有不同。因此,有时 IDE 确实允许 less-than-compatible 处理程序通过,特别是为了向后兼容(即 [=17 的 TRect
参数=] 并不总是 const
).
验证后,IDE仅将事件处理程序的名称存储到DFM中,但直到[=90]才将实际功能分配给事件=](因为它的内存地址在 design-time 处未知)。当 DFM 在 run-time 处流入时,将函数分配给事件时不会再次验证函数的类型,盲目取用 as-is。因此,即使在 run-time,如果处理程序不是 100% 与事件兼容,可能 会出现微妙的问题。
另一方面,编译器要求函数类型与分配给它的函数指针 100% 兼容。没有任何差异的余地。因此,在这种情况下,参数上 const
限定符的差异很大 no-no。这就是为什么编译器不允许您将 miThemesDrawItem()
分配给代码中的 TMenuItem::OnDrawItem
事件,直到您修复 miThemesDrawItem()
的签名以匹配 TMenuDrawItemEvent
正是.
TMenuItem *mi = new TMenuItem(this);
mi->OnDrawItem = &miThemesDrawItem;
导致错误:
[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)'
函数声明没问题,事实上我可以在设计时将其分配给设计时可用的菜单项。
void __fastcall miThemesDrawItem(TObject *Sender, TCanvas *ACanvas, TRect &ARect, bool Selected);
我找到了解决方法。我在设计时在分隔项 N1
上分配了这个处理程序,然后在 运行 时我只是做 mi->OnDrawItem = N1->OnDrawItem
,它工作正常,因为 OnDrawItem
没有得到需要菜单分隔符,但我不喜欢这个!
我错过了什么,如何分配这个处理程序?
Function declaration is okay
其实不然。 非常接近注意错误消息告诉您的内容:
assigning to 'Vcl::Menus::TMenuDrawItemEvent' ... from incompatible type ...'
那么,为什么 miThemesDrawItem()
不兼容?因为它的类型与 TMenuDrawItemEvent
预期的类型不同!
我们先来看TMenuDrawItemEvent
。错误信息说它的类型是:
void ((__closure *))(System::TObject *, Vcl::Graphics::TCanvas *, const System::Types::TRect &, bool) __attribute__((fastcall))
我们暂时可以忽略__attribute__
,因此类型是:
void (__closure *)(System::TObject *, Vcl::Graphics::TCanvas *, const System::Types::TRect &, bool)
这与 Vcl.Menus.hpp
中 TMenuDrawItemEvent
的 实际 声明相匹配:
typedef void __fastcall (__closure *TMenuDrawItemEvent)(System::TObject* Sender, Vcl::Graphics::TCanvas* ACanvas, const System::Types::TRect &ARect, bool Selected);
现在,让我们看看您的 miThemesDrawItem()
。错误信息说它的类型是:
void (__closure *)(System::TObject *, Vcl::Graphics::TCanvas *, System::Types::TRect &, bool)
这与您的实际声明相匹配:
void __fastcall miThemesDrawItem(TObject *Sender, TCanvas *ACanvas, TRect &ARect, bool Selected);
请注意 TMenuDrawItemEvent
的声明类型与 miThemesDrawItem()
的声明类型之间的任何差异? miThemesDrawItem()
中的TRect
参数是not声明为const
!
您需要声明 miThemesDrawItem()
以具有 与 TMenuDrawItemEvent
声明方式完全相同的签名,例如:
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
这本身并不能保证 miThemesDrawItem()
与 TMenuItem::OnDrawItem
事件 100% 兼容。虽然 IDE 确实在用户尝试将其分配给 design-time 处的事件时验证事件处理程序的类型,但 IDE 是一个 小 宽松在这个问题上。 IDE 在执行验证时依赖于 RTTI,而 RTTI 是基于 Delphi 信息,而不是 C++ 信息。 Const-correctness 在 Delphi 中的工作方式与在 C++ 中略有不同。因此,有时 IDE 确实允许 less-than-compatible 处理程序通过,特别是为了向后兼容(即 [=17 的 TRect
参数=] 并不总是 const
).
验证后,IDE仅将事件处理程序的名称存储到DFM中,但直到[=90]才将实际功能分配给事件=](因为它的内存地址在 design-time 处未知)。当 DFM 在 run-time 处流入时,将函数分配给事件时不会再次验证函数的类型,盲目取用 as-is。因此,即使在 run-time,如果处理程序不是 100% 与事件兼容,可能 会出现微妙的问题。
另一方面,编译器要求函数类型与分配给它的函数指针 100% 兼容。没有任何差异的余地。因此,在这种情况下,参数上 const
限定符的差异很大 no-no。这就是为什么编译器不允许您将 miThemesDrawItem()
分配给代码中的 TMenuItem::OnDrawItem
事件,直到您修复 miThemesDrawItem()
的签名以匹配 TMenuDrawItemEvent
正是.