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 ?

Foi útil?

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?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top