3. Vytváření vlastností

Vlastnosti jsou nejviditelnější částí komponent. Vývojář aplikace je vidí a manipuluje s nimi při návrhu a získává bezprostřední zpětnou vazbu reakcí komponenty na Návrhovém formuláři. Vlastnosti jsou také důležité, neboť usnadňují používání komponent. Vlastnosti poskytují významné výhody a to jak pro tvůrce komponent, tak i pro jejich uživatele. Nejzřejmější výhodou je, že vlastnost může být v době návrhu zobrazena v Inspektoru objektů. To zjednodušuje naše programování, neboť namísto zadávání několika parametrů při vytváření objektu, zpracujeme hodnoty přiřazené uživatelem.
K zajištění bezpečného použití vlastností v našich komponentách, musíme pochopit:

Kdy vytvářet vlastnosti?

Pro uživatele komponent se vlastnosti podobají proměnným. Uživatel může nastavovat nebo číst hodnoty vlastností, jako kdyby tyto vlastnosti byly složkami tříd. Rozdíl je pouze v tom, že vlastnost nemůže být použita jako parametr volaný odkazem.
Vlastnosti poskytují více možností než složky tříd neboť: Příkladem je vlastnost Top všech komponent. Přiřazení nové hodnoty vlastnosti Top, neznamená jenom změnu nějaké uložené hodnoty, ale způsobí i přemístění a překreslení komponenty samotné. Efekt nastavení vlastnosti není omezen na nějakou komponentu. Nastavení vlastnosti Down komponenty SpeedButton na true, způsobí nastavení Down všech ostatních tlačítek ve skupině na false.

Typy vlastností

Vlastnost může být libovolného typu. Důležitým aspektem volby typu pro naši vlastnost je to, že různé typy jsou různě zobrazovány v Inspektoru objektů. Inspektor objektů používá typ vlastnosti k určení co uživatel chce zobrazit. Při registraci komponenty můžeme specifikovat různé editory vlastností. V následující tabulce je uvedeno jak vlastnost je zobrazena v Inspektoru objektů.
 
Typ vlastnosti  Zacházení s vlastností v Inspektoru objektů
Jednoduchý Číselné, znakové a řetězcové vlastnosti se zobrazují v Inspektoru objektů jako čísla, znaky nebo řetězce. Uživatel může zadávat a editovat hodnotu vlastnosti přímo.
Výčtový Vlastnosti výčtových typů (včetně bool) zobrazují hodnotu tak, jak je definována ve zdrojovém kódu. Uživatel může cyklicky procházet možnými hodnotami dvojitým kliknutím ve sloupci hodnot. Hodnotu můžeme také vybírat ze seznamu obsahujícího všechny možné hodnoty výčtového typu.
Množina Vlastnosti typu množina se zobrazují v Inspektoru objektů jako množiny. Rozšířením množiny, uživatel může zacházet s každým prvkem množiny jako s logickou hodnotou: true, jestliže prvek je obsažen v množině nebo false není-li.
Objekt Vlastnost, která je sama objektem, často má svůj vlastní editor vlastností. Nicméně, jestliže třída, která je vlastností má také zveřejňované vlastnosti, pak Inspektor objektů umožňuje uživateli rozšířit seznam vlastností objektu a editovat je samostatně. Tyto vlastnosti musí být odvozeny od TPersistent.
Pole Pole vlastností musí mít svůj vlastní editor vlastností. Inspektor objektů nemá zabudovánu podporu pro editaci pole vlastností.

Zveřejňování zděděných vlastností

