Živě.cz o počítačích a internetu

Tipy a triky v Delphi, díl 152. - formuláře

Jan Šindelář 25.8.2004

Na minulý díl o formulářích dnes nenápadně navážeme, protože se mi hromadí dotazy na toto téma. Opět se jedná o základní postupy, které však mnozí začínající uživatelé (zvlášť samouci) neznají.

Dnes si tedy opět budeme povídat o formulářích. Začínající programátor v Delphi, obvykle samouk a fanoušek, je často stržen jednoduchostí, s jakou lze některé aplikace vytvářet. Zapomíná pak nahlédnout hlouběji "pod pokličku", protože jeho aplikace funguje a nemá důvod něco měnit. Změna nastane až tehdy, když chce vytvářet programy komplikovanější a kromě jednoho hlavního okna (formuláře) chce mít i okna podřízená. Pak nastanou první problémy. Například s tím, jak mezi jednotlivými okny aplikace předávat hodnoty proměnných. O tom i o dalších souvisejících problémech bude dnešní a příští díl.

Jak přidat do projektu další formulář je snadné a kromě hlavního okna pak aplikace může obsahovat prakticky "neomezené" množství dalších oken. I jejich vyvolání například metodou Show je bezproblémové. Základní projektový soubor v případě jednoho hlavního a dvou podřízených formulářů pak vypadá nějak takto:

program Project1;

uses
  Forms,
  Unit1 in `Unit1.pas` {Form1}
  Unit2 in `Unit2.pas` {Form2}
  Unit3 in `Unit3.pas` {Form3}

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.CreateForm(TForm2, Form2);
  Application.CreateForm(TForm3, Form3);
  Application.Run;
end.

Potud je všechno jasné, aplikace funguje. Co se dá tedy vylepšit? Jak vidíme ve zdrojovém kódu projektu, všechny formuláře jsou metodou CreateForm vytvořeny hned na začátku. V reálné aplikaci ale málokdy potřebujeme mít všechny formuláře vytvořeny hned. Jsou okna, která zahlédneme při používání aplikace stěží jednou (například okno s konfigurací aplikace po prvním spuštění programu po instalaci) a těch opravdu používaných (viditelných) oken může být jen pár. Proč tedy vytvářet třeba velké množství oken hned při startu? Není to zbytečné plýtvání systémovými zdroji? Jistě, v dnešní době operačních systémů generace NT už to není tak problematická záležitost, ale přesto by bylo dobré zvyknout si na určitou míru optimalizace a nespoléhat se jen na robustnost systému. Okna tedy budeme vytvářet dynamicky až tehdy, kdy budou potřeba.

Nejprve tedy ze zdrojového kódu projektu odstraníme řádky, kde jsou vytvářena sekundární okna, případně provedeme tuto úpravu jen u některých oken, kde to má smysl (viz. předchozí odstavec). Můžeme to udělat ručně přímo editací zdrojového kódu a nebo přes nabídku Project -> Options a zde na záložce Forms v seznamu Auto-create forms odstraníme (resp. uklidíme šipkami do sekce Available forms) příslušné formuláře.

Mimochodem, všimli jste si, že o tom, který z formulářů projektu bude ten hlavní, rozhoduje jen jejich pořadí? Nic jiného v tom není. Stačí prohodit na první místo například Form2 (buď opět přímo ve zdrojáku a nebo metodou drag & drop ve zmiňované nabídce) a při spuštění aplikace bude hlavním formulářem. A pro zvídavé čtenáře ještě jeden tip. Zkuste tímto způsobem uklidit všechny formuláře, tj. žádný nebude vytvářen hned po startu aplikace. Co se stane? Nic neočekávaného. Aplikace se prostě spustí a hned se zase ukončí.

Takže, teď se nám po startu aplikace vytvoří jen ty nejdůležitější formuláře (klidně jen ten hlavní). O vytvoření těch ostatních se musíme postarat sami dynamicky za běhu aplikace.

Prvním krokem, který k tomu vede, je to, že musíme přidat do řádku uses (v části implementation) u hlavního formuláře (resp. obecně u toho formuláře, ze kterého voláme jiný) příslušný unit volaného formuláře. V našem případě to bude Unit2. K vlastnímu vytvoření druhého formuláře nám pak poslouží tlačítko, které umístíme na hlavní formulář a příslušný kód vložíme do události OnClick.

