4. Otevírání, ukládání a tisk souborů 
  1. C++ Builder poskytuje komponenty k usnadnění úloh otevírání, ukládání a tisku souborů. Tyto komponenty jsou umístěny na stránce Dialog Palety komponent a již jsme se s nimi seznámili. Nyní naše znalosti doplníme.

  2. Aplikace obecně otevírá soubory v reakci na volbu v nabídce, ale můžeme použít i jiné mechanismy. Postup je ale obecně stejný. Aplikace umožní výběr souboru v dialogovém okně otevírání souboru, přečte zvolené jméno a otevře specifikovaný soubor. Následující kód ukazuje obsluhy událostí pro volbu File | Open na hlavním formuláři a v podřízeném formuláři. Obsluha události podřízeného formuláře volá obsluhu hlavního formuláře. Obsluha volby File | Open hlavního formuláře volá metodu Open přiřazenou k podřízenému formuláři.
    void __fastcall TFrameForm::Open1Click(TObject *Sender)
    {
      if(OpenFileDialog->Execute()){    // pokud uživatel vybere soubor
        EditForm=new TEditForm(this);   // je vytvořeno nové pořízené okno
        EditForm->Open(OpenFileDialog->FileName);  // a v něm otevřen soubor
    }
    void __fastcall TEditForm::Open1Click(TObject *Sender)
    {
      FrameForm->Open1Click(Sender);  // volání obsluhy v hlavním formuláři
    }
    Tímto způsobem se provádí obsluha voleb v nabídce u aplikací MDI. Všechny zde uvedené příklady pocházejí z aplikace textového editoru, který budeme vytvářet později.
  3. Je několik případů, kdy musíme k formuláři přidat metody. Jedním z nich je požadavek na přidání nové funkce k typu formuláře. Pro přidání metody k formuláři, přidáme deklaraci metody do deklarace třídy formuláře v hlavičkovém souboru jednotky a zapíšeme implementaci metody do zdrojového souboru jednotky formuláře. Metodu Open přidáme k podřízenému formuláři. Následující řádek zapíšeme do veřejné části deklarace formuláře:

  4. void __fastcall Open(const AnsiString AFileName);
    a implementace této metody vypadá takto:
    void __fastcallTEditForm::Open(const AnsiString AFileName)
    {
       PathName = AFileName;   // přiřazení parametru do proměnné formuláře
       Caption = ExtractFileName(AFileName);
       Editor->Lines->LoadFromFile(PathName);
       Editor->SelStart = 0;
       Editor->Modified = false;
    }
    Když editor zavede soubor, pak metoda Open nastaví hodnotu vlastnosti SelStart editoru na 0. Tato vlastnost reprezentuje počáteční pozici vybraného textu nebo současnou pozici kurzoru (pokud žádný text není vybrán). Implicitně, když editor zavede svůj obsah ze souboru, pak SelStart je nastaven na konec posledního řádku. Vynulováním této vlastnosti přesuneme kurzor na začátek textu.
    Nastavením vlastnosti Modified na false, umožníme zjistit, zda soubor byl změněn. Po provedení změny získá tato vlastnost hodnotu true. Tuto vlastnost můžeme testovat před uzavřením souboru a dotázat se, zda provedené změny chceme uložit.
  5. Po vytvoření nového souboru nebo modifikaci existujícího souboru by aplikace měla umožnit uložit výsledky provedené práce na disk. Aplikace musí umět ukládat existující soubory i nové nepojmenované soubory. Při volbě File | Save aplikace zapíše soubor na stejné místo, odkud byl zaveden. Ukládání souboru je obvykle řešeno následující obsluhou.

  6. void __fastcall TEditForm::Save1Click(TObject *Sender)
    {
      if(Caption == DefaultFileName){
        Saveas1Click(Sender);
      }
      else{
        Editor->Lines->SaveToFile(PathName);
        Editor->Modified = false;
      }
    }
    Nejprve je testováno, zda soubor již byl uložen a to tím, že se zjistí, zda soubor nemá implicitní jméno. Pokud soubor ještě uložen nebyl, pak je volána obsluha volby File | Save As.
    Když ukládáme soubor poprvé (nebo ukládáme existující soubor na jiné místo), pak aplikace zobrazí dialogové okno ukládání souboru, ve kterém můžeme zadat nové umístění a jméno souboru. Obsluha volby File | Save As je obvykle tvořena příkazy:
    void __fastcall TEditForm::SaveAs1Click(TObject *Sender)
    {
      SaveFileDialog->FileName = PathName;
      if (SaveFileDialog->Execute())
      {
        PathName = SaveFileDialog->FileName;
        Caption = ExtractFileName(PathName);
        Save1Click(Sender);
      }
    }
    Při ukládání souborů můžeme také přidat podporu pro vytváření záložních souborů. Zde postupujeme tak, že nejprve zrušíme případný existující záložní soubor, přejmenujeme existující soubor na záložní (obvykle změníme příponu) a uložíme modifikovaný soubor pod původním jménem. K naší původní obsluze File | Save tedy stačí připsat několik řádků:
    void __fastcall TEditForm::Save1Click(TObject *Sender)
    {
      if (PathName == DefaultFileName)
        SaveAs1Click(Sender);
      else
      {
        AnsiString BackupFileName = ChangeFileExt(PathName, ".BAK");
        DeleteFile(BackupFileName);
        RenameFile(PathName, BackupFileName);
        Editor->Lines->SaveToFile(PathName);
        Editor->Modified = false;
      }
    }
  7. Windows poskytuje dialogové okno pro nastavení tiskárny a zadávání tiskových voleb uživatele, ale za nastavení těchto voleb a předání tisku tiskárně je zodpovědná aplikace. C++ Builder také poskytuje objekt Printer, který zaobaluje většinu chování tiskárny, což zjednodušuje spolupráci s tiskárnou. K použití objektu tiskárny, přidáme následující řádek do hlavičkového souboru formuláře:

  8. #include <vcl\Printers.hpp>
    dále získáme ukazatel na objekt Printer (voláním globální funkce Printer) a zapíšeme text nebo grafiku na plátno objektu Printer.
    Dialogové okno Printer Setup je jedno z dialogových oken Windows. Toto dialogové okno pracuje přímo s konfiguračními soubory Windows a aplikace nemusí nic dělat. Pro přidání možnosti nastavování tiskárny k naší aplikaci umístíme komponentu PrinterSetupDialog na formulář a spustíme ji příkazem (např. v obsluze volby File | Print Setup):
    PrinterSetupDialog1->Execute();
    Komponenta PrinterSetupDialog má vlastnost ChangeDefault (dostupnou pouze za běhu aplikace), jejíž hodnota se změní na true, pokud uživatel vybere jiné nastavení tiskárny.
    Obecně tisk ve Windows spočívá v zaslání grafické reprezentace dat na kreslící plochu tiskárny. Pomocí stejné techniky můžeme text zaslat na tiskárnu jako grafiku, ale C++ Builder tuto činnost zjednodušuje. Pro tisk textu z aplikace umístíme na formulář komponentu PrintDialog, spustíme ji (umožňuje nastavení některých tiskových voleb) a zašleme text na tiskárnu.
    K tisku obsahu komponenty RichEdit voláme její metodu Print (tato komponenta umožňuje přímý tisk). Metoda Print přebírá jeden parametr (titulek dokumentu). K tisku obsahu komponenty RichEdit je tedy možno použít obsluhu:
    void __fastcall TEditForm::Print1Click(TObject *Sender)
    {
      if (PrintDialog1->Execute()){
      try {
            Editor->Print(PathName);
        }
      catch(...){
            Printer()->EndDoc();
            throw;
        }
      }
    }
    Tato metoda tisku ignoruje všechny volby v dialogovém okně tisku (je možno pouze vybrat jinou tiskárnu nebo tisk zrušit).
    Když potřebujeme tisknout text, který není obsažen v komponente RichEdit, musíme text zapsat přímo na plátno objektu Printer. Plátno je kreslící plocha komponenty, která odpovídá kontextu zařízení Windows. Plátno tiskárny používáme k nastavení písma tiskárny (vlastnost Font), testováním vlastnosti Font plátna získáme informace o metrice tiskárny a metodami TextOut a TextRect zašleme text na tiskárnu. Následující kód implementuje obsluhu volby File | Print. Je zde nastaveno písmo odpovídající komponentě Memo a potom obsah této komponenty je zaslán na tiskárnu. Písmo není určeno přesně (Windows vybere tiskové písmo, které se nejvíce odpovídá atributům obrazovkového písma).
    void __fastcall TForm1::Print1Click(TObject *Sender)
    {
      TPrinter *pPrinter = Printer(); // Získání ukazatela na Printer
      pPrinter->Canvas->Font = Memo1->Font; // určení písma
      int LineHeight = pPrinter->Canvas->Font->abs(pPrinter->Canvas->Font->Height);
      // výpočet výšky řádku
      int Xpos = pPrinter->Canvas->Font->PixelsPerInch;
      int Ypos = Xpos;    // jeden palec od horního a levého okraje
      pPrinter->BeginDoc();           // zahájení tisku
      for (int i = 0; i < Memo1->Lines->Count; i++)
      {
        // zápis textu dalšího řádku
        pPrinter->Canvas->TextOut(Xpos, Ypos, Memo1->Lines->Strings[i]);
        Ypos += LineHeight;  // přesun na další řádek
      }
      pPrinter->EndDoc();    // ukončení tisku
    }
    Pro tisk obrázku z aplikace C++ Builderu, jednoduše nakreslíme obrázek přímo na plátno tiskárny. Následující kód kopíruje obsah komponenty Image na tiskárnu:
    void __fastcall TForm1::Print1Click(TObject *Sender)
    {
      TPrinter *pPrinter = Printer();
      int AnInch = pPrinter->Canvas->Font->PixelsPerInch;
      pPrinter->BeginDoc();           // začátek tisku
      pPrinter->Canvas->Draw(AnInch, AnInch, Image->Picture->Graphic);
      pPrinter->EndDoc();    // konec tisku
    }
  9. Během návrhu používáme vlastnost Font editačních komponent k nastavení počátečního písma pro text v komponentě. Můžeme také umožnit uživateli změnit písmo za běhu aplikace. Následuje příklad obsluhy volby Character | Font:

  10. void __fastcall TEditForm::Font1Click(TObject *Sender)
    {
      FontDialog1->Font = Edit1->Font;
      if (FontDialog1->Execute())
        Edit1->Font = FontDialog1->Font;
    }
    V komponentě RichEdit lze změnit atributy písma pouze pro vybranou část textu. K modifikaci atributů písma vybraného textu v komponentě RichEdit přiřadíme změny vlastnosti SelAtributes komponenty (namísto vlastnosti Font). Např. následující kód nastavuje tlačítka stylů na paletě v závislosti na stylu vybraného písma:
    BoldButton->Down = RichEdit1->SelAttributes->Style.Contains(fsBold);
    ItalicButton->Down = RichEdit1->SelAttributes->Style.Contains(fsItalic);
    Další příklad používá metodu Assign ke kopírování atributů vybraného písma:
    void __fastcall TMainForm::SelectFont(TObject* /*Sender*/)
    {
      FontDialog1->Font->Assign( RichEdit1->SelAttributes );
      if( FontDialog1->Execute() )
        CurrText()->Assign( FontDialog1->Font );
    }
    Všechny zde uvedené ukázky budou použity ve složitějších aplikacích popisovaných v následujících kapitolách.
  11. Po dokončení vývoje aplikace ji můžeme distribuovat (jejím uživatelům) jedním ze dvou způsobů. Tyto způsoby využívají: statické sestavení nebo dynamické sestavení pomocí balíčků. Balíček je něco jako DLL, ale má příponu BPL. Protože balíček je jistým typem DLL, jsou termíny DLL a balíček zaměnitelné, V C++ Builderu jsou dva typy balíčků: běhové balíčky a návrhové balíčky.

  12. Běhové balíčky obsahují kód naší aplikace potřebný pro spuštění. I když C++ Builder poskytuje mnoho různých balíčků, základní balíček je VCL30.BPL. Tento balíček obsahuje základ kódu VCL v jednom DLL. Jestliže se rozhodneme, že naše aplikace bude používat balíčky, pak aplikace musí zavést VCL30.BPL a volat funkce z tohoto balíčku podle potřeby. Pokud se jedná o databázovou aplikaci, pak je nutno také zavést balíček VCLDB30.BPL. Mimo těchto dvou balíčků existují ještě další balíčky VCL. Mimo balíčků VCL, aplikace může požadovat další balíčky. To může nastat v případě, kdy aplikace používá komponenty jiných dodavatelů nebo komponenty, která jsme si vytvořili sami.
    Většina komponent vytvořených pro C++ Builder je obsažena v běhových i návrhových balíčcích. Běhové balíčky obsahují všechen kód potřebný pro práci s komponentami, zatímco návrhové balíčky obsahují pouze kód potřebný k operování na formuláři během návrhu. Jeden balíček (běhový nebo návrhový) může obsahovat kód pro několik komponent. Není nutno mít samostatný balíček pro každou komponentu. Protože návrhové balíčky obsahují pouze kód potřebný k zobrazení komponenty při návrhu, jsou obvykle mnohem menší než jejich běhové protějšky.
  13. Když aplikace používá statické sestavení VCL a RTL, pak se nepoužívají balíčky. Všechen kód naší aplikace potřebný pro spuštění je sestaven přímo v aplikačním spustitelném souboru. Naše aplikace je samostatný program, který nevyžaduje balíčky nebo DLL. Toto platí, pokud se nejedná o databázovou aplikaci nebo aplikaci používající ovladače ActiveX.

  14. C++ Builder verze 1.0 umožňoval pouze statické sestavení. Statické sestavení má dvě hlavní výhody oproti dynamickému sestavení. První je, že aplikace nevyžaduje žádné dodatečné soubory (vše je uloženo v jednom spustitelném souboru). Druhou výhodou je to, že staticky sestavená aplikace je obecně kratší než aplikace vyžadující balíčky. Statické sestavení má jeden hlavní nedostatek, který se projeví pouze, když používáme mnoho uživatelem definovaných DLL. Nevýhodou je, že kód VCL a RTL je opakovaně přítomný v každém modulu (v samotné aplikaci) a v každé DLL. Aplikace staticky sestavené nemohou tento kód sdílet.
    Při dynamickém sestavení, aplikace dynamicky zavádí kód svých knihoven za běhu. Požadované balíčky a DLL jsou zaváděny za běhu. Požadované balíčky jistě zahrnují jeden nebo více balíčků VCL a mohou zahrnovat i balíčky jiných dodavatelů. Zavádění balíčků naší aplikací probíhá automaticky (není nutno zapisovat žádný kód). Změna způsobu sestavení nevyžaduje žádné změny v našem kódu.
    Dynamické sestavení má oproti statickému sestavení jednu důležitou výhodu. Několik modulů může sdílet stejný kód (balíčky a DLL RTL). Je tedy výhodné u rozsáhlých aplikací, které se skládají z více modulů. Dynamické sestavení je spojeno s několika problémy. Jedním z těchto problémů je to, že balíčky a DLL požadované aplikací jsou značně dlouhé. Další problém se týká verzí. Naše aplikace a balíčky musí být ze stejné verze C++ Builderu.
    Závěrem je možno říci, že pro malé a střední aplikace je výhodnější statické sestavení a pro rozsáhlé aplikace dynamické sestavení. Pokud požadujeme dynamické sestavení nějaké aplikace, pak zvolíme Project | Options, přejdeme na stránku Linker zobrazeného dialogového okna, kde nastavíme značku Use Dynamic RTL a na stránce Packages nastavíme značku Build with runtime packages. Okno uzavřeme a zvolíme Project | Build. To je vše.
  15. Pro šíření aplikací s dynamickým sestavením, potřebujeme znát, které balíčky a DLL aplikace používá. Minimálně to budou VCL30.BPL a CP3240MT.DLL. Mohou to být i další balíčky a to v závislosti na tom, které komponenty aplikace používá. K zjištění, které balíčky jsou zapotřebí, je možno použít utilitu TDUMP.EXE (je v adresáři CBUILDER3\BIN). Výstup z této utility, je vhodné pro snadnější hledání, uložit do souboru. Spustíme ji tedy z příkazové řádky příkazem (je zapotřebí doplnit adresářové cesty):

  16. tdump mujproject.exe >dump.txt
    Vytvořený soubor otevřeme v nějakém textovém editoru a hledáme odkazy na balíčky a DLL. Jsou zde uvedeny ve tvaru:
    Imports from VCL30.bpl
        Consts::initialization() __fastcall
        Consts::Finalization() __fastcall
    Pokud prohledáme celý soubor, pak zjistíme, které balíčky a DLL jsou zapotřebí (je zapotřebí je distribuovat společně s EXE souborem aplikace).
  17.  Nyní se opět budeme zabývat formuláři. Budeme se zabývat řízením uložení formuláře v paměti, získáváním dat z formuláře a předáváním dalších parametrů formuláři.

  18. Implicitně C++ Builder vytváří hlavní formulář aplikace v paměti vložením následujícího kódu do funkce WinMain aplikace:
    Application->CreateForm(__classid(TForm1), &Form1);
    Tato funkce nastavuje globální proměnnou se stejným jménem jako má formulář (každý formulář v aplikaci má přiřazenu globální proměnnou). Tato proměnná je ukazatelem na objekt třídy formuláře (objekt je také vytvořen) a je používána k odkazům na formulář za běhu aplikace. Protože formulář je vytvářen ve funkci WinMain, je zobrazen po spuštění aplikace a existuje v paměti po celou dobu práce aplikace.
    Když k aplikaci přidáme další automaticky vytvářené formuláře, pak tyto formuláře mají implicitně nastavenu vlastnost Visible na false a nejsou viditelné, pokud je nezobrazíme např. voláním ShowModal. V tomto případě, formuláře jsou stále v paměti a není je tedy zapotřebí vytvářet (ani rušit).
    Můžeme ale mít aplikaci, která nevyžaduje stálou přítomnost formuláře v paměti. Některé formuláře můžeme vytvořit pouze pokud jsou zapotřebí (sníží se tím požadavky na paměť). Např. dialogová okna jsou zapotřebí v paměti, pouze v době, kdy s nimi uživatel pracuje. Takovýto formulář je nutno vyřadit ze seznamu automaticky vytvářených formulářů (volba Project | Options a na stránce Forms je přesuneme do pravého sloupce). Tím odstraníme z funkce WinMain vytvoření tohoto formuláře. Je to ekvivalentní ručnímu odstranění příslušného řádku v WinMain:
    Application->CreateForm(__classid(TForm2), &Form2);
    V tomto případě musíme instanci formuláře vytvořit a později zrušit sami. Jednou z možností je použití globální proměnné formuláře takto (předpokládejme formulář nazvaný ResultsForm):
    void __fastcall TMainMForm::FirstButtonClick(TObject *Sender)
    {
     ResultsForm = new TResultsForm(this);
     ResultsForm->ShowModal();
     delete ResultsForm;
    }
    Formulář je vytvořen pomocí operátoru new, funkcí ShowModal je zobrazen a po jeho uzavření je zrušen operátorem delete. V tomto případě si musíme být jisti, že formulář byl vyřazen ze seznamu automaticky vytvářených formulářů (automaticky vytvořený formulář by se stal nedostupný a nebylo by jej možno při ukončení aplikace zrušit).
    Při používání nemodálních formulářů musíme zajistit, že proměnná s ukazatelem na formulář existuje po celou dobu používání formuláře. Musí to být tedy globální proměnná. V mnoha případech použijeme globální proměnnou vytvořenou s formulářem (proměnná stejného jména jako jméno formuláře). Pokud naše aplikace požaduje další instanci formuláře, deklarujeme pro každou instanci další globální proměnnou (typu ukazatel na třídu formuláře).
    Bezpečný způsob k vytvoření unikátní instance modálního formuláře je použití lokální proměnné v obsluze událostí jako odkaz na novou instanci. Při použití lokální proměnné není podstatné zda formulář byl nebo nebyl vyřazen ze seznamu automaticky vytvářených formulářů. Obsluha události může vypadat např. takto:
    void __fastcall TMainMForm::FirstButtonClick(TObject *Sender)
    {
     TResultsForm *rf = new TResultsForm(this); // rf je lokální
     rf->ShowModal();
     delete rf;      // bezpečné zrušení formuláře
    }
    V této verzi nikdy nepoužijeme globální proměnnou formuláře. Tento postup nelze použít pro nemodální formuláře, protože nemodální formulář existuje i po skončení obsluhy.
  19. Většina aplikací se skládá z několika formulářů a mezi formuláři je zapotřebí si vyměňovat nějaké informace. Tyto informace mohou být předávány pomocí přidaných parametrů do konstruktoru formuláře nebo přiřazením hodnot do vlastností formuláře. Způsob získávání informací z formuláře závisí na tom zda formulář je modální nebo nemodální.

  20. Z nemodálního formuláře můžeme snadno získat data voláním metody formuláře nebo testováním vlastností formuláře. Např. předpokládejme aplikaci obsahující nemodální formulář ColorForm, který obsahuje okno seznamu ColorListBox se seznamem barev (red, green, blue, atd.). Jméno vybrané barvy v ColorListBox je automaticky uloženo při výběru nové barvy do vlastnosti CurrentColor. Deklarace třídy formuláře vypadá takto:
    class TColorForm : public TForm
    {
     __published:    // IDE-managed Components
      TListBox *ColorListBox;
      void __fastcall ColorListBoxClick(TObject *Sender);
     private:        // User declarations
      String getColor();
      void   setColor(String);
      String curColor;
     public:         // User declarations
      virtual __fastcall TColorForm(TComponent* Owner);
      __property String CurrentColor = {read=getColor, write=setColor};
    };
    Obsluha události OnClick pro okno seznamu nastaví hodnotu vlastnosti CurrentColor pokaždé, když v okně seznamu je vybrán nový prvek. Vlastnost CurrentColor používá nastavovací funkci setColor k uložení aktuální hodnoty vlastnosti do soukromé složky curColor:
    __fastcall TColorForm::ColorListBoxClick(TObject *Sender)
    {
     int index = ColorListBox->ItemIndex;
     if (index >= 0) {     // zjistíme, zda barva je vybrána
      CurrentColor = ColorListBox->Items->Strings[index];
     }
     else                  // barva není vybrána
      CurrentColor = "";
    }
    void TColorForm::setColor(String s)
    {
     curColor = s;
    }
    Předpokládejme v aplikaci další formulář nazvaný ResultsForm, který potřebuje zjistit právě vybranou barvu v ColorForm (např. při stisku tlačítka UpdateButton na ResultsForm). To provede obsluha:
    void __fastcall TResultsForm::UpdateButtonClick(TObject *Sender)
    {
     if (ColorForm->Name == "ColorForm") { // zjištění existence ColorForm
      String s = ColorForm->CurrentColor;
      // provedeme něco se jménem barvy
     }
    }
    Tato obsluha nejprve zjišťuje, zda formulář ColorForm existuje (testováním jeho vlastnosti Name). Potom je získána hodnota vlastnosti CurrentColor formuláře ColorForm. To je provedeno získávací funkcí getColor formuláře ColorForm:
    String TColorForm::getColor()
    {
     return curColor;
    }
    Nic ale také nezabraňuje jinému formuláři v získání vybrané barvy přímým testováním okna seznamu:
    String s = ColorListBox->Items->Strings[ColorListBox->ItemIndex];
    Použití vlastnosti dělá ale rozhraní ColorForm srozumitelnějším a jednodušším.
  21. Stejně jako nemodální formuláře i modální formuláře často obsahují informace požadované z jiných formulářů. Formulář A zobrazí modálně formulář B. Po uzavření B potřebuje A zjistit nastavení různých voleb na B. Pokud B je ještě umístěn v paměti, pak lze použít stejný postup jako u nemodálních oken. Jiná situace ale nastane, pokud B je uvolněn z paměti při svém uzavření. Jelikož formulář nemá explicitní návratovou hodnotu, musíme ochránit důležité informace z formuláře před jeho zrušením.

  22. Předpokládejme modifikovanou verzi formuláře ColorForm (modální formulář). Deklarace třídy formuláře je nyní tato:
    class TColorForm : public TForm
    {
     __published:    // IDE-managed Components
      TListBox *ColorListBox;
      TButton *SelectButton;
      TButton *CancelButton;
      void __fastcall CancelButtonClick(TObject *Sender);
      void __fastcall SelectButtonClick(TObject *Sender);
     private:        // User declarations
      String* curColor;
     public:         // User declarations
      virtual __fastcall TColorForm(TComponent* Owner);
      virtual __fastcall TColorForm(String* s, TComponent* Owner);
    };
    Formulář obsahuje okno seznamu ColorListBox se seznamem jmen barev. Stiskem tlačítka SelectButton předáme právě vybranou barvu v okně seznamu a formulář uzavřeme. Stiskem CancelButton pouze formulář uzavřeme. Do třídy formuláře byl přidán další konstruktor, přebírající parametr typu String *. Implementace tohoto konstruktoru je:
    void__fastcall TColorForm::TColorForm(String* s, TComponent* Owner)
     : TForm(Owner)
    {
     curColor = s;
     *curColor = "";
    }
    Konstruktor předává předaný ukazatel do soukromé složky curColor a inicializuje řetězec na prázdný řetězec. Takovýto konstruktor musí vytvářet formulář pomocí operátoru new (nelze použít automatické vytváření formuláře při spuštění aplikace). Obsluha události OnClick tlačítka SelectButton bude vypadat takto:
    void __fastcall TColorForm::SelectButtonClick(TObject *Sender)
    {
     int index = ColorListBox->ItemIndex;
     if (index >= 0)
      *curColor = ColorListBox->Items->Strings[index];
     Close();
    }
    Obsluha tedy ukládá vybrané jméno barvy do řetězce předaného v konstruktoru.
    K použití ColorForm, volající formulář musíme konstruktoru předat adresu existujícího řetězce. Např.
    void __fastcall TResultsForm::UpdateButtonClick(TObject *Sender)
    {
     String s;
     GetColor(&s);
     if (s != "") {
      // děláme něco se jménem barvy
     }
     else {
      // neděláme nic, protože barva nebyla zadána
     }
    }
    //---------------------------------------------------------------------
    void TResultsForm::GetColor(String *s)
    {
     ColorForm = new TColorForm(s, this);
     ColorForm->ShowModal();
     delete ColorForm;
    }
    Obsluha stisku tlačítka UpdateButton vytvoří řetězec s. Adresa s je předána funkci GetColor, která vytvoří ColorForm, a předá ukazatel na s jako parametr konstruktoru. Při uzavření ColorForm je tento formulář zrušen, ale jméno vybrané barvy je stále uloženo v s. Pokud ColorForm je uzavřen bez výběru barvy, pak v s je uložen prázdný řetězec.
    V tomto příkladě se používá jedna řetězcová proměnná pro uložení informací z modálního formuláře. V případě potřeby to může být mnohem složitější objekt. Nesmíme ale nikdy zapomenout na předání informace o uzavření modálního formuláře bez provedení výběru (např. použitím tlačítka Cancel).
  23. Obvykle vytváříme formulář v IDE C++ Builderu. Formulář vytvořený tímto způsobem má jeden konstruktor s parametrem ukazujícím na vlastníka formuláře. Při přidání dalších parametrů k formuláři, vytvoříme další konstruktor a formulář vytváříme operátorem new. Následující příklad třídy formuláře byl vytvořen v IDE C++ Builderu. Druhý konstruktor s přidaným parametrem whichButton byl přidán ručně.

  24. class TResultsForm : public TForm
    {
    __published:    // IDE-managed Components
       TLabel *ResultsLabel;
       TButton *OKButton;
       void __fastcall OKButtonClick(TObject *Sender);
    private:        // User declarations
    public:         // User declarations
       virtual __fastcall TResultsForm(TComponent* Owner);
       virtual __fastcall TResultsForm(int whichButton, TComponent* Owner);
    };
    Přidaný konstruktor používá první parametr k nastavení vlastnosti Caption ovladače Label jména ResultsLabel na formuláři:
    void__fastcall TResultsForm::TResultsForm(int whichButton,
        TComponent* Owner) : TForm(Owner)
    {
     switch (whichButton) {
      case 1:
       ResultsLabel->Caption = "Stisknuto první tlačítko!";
       break;
      case 2:
       ResultsLabel->Caption = "Stisknuto druhé tlačítko!";
       break;
      case 3:
       ResultsLabel->Caption = "Stisknuto třetí tlačítko!";
     }
    }
    Kdy vytváříme instanci formuláře s více konstruktory, pak si můžeme vybrat, který konstruktor vyhovuje našim potřebám. Např. následující obsluha stisku tlačítka používá konstruktor s přidaným parametrem:
    void __fastcall TMainMForm::SecondButtonClick(TObject *Sender)
    {
     TResultsForm *rf = new TResultsForm(2, this);
     rf->ShowModal();
     delete rf;
    }
4. Otevírání, ukládání a tisk souborů