Všechny komponenty dědí vlastnosti od svých předků. Když odvozujeme novou komponentu od existující komponenty, pak naše nová komponenta dědí všechny vlastnosti ze třídy předka. Jestliže odvozujeme komponentu od jednoho z abstraktních typů, pak zděděné vlastnosti jsou chráněné nebo veřejné, ale ne zveřejňované.
Aby chráněná nebo veřejná vlastnost byla přístupná pro uživatele komponenty v Inspektoru objektů, musíme ji opětovně deklarovat jako zveřejňovanou. To provedeme přidáním deklarace zděděné vlastnosti do deklarace třídy potomka.
Jestliže odvozujeme komponentu od TWinControl, komponenta např. zdědí vlastnost Ctl3D, ale tato vlastnost je chráněná a uživatel komponenty nemůže k Ctl3D během návrhu ani při běhu programu přistupovat. Opětovnou deklarací Ctl3D v naší nové komponentě, můžeme změnit úroveň ochrany na veřejnou nebo zveřejňovanou. Následující kód ukazuje opětnou deklaraci Ctl3D jako zveřejňovanou, což ji zpřístupní během návrhu:
class PACKAGE TPrikladKomponenty : public TWinControl
{
__published:
  __property Ctl3D;
}
Opětovnou deklarací můžeme pouze zmírnit omezení přístupu a nelze je zvětšit. Chráněnou vlastnost lze změnit na veřejnou, ale nelze změnit veřejnou vlastnost na chráněnou. Při opakované deklaraci, specifikujeme pouze jméno vlastnosti (typ a další informace se neuvádí). Můžeme také deklarovat novou implicitní hodnotu nebo specifikovat, zda vlastnost ukládat.

Definování vlastnosti komponenty

V této části je uvedeno jak deklarovat nové vlastnosti a jsou zde uvedeny některé konvence používané ve standardních komponentách. Jsou zde body:

Deklarace vlastnosti

Deklarace vlastnosti a její implementace je snadná. Přidáme deklaraci vlastnosti k deklaraci třídy naší komponenty. V deklaraci vlastnosti specifikujeme tři věci: jméno vlastnosti, typ vlastnosti a metody pro čtení a nastavování hodnot vlastnosti.
Vlastnosti komponent minimálně musíme deklarovat ve veřejné části deklarace typu objektu komponenty, což umožní číst a nastavovat vlastnosti z vnějšku komponenty při běhu programu. K vytvoření editovatelných komponent během návrhu musíme deklarovat vlastnost ve zveřejňované části deklarace třídy komponenty. Zveřejňované vlastnosti jsou automaticky zobrazovány v Inspektoru objektů. Veřejné vlastnosti jsou přístupné pouze za běhu programu.
Následuje typická deklarace vlastnosti Pocet:
class PACKAGE TNaseKomponenta : public TComponent
{
private:
  int FPocet;                            // složka pro uložení vlastnosti
  int __fastcall GetPocet();             // čtecí metoda
  void __fastcall SetPocet(int APocet);  // zápisová metoda
public:
  __property int Pocet={read=GetPocet,write=SetPocet} //deklarace vlastnosti
}

Interní uložení dat (vlastností)

Není žádné omezení na to jak ukládat data vlastnosti. Komponenty C++ Builderu ale používají tyto konvence: Základním principem těchto konvencí je, že pouze přístupové metody vlastnosti mohou přistupovat k datům této vlastnosti. Jestliže metoda nebo jiná vlastnost potřebuje změnit tato data, musí to provést prostřednictvím vlastnosti a ne přímým přístupem k uloženým datům. To zajišťuje, že implementaci zděděné metody můžeme měnit bez vlivu na potomky komponenty.

Přímý přístup

Nejjednodušší možností zpřístupnění dat je přímý přístup. Tj. části read a write deklarace vlastnosti specifikují, že přiřazení nebo čtení hodnoty vlastnosti probíhá přímo se složkou vnitřního uložení bez volání přístupové metody. Přímý přístup je užitečný, když vlastnost nemá vedlejší efekty a chceme ji zpřístupnit v Inspektoru objektů. Často používáme přímý přístup pro část read deklarace vlastnosti a přístupovou metodu pro část write neboť obvykle aktualizujeme stav komponenty na základě nové hodnoty vlastnosti.
Následující deklarace typu komponenty ukazuje vlastnost, která používá přímý přístup pro čtení i zápis:
class PACKAGE TPrikladKomponenty : public TComponent
{
private:
  bool FPouzeProCteni; //vnitřní uložení dat je soukromé
__published:           //umožňuje přístup k vlastnosti během návrhu
  __property bool PouzeProCteni={read=FPouzeProCteni,write=FPouzeProCteni};
}

