我最近开始将我的RAD Studio 2007项目升级到RAD Studio 2009.我注意到的一件事是看似简单的代码突然无法编译。
示例代码:
class CButtonPopupMenu
{
// Snip
public:
void Init( TButton* SrcButton )
{
SrcButton->OnClick = OnButtonClick;
}
private:
void __fastcall OnButtonClick( TObject* Sender )
{
// Do some button click stuff
}
};
// Snip
TButton button = new TButton( this );
TBitBtn bitBtn = new TBitBtn( this );
CButtonPopupMenu popupButton = new CButtonPopupMenu( button );
CButtonPopupMenu popupBitBtn = new CButtonPopupMenu( bitBtn );
这一切都用于编译,但在2009年它失败了。查看用于派生TBitBtn
的2007 TButton
的继承链。因此,TButton
类共享任何按钮控件(即OnClick)上预期的事件。因此,我能够将TBitBtn
课程视为TButton
。
2007年继承链:
2009年继承链:
2009年, TButton 和 TBitButton 都来自 TCustomButton ,如果像属性这样的按钮被保存在那里,我认为这很好。如果是这种情况,我可以改为代码来处理 TCustomButton 。不幸的是, TCustomButton 不包含 OnClick 之类的内容。因此,我不能再像 TButton 那样对待 TBitBtn 。这两个类现在都有自己独立的按钮,如属性(即它们都声明了自己的OnClick事件)。我的意思是,至少提供一个界面或类似 TButton 和 TBitBtn 的 IButton 。
似乎这些看似无辜的变化可能造成不必要的破坏。这看起来很奇怪,我想知道为什么有人知道为什么CodeGear(或任何框架作者)会做这种事情?
更重要的是,鉴于这种分散的继承,是否有优雅的解决方案来处理 TBitBtn ,如 TButton ?
答案 0 :(得分:7)
TButton和TBitBtn仍然继续共享一个常见的OnClick事件,因为它始终在TControl级别实现,并且始终如此。 TButton只是将受保护的TControl :: OnClick事件提升为已发布,TBitBtn将继承该事件。
在D2009中,TCustomButton与其他TCustom ...类一样,不会将受保护的成员从基类升级为已发布。 TButton和TBitBtn将受保护的TControl :: OnClick事件提升为单独发布。但事件本身仍然存在于TControl级别。
由于它在TControl级别受到保护,因此您可以使用访问者类来访问它,即:
class TCustomButtonAccess
{
public:
__property OnClick;
};
class CButtonPopupMenu
{
// Snip
public:
void Init( TCustomButton* SrcButton )
{
((TCustomButtonAccess*)SrcButton)->OnClick = OnButtonClick;
}
private:
void __fastcall OnButtonClick( TObject* Sender )
{
// Do some button click stuff
}
};
或者,对于任何常规TControl指针:
class TControlAccess
{
public:
__property OnClick;
};
class CControlPopupMenu
{
// Snip
public:
void Init( TControl* SrcControl )
{
((TControlAccess*)SrcControl)->OnClick = OnControlClick;
}
private:
void __fastcall OnControlClick( TObject* Sender )
{
// Do some click stuff
}
};
更优雅的解决方案是使用RTTI,这也可以让您处理其他类型的对象,例如TSpeedButton,它们有自己的OnClick事件,即:
#include <TypInfo.hpp>
class TControlAccess
{
public:
__property OnClick;
};
class CControlPopupMenu
{
// Snip
public:
void Init( TControl* SrcControl )
{
TMethod m;
m.Code = &OnControlClick;
m.Data = this;
SetMethodProp(SrcControl, "OnClick", m);
}
private:
void __fastcall OnControlClick( TObject* Sender )
{
// Do some click stuff
}
};
甚至:
#include <TypInfo.hpp>
class CObjectPopupMenu
{
// Snip
public:
void Init( TObject* SrcObject )
{
TMethod m;
m.Code = &OnObjectClick;
m.Data = this;
SetMethodProp(SrcObject, "OnClick", m);
}
private:
void __fastcall OnObjectClick( TObject* Sender )
{
// Do some click stuff
}
};
答案 1 :(得分:1)
如果这是Delphi,我会建议TCustomButton类使用是和作为运算符:
if (SrcButton is TButton) then
(SrcButton as TButton).OnClick := OnButtonClick
else if (SrcButton is TBitButton)
(SrcButton as TBitButton).OnClick := OnButtonClick;
C ++就是很久以前
顺便说一下,VCL有时候不包括在按钮,菜单等和调用代码之间提供单一界面的动作吗?