如何自定义VCL组件的Caption属性

发布于 2025-01-13 00:14:13 字数 3766 浏览 0 评论 0原文

(C++Builder 11)

由于我需要使用带有标题的 TSpeedButton 字形(不在字形的顶部、底部、左侧或右侧),我遵循特德·林莫 (Ted Lyngmo) 建议 (TSpeedButton 中的标题位置)来创建新的 VCL 组件。 我从 TCustomSpeedButton 开始创建了一个新组件,并且只发布了几个属性,不包括 Caption。我添加了 CustomCaption 属性,并尝试重写 Paint() 方法以在按钮中间写入文本。 生成的组件可以加载一个字形来显示按下和未按下的状态,并且 CustomCaption 的内容写在它的中间。

但我的 CustomCaption 的行为与原始 Caption 相去甚远。

首先,如果我在设计时更改 CustomCaption 的内容,则按钮不会发生任何变化,直到我不单击它(并且执行 Paint()< /code> 设计器中的方法,我认为...)

然后,如果我在设计时更改字体,我的 CustomCaption 字体根本不会改变。

我尝试使用 CM_FONTCHANGED 和 CM_TEXTCHANGED 消息,但可能方式不对。

代码

//header file
class PACKAGE TSpecialSpeedButton : public TCustomSpeedButton
{
    private:
        String fCustomCaption;
        //int fCustomCaptionTop, fCustomCaptionLeft;

        MESSAGE void __fastcall CMFontChanged(TMessage &Msg);
        MESSAGE void __fastcall CMTextChanged(TMessage &Msg);

    protected:
        void __fastcall Paint() override;


    public:
        __fastcall TSpecialSpeedButton(TComponent* Owner) override;

    __published:
        __property String CustomCaption = {read = fCustomCaption, write = fCustomCaption};
        //__property int CustomCaptionTop = {read = fCustomCaptionTop, write = fCustomCaptionTop};
        //__property int CustomCaptionLeft = {read = fCustomCaptionLeft, write = fCustomCaptionLeft};
        __property Glyph;
        __property GroupIndex = {default=0};
        __property Font;
        __property NumGlyphs = {default=1};
        __property OnClick;

    BEGIN_MESSAGE_MAP
            VCL_MESSAGE_HANDLER(CM_FONTCHANGED, TMessage, CMFontChanged)
            VCL_MESSAGE_HANDLER(CM_TEXTCHANGED, TMessage, CMTextChanged)
    END_MESSAGE_MAP(TCustomSpeedButton)

};

//cpp file
static inline void ValidCtrCheck(TSpecialSpeedButton *)
{
    new TSpecialSpeedButton(NULL);
}
//---------------------------------------------------------------------------
__fastcall TSpecialSpeedButton::TSpecialSpeedButton(TComponent* Owner)
    : TCustomSpeedButton(Owner)
{
    //fCustomCaptionTop = 0;
    //fCustomCaptionLeft = 0;

    Height = 50;
    Width = 50;
}
//---------------------------------------------------------------------------
namespace Tspecialspeedbutton
{
    void __fastcall PACKAGE Register()
    {
        TComponentClass classes[1] = {__classid(TSpecialSpeedButton)};
        RegisterComponents(L"My Components", classes, 0);
    }
}
//---------------------------------------------------------------------------