Přístupové metody

Syntaxe deklarace vlastnosti umožňuje, aby části read a write deklarace vlastnosti specifikovaly přístupové metody místo složky třídy. Přístupové metody musí být soukromé a jsou obvykle deklarované jako virtuální. To umožňuje potomkům přepisovat implementaci a uživatelé komponent nemohou tyto metody volat k modifikaci vlastnosti.
Přístupové metody by neměly být veřejné. Tím zabraňujeme vývojáři aplikace v nedovolené modifikaci vlastnosti voláním některé z těchto metod.
Následuje třída, která deklaruje tři vlastnosti používající indexový specifikátor, což umožňuje aby všechny tři vlastnosti měly stejné čtecí a zápisové metody.
class PACKAGE TPrikladKalendar : public TCustomGrid
{
private:
  int __fastcall GetDatumovyPrvek(int Index);
  void __fastcall SetDatumovyPrvek(int Index, int Hodnota);
public:
  __property int Den = {read=GetDatumovyPrvek, write=SetDatumovyPrvek, index=3, nodefault};
  __property int Mesic = {read=GetDatumovyPrvek, write=SetDatumovyPrvek, index=2, nodefault};
  __property int Rok = {read=GetDatumovyPrvek, write=SetDatumovyPrvek, index=1, nodefault};
};
Protože každý prvek datumu (den, měsíc a rok) je typu int a protože nastavování každého vyžaduje zakódování datumu při nastavení, kód zabraňuje duplikacím sdílením čtecích a zápisových metod pro všechny tři vlastnosti. Můžeme mít tedy pouze jednu metodu pro čtení datumového prvku a další pro zápis datumového prvku.
Následuje čtecí metoda, která získává datumový prvek:
int __fastcall TPrikladKalendar::GetDatumovyPrvek(int Index)
{
  unsigned short ARok, AMesic, ADen;
  int vysledek;
  FDate.DecodeDate(&ARok, &AMesic, &ADen); // rozložení datumu do prvků
  switch (Index)
  {
    case 1: vysledek = ARok;   break;
    case 2: vysledek = AMesic; break;
    case 3: vysledek = ADen;   break;
    default: vysledek = -1;
  }
  return vysledek;
}
Toto je zápisová metoda, která nastavuje příslušný datumový prvek:
void __fastcall TPrikladKalendar::SetDatumovyPrvek(int Index, int Hodnota)
{
  unsigned short ARok, AMesic, ADen;
  if (Hodnota > 0)        // všechny prvky musí být kladné
  {
    FDate.DecodeDate(&ARok, &AMesic, &ADen); // získání datumových prvků
    switch (Index)
    {
      case 1: ARok = Hodnota;   break;
      case 2: AMesic = Hodnota; break;
      case 3: ADen = Hodnota;   break;
      default: return;
    }
 }
 FDate = TDateTime(ARok, AMesic, ADen);  // zakódování modifikovaného datumu
 Refresh();                              // aktualizace viditelného kalendáře
}
Čtecí metoda pro vlastnost je funkce, která nemá parametry a vrací hodnotu stejného typu jako má vlastnost. Podle konvencí, jméno funkce začíná Get a pokračuje jménem vlastnosti. Např. čtecí metoda pro vlastnost nazvanou Pocet by se měla jmenovat GetPocet. Výjimkou je případ vlastnosti typu pole, která předává indexy jako parametry čtecí metodě.
Čtecí metoda pracuje s vnitřním uložením dat a vytváří hodnotu vlastnosti příslušného typu. Je-li vlastnost typu ?pouze pro zápis, není nutné deklarovat čtecí metodu. Vlastnosti, určené pouze pro zápis se používají velmi zřídka a obecně nejsou moc užitečnépis?,
Zápisová metoda pro vlastnost je vždy funkce typu void s jedním parametrem, který je stejného typu jako vlastnost. Parametr může být předáván odkazem nebo hodnotou a jeho jméno může být libovolné. Podle konvencí jméno funkce je Set následované jménem vlastnosti. Např. zápisová metoda pro vlastnost nazvanou Pocet by se měla jmenovat SetPocet. Hodnota předaná v parametru je použita k nastavení nové hodnoty vlastnosti a zápisová metoda musí provést operace potřebné k vytvoření příslušné hodnoty ve vnitřním formátu.Výjimkou k pravidlu jednoho parametru jsou vlastnosti typu pole a vlastnosti používající specifikátor indexu (zde je předávána hodnota indexu jako další parametr).
Je-li vlastnost typu pouze pro čtení, není nutné deklarovat zápisovou metodu. Aby zveřejňované vlastnosti mohly být použity během návrhu musí být definovány jako čtecí i zápisové.
Je vhodné testovat, zda se nová hodnota liší od současné hodnoty před jejím přiřazením. Např. následuje příklad zápisové metody pro vlastnost typu int nazvanou Pocet, která ukládá svou aktuální hodnotu v položce FPocet:
void __fastcall TMojeKomponenta::SetPocet(int Hodnota)
{
  if (Hodnota != FPocet) {
    FPocet = Hodnota;
    Update();
  }
}

