TBitBtn 和 TButton 继承链发生了什么?

发布于 2024-07-22 20:53:53 字数 1684 浏览 4 评论 0原文

我最近开始将我的 RAD Studio 2007 项目升级到 RAD Studio 2009。我注意到的一件事是,看似简单的代码突然无法编译。

示例代码:

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

这一切都用于编译,但在 2009 年它失败了。 查看 2007 年用于派生自 TButtonTBitBtn 的继承链。 因此,任何按钮控件上预期的事件(即 OnClick)都由 TButton 类共享。 因此,我能够将我的 TBitBtn 类视为 TButton

2007 继承链:

  • TBitBtn : TButton

2009 继承链:

  • TBitBtn : TCustomButton
  • TButton : TCustomButton

在 2009 年,TButtonTBitButton strong> 派生自 TCustomButton,我想如果按钮之类的属性保存在那里就可以了。 如果是这种情况,我只需更改代码来处理 TCustomButton 即可。 不幸的是,TCustomButton 不包含诸如 OnClick 之类的东西。 因此,我不能再像对待TButton那样对待TBitBtn。 这两个类现在都有自己独立的按钮类属性(即它们都声明了自己的 OnClick 事件)。 我的意思是,至少提供一个接口或其他东西,例如 TButtonTBitBtn 都实现的 IButton

似乎这些看似无害的变化可能会造成不必要的破坏。 这看起来很奇怪,我想知道是否有人知道为什么 CodeGear(或任何与此相关的框架作者)会做这种类型的事情?

更重要的是,考虑到这种碎片化的继承,是否存在一种优雅的解决方案来将TBitBtn视为TButton

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

沙与沫 2024-07-29 20:53:53

TButton 和 TBitBtn 仍然继续共享一个共同的 OnClick 事件,因为它从一开始就在 TControl 级别上一直实现,而且一直如此。 TButton 只是将受保护的 TControl::OnClick 事件提升为已发布,然后 TBitBtn 将继承该事件。

在 D2009 中,TCustomButton 与其他 TCustom... 类一样,不会将受保护的成员从基类提升为已发布。 TButton 和 TBitBtn 促进受保护的 TControl::OnClick 事件单独发布。 但事件本身仍然存在于TControl级别。

由于它在 TControl 级别受到保护,因此您可以使用访问器类来访问它,即:

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

或者,对于任何通用 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
    }
};

更优雅的解决方案是使用 RTTI,这也允许您处理其他类型的对象,例如 TSpeedButton,它们有自己的 OnClick 事件,即:

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

甚至:

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

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
    }
};
够钟 2024-07-29 20:53:53

如果这是 Delphi,我建议使用带有 isas 运算符的 TCustomButton 类:

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

顺便说一句, C++ 已经太久远了

,VCL 有时不包括要提供的操作按钮、菜单等与调用代码之间的单一接口?

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?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文