void __fastcall TSpecialSpeedButton::Paint()
{
    TRect PtRect;

    TCustomSpeedButton::Paint();

    PtRect.Left = 0;
    PtRect.Top = 0;
    PtRect.Right = Width;
    PtRect.Bottom = Height;

    Canvas->Font = Font;    //this seems to have no effect

    DrawTextW(Canvas->Handle, fCustomCaption.c_str(), fCustomCaption.Length(), &PtRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

}
//---------------------------------------------------------------------------

void __fastcall TSpecialSpeedButton::CMFontChanged(TMessage &Msg)
{
    Invalidate();
}
//---------------------------------------------------------------------------

void __fastcall TSpecialSpeedButton::CMTextChanged(TMessage &Msg)
{
    Invalidate();
}
//---------------------------------------------------------------------------

这是我无法以正确方式管理 CustomCaption 的 ,即作为原始 Caption 的代码。 我已经通过互联网、Embarcadero docwiki - 组件编写者指南、Vcl.Buttons.pas 文件搜索了信息,但我没有找到正确的方法,有人可以帮助我吗?

(C++Builder 11)

Since I need to use a TSpeedButton with a caption over thr glyph (not on the top, on the bottom, on the left or on the right of the glyph) I followed Ted Lyngmo suggestion (Caption position in a TSpeedButton) to create a new VCL component.
I have created a new component starting from TCustomSpeedButton and I have published only few property, not including the Caption. I have added my CustomCaption property and I have tried to override the Paint() method to write text in the middle of the button.
The resulting component can load a glyph to show pressed and unpressed statuses and the content of CustomCaption is written in the middle of it.

But my CustomCaption is far from behave like the original Caption.

Firs of all, if I change the content of CustomCaption, at design time, there is no change in the button until I don't click on it (and I execute the Paint() method in the designer, I think...)

Then, if I change the font, at design time, my CustomCaption font doesn't change at all.

I tried to use CM_FONTCHANGED and CM_TEXTCHANGED messages but perhaps not in the right way.

This is the code

//header file
class PACKAGE TSpecialSpeedButton : public TCustomSpeedButton
{
    private:
        String fCustomCaption;
        //int fCustomCaptionTop, fCustomCaptionLeft;

        MESSAGE void __fastcall CMFontChanged(TMessage &Msg);
        MESSAGE void __fastcall CMTextChanged(TMessage &Msg);

    protected:
        void __fastcall Paint() override;


    public:
        __fastcall TSpecialSpeedButton(TComponent* Owner) override;

    __published:
        __property String CustomCaption = {read = fCustomCaption, write = fCustomCaption};
        //__property int CustomCaptionTop = {read = fCustomCaptionTop, write = fCustomCaptionTop};
        //__property int CustomCaptionLeft = {read = fCustomCaptionLeft, write = fCustomCaptionLeft};
        __property Glyph;
        __property GroupIndex = {default=0};
        __property Font;
        __property NumGlyphs = {default=1};
        __property OnClick;

    BEGIN_MESSAGE_MAP
            VCL_MESSAGE_HANDLER(CM_FONTCHANGED, TMessage, CMFontChanged)
            VCL_MESSAGE_HANDLER(CM_TEXTCHANGED, TMessage, CMTextChanged)
    END_MESSAGE_MAP(TCustomSpeedButton)

};

//cpp file
static inline void ValidCtrCheck(TSpecialSpeedButton *)
{
    new TSpecialSpeedButton(NULL);
}
//---------------------------------------------------------------------------
__fastcall TSpecialSpeedButton::TSpecialSpeedButton(TComponent* Owner)
    : TCustomSpeedButton(Owner)
{
    //fCustomCaptionTop = 0;
    //fCustomCaptionLeft = 0;

    Height = 50;
    Width = 50;
}
//---------------------------------------------------------------------------
namespace Tspecialspeedbutton
{
    void __fastcall PACKAGE Register()
    {
        TComponentClass classes[1] = {__classid(TSpecialSpeedButton)};
        RegisterComponents(L"My Components", classes, 0);
    }
}
//---------------------------------------------------------------------------

void __fastcall TSpecialSpeedButton::Paint()
{
    TRect PtRect;

    TCustomSpeedButton::Paint();

    PtRect.Left = 0;
    PtRect.Top = 0;
    PtRect.Right = Width;
    PtRect.Bottom = Height;

    Canvas->Font = Font;    //this seems to have no effect

    DrawTextW(Canvas->Handle, fCustomCaption.c_str(), fCustomCaption.Length(), &PtRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

}
//---------------------------------------------------------------------------

void __fastcall TSpecialSpeedButton::CMFontChanged(TMessage &Msg)
{
    Invalidate();
}
//---------------------------------------------------------------------------

void __fastcall TSpecialSpeedButton::CMTextChanged(TMessage &Msg)
{
    Invalidate();
}
//---------------------------------------------------------------------------

I'm not able to manage the CustomCaption in the right way, that is as the original Caption.
I have searched information over the internet, in the Embarcadero docwiki - Component Writer's Guide, in the files Vcl.Buttons.pas but I don't find the right way, someone can help me?

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

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

发布评论

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

评论(1

赏烟花じ飞满天 2025-01-20 00:14:13

Embarcadero 不久前做了一些更改,导致 TFont、TBrush、TPen 等 Windows 资源出现微妙的问题。您遇到的问题是 TFont.Assign 并不总是导致 TCanvas 重新创建 Windows 字体对象。要强制重新创建字体对象,您需要强制内部调用 TFont.Changed。一种巧妙的方法是为 TFont.Color 分配一个不需要的值,然后将其设置回您真正想要的颜色。例如:

Canvas->Font = Font;
Canvas->Font->Color = clNone;
Canvas->Font->Color = Font->Color;

我无法以正确的方式管理 CustomCaption

我不确定您所说的“以正确的方式”是什么意思。如果您的意思是希望对 CustomCaption 进行更改以导致重绘,请为该属性添加一个编写器,然后在值更改时调用 Invalidate。

protected:
  void __fastcall SetCustomCaption(String Value);

__published:
  __property String CustomCaption = { read = fCustomCaption, write = SetCustomCaption };

void __fastcall TSpecialSpeedButton::SetCustomCaption(String Value)
{
  if (fCustomCaption != Value)
  {
    fCustomCaption = Value;
    Invalidate();
  }
}

CM_TEXTCHANGED 是当继承的 Text/Caption 属性更改时发送的 Windows 消息。由于您没有使用该继承的属性,因此您很可能不需要处理该消息。除非您想始终将其设置回空字符串。

Embarcadero made some changes a while back that cause subtle problems with Windows resources such as TFont, TBrush, TPen, etc. The problem you're running into is that TFont.Assign doesn't always cause TCanvas to recreate the Windows font object. To force the font object to be recreated you need to force an internal call to TFont.Changed. One hacky way to do this is to assign an unwanted value to TFont.Color and then set it back to the color you really want. For example:

Canvas->Font = Font;
Canvas->Font->Color = clNone;
Canvas->Font->Color = Font->Color;

I'm not able to manage the CustomCaption in the right way

I'm not sure what you mean by "in the right way". If you mean you want a change to CustomCaption to cause a redraw then add a writer for the property and then call Invalidate when the value is changed.

protected:
  void __fastcall SetCustomCaption(String Value);

__published:
  __property String CustomCaption = { read = fCustomCaption, write = SetCustomCaption };

void __fastcall TSpecialSpeedButton::SetCustomCaption(String Value)
{
  if (fCustomCaption != Value)
  {
    fCustomCaption = Value;
    Invalidate();
  }
}

CM_TEXTCHANGED is a Windows message sent when the inherited Text/Caption property is changed. Since you are not using that inherited property then you most likely won't need to process that message. Unless you want to always set it back to an empty string.

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