Implicitní hodnota vlastnosti

Když deklarujeme vlastnost, můžeme pro ní volitelně deklarovat implicitní hodnotu. Implicitní hodnota vlastnosti komponenty je hodnota nastavená pro tuto vlastnost konstruktorem komponenty. C++ Builder používá deklarovanou implicitní hodnotu k určení, zda ukládat vlastnost v souboru formuláře. Jestliže implicitní hodnotu pro vlastnost nespecifikujeme, pak C++ Builder vlastnost vždy ukládá. Např. když umístíme komponentu z Palety komponent na formulář, pak C++ Builder vytváří komponentu voláním konstruktoru komponenty, který určuje počáteční hodnoty vlastností komponenty.
K deklarování implicitní hodnoty pro vlastnost připojíme klauzuli default k deklaraci (nebo opětovné deklaraci) následovanou implicitní hodnotou. Např.
__property bool JePravda = {default=true};
Poznámka: Deklarací implicitní hodnoty v deklaraci vlastnosti nenastavujeme aktuálně vlastnost na tuto hodnotu. Jako tvůrce komponenty musíme zajistit, že konstruktor komponenty nastaví vlastnost na tuto hodnotu. Jelikož objekty vždy inicializují své datové složky na 0, není nutno v konstruktoru nastavovat celočíselné hodnoty na 0, ukazatele na NULL a logické vlastnosti na false.
Když opakovaně deklarujeme vlastnost, můžeme specifikovat, že vlastnost nemá implicitní hodnotu, i když zděděná vlastnost ji má. K určení, že vlastnost nemá implicitní hodnotu, připojíme klauzuli nodefault k deklaraci vlastnosti. Např.
__property int NoveCislo = {nodefault};
Když deklarujeme vlastnost poprvé, není nutno specifikovat nodefault, protože absence deklarace implicitní hodnoty znamená totéž.
Následuje deklarace komponenty, která obsahuje vlastnost JePravda typu bool s implicitní hodnotou true a konstruktor nastavující implicitní hodnotu:
class PACKAGE TPrikladKomponenty : public TComponent
{
private:
  bool FJePravda;
public:
  virtual __fastcall TPrikladKomponenty(TComponent* Owner);
__published:
  __property bool JePravda = {read=FJePravfa, write=FJePravda, default=true};
};
__fastcall TPrikladKomponenty::TPrikladKomponenty(TComponent * Owner)
  : TComponent(Owner)
{
  FJePravda = true;   //nastavení implicitní hodnoty
}
Pokud by implicitní hodnota pro JePravda měla být false, potom ji není nutno explicitně nastavovat v konstruktoru, neboť všechny třídy (a tedy i komponenty) vždy inicializují všechny své položky na nulu a ?nulová logická á? logická false.

