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.
Klepněte pro větší obrázek

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: Software, Windows, Programování, Komponenta, Images, Tvorba, FMI, Page, Šířka, Read, Item, Tvor, Brush, Canvas, Elsa, TV +, Záložka, Komp


Určitě si přečtěte

Nové názvy, upravený vývoj. Microsoft ukázal, jak teď bude vydávat Windows 10

Nové názvy, upravený vývoj. Microsoft ukázal, jak teď bude vydávat Windows 10

** Podzimní vydání Windows 10 přinese jen minimum novinek ** Aktualizace ponese formální označení 20H2 ** Microsoft mění názvy v programu Windows Insider

Lukáš Václavík | 17

Podívejte se na Windows z roku 1990. Před 30 lety přišly Windows 3.0 a líbily se nám

Podívejte se na Windows z roku 1990. Před 30 lety přišly Windows 3.0 a líbily se nám

** 22. května 1990 uvedl Microsoft Windows 3.0 ** Systém z Microsoftu definitivně udělal lídra na desktopu ** Tehdejší Windows byly vlastně grafickou nadstavbou nad MS-DOS

Jakub Čížek | 75

Windy je laboratoř na počasí: 12 tipů, jak se ve službě vyznat a využít ji naplno

Windy je laboratoř na počasí: 12 tipů, jak se ve službě vyznat a využít ji naplno

** Předpověď počasí Windy nabízí nepřebernou škálu funkcí ** Zorientovat se v nich nemusí být vždy snadné ** Proto přinášíme 12 užitečných tipů a triků

Karel Kilián | 10

Zapomeňte na kometu, české nebe každý den křižují mnohem zajímavější kousky

Zapomeňte na kometu, české nebe každý den křižují mnohem zajímavější kousky

** České nebe každý den křižuje hromada exotických letounů ** Na populární mapě Flightradar24 je ale nenajdete ** Jsou to vojenské letouny USA, UK a NATO

Jakub Čížek | 37

Je lepší hrát na PC, či na konzolích? Nebo jsou i jiné možnosti?

Je lepší hrát na PC, či na konzolích? Nebo jsou i jiné možnosti?

** Jaké jsou výhody a nevýhody hraní na počítači? ** Co mají společného a v čem se liší Xbox One, PS4 a Switch? ** Na čem hrát, když nemáte výkonné PC ani konzoli?

Lukáš Václavík | 124

AR není ani po letech žádný trhák. Teď to zkusí Hybri, který svleče vaše kamarádky

AR není ani po letech žádný trhák. Teď to zkusí Hybri, který svleče vaše kamarádky

** Rozšířené realitě i po letech chybí praktické využití ** Selhaly mobilní aplikace i AR brýle ** Floridské studio to proto zkusí přes bizarní erotiku Hybri

Jakub Čížek | 20


Aktuální číslo časopisu Computer

Megatest: nejlepší notebooky do 20 000 Kč

Test 8 levných IP kamer

Jak vybrat bezdrátová sluchátka

Testujeme Android 11