Tvorba komponent pro C++ Builder PageControl

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

Váš názor Další článek: Nová varianta červa BadTrans se rychle šíří

Témata článku: , , , , , , , , , , , , , , , , , , ,