O que aconteceu com TBitBtn e TButton cadeia de herança?
-
09-09-2019 - |
Pergunta
Eu recentemente começou a atualizar meu projeto RAD Studio 2007 para RAD Studio 2009. Uma coisa que notei é quando aparentemente simples código, de repente, não conseguiu compilar.
Exemplo de código:
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 );
Isso tudo uso para compilar, mas com 2009, está falhando. Olhando para a cadeia de herança para 2007 TBitBtn
utilizado para derivar a partir TButton
. Portanto, os eventos que são esperados em qualquer controle de botão (ou seja OnClick) foram compartilhados pela classe TButton
. Portanto, eu era capaz de tratar a minha classe TBitBtn
como um TButton
.
cadeia 2,007 herança:
- TBitBtn: TButton
cadeia 2,009 herança:
- TBitBtn: TCustomButton
- TButton: TCustomButton
Em 2009, tanto TButton e TBitButton derivam de TCustomButton , que seria bom eu suponho que se o botão como atributos foram realizadas lá. Se este fosse o caso, eu poderia simplesmente alterar o código para lidar com um TCustomButton em seu lugar. Infelizmente, TCustomButton não manter as coisas como OnClick . Portanto, já não posso tratar um TBitBtn como uma TButton . Ambas essas classes, agora têm o seu próprio botão separado como atributos (ou seja, ambos têm seu próprio evento OnClick declarado). Quer dizer, pelo menos, fornecer uma interface ou algo, como IButton que tanto TButton e TBitBtn implementar.
Parece que esses tipos de alterações aparentemente inocentes são os únicos que podem causar estragos desnecessários. Isso parece estranho e estou querendo saber se alguém sabe por que CodeGear (ou qualquer autor Framework para que o assunto) faria este tipo de coisa?
Mais importante, dada essa herança fragmentado, está lá e elegante solução para tratar uma TBitBtn como uma TButton ?
Solução
TButton e TBitBtn que ainda continuam a compartilhar um evento comum OnClick, como ele é implementado todo o caminho até ao nível TControl para começar, e sempre foi. TButton estava apenas promovendo o evento TControl :: OnClick protegida para publicado, que TBitBtn seria então herdar.
No D2009, membros TCustomButton, como outros TCustom ... aulas, não promove protegidos de classes base para publicado. TButton e TBitBtn promover o evento TControl :: OnClick protegida para publicado individualmente. Mas o evento em si ainda existe ao nível TControl.
Uma vez que é protegido ao nível TControl, você pode usar uma classe de acessor para alcançá-lo, ou seja:
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
}
};
Ou, para qualquer ponteiro geral 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
}
};
A solução mais elegante seria usar RTTI em vez disso, o que também permite que você lidar com outros tipos de objetos, como TSpeedButton, que têm seu próprio evento OnClick, ou seja:
#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
}
};
Ou mesmo:
#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
}
};
Outras dicas
Se este era Delphi, gostaria de sugerir a classe TCustomButton com o é e como operadores:
if (SrcButton is TButton) then
(SrcButton as TButton).OnClick := OnButtonClick
else if (SrcButton is TBitButton)
(SrcButton as TBitButton).OnClick := OnButtonClick;
C ++ é simplesmente muito tempo atrás
btw, não o VCL em algum momento incluem Ações para fornecer uma única interface entre botões, menus, etc e código chamado?