如何自定义VCL组件的Caption 属性
How to customize the Caption property of a VCL component
(C++Builder 11)
因为我需要使用 TSpeedButton
和字形上的标题(不是在字形的顶部、底部、左侧或右侧),所以我遵循了 Ted Lyngmo 的建议(Caption position in a TSpeedButton) 创建一个新的 VCL 组件。
我创建了一个从 TCustomSpeedButton
开始的新组件,我只发布了几个 属性,不包括 Caption
。我已经添加了我的 CustomCaption
属性 并且我试图覆盖 Paint()
方法以在按钮中间写入文本。
生成的组件可以加载一个字形来显示按下和未按下状态,CustomCaption
的内容写在它的中间。
但我的 CustomCaption
与原来的 Caption
相去甚远。
首先,如果我更改 CustomCaption
的内容,在设计时,按钮不会发生变化,直到我不点击它(并且我执行 Paint()
设计器中的方法,我认为...)
然后,如果我更改字体,在设计时,我的 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
一样。
我已经通过 Internet 在 Embarcadero docwiki - Component Writer's Guide 和文件 Vcl.Buttons.pas 中搜索了信息,但我没有找到正确的方法,有人可以帮助我吗?
Embarcadero 不久前进行了一些更改,导致 Windows 资源(例如 TFont、TBrush、TPen 等)出现细微问题。您 运行 遇到的问题是 TFont.Assign 并不总是导致 TCanvas 重新创建 Windows 字体对象。要强制重新创建字体对象,您需要强制对 TFont.Changed 进行内部调用。执行此操作的一种 hacky 方法是将不需要的值分配给 TFont.Color,然后将其设置回您真正想要的颜色。例如:
Canvas->Font = Font;
Canvas->Font->Color = clNone;
Canvas->Font->Color = Font->Color;
I'm not able to manage the CustomCaption in the right way
我不确定你所说的“以正确的方式”是什么意思。如果您的意思是您想要更改 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消息。由于您没有使用继承的 属性 那么您很可能不需要处理该消息。除非您想始终将其设置回空字符串。
(C++Builder 11)
因为我需要使用 TSpeedButton
和字形上的标题(不是在字形的顶部、底部、左侧或右侧),所以我遵循了 Ted Lyngmo 的建议(Caption position in a TSpeedButton) 创建一个新的 VCL 组件。
我创建了一个从 TCustomSpeedButton
开始的新组件,我只发布了几个 属性,不包括 Caption
。我已经添加了我的 CustomCaption
属性 并且我试图覆盖 Paint()
方法以在按钮中间写入文本。
生成的组件可以加载一个字形来显示按下和未按下状态,CustomCaption
的内容写在它的中间。
但我的 CustomCaption
与原来的 Caption
相去甚远。
首先,如果我更改 CustomCaption
的内容,在设计时,按钮不会发生变化,直到我不点击它(并且我执行 Paint()
设计器中的方法,我认为...)
然后,如果我更改字体,在设计时,我的 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
一样。
我已经通过 Internet 在 Embarcadero docwiki - Component Writer's Guide 和文件 Vcl.Buttons.pas 中搜索了信息,但我没有找到正确的方法,有人可以帮助我吗?
Embarcadero 不久前进行了一些更改,导致 Windows 资源(例如 TFont、TBrush、TPen 等)出现细微问题。您 运行 遇到的问题是 TFont.Assign 并不总是导致 TCanvas 重新创建 Windows 字体对象。要强制重新创建字体对象,您需要强制对 TFont.Changed 进行内部调用。执行此操作的一种 hacky 方法是将不需要的值分配给 TFont.Color,然后将其设置回您真正想要的颜色。例如:
Canvas->Font = Font;
Canvas->Font->Color = clNone;
Canvas->Font->Color = Font->Color;
I'm not able to manage the CustomCaption in the right way
我不确定你所说的“以正确的方式”是什么意思。如果您的意思是您想要更改 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消息。由于您没有使用继承的 属性 那么您很可能不需要处理该消息。除非您想始终将其设置回空字符串。