¿Qué pasó con la cadena de herencia TBitBtn y TButton?
-
09-09-2019 - |
Pregunta
Recientemente he empecé a actualizar mi proyecto RAD Studio 2007 para RAD Studio 2009. Una cosa que he notado es cuando aparentemente simple código, de repente, no lograron compilar.
Ejemplo 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 );
Todo esto se utiliza para compilar, pero con 2009 está fallando. En cuanto a la cadena de herencia para 2007 TBitBtn
utiliza para derivar de TButton
. Por lo tanto, los eventos que se espera en cualquier botón de control (es decir OnClick) eran compartidos por la clase TButton
. Por lo tanto, yo era capaz de tratar a mi clase TBitBtn
como TButton
.
2007 cadena de herencia:
- TBitBtn: TButton
2009 cadena de herencia:
- TBitBtn: TCustomButton
- TButton: TCustomButton
En 2009, ambos TButton y TBitButton se derivan de TCustomButton , que estaría bien supongo que si el botón atributos se llevaron a cabo allí. Si este fuera el caso, tan sólo pudiera cambiar el código para hacer frente a una TCustomButton en su lugar. Por desgracia, TCustomButton no mantener las cosas como OnClick . Por lo tanto, ya no puedo tratar a un TBitBtn como un TButton . Ambas clases, ahora tienen su propio botón separado como atributos (es decir, que ambos tienen su propio evento OnClick declarado). Es decir, al menos proporcionar una interfaz o algo, como IButton que tanto TButton y TBitBtn en práctica.
Parece que este tipo de cambios aparentemente inocentes son los que pueden causar estragos innecesarios. Esto parece extraño y me pregunto si alguien sabe por qué CodeGear (o cualquier autor Marco para el caso) harían este tipo de cosas?
Más importante aún, dada esta herencia fragmentada, es allí y elegante solución para tratar una TBitBtn como un TButton
Solución
TButton y TBitBtn no todavía continúan compartiendo un evento común OnClick, ya que se implementa todo el camino a nivel TControl para empezar, y siempre lo ha sido. TButton simplemente estaba promoviendo el evento OnClick TControl :: protegida para publicado, que TBitBtn entonces heredar.
En D2009, TCustomButton, al igual que otras clases TCustom ..., no promueve los miembros protegidos de clases base a publicado. TButton y TBitBtn promueven el evento OnClick TControl :: protegida para publicada de forma individual. Pero el evento en sí todavía existe a nivel TControl.
Desde que se protege a nivel TControl, se puede utilizar una clase de acceso para llegar a él, es decir:
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
}
};
O, para cualquier puntero TControl generales:
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
}
};
Una solución más elegante sería utilizar RTTI en su lugar, lo que también permitirá manejar otros tipos de objetos, como TSpeedButton, que tienen su propio evento OnClick, es decir:
#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
}
};
O incluso:
#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
}
};
Otros consejos
Si esto era Delphi, sugeriría la clase TCustomButton con el es y como los operadores:
if (SrcButton is TButton) then
(SrcButton as TButton).OnClick := OnButtonClick
else if (SrcButton is TBitButton)
(SrcButton as TBitButton).OnClick := OnButtonClick;
C ++ es simplemente hace mucho tiempo
por cierto, no la VCL incluye algún acciones para proporcionar una interfaz única entre los botones, menús, etc y el código invoca?