如何自定义VCL组件的Caption属性
(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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Embarcadero 不久前做了一些更改,导致 TFont、TBrush、TPen 等 Windows 资源出现微妙的问题。您遇到的问题是 TFont.Assign 并不总是导致 TCanvas 重新创建 Windows 字体对象。要强制重新创建字体对象,您需要强制内部调用 TFont.Changed。一种巧妙的方法是为 TFont.Color 分配一个不需要的值,然后将其设置回您真正想要的颜色。例如:
我不确定您所说的“以正确的方式”是什么意思。如果您的意思是希望对 CustomCaption 进行更改以导致重绘,请为该属性添加一个编写器,然后在值更改时调用 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:
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.
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.