建立一個可重複使用的對話框控件 (TvCaptionDlg),其視窗標題列位於左側垂直方向,文字由下而上 90 度旋轉,綠色背景,保留拖曳移動視窗功能。
HTCAPTION,系統即賦予標題列拖曳行為TFont 不支援文字旋轉,需使用 CreateFontIndirect 設定 lfEscapement = 900 (0.1 度單位)CreateParams 移除 WS_DLGFRAME、加入 WS_POPUP 實現無框自繪TComponent 衍生 → __published 屬性 → RegisterComponents 註冊至工具箱class TVCForm : public TForm {
__published:
TBitBtn *BitBtn1;
void __fastcall FormPaint(TObject *Sender);
void __fastcall BitBtn1Click(TObject *Sender);
protected:
void __fastcall OnNcHitTest(TMessage &Msg);
void __fastcall CreateParams(TCreateParams &Params);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_NCHITTEST, TMessage, OnNcHitTest)
END_MESSAGE_MAP(TForm)
public:
__fastcall TVCForm(TComponent* Owner);
};
void __fastcall TVCForm::FormPaint(TObject *Sender) {
// 1. 繪製左側 20px 綠色標題列
Canvas->Pen->Color = clGreen;
Canvas->Brush->Color = clGreen;
Canvas->Rectangle(0, 0, 20, ClientHeight);
// 2. SDK 繪製 90 度旋轉文字
LOGFONT lf = {0};
lf.lfHeight = -13;
lf.lfWeight = FW_NORMAL;
lf.lfEscapement = 900; // 90 度 (單位 0.1 度)
lstrcpy(lf.lfFaceName, "新細明體");
HFONT hFont = CreateFontIndirect(&lf);
HFONT hOld = (HFONT)SelectObject(Canvas->Handle, hFont);
SetTextColor(Canvas->Handle, RGB(255,255,255));
RECT rc = {0, 0, 20, ClientHeight};
TextOut(Canvas->Handle, 3, ClientHeight - 3, Caption.c_str(), Caption.Length());
SelectObject(Canvas->Handle, hOld);
DeleteObject(hFont);
}
void __fastcall TVCForm::OnNcHitTest(TMessage &Msg) {
TPoint pt = ScreenToClient(SmallPointToPoint(*(TSmallPoint*)&Msg.LParam));
RECT rc = {0, 0, 20, ClientHeight};
if (PtInRect(&rc, pt))
Msg.Result = HTCAPTION; // 告訴系統此處為標題列
else
DefaultHandler(&Msg);
}
void __fastcall TVCForm::CreateParams(TCreateParams &Params) {
TForm::CreateParams(Params);
Params.Style |= WS_POPUP;
Params.Style &= ~WS_DLGFRAME; // 移除對話框框架
}
// vcapdlg.h
class PACKAGE TvCaptionDlg : public TComponent {
private:
AnsiString FCaption;
protected:
public:
virtual __fastcall TvCaptionDlg(TComponent* Owner);
virtual void __fastcall Execute();
__published:
__property AnsiString Caption = {read=FCaption, write=FCaption};
};
// vcapdlg.cpp
#include "vcapdlg.h"
#include "vcap.cpp" // 直接包含實作,注意註解掉 vcap.cpp 中的 #pragma package(smart_init)
namespace Vcapdlg {
void __fastcall PACKAGE Register() {
TComponentClass classes[1] = {__classid(TvCaptionDlg)};
RegisterComponents("MyComponents", classes, 0);
}
}
__fastcall TvCaptionDlg::TvCaptionDlg(TComponent* Owner) : TComponent(Owner) {
FCaption = "My Vertical Caption";
}
void __fastcall TvCaptionDlg::Execute() {
TVCForm* dlg = new TVCForm(Application);
dlg->Caption = Caption;
dlg->ShowModal();
delete dlg;
}
| 項目 | 說明 |
|---|---|
| #pragma package(smart_init) | vcap.cpp 中必須註解掉,否則安裝套件時會失敗 |
| 包含 vcap.cpp | 而非 .obj,避免需先編譯產生 .obj 的麻煩 |
| 設計時屬性 | Caption 為 AnsiString,無需自訂屬性編輯器 |
| DFM 資源 | BitBtn1 的 Caption/Kind 等非預設屬性需在執行期設定,或將 TVCForm 完整定義放入控件單元 |
TvCaptionDlgCaption 屬性TvCaptionDlg1->Execute() 顯示對話框BCB 線上說明 "Creating custom components" 為最佳入門教材,本文實作參考其 "Making a dialog box a component" 範例並修正兩處已知問題。