Pro zobrazení podřízeného okna máme vlastně dvě možnosti. Buď jej můžeme zobrazit "normálně", takže okno je více či méně rovnocenné oknu hlavnímu, nebo modálně. Modální okno, jak jistě víte, překryje všechno okna dané aplikace a dokud okno není uzavřeno, nelze překliknout do oken jiných (v rámci aplikace). Používají se obvykle pro důležitá dialogová okna, kde pro další běh programu je bezpodmínečně nutné rozhodnutí uživatele.

Začneme tou jednodušší variantou, což je zobrazení modální. V takovém případě nám stačí následující procedura:

procedure TForm1.Button1Click(Sender: TObject);
var
  f: TForm2;
begin
  f := TForm2.Create(Self);
  try
    f.ShowModal;
  finally
    f.Release;
  end;
end;

Jak vidíte, postup je velmi snadný. Potřebujeme pouze nadeklarovat proměnnou typu TForm2 (tedy náš připravený druhý formulář) a metodou Create ho vytvořit. Jediným parametrem tohoto konstruktoru je vlastník nově vznikajícího formuláře. Hodnota Self říká, že to bude náš hlavní formulář. Poté již jen okno zobrazíme metodou ShowModal a po jeho uzavření objekt zaniká metodou Release.

Druhá možnost je zobrazit okno klasickým nemodálním způsobem. Ihned se nám nabízí myšlenka použít stejný kód a nahradit pouze metodu ShowModal metodou Show. Bohužel, tak snadné to není a věc má hned dvě úskalí. Pokud totiž takový kód spustíte, okno se sice na moment zobrazí, ale hned se zase uzavře. Nečeká se totiž na žádnou reakci uživatele jako v případě modálního okna, takže metoda Release je spuštěna ihned po Show. Řešení je jednoduché. V hlavním okně budeme podřízený formulář "jen" zobrazovat a o jeho následný úklid se pak postará podřízený formulář sám v metodě OnClose.

procedure TForm1.Button2Click(Sender: TObject);
var
  f: TForm2;
begin
  f := TForm2.Create(Self);
  f.Show;
end;

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree ;
end;

Druhý problém může být problémem jen v určitém případě, záleží čistě na stavbě vaší aplikace. Zobrazíme-li totiž například pomocí tlačítka druhý formulář nemodálně, nic nám nebrání po jeho zobrazení překliknout zpět do hlavního formuláře a tlačítko stisknout znovu. Co se stane? Celkem nic tragického, bude vytvořena další instance druhého formuláře. To ale někdy může dost vadit a proto si ještě na závěr ukážeme, jak tento problém přelstít.

Myšlenka je celkem zřejmá. Než vytvoříme danou instanci druhého formuláře, přesvědčíme se jednoduchým cyklem, zda již náhodou neexistuje. Pokud ano, přepneme zobrazení na něj, pokud ne, vytvoříme jej.

procedure TForm1.Button2Click(Sender: TObject);
var
  f: TForm2;
  i, ex: integer;
begin
  ex := -1;
  for i := 0 to Screen.FormCount -1 do
    if Screen.Forms[i] is TForm2 then ex := i;
  if ex >= 0 then Screen.Forms[ex].Show
            else
              begin
                f := TForm2.Create(Self);
                f.Show;
              end;
end;

To by tedy mohlo pro dnešek stačit. Teď už tedy víme, že formuláře můžeme z úsporných důvodů vytvářet až dynamicky za běhu aplikace (i když připravené je máme z Delphi IDE pochopitelně již předem) a není k tomu ani potřeba příliš mnoho kódu navíc. Jistá nevýhoda je samozřejmě pomalejší reakce při zobrazení takového okna, protože musí být nejprve vytvořeno. Tento problém by ale mohl být patrný maximálně na opravdu velmi pomalých a starých počítačích. Dnešní ukázkový projekt můžete stahovat zde.

Příště si povídání o formulářích dokončíme a řekneme si něco o jejich vzájemné interakci, předávání hodnot a podobně.