Dnešní komponenta bude odvozena od VCL komponenty TPageControl, bude navíc umět nastavit barvu pozadí TabSheets, dále barvu, popřípadě pozadí jako bitmapu záložek a nezávisle aktivní záložky. Dále budeme moci rozlišit font „běžné“ a vybrané záložky. Bude také umět nastavit požadovanou pevnou šířku a výšku záložky, minimální šířku záložky, určit, zda záložky mají mít stejnou šířku.
Vytvoříme si tedy komponentu odvozenou od TPageControl a přidáme příslušné property tak, jak je vidět na opisu deklarace třídy:
class PACKAGE TRPJPageControl : public TPageControl
{
private:
TFont* FFontActiveTab;
TColor FColorTab;
TColor FColorActiveTab;
Graphics::TBitmap* FBackground;
Graphics::TBitmap* FBackgroundActTab;
bool FFixedWidth;
int FMinTabWidth;
int FItemWidth;
int FItemHeight;
void __fastcall SetFontActiveTab(TFont* value);
void __fastcall SetColorTab(TColor value);
void __fastcall SetColorActiveTab(TColor value);
void __fastcall SetBackground(Graphics::TBitmap* value);
void __fastcall SetBackgroundActTab(Graphics::TBitmap* value);
void __fastcall SetFixedWidth(bool value);
void __fastcall SetMinTabWidth(int value);
void __fastcall SetItemWidth(int value);
void __fastcall SetItemHeight(int value);
protected:
virtual void __fastcall CreateParams(TCreateParams &Params);
virtual void __fastcall CreateWindowHandle(const TCreateParams &Params);
virtual void __fastcall DrawTab(int TabIndex, const Windows::TRect &Rect, bool Active);
int __fastcall GetAbsoluteIndex(int VisibleIndex);
__fastcall ~TRPJPageControl();
public:
__fastcall TRPJPageControl(TComponent* Owner);
__published:
// zděděné property
__property Color;
// nové property
__property TFont* FontActiveTab = { read=FFontActiveTab, write=SetFontActiveTab };
__property bool FixedWidth =
{ read=FFixedWidth, write=SetFixedWidth, default=false };
__property TColor ColorTab =
{ read=FColorTab, write=SetColorTab, default = clBtnFace };
__property TColor ColorActiveTab =
{ read=FColorActiveTab, write=SetColorActiveTab, default = clBtnFace };
__property Graphics::TBitmap* Background
= { read=FBackground, write=SetBackground };
__property Graphics::TBitmap* BackgroundActTab
= { read=FBackgroundActTab, write=SetBackgroundActTab };
__property int MinTabWidth = { read=FMinTabWidth, write=SetMinTabWidth, default=-1 };
__property int ItemWidth = { read=FItemWidth, write=SetItemWidth };
__property int ItemHeight = { read=FItemHeight, write=SetItemHeight };
};
Co budeme aplikovat ve vlastním zdrojovém kódu? Především budeme chtít vytvořit instance pomocných úložných proměnných pro nové property, nastavit některé výchozí hodnoty. To uděláme v konstruktoru třídy komponenty a v destruktoru tyto instance zase zrušíme:
__fastcall TRPJPageControl::TRPJPageControl(TComponent* Owner)
: TPageControl(Owner)
{
FFontActiveTab = new TFont;
FBackground = new Graphics::TBitmap;
FBackgroundActTab = new Graphics::TBitmap;
FFontActiveTab->Assign(Font);
ColorActiveTab = clBtnFace;
ColorTab = clBtnFace;
MinTabWidth = -1;
}
__fastcall TRPJPageControl::~TRPJPageControl()
{
delete FFontActiveTab;
delete FBackground;
delete FBackgroundActTab;
}
Dále musíme PageControlu nastavit vlastnost „uživatelsky-kreslený“, abychom mohli napsat vlastní funkci pro vykreslení záložek. To uděláme nejlépe v přepsané členské funkci CreateParams:
void __fastcall TRPJPageControl::CreateParams(Controls::TCreateParams &Params)
{
TPageControl::CreateParams(Params);
Params.Style = Params.Style | TCS_OWNERDRAWFIXED;
if ( FFixedWidth )
Params.Style = Params.Style | TCS_FIXEDWIDTH;
}
kde pouze přidáme příznak TCS_OWNERDRAWFIXED.
Dále přepíšeme funkci CreateWindowHandle, do které přidáme nastavení rozměrů záložky a minimální šířky záložky:
void __fastcall TRPJPageControl::CreateWindowHandle(const TCreateParams &Params)
{
TPageControl::CreateWindowHandle(Params);
SendMessage(Handle, TCM_SETITEMSIZE, 0,
MAKELPARAM(FItemWidth, FItemHeight));
SendMessage(Handle, TCM_SETMINTABWIDTH, 0, FMinTabWidth);
}
Jak je vidět z výpisu, používáme zde zprávy prvku tab-control, neboť PageControl je ve skutečnosti zapouzdření tohoto standardního prvku Windows s tím, že je k němu „nalepen“ a programové svázán také prostor pro objekty tabsheets. Samotný tab-control pak představuje komponenta TTabControl. V našem případě tedy použijeme 2 z těchto zpráv, a sice pro rozměry záložky zprávu TCM_SETITEMSIZE:
TCM_SETITEMSIZE
wParam = 0;
lParam = MAKELPARAM(cx, cy); // cx = šířka záložky, cy = délka
a pro minimální šířku záložky zprávu TCM_SETMINTABWIDTH:
TCM_SETMINTABWIDTH
wParam = 0;
lParam = (LPARAM) (INT) cx; // minimální šířka záložky
Celé vlastní kreslení pak provedeme v přepsané virtuální funkci DrawTab:
void __fastcall TRPJPageControl::DrawTab
(int TabIndex, const Windows::TRect &Rect, bool Active)
{
if ( Active )
{
Canvas->Font = FontActiveTab;
if ( FBackgroundActTab->Empty )
Canvas->Brush->Color = FColorActiveTab;
else
Canvas->Brush->Bitmap = FBackgroundActTab;
SetTextColor(Canvas->Handle, FontActiveTab->Color);
}
else
{
Canvas->Font = Font;
if ( FBackground->Empty )
Canvas->Brush->Color = FColorTab;
else
Canvas->Brush->Bitmap = FBackground;
SetTextColor(Canvas->Handle, Font->Color);
}
SetBkMode(Canvas->Handle, TRANSPARENT);
TRect rect = Rect;
Canvas->FillRect(rect);
if ( Images != NULL )
{
int ix, iy; // pozice image
iy =( rect.Bottom - rect.Top - Images->Height) / 2;
if ( iy < 0 ) iy = 0;
if ( Active )
ix = 4;
else
ix = 2;
Images->Draw(Canvas, rect.left + ix , iy, TabIndex, true);
rect.Right += (ix + 2 + Images->Width);
}
int Index = GetAbsoluteIndex(TabIndex);
if (Index >= 0)
DrawText(Canvas->Handle, Pages[Index]->Caption.c_str(),
strlen(Pages[Index]->Caption.c_str()), (LPRECT)&rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}
int __fastcall TRPJPageControl::GetAbsoluteIndex(int VisibleIndex)
{
for (int i=0; i<PageCount; i++)
{
TTabSheet *item = Pages[i];
if (item->TabIndex == VisibleIndex )
return i;
}
return -1;
}
Nyní ještě k vlastní realizaci některých property. U nastavení bitmapy pozadí či fontu je opět třeba pamatovat na nutnost použít metodu Assign k přiřazení příslušné hodnoty, tedy třeba takto pro property Background (pozadí záložek)
void __fastcall TRPJPageControl::SetBackground(Graphics::TBitmap* value)
{
if (FBackground != value)
{
FBackground->Assign(value);
RecreateWnd();
}
}
U nastavení volby FixedWidth, tedy zda všechny záložky mají stejnou šířku, a dalších uvedených vlastností týkajících se rozměrů záložek, si v tomto případě trochu zjednodušíme situaci a vždy zavoláme funkci RecreateWnd, která nám znovu vytvoří okno prvku tab-control:
void __fastcall TRPJPageControl::SetFixedWidth(bool value)
{
if (FFixedWidth != value) {
FFixedWidth = value;
RecreateWnd();
}
}
void __fastcall TRPJPageControl::SetMinTabWidth(int value)
{
if(FMinTabWidth != value) {
FMinTabWidth = value;
SendMessage(Handle, TCM_SETMINTABWIDTH, 0, value);
RecreateWnd();
}
}
void __fastcall TRPJPageControl::SetItemWidth(int value)
{
if(FItemWidth != value) {
FItemWidth = value;
SendMessage(Handle, TCM_SETITEMSIZE, 0, MAKELPARAM(value,0));
RecreateWnd();
}
}
void __fastcall TRPJPageControl::SetItemHeight(int value)
{
if(FItemHeight != value) {
FItemHeight = value;
SendMessage(Handle, TCM_SETITEMSIZE, 0, MAKELPARAM(0,value));
RecreateWnd();
}
}
Zde si můžete stáhnout zdrojový kód komponenty: page_control.zip