What happened to TBitBtn and TButton inheritance chain?

吃可爱长大的小学妹 提交于 2019-12-07 07:05:16

问题


I've recently began to upgrade my RAD Studio 2007 project to RAD Studio 2009. One thing I noticed is when seemingly simple code all of a sudden failed to compile.

Example Code:

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 );

This all use to compile, but with 2009 it's failing. Looking at the inheritance chain for 2007 TBitBtn used to derive from TButton. Therefore, events that are expected on any button control (i.e. OnClick) were shared by the TButton class. Therefore, I was able to treat my TBitBtn class as a TButton.

2007 inheritance chain:

  • TBitBtn : TButton

2009 inheritance chain:

  • TBitBtn : TCustomButton
  • TButton : TCustomButton

In 2009, both TButton and TBitButton derive from TCustomButton, which would be fine I suppose if the button like attributes were held there. If this were the case, I could just change the code to deal with a TCustomButton instead. Unfortunately, TCustomButton does not hold things like OnClick. Therefore, I can no longer treat a TBitBtn like a TButton. Both of these classes, now have their own separate button like attributes (i.e. they both have their own OnClick event declared). I mean, at least provide an interface or something, like IButton that both TButton and TBitBtn implement.

It seems that these types of seemingly innocent changes are the ones that can wreak unnecessary havoc. This seems odd and am wondering if anyone knows why CodeGear (or any Framework author for that matter) would do this type of thing?

More importantly, given this fragmented inheritance, is there and elegant solution to treat a TBitBtn like a TButton?


回答1:


TButton and TBitBtn do still continue to share a common OnClick event, as it is implemented all the way down at the TControl level to begin with, and always has been. TButton was merely promoting the protected TControl::OnClick event to published, which TBitBtn would then inherit.

In D2009, TCustomButton, like other TCustom... classes, does not promote protected members from base classes to published. TButton and TBitBtn promote the protected TControl::OnClick event to published individually. But the event itself still exists at the TControl level.

Since it is protected at the TControl level, you can use an accessor class to reach it, ie:

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
    }
};

Or, for any general TControl pointer:

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 more elegant solution would be to use RTTI instead, which would also allow you to handle other types of objects, such as TSpeedButton, which have their own OnClick event, ie:

#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
    }
};

Or even:

#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
    }
};



回答2:


If this was Delphi, I would suggest the TCustomButton class with the is and as operators:

if (SrcButton is TButton) then
  (SrcButton as TButton).OnClick := OnButtonClick
else if (SrcButton is TBitButton)
  (SrcButton as TBitButton).OnClick := OnButtonClick;

C++ is simply too long ago

btw, didn't the VCL sometime include Actions to provide a single interface between buttons, menus, etc and invoked code?



来源:https://stackoverflow.com/questions/946316/what-happened-to-tbitbtn-and-tbutton-inheritance-chain

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!