Vytváření pole vlastností

Některé vlastnosti mohou být indexované, podobně jako pole. Mají více hodnot, které rozlišujeme indexem. Příkladem ve standardních komponentách je vlastnost Lines komponenty Memo. Lines je indexovaný seznam řetězců, které tvoří text komponenty a můžeme k němu přistupovat jako k poli řetězců. V tomto případě, vlastnost typu pole dává uživateli přirozený přístup k jistému prvku (řetězci - řádku) ve větší množině dat (textu komponenty).
Vlastnost typu pole pracuje stejně jako ostatní vlastnosti a deklarujeme ji většinou stejně (jsou zde pouze tyto rozdíly): Deklarace vlastnosti obsahuje jeden nebo více indexů určitého typu. Indexy mohou být libovolného typu. Části read a write deklarace vlastnosti, jsou-li specifikovány, pak musí být metodami. Nelze zde specifikovat položky třídy.
Přístupové metody pro čtení a zápis hodnoty vlastnosti přibírají další parametry, které odpovídají indexu nebo indexům. Parametry musí být ve stejném pořadí a stejného typu jako indexy specifikované v deklaraci vlastnosti. Na rozdíl od indexu pole, typ indexu pro vlastnost typu pole nemusí být celočíselného typu. Např. jako index vlastnosti typu pole může být i řetězec. Můžeme se odkazovat pouze na jednotlivé prvky pole a ne na celé pole.
Následuje deklarace vlastnosti, která vrací na základě celočíselného indexu řetězec:
Class PACKAGE TPrikladKomponenty : public TComponent
{
private:
  System::AnsiString __fastcall GetJmenoCisla(int Index);
public:
  __property System::AnsiString JmenoCisla[int Index] = {read=GetJmenoCisla};
};
Toto je metoda GetJmenoCisla v CPP souboru:
System::AnsiString __fastcall TPrikladKomponenty::GetJmenoCisla(int Index)
{
  System::AnsiString Vysledek;
  switch (Index){
    case 0:
      Vysledek = "Nula";
      break;
    case 100:
      Vysledek = "Malé";
      break;
    case 1000:
      Vysledek = "Velké";
      break;
    default Vysledek = "Neznámé";
  }
  return Vysledek;
}

Ukládání a zavádění vlastností

C++ Builder ukládá formuláře a jejich komponenty v DFM souborech. Tyto soubory jsou binární reprezentací vlastností formuláře a jeho komponent. Komponenta přidaná na formulář je zapsána do formuláře a tedy komponenta musí mít schopnost zapisovat své vlastnosti do souboru formuláře při uložení. Obdobně, při zavádění ze souboru formuláře se komponenta musí sama obnovit. Schopnost ukládat a obnovovat svou reprezentaci je zděděné chování komponenty. Někdy ale můžeme potřebovat v tomto mechanismu provést nějakou změnu a potřebuje se tedy s ním seznámit. Následují body:

Používání ukládacího a zavádějícího mechanismu

