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.
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.
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:
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.
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.
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; } }
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:
#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 }
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:
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.
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.
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.
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.
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.
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):
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).
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.
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.
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í.
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.
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.
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).
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ě.
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; }