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

¿Fue útil?

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?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top