Popis formuláře obsahuje seznam vlastností formuláře společně s popisem všech komponent vložených na formulář. Každá komponenta včetně samotného formuláře zodpovídá za ukládání a zavádění svého vlastního popisu.
Implicitně (při ukládání sama sebe) komponenta zapisuje hodnoty všech svých veřejných a zveřejňovaných vlastností, které se liší od svých implicitních hodnot a to v pořadí jejich deklarace. Při zavádění, komponenta nejprve vytvoří sama sebe, nastaví všechny vlastnosti na jejich implicitní hodnoty a potom čte neimplicitní uložené hodnoty vlastností.
Tento mechanismus vyhovuje většině komponent a nevyžaduje žádnou akci od tvůrce komponenty. Je ale několik způsobů jak přizpůsobit ukládací a zaváděcí proces potřebný pro některé komponenty.

Specifikování implicitních hodnot

Komponenty ukládají své vlastnosti pouze tehdy, když se jejich hodnoty liší od implicitních hodnot. Pokud nespecifikujeme jinak, pak C++ Builder předpokládá, že vlastnost nemá implicitní hodnotu, což znamená, že komponenta vlastnost ukládá.
Vlastnost jejíž hodnota není nastavena konstruktorem má nulovou hodnotu. Nulová hodnota znamená, ze oblast paměti rezervovaná pro vlastnost je vynulována. Pokud potřebujeme něco jiného, pak implicitní hodnotu specifikujeme explicitně. Např.
__property Alignment = {default=taCenter};
Implicitní hodnotu lze také specifikovat při opětovné deklaraci vlastnosti. Významem opětovné deklarace vlastnosti je určit jinou implicitní hodnotu.
Poznámka: Nezapomeňte, že specifikací implicitní hodnoty není automaticky přiřazena tato hodnota vlastnosti při vytváření objektu. Přiřazení hodnoty je nutno provést v konstruktoru komponenty.
Následující kód ukazuje deklaraci komponenty, která specifikuje implicitní hodnotu pro vlastnost Align a implementaci konstruktoru komponenty, který nastavuje implicitní hodnotu. V našem případě, nová komponenta je speciální případ standardního panelu, který je použit jako stavový řádek v okně (implicitně je umístěn u spodního okraje svého vlastníka).
class PACKAGE TMujStavovyRadek : public TPanel
{
public:
  virtual __fastcall TMujStavovyRadek(TComponent* AOwner);
__published:
  __property Align = {default=alBottom};
};
Konstruktor TMujStavovyRadek v CPP souboru:
__fastcall TMujStavovyRadek::TMujStavovyRadek (TComponent* AOwner)
  : TPanel(AOwner)
{
   Align = alBottom;
}

Určování co ukládat

Můžeme určovat, zda C++ Builder ukládá všechny vlastnosti komponenty. Implicitně všechny vlastnosti ze zveřejňované části deklarace třídy jsou ukládány. Můžeme zvolit, že daná vlastnost nebude ukládána nebo můžeme navrhnout funkci, která bude určovat za běhu, zda vlastnost ukládat.
K určení zda ukládat vlastnost používáme specifikátor stored následovaný znakem = a true, false nebo jménem metody vracející logickou hodnotu.
Následující kód ukazuje komponentu, která deklaruje tři nové vlastnosti. Jedna je vždy ukládána, druhá není nikdy ukládána a třetí je ukládána na základě hodnoty metody Ulozit:
class PACKAGE TPrikladKomponenty : public TComponent
{
protected:
  bool __fastcall Ulozit();
public:
  __property int Ukladana = {stored=true};    // vždy ukládána
   ...
__published:
  __property int Neukladana = {stored=false}; // nikdy ukládána
  __property int Nekdy = {stored=Ulozit};     // uložení závisí na hodnotě funkce
};

Inicializace po zavedení

Po přečtení všech hodnot vlastností komponenty z jejího uloženého popisu, je volána virtuální metoda nazvaná Loaded, která provádí požadované inicializace. Volání Loaded nastane dříve než formulář a jeho ovladače jsou zobrazeny a tedy inicializace nezpůsobí blikání na obrazovce.
K inicializaci komponenty po zavedení jejich hodnot vlastností, přepíšeme metodu Loaded.
Poznámka: První věcí, kterou v této metodě musíme provést je volat zděděnou metodu Loaded. Tím zajistíme, že zděděné vlastnosti jsou správně inicializovány před inicializací našich vlastních vlastností.

  1. Přejděme ke konkrétnímu příkladu. Budeme modifikovat standardní komponentu Memo k vytvoření komponenty, která implicitně neprovádí lámání slov a má žluté pozadí. Je to velmi jednoduchý příklad, ale ukazuje vše, co je zapotřebí provést k modifikaci existující komponenty. Implicitně hodnota vlastnosti WordWrap komponenty Memo je true. Jestliže používáme několik těchto komponent u nichž nechceme provádět lámání slov (automatický přechod na další řádek při dosažení konce řádku) můžeme snadno vytvořit novou komponentu, která implicitně lámání slov neprovádí (implicitní hodnotu vlastnosti WordWrap nastavíme na false). Modifikace existující komponenty probíhá ve dvou krocích: vytvoření a registrace komponenty a modifikace objektu komponenty.

  2. Základní proces je ale vždy stejný, ale u složitějších komponent budeme muset provést více kroků pro přizpůsobení nové třídy. Vytváření každé komponenty začíná stejně: vytvoříme programovou jednotku, odvodíme třídu komponenty, registrujeme ji a instalujeme ji na Paletu komponent.
    V našem příkladě použijeme uvedený obecný postup s těmito specifikami: jednotku komponenty nazveme Memos, od TMemo odvodíme nový typ komponenty nazvaný TWrapMemo a registrujeme TWrapMemo na stránce Samples Palety komponent. Výsledek naši práce je tento (nejdříve je uveden výpis hlavičkového souboru):
    #ifndef MemosH
    #define MemosH
    #include <vcl\sysutils.hpp>
    #include <vcl\controls.hpp>
    #include <vcl\classes.hpp>
    #include <vcl\forms.hpp>
    #include <vcl\StdCtrls.hpp>
    class PACKAGE TWrapMemo : public TMemo
    {
    private:
    protected:
    public:
    __published:
    };
    #endif
    Následuje CPP soubor:
    #include <vcl.h>
    #pragma hdrstop
    #include "Memos.h"
    #pragma package(smart_init)
    static inline TWrapMemo *ValidCtrCheck()
    {
      return new TWrapMemo(NULL);
    }
    namespace Memos
    {
      void __fastcall PACKAGE Register()
      {
        TComponentClass classes[1] = {__classid(TWrapMemo)};
        RegisterComponents("Samples", classes, 0);
      }
    }
    Poznámka: V tomto případě jsme nepoužili k vytvoření komponenty Experta komponent, ale vytvořili jsme ji manuálně. Pokud bychom použili Experta komponent, pak by byl do vytvořené třídy automaticky přidán konstruktor.
    Všechny komponenty nastavují své hodnoty vlastností při vytváření. Když umístíme komponentu během návrhu na formulář nebo když spustíme aplikaci vytvářející komponentu a přečteme její vlastnosti ze souboru formuláře, je nejprve volán konstruktor komponenty k nastavení implicitních hodnot komponenty. V případě zavedení komponenty ze souboru formuláře, po vytvoření objektu s jeho implicitními hodnotami vlastností, aplikace dále nastavuje vlastnosti změněné při návrhu a když je komponenta zobrazena vidíme ji tak, jak jsme ji navrhli. Konstruktor ale vždy určuje implicitní hodnoty vlastností. Pro změnu implicitní hodnoty vlastnosti, předefinujeme konstruktor komponenty k nastavení určené hodnoty. Když předefinujeme konstruktor, pak nový konstruktor musí vždy volat zděděný konstruktor a to dříve než provedeme cokoliv jiného.
    V našem příkladě, naše nová komponenta musí předefinovat konstruktor zděděný od TMemo k nastavení vlastnosti WordWrap na false a vlastnosti Color na clYellow. Přidáme tedy deklaraci předefinovaného konstruktoru do deklarace třídy a zapíšeme nový konstruktor do CPP souboru:
    class PACKAGE TWrapMemo : public TMemo
    {
    public:
      virtual __fastcall TWrapMemo(TComponent* Owner);
    };

    __fastcall TWrapMemo::TWrapMemo(TComponent* Owner)
     : TMemo(Owner)
    {
      Color = clYellow;
      WordWrap = false;
    }
    Poznámka: Pokud použijeme Průvodce komponentou k vytvoření komponenty, pak vše co potřebujeme provést je přidání červeně označených příkazů do konstruktoru.
    Nyní můžeme instalovat novou komponentu na Paletu komponent a přidat ji na formulář. Vlastnost WordWrap je nyní implicitně false a vlastnost Color clYellow.
    Jestliže změníme (nebo vytvoříme) novou implicitní hodnotu vlastnosti, musíme také určit, že hodnota je implicitní. Jestliže to neprovedeme, Builder nemůže ukládat a obnovovat hodnotu vlastnosti. Když C++ Builder ukládá popis formuláře do souboru formuláře, ukládá pouze hodnoty vlastností, které se liší od svých implicitních hodnot. Má to dvě výhody: zmenšuje to soubor formuláře a urychluje zavádění formuláře. Jestliže vytvoříme vlastnost nebo změníme implicitní hodnotu vlastnosti je vhodné aktualizovat deklaraci vlastnosti na novou implicitní hodnotu. Ke změně implicitní hodnoty vlastnosti, opětovně deklarujeme vlastnost a připojíme klauzuli default s novou implicitní hodnotou. Není nutno opětovně deklarovat prvky vlastnosti, pouze jméno a implicitní hodnotu. V našem příkladě provedeme:
    class PACKAGE TWrapMemo : public TMemo
    {
    public:
      virtual __fastcall TWrapMemo(TComponent* Owner);
    __published:
      __property Color = {default=clYellow};
      __property WordWrap = {default=false};
    };
    Specifikace implicitní hodnoty vlastnosti nemá vliv na celkovou práci komponenty. Musíme stále explicitně nastavovat implicitní hodnotu v konstruktoru komponenty. Rozdíl je ve vnitřní práci aplikace: Builder nezapisuje WordWrap do souboru formuláře, jestliže je false, neboť předpokládá, že konstruktor nastaví tuto hodnotu automaticky. Totéž platí i o vlastnosti Color.

  3. Vytvořte komponentu FontCombo (kombinované okno volby písma). Tuto komponentu odvoďte od komponenty ComboBox tak, že změníte implicitní hodnotu vlastnosti Style na csDropDownList a do vlastnosti Items přiřadíte Screen->Fonts. Vyzkoušejte použití této komponenty v nějaké aplikaci (bude signalizována chyba).
  4. Namísto přiřazení seznamu písem v konstruktoru komponenty FontCombo je nutno jej přiřadit v metodě Loaded, kterou předefinujeme (nejprve voláme metodu předka a potom provedeme přiřazení). Vyzkoušejte provést tuto změnu. Nyní již tuto komponentu můžeme používat bez problémů.
  5. V dalším zadání se pokusíme modifikovat komponentu ListBox a to tak, aby v zobrazeném textu mohly být použity znaky tabulátorů (ke stylu okna je nutno přidat příznak LBS_USETABSTOPS). Komponentu nazveme TabList a předefinujeme v ní metodu CreateParams, kterou Builder používá ke změně některých standardních hodnot používaných při tvorbě komponenty. Tato metoda bude vypadat takto:

  6. void __fastcall TTabBox::CreateParams(Controls::TCreateParams &Params)
    {
      TListBox::CreateParams(Params);
      Params.Style |= LBS_USETABSTOPS;
    }
    Komponentu vyzkoušejte.
3. Vytváření vlastností