Scrigroup - Documente si articole

     

HomeDocumenteUploadResurseAlte limbi doc
BulgaraCeha slovacaCroataEnglezaEstonaFinlandezaFranceza
GermanaItalianaLetonaLituanianaMaghiaraOlandezaPoloneza
SarbaSlovenaSpaniolaSuedezaTurcaUcraineana

BiologieBudovaChemieEkologieEkonomieElektřinaFinanceFyzikální
GramatikaHistorieHudbaJídloKnihyKomunikaceKosmetikaLékařství
LiteraturaManagementMarketingMatematikaObchodPočítačůPolitikaPrávo
PsychologieRůznéReceptySociologieSportSprávaTechnikaúčetní
VzděláníZemědělstvíZeměpisžurnalistika

Umíme to s Delphi - Proč Delphi

počítačů



+ Font mai mare | - Font mai mic



DOCUMENTE SIMILARE

TERMENI importanti pentru acest document

Umíme to s Delphi

Delphi je vývojovým nástrojem, jehož první verze byla vypuštěna firmou Borland v roce 1994. Dnes – o sedm let později – patří Delphi k nejpoužívanějším programovacím prostředkům. Zajímá-li i vás, čím Delphi tak magicky přitahuje vývojáře na celém světě, setrvejte u tohoto seriálu, který vám o Delphi prozradí vše podstatné.



 Delphi je vývojovým nástrojem, jehož první verze byla vypuštěna firmou Borland v roce 1994. Dnes – o sedm let později – patří Delphi k nejpoužívanějším programovacím prostředkům. Přestože je založeno na jazyce Pascal, jenž je sám již poněkud za zenitem, jeho popularita snad stále roste. Zajímá-li i vás, čím Delphi tak magicky přitahuje vývojáře na celém světě, setrvejte u tohoto seriálu, který vám o Delphi prozradí vše podstatné.

Pro koho je tento seriál

Seriál není určen pro úplné začátečníky. Za minimální považuji tuto vaši „konfiguraci“:

  • základní uživatelská znalost operačního systému Windows verze alespoň 95;
  • základní znalosti programovacích technik (např. byste měli vědět, co je cyklus for :-));
  • základní znalost programovacího jazyka Pascal v libovolné mutaci (Turbo Pascal, Object Pascal výhodou).

Co je Delphi

Tento odstavec mohou směle přeskočit všichni, kteří již Delphi někdy viděli. Ostatním prozradím, že Delphi je komplexní vizuální programovací prostředí pro operační systém Windows. Jeho hlavní síla spočívá především ve významu slova vizuální. Vzhled, rozhraní, prostředí vaší aplikace, které byste v Turbo Pascalu nebo v podobném programovacím jazyce tvořili týden, zvládnete vytvořit v Delphi za hodinu.

Vysvětlení tohoto zdánlivého zázraku je dvojí:

  • vlastnosti Windows a vůbec způsob programování pod tímto operačním systémem: o mnoho problémů se vůbec nemusíte starat, zvládne to systém;
  • vytvoření vizuálního prostředí a maximální zjednodušení návrhové fáze umožňuje programátorům věnovat se vlastní algoritmizaci.

Co si představit pod kouzelným slůvkem „vizuální“, kterým se neustále oháním? Nejlepší představu si zřejmě uděláte z obrázku. Největší šedá plocha se nazývá formulář. Na něj myší umístíte tlačítka, nápisy, dialogy, textová pole a veškeré další prvky. Nazýváme je komponenty. Tím vytvoříte vzhled aplikace.

Na zvolenou komponentu budeme nahlížet za dvou pohledů.

  • Z pohledu jejích vlastností. Můžete nastavovat např. velikost, barvu, kurzor, font, styl, okraj, viditelnost nebo vlastnosti spojené s dědičností.
  • Z pohledu jejích událostí, přesněji z pohledu událostí, které detekuje systém, ale vy na ně chcete reagovat. Zjednodušeně řečeno - co se stane, když např. na komponentu kliknete myší (lze rozlišit událost typu „tlačítko myši bylo stisknuto“, „tlačítko myši bylo uvolněno“), změníte její velikost, stisknete nad ní klávesu, apod.

Proč Delphi

Již v době, kdy bylo vytvořeno první Delphi, si programátor mohl vybírat z mnoha vývojových nástrojů. Zdůrazňuji to proto, že totéž platí i dnes. Rozdíl je však v tom, že slovo „mnoho“ dnes označuje mnohonásobně větší počet vývojových nástrojů a pomůcek. Programátoři tedy musejí mít mnoho pádných důvodů, proč preferovat právě Delphi. Vyjmenovat valnou většinu je obtížné, vyjmenovat všechny je nemožné. Přesto se lze alespoň pokusit dobrat se podstatných bodů:

firma Borland je známým výrobcem spolehlivých, osvědčených kompilátorů pro Pascal a C;

jednoduchý návrh vizuálního prostředí aplikace s využitím vlastností operačního systému;

objektově orientovaný přístup;

podpora databází, v posledních verzích velmi podstatná a v dnešní době velmi ceněná (nezbytná);

neobsáhnutelné množství integrovaných nástrojů (namátkou TeamSource pro správu činností);

významná podpora Internetu (např. integrovaná komponenta WebBrowser);

možnost vytvářet vlastní komponenty;

a mnoho dalších důvodů

Co je nového

Již jsem zmínil, že poslední vydanou verzí Delphi (informace ze dne 2.3.2001) je verze 5. Verze 6 se podle Richarda Kubáta, ředitele Borlandu.cz, připravuje pro druhé až čtvrté čtvrtletí roku 2001. Pátá verze existuje ve třech edicích - Standard, Professional a Enterprise (řazeno od nejořezanější). Abyste si mohli vybrat edici, která vám bude nejvíce vyhovovat, podívejme se na hlavní rozdíly:

  • Edice Standard je určena především začátečníkům v oblasti programování pod Windows. Neobsahuje zdrojové texty knihoven komponent a nenajdeme v ní ani podporu pro práce s databázemi. Cena se pohybuje kolem 4 370 Kč.
  • Edice Professional je již podobná verzi Enterprise, obsahuje zdrojové kódy, podstatné omezení spočívá v absenci komponent pro vývoj aplikací Klient/Server. Nelze také přistupovat k datům pomocí technologie ADO, tento nedostatek však lze eliminovat dokoupením příslušných komponent. Cena je podstatně vyšší, zhruba 21 360 Kč.
  • Edice Enterpise je kompletní vývojový nástroj; je samozřejmě také nejdražší: chcete-li jej, musíte se rozloučit s 97 150 Kč! Stále je to však méně, než když budete pořizovat nejnovější JBuilder ve verzi Enterprise (vizuální vývojový nástroj, na rozdíl od Pascalu je však základem jazyk Java), který stojí skoro 125 000 Kč.

Pozn.: ceny jsou uvedeny např. na https://shop.inprise.cz/shop/template_category.php

Podrobnější seznam dostupných komponent jednotlivých verzí viz následující tabulka:

Součást

Standard

Professional

Enterprise

Rychlý vývoj aplikací (RAD)

částečně

úplně

úplně

Standardní komponenty pro RAD

Objektově orientovaná architektura

částečně

úplně

úplně

Integrované vývojové prostředí

částečně

úplně

úplně

Podpora vývoje vícejazyčných aplikací

částečně

úplně

úplně

Nástroje Translation Suite

ne

ne

úplně

Nástroje CodeInsight

částečně

úplně

úplně

ActiveInsight

částečně

úplně

úplně

Ladicí nástroje

částečně

částečně

úplně

Visual Component Library

částečně

úplně

úplně

Corba

ne

ne

úplně

Podpora Oracle8

ne

ne

úplně

ActiveX

ne

úplně

úplně

Vývoj databázových aplikací

ne

částečně

úplně

Na závěr této kapitoly poznámku ke kompatibilitě verze 5 s předchozími verzemi: platí, že aplikaci napsanou ve starší verzi Delphi v „pětce“ otevřete, ale je zde právě to jedno významné ale. Pátá verze obsahuje poměrně velké množství novinek a některé mohou (negativně) změnit chování vaší aplikace. Nejde o základní věci – budete-li mít „obyčejnou“ aplikaci, která bude obsahovat „obyčejné“ komponenty, nemusíte se ničeho bát. Jde-li ovšem o „vypečenější“ projekt, můžete se dostat do problémů. Většina jich je popsána v nápovědě v páté verzi, takže se nemusíte bát, že byste se dostali do slepé uličky, považuji ovšem za slušné na toto úskalí upozornit. Problémy mohou způsobit změny v definici datových typů (např. HRESULT byl definován jako 32bitový integer bez znaménka, nyní je to 32bitový integer se znaménkem), změny parametrů funkcí (do funkce CustomDrawItem přibyl parametr) apod.

Konec začátku

Tolik na úvodní stručné seznámení s vývojovým prostředím Delphi. V příštích dílech seriálu se už podíváme na konkrétní ukázku programu, na základní prvky aplikací (tj. komponenty), vysvětlíme si, jak vytvořit aplikaci a kterak reagovat na události.

Proč zdravit svět?

Pokud jste někdy otevřeli libovolnou učebnici programování, s velkou pravděpodobností jste kdesi na začátku spatřili vytvoření „programu Ahoj, světe!“. Je to tradice, ukázat na tomto triviálním příkladu možnosti, způsob práce a vůbec styl programování daného jazyka. My se tohoto zvyku přidržíme.

Volba komponent

Vytvoření první aplikace rozložíme do posloupnosti jednoduchých kroků.

  • Nainstalujte a spusťte Delphi :). Další popis se bude týkat verze 5, ovšem ve většině starších verzí bude zcela identický.
  • Z hlavní nabídky vyberte File – New… - Application (naleznete v záložce New). Uprostřed obrazovky se objeví hlavní formulář (okno) vaší nové aplikace.
  • V paletě komponent (v horní části obrazovky, vpravo vedle panelu s tlačítky) vyberte záložku Standard a v ní komponentu Label (je to velké písmeno A). Necháte-li kurzor myši chvíli nad libovolnou komponentou, objeví se „hint“ (krátká nápověda) s názvem komponenty.
  • Klikněte na formulář do místa, kde chcete Label (nápis) mít. Label bude vložen a Delphi mu automaticky přiřadí název Label1
  • Stejným způsobem umístěte na formulář komponentu Button (je také pod záložkou Standard a skrývá se pod malým tlačítkem s nápisem OK).
  • Na formuláři máme nyní dvě komponenty: Label1 a Button1

Nastavení vlastností komponent

  • Klikněte na Label1 na formuláři. V levé části pracovní plochy uvidíte tzv. Object Inspector, což je okno umožňující správu komponent. Klikněte na záložku Properties v Object Inspectoru. Tím jste zvolili, že budete nastavovat vlastnosti komponenty. V levém sloupci Object Inspectoru vidíte názvy vlastností, v pravém jejich hodnoty. Klikněte tedy do levého sloupečku na slovo Caption a napište Ahoj, světe!. Stiskem Enteru název potvrdíte (není nutné).
  • Na formuláři se již změnilo slovo Label1 na Ahoj, světe! Nyní klikněte na tlačítko s nápisem Button1. V Object Inspectoru se otevřou vlastnosti tohoto tlačítka.
  • Opět klikněte v Object Inspectoru na slovo Caption a napište Konec

Nastavení reakcí na události

  • Nyní máme nastaveny vlastnosti našich dvou komponent (v tomto elementárním příkladě jsme nastavovali jen vlastnost Caption, jistě si ale dovedete představit, že tímto způsobem se budou nastavovat i další vlastnosti).
  • Pokračovat budeme reakcemi na události. Nejprve si řekneme, které události budeme ošetřovat. Opět jich nebude mnoho, konkrétně jedna. Kliknutí na tlačítko Konec by mělo ukončit aplikaci. Proto otevřete v Object Inspectoru komponentu Button1 (pozor: změnou Caption se změnil jen nápis na komponentě, ne její jméno; v programu proto nadále bude vystupovat jako Button1!). Otevření komponenty provedete nejsnáze tak, že na ni kliknete na formuláři, případně ji vyberete z rozbalovacího seznamu v horní části Object Inspectoru.
  • V Object Inspectoru dále vyberte záložku Events (události). Budeme ošetřovat kliknutí na komponentu, proto se zaměřte na řádek OnClick. Teď přijde klíčový okamžik: dvakrát klikněte do prázdného políčka v pravém sloupci v řádce OnClick.

Vlastní programování

  • V Object Inspectoru se v místě, kde jste poklepali myší, objevil nápis Button1Click. Procedura tohoto jména bude spuštěna vždy, když uživatel klikne na tlačítko Button1. Navíc zmizel hlavní formulář a místo něho se v prostřední části obrazovky otevřelo okno Unit1.pas. To je hlavní vývojové okno, do kterého se píše vlastní programový kód. Delphi automaticky vytvořilo hlavičku procedury Button1Click, do ní vložilo klíčová slova begin a end a na vás je poslední maličkost: říci, co se vlastně bude dít. To za vás nikdo nevymyslí.
  • V našem případě ovšem nepůjde o nic převratně složitého. Do řádky, ve které bliká kurzor, jednoduše napište Application.Terminate
  • Středník na konci příkazu je velmi důležitý, nezapomeňte na něj.

Uložit, přeložit, spustit

  • První aplikace je hotova! Zbývá nám překlad a spuštění. Jde opět o velmi jednoduchou činnost. Zvykněte si ovšem před kompilací vždy ukládat všechny zdrojové texty – nevíte, co se může stát.
  • Proto vyberte v hlavním menu File – Save All. Ke stejnému účelu můžete použít i tlačítko v horní liště.
  • Nyní se otevřelo okno, ve kterém musíte zadat názvy ukládaných souborů. Doporučuji nejprve vytvořit adresář a do něho uložit všechny soubory projektu – ke každému projektu vytvoří Delphi více souborů a bude-li více projektů sdílet jeden adresář, nevyznáte se v něm. Potom zadejte jméno souboru s unitou (programovou jednotkou), tedy se zdrojovým textem (v našem případě s procedurou Button1Click). Název si zvolte libovolný, například Okno1
  • Pak se otevře druhé okno, do kterého zadáváte název celého projektu. Napište třeba Ahoj
  • Tím je hotovo uložení. Teď už jen klikněte na zelený trojúhelníček v liště (nebo vyberte z menu Run – Run, případně stiskněte F9).
  • Spustila se naše slavná aplikace, zkuste si kliknout na tlačítko Konec. Funguje?

Gratuluji!

Vytvoření první aplikace máme úspěšně za sebou. Podíváte-li se do adresáře, do kterého jste ukládali zdrojové soubory (Okno1.pas a Ahoj.dpr), uvidíte tam mimo jiné soubor Ahoj.exe. Ten můžete kdykoliv (ve Windows) spustit a kochat se pohledem na svou krásnou, funkční první aplikaci.

Palety komponent

Komponenty, které chceme použít, vybíráme z tzv. palety komponent.

Palet je více a každá z nich shromažďuje komponenty určitého typu. Spustíte-li Delphi, budou palety komponent standardně zobrazeny v pravé horní části obrazovky (viz obrázek). Seznam dostupných komponent můžete získat (a žádanou komponentu zvolit) také v hlavním menu (View – Component List).

Paleta Standard

Nyní se podíváme na jednotlivé komponenty. Nelze popsat všechny, vybereme jen ty nejpodstatnější. Paleta Standard obsahuje nejzákladnější, nejpoužívanější, nejjednodušší komponenty:

  • MainMenu, PopupMenu – komponenty pro tvorbu hlavního a kontextového menu. Delphi má k dispozici tzv. Menu Designer, ve kterém provedete detailní návrh položek menu. Tvorbě nabídek budeme věnovat celou kapitolu našeho seriálu.
  • Label – nápis. Tato komponenta neumí (zjednodušeně řečeno) nic jiného, než se zobrazit. Přesto u ní lze reagovat na 11 různých událostí!
  • Edit – editační pole. Slouží k výpisu nebo k zadání jednořádkového údaje.
  • Memo – rozšířené editační pole. Slouží k výpisu nebo zadání delšího textu. Může fungovat např. jako základ primitivního textového editoru, nechcete-li použít složitější RichEdit.
  • Button – tlačítko. Jedna z nejpoužívanějších komponent.
  • ChechBox – zaškrtávací políčko. Slouží k zadání nebo zobrazení informace s logickou hodnotou (ano, ne). Umožňuje uživateli vybírat z několika voleb v případě, že lze vybrat více hodnot, ale zároveň není třeba vybrat žádnou.
  • RadioButton – přepínací tlačítko. Jde o podobnou komponentu jako CheckBox, ale na rozdíl od ní odpovídá výlučnému výběru. Jeho dalším rysem je nutnost výběru (jedno tlačítko musí být vždy stisknuté). Smysluplným příkladem by mohlo být použití RadioButtonu pro volbu fontu písma a CheckBoxu pro výběr typu tučné, kurzíva, podtržené.
  • ListBox – seznam. Slouží pro výpis většího množství hodnot stejného (podobného) typu.
  • ComboBox – rozbalovací seznam. Jeho výhodou je úspora místa. Kromě toho, je-li použit pro výběr hodnoty, umožňuje uživateli zadat nový údaj, který v seznamu dosud není.
  • ScrollBar – posuvná lišta.
  • GroupBox, RadioGroup, Panel – komponenty umožňující shlukovat jiné prvky do logických skupin. Mají podstatný (nejen vizuální) význam.

Další palety

Z dalších palet vybereme opravdu jen reprezentativní vzorky.

  • BitBtn (paleta Additional) – tlačítko, na rozdíl od Buttonu však umožňuje zobrazit na sobě bitmapu. Můžete tak snadno vytvořit tlačítko libovolného vzhledu.
  • Image (Additional) – obrázek. Na formulář můžete umístit obrázek v mnoha formátech.
  • RichEdit (Additional) – editační pole s mnoha nadstandardními vlastnostmi. Správným použitím RichEditu můžete například vytvořit relativně kvalitní textový editor.
  • StatusBar (Additional) – stavový pruh. Vaše aplikace bude vypadat mnohem lépe, rozšíříte-li ji o stavový pruh, ve kterém budete uživatele informovat o aktuálně platných volbách, případně o tom, co se právě děje. Zkuste si představit Word bez stavového pruhu.
  • Timer (System) – časovač. Bude-li např. vaše aplikace provádět jakoukoliv činnost periodicky, musíte použít časovač.
  • MediaPlayer (System) - přehrávač záznamů. Umožňuje pracovat s běžnými hudebními a video formáty.
  • DataSource, Table, Query (Data Access) – komponenty pro práci s databázemi. Umožňují standardním způsobem přistupovat k databázovým tabulkám a velmi tak usnadňují práci s databázemi v Delphi. Databázím v Delphi bude věnována vlastní kapitola.
  • OpenDialog, SaveDialog… (Dialogs) – standardní dialogy. Chcete-li otevřít soubor, nemusíte vytvářet vlastní okno, ve kterém jej uživatel bude moci najít. Totéž platí např. pro výběr barvy či fontu, pro tisk nebo pro vyhledávání slova v textu.
  • SpinEdit (Samples) – praktická komponenta, vhodná např. k zadávání celočíselné hodnoty s omezeným rozsahem. Kromě klasického zapsání dovoluje uživateli také najít hodnotu kliknutím na šipku

Můžete namítnout, že jsem opomenul množství důležitých a zajímavých komponent. Vzhledem k rozsahu článku a k jeho zaměření na začátečníky ale nelze jinak. Slibuji, že ke spoustě dalších komponent se dostaneme v dalších kapitolách.

Vyzkoušejte sami

Zkusíte-li si vytvořit novou aplikaci a umístit na její formulář několik komponent, ať už výše popsaných nebo úplně jiných, zjistíte zajímavý fakt. Některé komponenty se na formuláři zobrazí ve své konečné podobě (Label, Button, Edit…), některé ale jsou reprezentovány malým čtverečkem. Může jít například o komponenty, které budou za běhu aplikace vypadat pokaždé jinak, nebudou vidět vůbec, případně vytvoří vlastní nové okno. Příkladem neviditelné komponenty je třeba časovač (Timer), který se navenek nijak neprojevuje, ale v aplikaci jej máte a můžete využívat všech jeho funkcí (přesněji metod).

Některé komponenty mají svůj „Designer“, který usnadní nastavení vzhledu a vlastností. Příkladem je již zmíněné MainMenu, ale také PopupMenu nebo StatusBar. Pro ilustraci práce s touto pomůckou si ukážeme, jak navrhnout stavový řádek rozdělený do čtyř částí.

Vytvoření stavového řádku

Umístěte kamkoliv na formulář komponentu StatusBar. Sama se „přichytí“ ke spodnímu okraji formuláře; to je způsobeno nastavením vlastnosti Align (zarovnání), o které si povíme v příštím díle. Nyní na stavový řádek dvakrát klikněte a otevře se vám okno, jehož vzhled závisí na používané verzi Delphi. Na obrázku vidíte okno verze 5.

Nyní čtyřikrát klikněte na jedinou aktivní ikonku (ve verzi 3 na tlačítko Add). Všimněte si, že stavový pruh se okamžitě rozdělil na čtyři panely. Představte si, že programujete grafický editor a že byste chtěli mít ve stavovém pruhu panely s těmito informacemi:

  • název otevřeného souboru,
  • aktuální styl čáry,
  • aktuální barva,
  • aktuální font písma.

Aktuální obsahy jednotlivých panelů budete muset samozřejmě nastavovat za běhu programu, při návrhu je však dobré nastavit hodnoty, které předpokládáte při prvním spuštění aplikace (tedy jakési „default“ hodnoty). Řekněme, že budete mít standardně předvolen soubor Nový.bmp, plnou čáru, černou barvu a font Times New Roman.

V okně Editing StatusBar1.Panels postupně klikejte na názvy jednotlivých panelů (TstatusPanel). V Object Inspectoru se budou zobrazovat vlastnosti jednotlivých panelů, nikoliv celého stavového řádku. Nastavením vlastnosti Text docílíte vložení hodnot (viz obrázek).

Aby náš stavový pruh vypadal opravdu profesionálně, je třeba změnit šířky jednotlivých panelů. To provedete změnou vlastnosti Width (šířka) jednotlivých panelů. Rozměry se zadávají v pixelech. Název souboru může být poměrně dlouhý, proto nastavte minimálně 300 pixelů (ale i to bude pravděpodobně málo). Pro styl a barvu snad postačí 80 pixelů. Šířku posledního panelu nastavovat nemusíte; panel bude vždy široký přesně do konce okna (nezměníte-li nastavení zmíněné vlastnosti Align celého StatusBaru). Výsledný vzhled stavového pruhu viz obrázek.

Všimněte si (ale to už je trochu „vyšší matematika“), že k jednotlivým panelům budete přistupovat pomocí indexů. Indexace začíná od nuly, takže změníte-li v programu barvu na zelenou, musíte také provést následující přiřazení:

StatusBar1.Panels[2].Text := ‘Zelená’;

Co jsou to vlastnosti

Obecně řečeno, vlastnosti jsou atributy tříd, které určují stav objektu (v našem případě komponenty) a jeho chování. Neznáte-li pojmy jako atribut, třída či objekt, nezoufejte, při práci v Delphi se bez nich zpočátku hravě obejdete. Během tvorby aplikace můžeme zjistit (a modifikovat) hodnotu vlastnosti pomocí Object Inspectoru (viz obrázek). Za běhu aplikace je možné k vlastnostem přistupovat pomocí čtení a zápisu zprostředkovanými jednoduchým programovým kódem.

V Object Inspectoru nalezneme pouze ty vlastnosti, které jsou přístupné v době návrhu aplikace. Kromě nich ovšem existují tzv. run-time vlastnosti (vlastnosti běžící komponenty). Ty jsou přístupné až při běhu aplikace, nikoliv v době návrhu. Aby nebylo teorie málo, rozlišujeme dále read-only a write-only vlastnosti (pouze ke čtení, resp. k zápisu). Dva posledně zmíněné typy jsou obyčejně přístupné jen za běhu aplikace. Jakého typu je vlastnost, kterou právě chcete použít, zjistíte vždy v nápovědě. V dalším textu této kapitoly se budeme zpravidla (avšak nejen) zabývat vlastnostmi přístupnými v době návrhu.

Poznámky pro pokročilé uživatele

  • Z technického hlediska spočívá rozdíl mezi návrhovými a run-time vlastnostmi pouze v tom, že návrhové vlastnosti jsou deklarovány v sekci published deklarace třídy. Vše, co není deklarováno v sekci published, je nepřístupné v době návrhu, a je tedy run-time. Důvodem je fakt, že pro published atributy je generována tzv. runtime type information, která umožňuje Delphi kromě jiného zpřístupnit hodnoty vlastností např. při ukládání a načítání formuláře <.DFM) nebo při zobrazení v Object Inspectoru.
  • Vlastnosti lze přidělit hodnotu nebo lze tuto hodnotu číst, lze ji použít ve výrazu, ale ne vždy lze vlastnost předávat jako parametr do procedury, funkce či metody. Přesněji řečeno – vlastnost můžete použít jako parametr předávaný hodnotou, ale nikoliv odkazem (var).

Společné vlastnosti

Již jsem zmínil, že mnohé vlastnosti jsou společné pro většinu komponent. Nyní se na ty nejdůležitější podíváme.

Jméno a titulek komponenty

Každá komponenta v Delphi má své jméno (vlastnost Name). Pokud nepřidělíte jméno ručně, Delphi pojmenuje komponentu automaticky. Názvy jsou tvořeny složením názvu komponenty (např. Button) a pořadového čísla, např. Button5. Jméno komponenty musí být jedinečné v rámci vlastníka. Zjednodušeně řečeno: můžete mít v aplikaci dva formuláře, z nichž každý obsahuje komponentu stejného jména.

Naproti tomu titulek komponenty (vlastnost Caption) může být zcela libovolný, může obsahovat mezery a může být stejný jako titulky jiných komponent, bez ohledu na vlastníka. Titulek se objeví např. v záhlaví okna (u komponenty Form) nebo přímo na těle tlačítka (Button). Titulkem nelze opatřit zdaleka všechny komponenty, ale jen ty, u kterých to má smysl (dovedete si představit titulek u scrollbaru?).

Velikost a poloha komponenty

Polohu komponenty udávají vlastnosti Left a Top. Souřadnice se nevztahují k celé obrazovce, ale ke klientské oblasti komponenty předka. Znamená to, že umístíte-li na formulář tlačítko, budou se souřadnice tlačítka vztahovat k levému hornímu rohu formuláře. Dáte-li ale na formulář nejprve panel a na něj tlačítko, vztažným bodem pro tlačítko bude levý horní roh panelu.

Velikost komponenty je určována vlastnostmi Height a Width (výška, šířka). Stejně jako v případě polohových vlastností je velikost udávána v pixelech; formulář veliký 1024x768 bude (při stejném rozlišení monitoru) zabírat celou plochu obrazovky.

U některých komponent můžete nastavit, jakým způsobem budou „přichyceny“ k formuláři nebo panelu. Tato vlastnost se jmenuje Align. Většina jejích možných hodnot vás nepřekvapí (alBottom, alTop, alLeft, alRight, alNone), zajímavá je hodnota alClient. Znamená, že komponenta se bude „rozprostírat“ v celé klientské oblasti.

Dostupnost komponenty

Pomocí dvou základních vlastností lze řídit působení uživatele na komponentu. Nastavíte-li hodnotu vlastnosti Enabled na false, komponentu zakážete. V době návrhu nebude mít toto nastavení žádný vizuální efekt, v době běhu aplikace bude komponenta zpravidla šedá, což uživateli naznačí, že je nedostupná (viz obrázek).

Chcete-li být radikálnější, můžete komponentu úplně skrýt, buď použitím příslušné metody nebo nastavením vlastnosti Visible na false.

Poznámka pro pokročilé uživatele

Přečtením vlastnosti Visible ještě nezjistíte, zda je komponenta opravdu viditelná. Je-li totiž skrytý vlastník této komponenty, nic jí nebude platná kladná hodnota její vlastnosti Visible. Z toho důvodu existuje vlastnost Showing, která je typu run-time a read-only. Přečtením její hodnoty zjistíte, zda je prvek pro uživatele v daném okamžiku skutečně viditelný.

Vlastnost Tag

Tag (v překladu přívěsek) je velmi zvláštní vlastnost, protože její nastavení nemá vůbec žádný efekt. Jde pouze o dodatečné paměťové místo, kam lze ukládat různé, libovolné uživatelské hodnoty. Standardně lze do přívěsku uložit hodnotu LongInt (což je celé číslo v rozsahu –2147483648 až 2147483647), ale pomocí přetypování do něj můžete vložit ukazatel nebo cokoliv jiného o velikosti 4 bajty.

Barva a typ písma komponenty

Vlastnosti Color a Font se užívají k úpravě vzhledu komponenty. Samotná vlastnost Color obvykle udává barvu pozadí komponenty. Atribut Color existuje také pro typ písma a mnohých dalších grafických prvků. Název barvy je možné specifikovat pomocí konstant clXXX, kde za XXX dosadíte buďto název barvy (anglicky) nebo název barvy, kterou Windows používají pro systémové prvky. Příklady viz tabulka.

Označení

Význam

clGreen

zelená barva

clBtnFace

barva povrchu tlačítka

clCaptionText

barva textu v titulním pruhu aktivního okna

clHighlight

barva pozadí vybraného textu

clWindow

barva pozadí oken

Další možností je specifikovat barvu číslem.

Poznámka pro pokročilé uživatele

Většina komponent obsahuje také vlastnosti ParentColor a ParentFont udávající, zda má daný prvek používat stávající typ písma a barvu svého vlastníka (kterým je často formulář). Pomocí těchto vlastností můžete změnit písmo každého řídicího prvku formuláře pouhým nastavením vlastnosti Font vlastního formuláře.

Plovoucí nápověda

Velkou výhodou každé aplikace je, když se uživateli zobrazí plovoucí nápověda, jestliže nechá kurzor chvíli bez pohybu nad některým prvkem (např. dozví-li se, co se přesně stane po stisku daného tlačítka). K tomu slouží vlastnost Hint, do které napíšete text nápovědy, a vlastnost ShowHint, která říká, má-li tato nápověda být zobrazena (např. pokročilý uživatel může zobrazování této nápovědy potlačit volbou v menu a vy jen nastavíte ShowHint, příp. ParentShowHint.

Tabulátor

Pokud má vaše aplikace více ovládacích prvků (což má zpravidla vždy), je dobré, aby „inteligentně“ fungovala klávesa Tab. Vytvoříte-li okénko s dotazem typu „opravdu chcete skončit?“, měl by stačit stisk Enteru ke kladné odpovědi. Stisk Tab a Enteru by naopak měl vyvolat zápornou odpověď. Toho lze docílit nastavením vlastností TabOrder a TabStop. TabStop říká, zda se vůbec na danou komponentu lze dostat tabulátorem, a TabOrder říká, kolikátá v pořadí bude. Počítá se od nuly.

Co jsou události

Když uživatel provede nějakou činnost s komponentou, například na ni klikne, generuje tato komponenta odpovídající událost. Někdy jsou události také generovány operačním systémem, bez zjevného uživatelova přispění.

Poznámka pro pokročilé uživatele:

Z technického hlediska jsou události Delphi vyvolávány poté, co dojde k obdržení zprávy operačního systému. Počet těchto událostí nemusí vždy nutně odpovídat počtu zpráv.

Podobně, jako existuje skupina vlastností, která je společná více (případně všem) komponentám, nalézáme v Delphi události, jež jsou dostupné více (všem) komponentám. Krátký popis nejpoužívanějších z nich (a případné tipy) viz následující tabulka:

Událost

Kdy k ní dojde

Poznámky

OnChange

při změně objektu (nebo jeho obsahu)

Používána často u komponent typu Edit, Memo. Souvisí s read-only run-time vlastností Modified, která říká, byl-li obsah změněn.

OnClick

při kliknutí levým tlačítkem myši na komponentu

Jedna z nejpoužívanějších komponent vůbec.

OnDblClick

při dvojkliknutí levým tlačítkem myši na komponentu

OnEnter

v okamžiku, kdy je komponenta aktivována

Nejde o aktivaci formuláře (okna), například při přepnutí z jiné běžící aplikace, ale o aktivaci dané komponenty; tedy například kliknete-li do Editu, apod.

OnExit

V okamžiku, kdy je komponenta deaktivována

Opak předchozí události; je např. vyvolána, skončíte-li zadávání do Editu, a kliknete jinam, apod.

OnKeyDown

v okamžiku, kdy je daná komponenta aktivní a uživatel stiskne klávesu

Lze použít parametru Key, který říká, která klávesa byla stisknuta; lze rozlišovat současný stisk kláves Shift, Alt, Ctrl.

OnKeyPress

v okamžiku, kdy je daná komponenta aktivní a uživatel stiskne klávesu

Rozdíl oproti OnKeyDown spočívá v tom, že parametr Key je typu char, událost je vyvolána jen při stisku kláves odpovídajících ASCII znakům (tedy ne Shift, F1, apod.).

OnKeyUp

v okamžiku, kdy daná komponenta je aktivní a uživatel uvolní klávesu

Klíčovým momentem je uvolnění klávesy (pohyb nahoru); parametr Key je stejný jako u OnKeyDown.

OnMouseDown

v okamžiku, kdy uživatel stiskne některé tlačítko myši

Zpravidla je poslána komponentě, která leží pod kurzorem myši.

OnMouseMove

v okamžiku, kdy uživatel pohne myší nad komponentou

OnMouseUp

v okamžiku, kdy uživatel uvolní některé tlačítko myši

Je-li současně stisknutých více tlačítek, OnMouseUp je generována vícekrát při uvolnění každého z nich.

Události formuláře (okna)

Výše uvedené události jsou často používané a patří asi k nejdůležitějším, rád bych ale ještě zmínil některé unikátní události formuláře. Když budete mít v programu dvacet tlačítek, budete pravděpodobně ošetřovat dvacet událostí OnClick. Formulář budete mít jeden, přesto je ošetření jediné vlastnosti OnActivate nebo OnCreate možná důležitější než dvacet „OnClicků“.

Událost

Kdy k ní dojde

Poznámky

OnActivate

stane-li se okno aktivním

Je generována, přepne-li se uživatel do okna z jiného okna téže aplikace, nikoliv z jiné aplikace.

OnDeactivate

přestane-li být okno aktivním

Je generována, přepne-li se uživatel do jiného okna téže aplikace, nikoliv do jiné aplikace.

OnClose, OnCloseQuery

je-li formulář zavřen (Alt-F4, křížek v rohu, systémové menu…)

Při zavírání formuláře je nejprve generována OnCloseQuery, poté OnClose. První se používá např. pro potvrzení („Chcete opravdu skončit?“) nebo pro výzvu k uložení dat. Nicméně i v OnClose je ještě šance zavření zabránit a pomocí parametrů lze okno třeba jen skrýt či minimalizovat.

OnCreate, OnDestroy

při vytvoření, resp. zrušení formuláře

OnShow, OnHide

při zobrazení, resp. skrytí formuláře

Tato událost úzce souvisí s vlastností formuláře Visible.

Formulář má nepochybně další důležité vlastnosti, nemůžeme však popisovat všechny.

Poznámka pro pokročilé uživatele:

Při vytváření viditelného formuláře (např. při startu aplikace, vlastnost Visible = true) , je sekvence generovaných událostí následující:

  • OnCreate
  • OnShow
  • OnActivate
  • OnPaint

V události OnDestroy by měly být zrušeny (Dispose) všechny objekty dynamicky vytvořené v OnCreate (New).

Jak pracovat s událostmi

Vlastní vložení reakcí na události je velmi jednoduché, problémy zpravidla nastávají až v okamžiku, kdy je třeba naprogramovat konkrétní akce. Vše si vysvětlíme na jednoduchém příkladu, který využijeme i v dalším textu při hledání chyb.

Vytvoříme jednoduchou aplikaci, která po svém otevření nastaví jako svůj titulek slovo „Události“. Bude obsahovat dvě tlačítka, na jednom se zobrazí nápis „Konec“ jen tehdy, když nad ním bude kurzor myši (to není zrovna chytré řešení a v reálných aplikacích jej příliš často nevyužívejte J.). Druhé tlačítko bude mít titulek „Vypočti“ a po jeho stisknutí proběhne jednoduchá smyčka (for cyklus).

  • Vytvoříme novou aplikaci a umístíme na formulář dvě tlačítka. Titulek jednoho tlačítka (Caption) nastavíme na prázdný (prostě nic), druhého na „Vypočti“. Titulek okna necháme zatím beze změny. Tlačítka přejmenujeme (Name) na btnKonec, resp. btnVypocti, formulář na wndHlavni (tím se změní jeho titulek; nevadí, ignorujme to). Zmenšíme formulář – nastavíme výšku (Height) na 130 a šířku (Width) na 200. Pokud máte s těmito činnostmi problémy, přečtěte si předchozí díly seriálu, najdete tam jejich podrobný popis a ukázky.
  • V Object Inspectoru si otevřeme hlavní okno (wndHlavni) a klikneme na záložku Events (události). Vybereme např. vlastnost OnCreate a dvakrát klikneme do políčka vedle ní. Otevře se nám okno Unit1.pas. Nyní budeme programovat obsluhu události. Na místo, kde bliká kurzor, napíšeme (je vhodné dodržet odsazení, např. 2 mezery nebo tabulátor) wndHlavni.Caption := `Události`; Středník na konci sice není velmi důležitý J (za posledním příkazem před Endem není v Pascalu nutný), ale vřele doporučuji jej uvést!
  • Zařídíme, aby se na ukončovacím tlačítku zobrazoval titulek. Toho dosáhneme tak, že ošetříme událost formuláře wndHlavni OnMouseMove. Kód bude btnKonec.Caption := ``;
  • Nyní nastavíme stejnou vlastnost (OnMouseMove) tlačítka btnKonec: btnKonec.Caption := `Konec`;
  • Další akcí bude ošetření události OnClick tlačítka btnKonec: Application.Terminate; J
  • Posledním úkolem je nastavit událost OnClick druhého tlačítka, tedy btnVypocti. Tam napíšeme krátký cyklus, který využijeme v dalším textu. Deklarujeme také celočíselné proměnné i, j. Celá procedura bude vypadat takto (tučně je kód, který píšeme my):

procedure TwndHlavni.btnVypoctiClick(Sender: TObject); var   i, j: integer; begin   j := 0;   for i := 1 to 10 do     j := j + 10; end;

Hledáme chyby

Pokud vytvořenou aplikaci přeložíte a spustíte (doporučuji též uložit), bude pracovat správně. Takový ideální stav ale bohužel není dosažen vždy. Proto v Delphi nacházíme integrovaný debugger s množstvím ladicích nástrojů a nástrojů pro detekci chyb v kódu. Je jich opravdu dost, my se podíváme na několik nejpoužívanějších, které patří k těm jednodušším, přesto jsou vcelku výkonné.

Poznámka pro pokročilé uživatele:

Kdykoliv spustíte program v prostředí Delphi, běží vlastně v integrovaném debuggeru. Tím je zajištěna použitelnost breakpointů a všech dalších ladicích nástrojů. Trochu jiná je také reakce na výjimky v debuggeru a ve vlastním operačním systému.

Co si vlastně představit pod pojmem „chyby v kódu“? Chyby můžeme (velmi hrubě) rozdělit do dvou částí:

  • chyby detekované překladačem,
  • chyby, které překladač neobjeví.

Chybami, na které upozorní již překladač, se nebudeme zabývat. V takovém případě kurzor spočívá na řádce ve zdrojovém souboru, kde se chyba vyskytuje a je zobrazena chybová hláška. Kliknete-li na tuto hlášku a stisknete-li F1, vypíše se nápověda s podrobným popisem příslušné chyby.

Horší jsou zpravidla chyby, které překladač nedetekuje. V takovém případě program spustíte a žijete v přesvědčení, že běží správně. Pokud je chyba skutečně „vypečená“, program většinou běhá dobře a jen někdy, zřídkakdy jsou třeba výsledky výpočtu špatné. Pak nastává fáze ladění a známého „odvšivování“ (debugg).

Ladíme a odvšivujeme

Podotýkám, že následující text není kompletním referenčním popisem příslušných nástrojů. Klade si za cíl jen seznámit začínající uživatele s nejběžnějšími možnostmi integrovaného debuggeru. Všechny níže uvedené nástroje se nacházejí v hlavním menu v položce Run.

Krokování Trace Into

Trace Into je klasické krokování programu po jednotlivých řádkách. Nejsnáze jej vyvoláte klávesou F7 a jejím každým dalším stiskem se posune provádění programu o další řádku. Narazí-li se přitom na volání podprogramu, skočí se do něj a pokračuje se stejným způsobem řádku po řádce.

Krokování Step Over

Step Over je podobné krokování jako Trace Into, ale s tím rozdílem, že při nalezení volání podprogramu je tento vykonán jako jednolitý blok a není tedy prováděn po řádkách. Vyvolá se klávesou F8.

Run to Cursor

Nastavíte-li kurzor na kteroukoliv řádku v kódu a stisknete-li F4, program se normálně spustí a poběží až do řádky, na které byl kurzor. Pak se provádění zastaví.

Breakpoints

Breakpoint (bod přerušení) funguje tak, že řeknete Delphi, aby běh programu zastavil v určeném bodě. Běžným použitím je nastavení breakpointu na některou řádku v kódu. Pak program spustíte (F9) a jakmile se vykonávání programu dostane na danou řádku, je zastaveno a vy můžete používat další nástroje, např. prohlížení obsahu proměnných, apod. – viz dále. Breakpointů můžete mít ve zdrojovém kódu nastaveno více najednou. Dostanete je do svého programu např. tak, že najedete na příslušnou řádku kurzorem a v menu najdete Run – Add Breakpoint… (ve verzi 3) nebo Run – Add Breakpoint – Source Breakpoint (ve verzi 5). Breakpointů existuje více typů, ale o tom se prozatím netřeba šířit.

Watch

Jedním z mnoha významů slova Watch je „číhat“. Myslím, že celkem vystihuje to, co se s nástrojem Watch často dělá. Zvolíte si některou (i nejednu) z proměnných (nebo libovolný výraz), breakpointem (nebo F4) se dostanete na začátek „problematického“ bloku a třeba krokováním se posouváte řádku po řádce a stále sledujete obsah dané proměnné (eventuálně hodnotu zadaného výrazu). Číháte na okamžik, kdy hodnota začne být podezřelá nebo vyloženě špatná, a tak se doberete původu chyby. Watches naleznete v menu Run – Add Watch… (nebo pomocí zkratky Ctrl-F5).

Evaluate/Modify

Tímto nástrojem můžete za běhu prohlížet, ale především měnit hodnoty výrazů, proměnných či vlastností. Je to výborný nástroj třeba v okamžiku, kdy chcete zjistit, jak by se program choval, kdyby v tom íčku nebyla sedmička, ale dva tisíce. Evaluate/Modify se skrývá v menu Run – Evaluate/Modify… (nebo Ctrl-F7).

Poznámka pro pokročilé uživatele:

Jediné, v čem Evaliate/Modify odmítne spolupracovat, je použití výrazu obsahující lokální nebo statické proměnné, které nejsou přístupné z daného místa; a dále funkční volání.

Co když zamrznete?

V průběhu ladění dojde občas k zamrznutí programu, nebo se prostě dostanete do situace, kdy chcete ladění přerušit a začít znovu. V takovém případě použijte příkaz Run – Program Reset

(nebo Ctrl-F2).

Vyzkoušejte a vymyslete

Vyzkoušejte si použití zmíněných nástrojů na programu, který jsme vytvořili výše. Nebude to snadné, protože v něm není příliš co zkoumat (a nač číhat J), ale pro seznámení se zmíněnými nástroji postačuje.

Budete-li chtít sami sobě, případně někomu z vašich blízkých dokázat, že se z vás skutečně stává Delphi – expert, zamyslete se nad následující otázkou: umístíte-li breakpoint třeba na řádku j := j + 10;

breakpoint nezabere a program se na dané řádce nezastaví (přestože by měl, a to nejednou). A co je ještě tajuplnější – když třeba tělo for cyklu rozšíříte o podmínku if j = 40 then   wndHlavni.color := clRed;

Breakpoint ponechaný na původním místě najednou zabere! Víte, proč tomu tak je? Důvod prozradím v dalším díle seriálu (pokud jej tedy některý aktivnější Delphař nenapíše do příspěvku pod článek :).

Řešení domácího úkolu

V minulém díle (5. kapitola) se na konci článku vyskytoval jakýsi námět k zamyšlení pro vás. Ptal jsem se, proč „nezabere“ breakpoint umístěný dovnitř jednoduchého for cyklu v našem jednoduchém příkladě. Nuže, důvodem je to, že Delphi provádí při překladu (nenastavíte-li jinak) optimalizaci. Překladač zjistil, že proměnnou, do které jsme uvnitř cyklu přičítali číslo 10, nikde za cyklem v programu nevyužijeme (výslednou hodnotu z jí nečteme), proto celý cyklus vyřadil a ten se v přeloženém programu neobjevil. Breakpoint umístěný do jeho těla tudíž nemohl „zabrat“. Když jsme potom do cyklu přidali test na hodnotu proměnné j, cyklus se ve výsledku objevil a breakpoint „zabral“.

Struktura programu v Delphi

A teď už k dnešnímu tématu. Ze všeho nejdříve se podívejme na to, jak vypadá struktura souborů projektu, tedy těch souborů, které použije Delphi k vytvoření spustitelného programu.

Program v Delphi je „zkonstruován“ ze zdrojových souborů (lidově zdrojáků), které se nazývají units (programové jednotky, soubory *.PAS). Každá jednotka je uložena v separátním souboru a je kompilována samostatně. Když jsme v předchozích kapitolách psali libovolný programový kód, psali jsme jej právě do jednotky *.PAS. Zkompilované jednotky (soubory *.DCU) jsou již přímo spojeny s vlastním vytvářením aplikace (tzv. linkování).

Výhody jednotek jsou zřejmé. Jednotky umožňují (kromě jiného):

  • rozdělení většího programu do modulů, které tvoří logické celky a mohou být editovány samostatně,
  • vytváření knihoven, které můžeme používat více aplikacemi,
  • šíření knihoven mezi ostatní vývojáře, aniž bychom uvolnili zdrojový kód.

V tradičním Pascalském programu je veškerý zdrojový kód (včetně „hlavního“ programu) uložen v souboru *.PAS. Delphi využívá k uložení „hlavního“ programu souboru *.DPR, přičemž valná většina ostatního zdrojového textu se nachází v jednotce (jednotkách) - *.PAS. Kód, který je v souboru projektu <.DPR), je vytvářen automaticky a v drtivé většině případů jej sami nijak needitujeme. Tento soubor především organizuje soubory jednotek v aplikaci. Vše, co sami po aplikaci chceme, uvádíme zpravidla do jednotek <.PAS).

Poznámka pro pokročilé uživatele:

Soubor *.DPR z technického hlediska hlavně vytváří instance tříd formulářů (form) a po jejich úspěšném vytvoření spouští aplikaci.

Každá aplikace (nebo projekt) sestává z jednoho souboru projektu <.DPR) a jedné nebo více jednotek <.PAS). K vytvoření spustitelného programu potřebuje Delphi buď zdrojové kódy nebo již kompilované soubory všech jednotek <.PAS nebo *.DCU). Jak asi víte, seznam používaných jednotek se uvádí za klausuli Uses.Většinou jej ovšem Delphi spáchají automaticky.

Klausule uses poskytuje kompilátoru informaci o vztazích mezi moduly. Protože tato informace je uložena přímo v jednotkách (modulech) samotných, Object Pascal nevyžaduje makefiles, hlavičkové soubory (headers) nebo instrukci preprocesoru „include“, jak to známe třeba z jazyka C.

Poznámky pro pokročilé uživatele:

  • Project Manager v Delphi generuje makefile pokaždé, když je projekt načten v IDE, ale ukládá tyto soubory jen pro skupiny projektů (které obsahují více než jeden projekt).
  • Také „include“ soubory lze v Delphi použít. Používá se k tomu direktiva překladače . Neuvedete-li příponu, předpokládá se *.PAS. Text z uvedeného souboru se přímo vloží před překladem.

Soubory projektu

Abych shrnul výklad z předchozí kapitoly, uvedu nyní seznam všech souborů používaných Delphi při překladu a vytváření projektu:

„Pascalské“ zdrojové soubory

Přípona

Typ

Obsah a význam

*.PAS

soubory jednotek (units)

většina zdrojového kódu aplikace. Jedna aplikace jich zpravidla obsahuje větší počet.

*.DPR

soubor projektu

aplikace obsahuje právě jeden. Organizuje soubory jednotek.

*.DPK

soubory balíků (packages)

tyto soubory jsou podobné souborům projektu, ale jsou používány ke konstrukci speciálních dynamicky linkovaných knihoven zvaných packages. V jednoduchých aplikacích zpravidla nejsou použity.

Automaticky generované, „nepascalské“ soubory

Přípona

Typ

Obsah a význam

*.DFM

soubory formulářů

zpravidla textové soubory obsahující informace o formulářích, vč. řetězců, bitmap, atd.

*.RES

resource soubory

standardní Windows-resource soubor, zde je kvůli ikoně aplikace

*.DOF

soubor s volbami projektu

obsahuje nastavení kompilátoru a linkeru, adresáře, informace o verzi apod.

Poznámka pro pokročilé uživatele:

Kromě těchto nejdůležitějších souborů se v adresáři s aplikací nalézá mnoho dalších souborů. Mnohé jsou sice vcelku důležité, ale málokdy je uživatel ručně edituje. Stručně si je vyjmenujme:

  • *.CFG – konfigurační soubor projektu,
  • *.DCI – změny Code Insight,
  • *.DCT – změny šablon komponent,
  • *.DMT – změny šablon menu,
  • *.DSK – desktop settings - obsahuje informace o uspořádání oken na obrazovce a o další konfiguraci,
  • *.RPS, *.DFN – údaje o lokalizaci zdrojů (generuje nástroj Integrated Translation Environment),
  • *.TDS – externí tabulka symbolů ladění (debug symbol table)
  • *.TODO – soubor to-do, obsahuje aktuální seznam operací, jež je třeba vykonat, vztažené k aktuálnímu projektu.

Správa většího projektu

V této kapitole si povíme několik základních zásad správy projektu a štábní kultury.

Pojmenování komponent

Není vhodné ponechávat komponentám jejich implicitní jména, která jim přiděluje Delphi. Pokud vytváříte jednoduchou aplikaci, jejímž jediným úkolem je spustit výpočet po stisku jediného přítomného tlačítka, je to celkem jedno; takové aplikace ovšem nejsou příliš časté. Mnohem lepší je přejmenovávat komponenty podle nějakého transparentního klíče, tedy např. tlačítka na btnXXX, kde XXX popisuje funkci tlačítka, např. btnKonec, btnVypocti, apod. Formuláře je dobré pojmenovávat frmXXX (a možná ještě lépe wndXXX), editační okna třeba edtXXX, apod. Jde o to, aby byl program čitelný s odstupem času pro vás samotné (a ještě spíše pro někoho, kdo bude číst „zdrojáky“ po vás).

Více formulářů

Není problémem mít v aplikaci více oken. Je to zcela běžné a většina aplikací disponuje více okny (formuláři). Chcete-li přidat do návrhu aplikace nový formulář, vyberte jednoduše z menu File – New Form. Delphi automaticky vytvoří nové soubory související s přidávaným formulářem. Pokud např. do jednoho modulu ze svého programu doplníte volání funkce (metody) z jiného vašeho modulu a zapomenete v sekci uses uvést jméno modulu, ve kterém se funkce (metoda) nachází, Delphi se zeptají (při kompilaci), zda to mají provést, a vám stačí jednou kladně odpovědět.

Poznámka pro pokročilé uživatele:

Přesněji řečeno, dojde k tomu, že Delphi přidají sekci uses do části implementation (nikoliv interface), a pokud tam již existuje, přidají do ní nový modul.

Není ovšem dobré zbytečně formuláři „hýřit“. Na spoustu věcí vůbec nemusíte navrhovat nová okna, stačí použít některý standardní dialog nebo box. Uživatelské prostředí vaší aplikace by mělo být vytvářeno v souladu s principem prvořadosti uživatele. To znamená, že primárním cílem vývojáře není vyřádit se při návrhu designu aplikace, ale využít uživatelových návyků při určitých činnostech a umožnit mu aplikovat je i v novém produktu. Pokud je uživatel zvyklý na standardní způsob, jakým mu pět aplikací něco sděluje, je nanejvýš vhodné v šesté aplikaci využít zcela stejného způsobu.

Štábní kultura kódu

O štábní kultuře by spíše měl pojednávat seriál vyučující Pascal, proto se omezím skutečně jen na naprosté minimum. Následující doporučení by vám měla umožnit psát čitelné a přehledné zdrojové texty. Jde skutečně pouze o doporučení a nemusíte je dodržovat.

  • Velká a malá písmena. Delphi (a vůbec Pascal) není case-sensitive jazyk. Přesto je dobré zavést si v malých a velkých písmenech pořádek a dodržovat určitou logiku (název „bntKonecClick“ vypadá rozhodně lépe než „btnkonecclick“ nebo (nedej Bože) dokonce „BTNKONECCLICK“. Totéž platí o názvech proměnných, konstant a objektů.
  • Komentáře. Hojným (avšak účelným) používáním komentářů si ušetříte mnoho problémů v budoucnu. Komentář vytvoříte buď uzavřením příslušného textu do nebo napsáním dvou lomítek // na začátek řádky.
  • Odsazení, volné řádky, mezery. Nešetřete volnými řádkami a maximálně využívejte odsazení. Také mezery okolo operátoru udělají své!

Nyní se podívejte na příklad jedné metody. Nejprve bude napsána „ošklivě“:

procedure TForm3.FormActivate(Sender: TObject); var i,x,y:integer; begin i:=-1; repeat inc(i); until(Form2.ListBox1.Selected[i]); Edit1.Text:=IntToStr(PreparujX(Form2.ListBox1.Items[i])); Edit2.Text:=IntToStr(PreparujY(Form2.ListBox1.Items[i])); end;

Nyní „hezká“ verze:

procedure TwndSeparuj.FormActivate(Sender: TObject); var   i : integer; begin   i := -1;   repeat        inc (i);   until (wndData.lstKomplex.Selected[i]);   edtSourX.Text := IntToStr(PreparujX(wndData.lstKomplex.Items[i]));   edtSourY.Text := IntToStr(PreparujY(wndData.lstKomplex.Items[i])); end;

Úplně nejlepší je podívat se, jakým způsobem řeší „štábní kulturu“ samotné Delphi. Budete-li pokračovat v jeho stylu (pokud jde např. o velikost písmen), bude zdroják vypadat konzistentně a čitelně.

Hierarchie tříd

Následující text může začátečníkovi připadat poněkud složitý. Stane-li se vám při čtení něco podobného, neházejte flintu do žita. Hierarchie tříd není věc, kterou byste při programování jednoduchých aplikací museli nezbytně znát. Přesto jsem její popis zařadil, protože jednoho dne nastane okamžik (a ještě ke všemu u každého programátora jindy), kdy se může hodit, že o ní něco víte.

Systémová knihovna Delphi se nazývá Knihovna vizuálních komponent (Visual Component Library, VCL). Srdcem Delphi je hierarchie tříd. Každá třída v systému je potomkem datového typu TObject, takže celá hierarchie má jediný kořen. Tím je umožněno používat TObject jako náhradu za jakýkoliv datový typ v systému. K detailnímu vysvětlení tohoto jevu bychom museli zabrousit do hloubi objektově orientovaného přístupu, na což bohužel nemáme prostor. Stačí, když budete vědět, že při používání komponent vlastně vytváříme instance (tj. cosi jako konkrétní výskyty) koncových tříd hierarchie - listů ze stromu hierarchie. Dvojice Object Inspector a paleta komponent umožňuje umisťovat komponenty VCL na formulář, stejně jako měnit jejich vlastnosti, bez nutnosti psát kód.

Poznámka pro pokročilé uživatele:

Například metody reagující na událost obvykle obsahují parametr Sender typu TObject. Z výše uvedeného důvodu tedy můžeme jako odesílatele uvést prvek libovolné třídy VCL, protože je určitě odvozen od třídy TObject. TObject je abstraktní třídou, jejíž metody zapouzdřují základní chování, jako jsou např. vytvoření, zrušení a manipulace se zprávami.

Abyste nebyli z chaosu popsaného v předchozím odstavci úplně znechuceni, pokusím se jemně osvětlit rozdíl mezi instancí a třídou na krátkém příkladu:

var   Neco : Tneco  // TNeco - třída begin     Neco := TNeco.Create;  //  Neco - nově vytvořená instance     práce s instancí ..// nyní můžu pracovat s Neco     Neco.Free;    // zrušení instance end;

Tímto způsobem je také možné přidávat komponenty do programu za jeho běhu, tedy nikoliv ve fázi návrhu, ale run-time. V jakékoliv metodě formuláře můžeme například vytvořit nové tlačítko následujícím způsobem:

var   btnNovy : TButton begin   btnNovy := TButton.Create(self);   btnNovy.Parent := self;   btnNovy.Left := 100;   btnNovy.Top := 200;   btnNovy.Visible := True; end;

V okamžiku, kdy vytváříme novou komponentu, v zásadě knihovnu VCL rozšiřujeme, a proto používáme množství nástrojů Object Pascal, které uživatelé komponent zřídka potřebují. Problematice tvorby vlastních komponent bude věnována samostatná kapitola tohoto seriálu. Velkou výhodou knihovny VCL (a Delphi vůbec) je, že k používání komponent nepotřebujeme znát detailně strukturu hierarchie tříd. Potřebujeme pouze rozumět listům stromu, tj. komponentám.

Standardní boxy aneb „Ano, Ne, Storno“

V aplikacích velmi často potřebujeme získat od uživatele nějaký vstup. Komplexně se na zadávání informací a uživatelský vstup podíváme v příštím díle, ale už dnes začneme jednodušší variantou problému. Jedním z nejčastějších uživatelských vstupů je „parlamentní hlasovací“ odpověď - potvrzení, odmítnutí, nebo zrušení (zdržení se rozhodnutí).

Kromě toho (ne úplně zřídka) nastává situace, kdy je třeba uživatele informovat o aktuálním stavu výpočtu, upozornit jej na chybu nebo mu vynadat za špatný vstup. Pro oba zmíněné případy disponuje Delphi velmi elegantním řešením - standardními dialogovými boxy. Jak uvidíme za chvíli, použití těchto boxů je naprosto triviální, přesto poskytují širokou škálu voleb a možností zobrazení.

Nejjednodušší je ShowMessage

Při popisu boxů začneme od toho nejjednoduššího. Potřebujeme-li vypsat jednu krátkou informaci (zpravidla větu, alespoň se to tak doporučuje), nejsnáze použijeme proceduru ShowMessage. Člověk nemusí být zrovna expertem na angličtinu, aby si název funkce přeložil a aby jej pochopil. Syntaxe je následující:

procedure ShowMessage(const Msg: string);

Příklad:

ShowMessage(’Toto je krátká informace, milý uživateli.’);

Více umí MessageDlg

Vše, co umí ShowMessage, ale ještě mnohem víc, nabízí funkce MessageDlg. Umožňuje poměrně velkou variabilitu při návrhu vzhledu boxu. Syntaxe:

function MessageDlg(const Msg: string;     DlgType: TMsgDlgType;     Buttons: TMsgDlgButtons;     HelpCtx: Longint): Word;

Popis parametrů:

  • Msg: řetězec, který chcete zobrazit
  • DlgType: indikuje účel dialogu. Možné hodnoty:
    • mtWarning – žlutočerná ikonka značící výstrahu,
    • mtError – červená „stopka“ značící chybu, všichni ji dobře známe :),
    • mtInformation – modré písmeno 'i' značící informaci,
    • mtConfirmation – modrý otazník značící dotaz (žádost o potvrzení),
    • mtCustom – na boxu nebude standardně zhola nic.
  • Buttons: indikuje, která tlačítka se na dialogu objeví. Možné hodnoty:
    • mbYes, mbNo, mbOK, mbCancel, mbAbort, mbRetry, mbIgnore, mbAll, mnNoToAll, mbYesToAll, mbHelp
    • Pozor, jde o množinu, proto musíte zvolená tlačítka uvádět vždy v hranatých závorkách, např. [mbAbort, mbRetry, mbIgnore].
    • Jedinou výjimkou je použití některé z předdefinovaných konstant (např. mbOKCancel má stejný význam jako [mbOK, mbCancel]).
  • HelpCtx: „context ID“ tématu nápovědy, který se má objevit, stiskne-li uživatel F1 (nebo klikne na tlačítko Help) při otevřeném dialogu. Nechcete-li použít, nechte 0.

MessageDlg vrací hodnotu tlačítka, jehož stisknutím uživatel dialog opustil. Možné hodnoty: mrNone, mrAbort, mrYes, mrOk, mrRetry, mrNo, mrCancel, mrIgnore, mrAll.

Titulek okénka bude stejný jako název spustitelného souboru s aplikací (tedy název projektu v Delphi). To platí i o boxíku vytvořeném procedurou ShowMessage.

Příklad:

if MessageDlg(`Soubor byl změněn. Chcete jej uložit?`,     mtConfirmation, [mbYes, mbNo, mbCancel], 0) = mrYes then     uloz_soubor(aktualni);

Poslední vylepšení: MessageDlgPos

Jedinou větší nevýhodou funkce MessageDlg je, že okénko se otevře uprostřed obrazovky. To je někdy velmi nevhodné, naštěstí v Delphi existuje rozšířená funkce MessageDlgPos, která má úplně stejné parametry jako MessageDlg, ale navíc disponuje možností zadat souřadnice, na kterých se má boxík objevit:

function MessageDlgPos (const Msg: string;     DlgType: TMsgDlgType;     Buttons: TMsgDlgButtons;     HelpCtx: Longint;     X, Y: Integer): Word;

Funkcí je více

Funkcí pro výpis krátké informace a pro získání „jednobitové“ informace o dalším postupu je samozřejmě více. Pokud byste chtěli experimentovat s dalšími podobnými funkcemi, doporučuji především funkci MessageBox, což je zapouzdřená funkce Windows API MessageBox.

Zadávání logických hodnot

Začneme od toho nejjednoduššího. Potřebujeme-li získat od uživatele pouze logickou hodnotu (tj, „ano“ nebo „ne“, příp. „1“ nebo „0“, apod.), je situace poměrně jednoduchá. Často stačí třeba použít obyčejný „MessageBox“ (nebo jiný standardní nástroj popsaný v 6. kapitole našeho seriálu).

Někdy to ovšem není příliš elegantní řešení. Naproti tomu elegancí oplývá typický představitel logické hodnoty – zatrhávací pole (CheckBox). Jeho výhody jsou nesporné: je-li zobrazen stále na formuláři nebo v menu, může uživatel kdykoliv změnit hodnotu jediným kliknutím. Je přehledný, na uživatele stále nevyskakují další a další okénka (jako by tomu bylo u soustavného používání MessageBoxu, apod.).

Jak CheckBox na formulář dostat? Je to snadné:

  • na formulář umístěte příslušnou komponentu (tedy CheckBox, paleta Standard),
  • nastavte důležité vlastnosti:
    • AllowGrayed – je-li povoleno (True), může mít CheckBox tři hodnoty: Checked, Unchecked, Grayed. Jinak má standardní dvě hodnoty.
    • Caption – titulek, tedy to, co bude u CheckBoxu napsáno,
    • Checked – jaký bude stav CheckBoxu při spuštění programu
    • State – podobné předchozí vlastnosti, navíc je možno nastavit hodnotu Grayed.

Velmi jednoduché je také čtení hodnot ze zatrhávacích boxů. Chcete-li např. zjistit, zda si uživatel přeje automaticky ukládat soubor, provedete to takto:

if chbAutoSave.Checked = True then …

Poznámka: podmínku lze použít z také „zkráceně“:

if chbAutoSave.Checked then …

Tento tvar už sám o sobě dostatečně vyjadřuje, oč v podmínce jde, a není nijak matoucí. Velmi často se proto používá.

Kromě vstupu lze samozřejmě s výhodou použít komponentu CheckBox také k zobrazení „jednobitové“ informace (uvědomuji si, že pojem „jednobitové“ není přesný, zvláště povolíte-li třetí stav (Grayed)).

Přepínací boxy

Komponenta RadioButton pro nás také není žádnou novinkou. Tato přepínací tlačítka se ale zpravidla vyskytují ve skupinách, protože lze zároveň vybrat jen jednu možnost. Je možné, aby nejprve (na startu aplikace) nebylo vybráno žádné tlačítko, nicméně tato možnost nebývá příliš obvyklá.

Jedinou důležitou vlastností je Checked, která signalizuje, že tlačítko bylo vybráno.

Komponenty RadioButton se (skoro) vždy sdružují na některou kontejnerovou komponentu, nejčastěji na GroupBox Panel, příp. samotný formulář. Místo použití několika RadioButtonů ovšem raději využijte jednu komponentu RadioGroup

Skupina přepínacích tlačítek:

Vložíte-li na formulář komponentu RadioGroup, nastavte vlastnost Items. Ta je stejného typu (TStrings) jako vlastnost Lines komponenty Memo. Podrobný popis naleznete níže v této kapitole. Do editoru napište pouze popisky jednotlivých RadioButtonů, kolečka budou doplněna automaticky. Editací vlastnosti Columns volíte počet sloupců, ve kterých budou tlačítka zobrazena.

Zjišťování, které tlačítko je vybráno, se provádí testováním hodnoty vlastnosti ItemIndex. Mnohým programátorům ale tento způsob nevyhovuje, protože si musí pamatovat, jakou číselnou hodnotu má které „přepínátko“, navíc tato hodnota se může (teoreticky) za běhu programu měnit, budete-li seznam editovat (což není, pravda, příliš šikovné).

Dávají proto přednost testování textu (popisku) tlačítka na základě tohoto indexu (rdgpPozdrav je komponenta RadioGroup):

if rdgpPozdrav.Items.Strings[rdgpPozdrav.ItemIndex] = `Nazdar` then …

Poznámka pro pokročilé uživatele:

Je třeba podotknout, že tento způsob je poněkud „drsný“. Řetězce u jednotlivych tlačítek jsou zpravidla delší než krátké pozdravy v příkladu. Proto lze tuto metodu „vylepšit“, a to například nadefinováním konstant místo čísel ItemIndex (NAZDAR, AHOJ, ) a následné testování podmínek typu

if rdgpPozdrav.ItemIndex = NAZDAR then

Jednořádkový vstup

Ne vždy si ale vystačíme se zatrhávacími boxy, představte si je třeba v situaci, kdy uživatel má zadat číslo (byť celé) z intervalu <0, 1000>. Nestačí ani přepínací tlačítka.

Nejjednodušší metodou je použití dalšího typu „MessageBoxu“ (podrobný popis podstatných boxů viz 6. díl seriálu). Konkrétně jde o funkci InputBox. Používá se velmi snadno:

function InputBox(const ACaption, APrompt, ADefault: string): string;

Význam jednotlivých parametrů:

  • ACaption – titulek dialogového boxu,
  • APrompt – text, který se objeví nad editačním okénkem,
  • ADefault - text, který se objeví v editačním okénku (jakýsi přednastavený text).

Mocnějším nástrojem pro vložení jednoho řádku textu je ovšem komponenta Edit. Má mnoho specifických vlastností, jimiž můžeme tento jednořádkový vstup omezit či formátovat (např. lze místo zadávaných znaků vypisovat hvězdičky; použití si jistě dovedete živě představit).

Vizte tabulku s nejdůležitějšími vlastnostmi komponenty Edit.

Vlastnost

Význam

Text

Nejdůležitější vlastnost; v návrhové fázi je výhodné vložit do ní počáteční text.

MaxLength

Maximální počet znaků, které lze do Editu zadat. Doporučuji vyhradit na formuláři dostatek místa, aby Edit nemusel scrollovat (uživatele mate, když mu část textu kamsi ujíždí).

Modified

Slouží ke zjištění, zda došlo ke změně.

AutoSelect

Podle ní je/není při vstupu do pole stávající text automaticky vybrán. Nastavte podle toho, předpokládáte-li spíše zadání zcela nové hodnoty nebo editaci stávající.

ReadOnly

Určuje, může-li uživatel hodnotu v Editu měnit.

PasswordChar

Hodnotou této vlastnosti je znak, který se bude vypisovat do Editu místo skutečně zadávaných znaků.

Komponenta Edit oplývá také několika skvělými metodami. Jen pro ukázku – jedna z metod se jmenuje CopyToClipboard. Hádejte, co dělá!

Potřebujete-li získat řetězec (tedy vstup typu String), je situace nejjednodušší. Na formulář umístěte komponentu Edit (paleta Standard). Doporučuji k ní rovnou umístit štítek (komponenta Label), do něhož napíšete, co vlastně má uživatel do Editu zadat. Toť vše, pak už jen v kterékoliv metodě (např. v metodě OnClick tlačítka na tomtéž formuláři) vstupní řetězec zkontrolujete (je-li to nutné) a použijete (kamsi přiřadíte apod.).

Situace je zajímavější v případě, že potřebujete od uživatele získat číselný vstup. Možností je několik, například:

použít komponentu Edit, standardně získat řetězec a ten následně konvertovat na číslo dostupnými funkcemi. Tento postup je možný, ale má několik nevýhod. Nedisciplinovaný (nebo prostě hloupý) uživatel může zadat (takže stoprocentně zadá) místo čísel písmena. V takovém případě samozřejmě nastává chyba, kterou je nutno testovat a ošetřovat (pozn.: použití funkce val je tak trochu přežitek ze starého dobrého Pascalu. V Delphi existuje efektivnější a rychlejší funkce StrToInt. Jejím problémem ovšem je, že k ošetření chybového stavu musíme pracovat s výjimkami. Pokud na ně nemáte náladu, použijte klidně Val, která vrací výsledek konverze v posledním parametru. Popis funkce StrToInt, včetně reakce na chybu, naleznete v závěru tohoto dílu).

Příklad:

val (edtPlat.Text, plat, vysledek); if vysledek <> 0 then begin   edtPlat.SetFocus;   MessageDlg(`Výše platu musí být číselný údaj!`, mtError, [mbOk], 0); end;

použít komponentu Edit, ale ze vstupního řetězce zrušit všechny nečíselné hodnoty. Toto „rušení“ však musíme provést velmi důsledně, jinak uživatel - hnidopich může dostat text do pole přes schránku. Navíc celková koncepce tohoto řešení je taková poněkud no, nepříliš „hezká“. Nicméně obecně funguje dobře a snadno se implementuje.

Příklad:

if not (key in [`0`..`9`, #8]) then begin   key := #0;   messagebeep($FFFFFFFF); end;

použít komponentu MaskEdit. Ta oproti komponentě Edit oplývá vlastností EditMask, která představuje omezení aplikovaná na vstupní data. Řada formátů je již předdefinována v Delphi a je samozřejmě možné vytvořit libovolný jiný formát. V Delphi k tomu existuje nástroj (Input Mask Editor), který spustíte kliknutím na tlačítko se třemi malými tečkami v Object Inspectoru při kliknutí na vlastnost EditMask.

Podrobný popis tvorby masek naleznete k nápovědě.

Vlastní text je uložen ve vlastnosti Text (jako u komponenty Edit). EditText obsahuje text formátovaný aplikací masky (tedy tak, jak jej vidí uživatel). Vlastnost IsMasked umožňuje testovat, zda byla maska definována.

Poznámka pro pokročilé uživatele:

Funkce MessageBeep zasluhuje krátké vysvětlení. Jde totiž o funkci Windows API, a proto ji v nápovědě Delphi příliš popsanou nenaleznete (záleží ovšem na konkrétní instalaci, někdy bývá v menu Help k nalezení položka Windows SDK). Tato funkce se používá pro přehrání zvuku definovaného ve Windows pro určitou událost. Možné hodnoty jsou 0xFFFFFFFF, MB_ICONASTERISK, MB_ICONEXCLAMATION, MB_ICONHAND, MB_ICONQUESTION, MB_OK.

Víceřádkový vstup

Komponenta Edit (včetně její rozšířené odrůdy – MaskEdit) může obsloužit omezené množství textu, a to pouze na jednom řádku. Potřebujeme-li něco podobného jako Edit (tedy ne moc lepšího), použijeme komponentu Memo. Ta se liší především možností obsáhnout několik řádků (a v důsledku toho např. přítomností posunovacích lišt pro pohyb v textu atd.).

Přehled nejdůležitějších vlastností opět shrneme do tabulky:

Vlastnost

Význam

Alignment

Komponentu Memo můžeme výhodně využít i pro zadávání jednořádkového vstupu, pokud tento řádek chceme zarovnávat doprava. K tomu právě slouží Alignment.

ScrollBars

Umožňuje nastavit nezávisle na sobě oba posuvníky (tedy vodorovné i svislé).

WordWrap

Kladná hodnota této vlastnosti (True) znamená, že dojde k zalamování řádků. Pak byste neměli „zapnout“ vodorovné posuvnítko; už z logiky věci vyplývá, že se tyto dvě vlastnosti vzájemně vylučují.

WantTabs, WantReturns

Klávesy Tab a Enter mají u většiny komponent jinou funkci. Stisk tabulátoru zpravidla způsobí přechod na další komponentu (její „aktivaci“). Chceme-li ale uživateli umožnit, aby do komponenty Memo mohl zadávat tabelátory a Entery, umožníme mu to těmito vlastnostmi. Podotýkám, že v každém případě může uživatel zmíněné znaky do textu dostat současným stiskem Ctrl (Ctrl+Tab, Ctrl+Enter).

Text

Podobná vlastnost jako u Editu; zde znamená práci s celým textem.

Lines

Zcela stěžejní vlastnost! Podrobnější popis následuje pod tabulkou.

Nejpodstatnější vlastností komponenty Memo je Lines. Ta umožňuje pracovat s jednotlivými řádky textu. Lines je typu TStrings a jako taková má mnoho užitečných vlastností a metod. Ukážeme si dvě z nejpůvabnějších – LoadFromFile, resp. SaveToFile (tedy načtení textového souboru z disku, resp. uložení textového souboru na disk). Jejich velmi elegantní použití ilustruje následující příklad:

procedure wndHlavni.btnNactiClick(Sender: TObject); begin   memoHlavni.Lines.LoadFromFile(`C:AUTOEXEC.BAT`);   ShowMessage(`6. řádka souboru AUTOEXEC.BAT je: ` + memoHlavni.Lines[5]); end;

Číselný vstup – posuvné lišty

Dalším často řešeným problémem je zadávání číselných hodnot z daného intervalu. Lze k tomu použít komponentu Memo, ale velmi pěkným řešením je užití komponent ScrollBar (posuvných lišt).

Nejdůležitější vlastnosti komponenty ScrollBar:

Vlastnost

Popis

Kind

Určuje orientaci (vodorovná (sbHorizontal) nebo svislá (sbVertical)). Nově umístěná komponenta je na formuláři vždy vodorovná.

Min, Max

Určují mezní hodnoty.

Position

Stanovuje aktuální pozici jezdce.

SmallChange

Jemnost posuvu, tato vlastnost platí pro posuv pomocí koncových šipek ScrollBaru nebo kurzorových kláves.

LargeChange

Jemnost posuvu, vlastnost platí pro posuv pomocí kliknutí kamkoliv na pruh ScrollBaru nebo pomocí kláves PageUp, PageDn.

Chcete-li přečíst hodnotu, kterou uživatel nastavil na ScrollBaru, provedete to nejlépe ošetřením události OnChange.

Pokud chcete mít posuvné lišty skutečně elegantní, je vhodné doplnit je popiskou (štítkem, komponenta Label), která bude vždy popisovat aktuální pozici jezdce (aktuální hodnotu).

Poznámka: v tomto příkladu bylo použito volání Windows API funkce RGB, jejíž syntaxe je velmi jednoduchá:

RGB(red, green, blue);

kde red, green a blue jsou barevné složky klasického RGB modelu v rozsahu 0 – 255 (tedy hodnota typu byte). Tyto hodnoty tedy (0 a 255) byly nastaveny jako minimální a maximální ve vlastnostech Min a Max všech posuvných lišt. Události OnChange všech tří lišt proto mají zhruba tento tvar:

procedure TwndBarvy.scrCervenaChange(Sender: TObject); begin   memoUkazka.Color := RGB(scrCervena.Position,                           scrZelena.Position,                           scrModra.Position);   lblCervenaHodnota.Caption := IntToStr(scrCervena.Position); end;

Poznámka:

V předchozím textu byly uvedeny dvě funkce, o nichž možná zatím nic nevíme: StrToInt a IntToStr. Jde o konverzní funkce; první z nich převádí řetězec na číslo a druhá naopak (samozřejmě lze na číslo převést pouze řetězec obsahující číslo, řetězec „Nazdar“ bude převeden jen těžko). Syntaxe je následující:

function IntToStr(Value: Integer): string; function StrToInt(const S: string): Integer;

Pokud řetězec S neobsahuje platný číselný údaj, vyvolá StrToInt výjimku EconvertError. O výjimkách budeme (psát) mluvit později, proto napíšu jen způsob použití:

try   Hodnota := StrToInt(Retezec); except   on EConvertError do . end;

Číselné vstupy – kombinace editačního pole a myši

Na závěr si ukážeme komponentu, která se také používá k zadávání číselných údajů. Jde o komponentu SpinEdit z palety Samples.

Tato komponenta v sobě částečně integruje vlastnosti normálního editačního pole a posuvných lišt. Nebudu v tabulce popisovat všechny její důležité vlastnosti, omezím se na konstatování, že místo vlastnosti Text (kterou má Edit) disponuje vlastností Value a místo vlastností Min, Max, SmallChange (které má ScrollBar) má vlastnosti MinValue, MaxValue a Increment.

Do SpinEditu je možné zadávat údaje jak pomocí klávesnice (jako do Editu), tak klikáním na šipky vpravo.

Zadávat lze i seznamem

Vstup údajů je možný také pomocí seznamů (ListBox, ComboBox, atd.). Protože se ale tyto seznamy často používají také k výpisům, necháme si jejich popis na příští díl seriálu. Kromě nich se podíváme i na další komponenty umožňující výstup a výpis informací, tedy druhý směr komunikace uživatel « aplikace.

Vstup = výstup, alespoň občas

Na úvod si neodpustím jemnou poznámku, která zdánlivě zhatí naše úsilí rozdělit si komunikaci s uživatelem na dva směry (aplikace -> uživatel, uživatel -> aplikace). Pravdou je, že velmi často (skoro vždy) můžeme použít tutéž komponentu pro vstup i výstup informací; pro zadávání i výstup hodnot. Příkladem budiž v minulé kapitole zmíněné zatrhávací políčko (CheckBox). Buď jej můžeme použít (tak, jak bylo posledně popsáno) ke vstupu informace, nebo naopak kupříkladu načteme konfiguraci z nějakého souboru a pomocí CheckBoxů ji přehledně, efektně a úsporně zobrazíme (předáme) uživateli.

Proto prosím neberte dělení komponent striktně. Není možné v jednom dílu popsat všechny komponenty pro vstup/výstup, tak jsem je rozdělil do minulé kapitoly a do této.

Sezname, otevři se (komponenta ListBox)

V odstavci, jehož nadpis hyzdí poněkud laciná hrátka se slovy (přiznávám), se podíváme na jednu z nejtypičtějších výstupních komponent - na klasický seznam. Tento seznam se v terminologii komponent Delphi nazývá ListBox. Slůvko „klasický“ jsem použil z toho důvodu, že jsou i jiné seznamy, na které se podíváme níže, a které mají různá vylepšení.

Nejprve se podívejme na několik vlastností seznamů a níže si je vysvětlíme a předvedeme.

Vlastnost

Význam

Columns

Počet sloupců, ve kterých budou údaje (položky) zobrazeny

Items

Klíčová vlastnost seznamu – jeho položky

ItemIndex

Číslo položky, která je právě vybrána

Multiselect

Povoluje / zakazuje současné vybrání (označení) více hodnot (položek)

SelCount

Udává počet vybraných položek (je-li Multiselect = True)

Sorted

Udává, mají-li hodnoty (řádky) v seznamu být (abecedně) seřazeny

Číslování položek (vlastnost ItemIndex) začíná od 0 (0 je tedy číslo první položky). Vlastnost ItemIndex je logicky run-time vlastností (je tedy přístupná jen za běhu programu), a má hodnotu –1, není-li vybrána žádná položka.

Zajímavá vlastnost je Sorted. Uvědomme si, že potřebujeme-li setřídit nějaké údaje, nemusíme vytvářet své algoritmy třídění, stačí ony položky „nastrkat“ do ListBoxu, který má Sorted = True (a klidně může mít Visible = False, a uživatel aplikace vůbec neuvidí, že třídění provedl právě ListBox). Budete-li přidávat položku do seznamu s takto nastavenou vlastností Sorted, bude položka automaticky zatříděna. Jde tedy o velmi mocný nástroj, přičemž jeho použití je velmi jednoduché.

Nejpodstatnější vlastností seznamu jsou ovšem Items (položky). Vzpomene-li si ještě někdo ze čtenářů na předchozí díl seriálu, jednak mě nesmírně potěší a jednak si možná uvědomí souvislost s komponentou Memo a její vlastností Lines. Ano, Items je opět typu TStrings a jako taková má mnoho šikovných metod. Protože ListBox (a seznam vůbec) je jednou z nejpoužívanějších komponent, uvedeme si v tabulce několik nejpůvabnějších metod jeho vlastnosti Items:

Metoda

Význam

Add

Přidá položku na konec seznamu

Clear

Zajistí vymazání všech údajů z ListBoxu

Delete

Vymaže položku ze seznamu

Equals

Testuje, jsou-li dva seznamy totožné. Vrací False, liší-li se dva seznamy v délce, obsahují-li různé řetězce nebo nesouhlasí-li pořadí položek

Insert

Vloží položku do seznamu na zadanou pozici

LoadFromFile

Načte položky seznamu z textového souboru a zobrazí je v seznamu. Nepodaří-li se načtení, řeší se to tzv. výjimkami. Problematice výjimek budeme věnovat některou z příštích kapitol.

Move

Přesune položku ležící na zadané pozici na jinou (také zadanou J ) pozici

SaveToFile

Uloží položky seznamu do textového souboru. Každý údaj z ListBoxu bude na svém řádku v cílovém souboru. Nepodaří-li se uložení, je generována tzv. výjimka. Problematice výjimek budeme věnovat některou z příštích kapitol.

Zřejmě uzrála doba k uvedení příkladů jednotlivých metod, protože z popisu v tabulce těžko získáte představu o vlastní implementaci.

Nejprve vložíme na formulář komponentu ListBox, nazveme ji dle našich konvencí lbxStamgasti a vložíme do její vlastnosti Items několik jmen. Na formulář přidáme pár tlačítek (Seřaď, Přidej, Zruš, Vymaž vše, Načti, Ulož, Konec).

Nyní budeme ošetřovat události OnClick jednotlivých tlačítek:

Přidání štamgasta se jménem Vopička, František na konec seznamu (to samozřejmě není příliš univerzální řešení, neboť vždy přidá právě štamgasta Vopičku; s využitím poznatků z minulého dílu ale jistě bez problémů najdete řešení, kterak přidat právě to jméno, které uživatel někam zadá):

procedure wndStamgasti.btnPridejClick(Sender: TObject); begin   lbxStamgasti.Items.Add(`Vopička, František`); end;

Seřazení seznamu podle abecedy:

procedure wndStamgasti.btnSeradClick(Sender: TObject); begin   lbxStamgasti.Sorted := True; end;

Zrušení (vymazání ze seznamu) označeného štamgasta:

procedure wndStamgasti.btnZrusClick(Sender: TObject); begin   lbxStamgasti.Items.Delete(lbxStamgasti.ItemIndex); end;

Vymazání všech štamgastů (vyprázdnění seznamu):

procedure wndStamgasti.btnVymazVseClick(Sender: TObject); begin   lbxStamgasti.Clear; end;

Poznámka pro pokročilé uživatele:

Vymazání celého seznamu by bylo možné také takto: lbxStamgasti.Items.Clear; tedy zavolat nikoliv metodu Clear seznamu ListBox, ale tutéž metodu položek Items (typu TStrings). V tomto případě se ale doporučuje volat spíše metodu celého seznamu, neboť to umožňuje provést kromě výmazu položek i další nezbytné “čistící” akce seznamu. Prakticky to má význam třeba u ComboBoxu: použijete-li ComboBox.Clear, seznam se vymaže; ale napíšete-li ComboBox.Items.Clear, seznam se vymaže, ale poslední vybraná položka zůstane zobrazena v editačním okně!

Uložení seznamu do souboru stamgasti.txt:

procedure wndStamgasti.btnUlozClick(Sender: TObject); begin   lbxStamgasti.Items.SaveToFile(`stamgasti.txt`); end;

Načtení seznamu ze souboru stamgasti.txt:

procedure wndStamgasti.btnNactiClick(Sender: TObject); begin   lbxStamgasti.Items.LoadFromFile(`stamgasti.txt`); end;

Vidíte, že poslední dvě zmíněné metody se zcela samy postarají o režii v souvislosti s otvíráním a zavíráním (případně i zakládáním) souborů.

Předvedeme si (už jen v krátkosti) příklady volání dalších metod:

Vložení štamgasta Adolfa Medvěda na 3. pozici v seznamu:

lbxStamgasti.Items.Insert(2, `Medvěd, Adolf`);

Přesunutí prvního štamgasta na třetí pozici v seznamu:

lbxStamgasti.Items.Move(0, 2);

Tolik tedy k základní práci se seznamem ListBox. Je vidět, že tento seznam má mnoho možností využití, ale občas se nám přesto nelíbí, protože má jednu nevýhodu: na formuláři (okně) zabírá standardně (a nepřetržitě) docela dost místa. Druhou potencionální nevýhodou by mohl být fakt, že používáme-li seznam jako vstupní komponentu, může uživatel vybírat pouze z hodnot, které již v seznamu jsou. Někdy je to samozřejmě dobře, ale čas od času chceme dát uživateli více volnosti. Co s tím?

Sezname, rozbal se (komponenta ComboBox)

Oba zmíněné problémy můžeme řešit komponentou ComboBox. Tento rozbalovací box je na obrazovce podobný editačnímu oknu (komponenta Edit) a uživatel do něj může často vložit nějaký (vlastní) text. Úzce ovšem také souvisí se seznamem (komponenta ListBox), neboť po kliknutí na šipku vpravo na ComboBoxu (nebo stisknete-li Alt+šipka dolů, příp. Alt+šipka nahoru) se seznam zobrazí (rozbalí).

Protože vlastnosti, metody i používání ComboBoxu se v mnohém zcela shodují (nebo velmi přibližují) k téže činnosti v ListBoxu, nebudu znova vše popisovat; zaměříme se jen na změny a doplňky.

Na nastavení vlastnosti Style komponenty ComboBox závisí nejen vizuální vzhled, ale také chování komponenty a možnosti uživatelského vstupu:

  • Style = csDropDown: typický rozbalovací box, který na požádání zobrazí seznam a umožňuje přímou editaci.
  • Style = csDropDownList: rozbalovací box, který neumožňuje editaci. Stiskem klávesy s nějakým písmenem vybírá uživatel první položku seznamu začínající tímto písmenem.
  • Style = csSimple: rozbalovací box, který umožňuje přímou editaci a seznam zobrazuje těsně pod editační okno ComboBoxu. Velikost zobrazeného seznamu závisí na nastavení vlastnosti Height komponenty ComboBox.
  • Style = csOwnerDrawFixed: rozbalovací box s vlastníkem vykresleným seznamem a možností přímé editace. Výška (velikost) všech položek v seznamu je specifikována vlastností ItemHeight.
  • Style = csOwnerDrawVariable: rozbalovací box s vlastníkem vykresleným seznamem a možností přímé editace. Položky v seznamu mohou mít různou výšku (velikost).

Poslední dva styly slouží například pro seznamy bez řetězců a místo toho s grafikou.

Nestačí seznamy? Zkusíme mřížku (komponenta StringGrid)

Tato komponenta se nachází v paletě Additional a není zpravidla tak používaná, jako seznamy. Její „zobrazovací“ význam spočívá v prezentování textových dat v tabulkovém formátu. Divíte-li se, proč to zdůrazňuji, jen podotýkám, že existuje podobná komponenta, DrawGrid, která zobrazuje právě ta „druhá“ data – tedy netextová.

Mřížka (budeme teď mluvit o řetězcové mřížce, tedy o komponentě StringGrid) je poněkud komplikovanější komponentou. Kromě vypisování informací slouží také ke „svázání“ objektu s buňkou. Lze asociovat objekt(y) s každou buňkou (lépe – s každým řetězcem v mřížce). Tyto objekty mohou zapouzdřovat nějakou informaci nebo chování reprezentované řetězcem, který předkládáme uživateli.

Skutečná síla spočívá spíše v odvozené komponentě – DBGrid, která umožňuje zobrazovat údaje z tabulky databáze. To ovšem nebude cílem tohoto dílu seriálu. My se podíváme na jednoduchý příklad, který nám ukáže, kterak vypisovat do obyčejné textové mřížky (StringGrid) jednoduché údaje.

Stačí umístit na formulář komponentu StringGrid a jedno tlačítko. Do události OnClick tohoto tlačítka umístíme následující kód (pro větší názornost je komponenta StringGrid pojmenovaná StringGrid1, ač víme, že je to nepěkné a odsouzeníhodné):

procedure wndHlavni.btnZobrazClick(Sender: TObject); var   i, j, k : integer; begin   k := 0;   with StringGrid1 do     for i := 1 to ColCount - 1 do       for j:= 1 to RowCount - 1 do       begin         k := k + 1;         Cells[i,j] := IntToStr(k);       end; end;

Tento příklad ukazuje hned několik vlastností mřížky:

  • ColCount – počet sloupců, zahrnuje všechny sloupce: jak pevné (fixed), tak „scrolovací“;
  • RowCount – počet řádků, platí totéž, jako o sloupcích;
  • Cells – matice řetězců celé tabulky.

V příkladu se nevyskytuje několik dalších hezkých vlastností, především:

  • FixedCols – počet „fixních“ sloupců, tedy takových., které nescrollují a které jsou na formuláři stále vidět.
  • FixedRows – počet „fixních“ řádků. Fixní sloupce a řádky se občas dají využít za účelem vložení čehosi jako „záhlaví“, nebo jiného druhu nadpisu.
  • FixedColor - barva, která bude použita na pozadí 'fixních' řádků a sloupců.
  • GridLineWidth – tloušťka „čáry“ mezi buňkami. Tuto vlastnost jsem zařadil spíše z důvodu demonstrace, že s mřížkou lze dělat mnohé „psí kusy“ a že její vzhled je velmi variabilní.

Na závěr si řekněme malou 'perličku': metoda MouseToCell převádí souřadnice X, Y na korespondující číslo sloupce a řádky v tabulce, metoda CellRect naopak vrátí obrazovkové souřadnice (v pixelech) zadané buňky.

Cimrmanovský úkrok stranou: komponenta Timer

O podkapitolku níže se podíváme na dvě poslední komponenty sloužící k dobré informovanosti uživatele. Obě slouží k zobrazení průběhu (déletrvajícího) procesu. K tomu ale potřebujeme komponentu, která umí (alespoň nějak) pracovat s časem. V Delphi taková samozřejmě je a jmenuje se Timer.

Timer je systémový časovač a nachází se tedy v paletě System. K čemu je vlastně časovač dobrý? Často potřebujeme po určitých časových intervalech přerušit normální běh programu, vykonat nějakou specifickou (leč krátkodobou!) činnost a poté se vrátit zpět do normálního chodu. Časovač potřebujeme také například k vytváření posuvných textů.

Timer nemá příliš vlastností ani událostí (k čemu taky?), vlastně jedinou speciální vlastností je Interval, který říká, kolik času (v milisekundách) uplyne, než časovač opět „tikne“. „Tiknutí“ je vlastně generování události OnTimer, jediné to události, kterou Timer oplývá. Do obsluhy této události napíšeme veškerý kód, který se má periodicky zpracovávat.

Systémový časovač je velmi zajímavou komponentou, ale z hlediska začátečníka stačí znát informace uvedené dosud. Proto další popis označím jako poznámku pro pokročilé; neznamená to, že pro začátečníky bude nesrozumitelný, spíše, že informace z něho zatím nepotřebují.

Poznámky pro pokročilé uživatele:

  • Aplikace Windows neodchytávají přerušení BIOSu (jako to bylo nutno dělat v dobách 'před Windows'), místo nich hardwarová přerušení zpracovávají samotná Windows. Je ovšem třeba říci, že vnitřní hodiny počítače nejsou příliš přesné. Ve Windows 98 má časovač interval 55 milisekund, ve Windows NT je to asi 10 milisekund. Aplikace Windows nemohou dostávat 'tiky' rychleji, než je toto rozlišení. Navíc vlivem zaokrouhlování i při nastavení intervalu 1000 milisekund bude skutečná doba trochu jiná, zde konkrétně 989 milisekund.
  • Zprávy Windows WM_TIMER, které komponenta Timer používá ke své činnosti, nejsou asynchronní. Tyto zprávy jsou ukládány do normální fronty zpráv ve Windows. Proto při zadání intervalu např. 1000 milisekund nemáme jistotu, že Delphi budou skutečně generovat událost OnTimer každou vteřinu (ani každých 989 milisekund). Bude-li aplikace zaneprázdněna více než jednu vteřinu, nedostane po celou dobu žádné zprávy WM_TIMER a nebude generovat žádnou událost OnTimer.
  • Windows nedovolí, aby se do fronty dostalo více zpráv WM_TIMER současně. V takovém případě jsou všechny postupně slučovány vždy do jediné. Naše aplikace tedy po 'uvolnění' zahlceného systému nedostane v rychlém sledu třeba pět zpráv, ale jen jednu. Navíc se nelze dopátrat počtu 'chybějících' zpráv! Dostaneme-li tedy zprávu od časovače, s určitostí víme, že nějaký čas uplynul, bohužel však nevíme, jaký.

Zobrazení průběhu procesu (komponenty Gauge, ProgressBar)

Zobrazit průběh běžícího procesu (se znalostí komponenty Timer) je hračka. Nejprve si popíšeme komponentu Gauge, v závěru se krátce podíváme na modernější ProgressBar.

Komponenta Gauge se nachází v paletě Samples. Jde o jakýsi „procentuální pruh“, o stupnici. Její důležitými vlastnostmi jsou MinValue a MaxValue (je tedy vidět, že nejvyšší hodnota nemusí být vždycky nutně 100) a Progress, což znamená (zjednodušeně), jaká je aktuální hodnota pruhu.

Příklad:

Bude-li MinValue = 0 a MaxValue = 200, hodnota Progress = 5 bude odpovídat 2%, které se také zobrazí na komponentě. Je vidět, že dochází k zaokrouhlování dolů.

Další pěknou vlastností je ForeColor, která znamená, jakou barvu bude mít „vytvářený“ (posouvající se) pruh. Poslední vlastností, která stojí za zmínku, je Kind, která specifikuje vzhled komponenty. Možné hodnoty:

  • gkHorizontalBar – standardní vodorovný pruh;
  • gkVerticalBar – standardní svislý pruh;
  • gkNeedle – jakýsi „analogový tachometr“;
  • gkPie – „koláč“, jde o standardní kruhový tvar se zvětšující se výsečí;
  • gkText – je jen zobrazena procentuální hodnota, žádné grafické „serepetičky“ okolo.

Teď už máme všechny potřebné informace, které potřebujeme k vytvoření procentuálního pruhu. Pro jednoduchost opět nespácháme nic smysluplného, vytvoříme Gauge (raději se již přidržím originálního výrazu), který jen během 10 sekund doběhne do konce.

Na formulář umístíme komponenty Gauge a Timer. Do vlastnosti Interval komponenty Timer zadáme 100 milisekund (tedy desetkrát za sekundu hodiny tiknou“ a událost OnTimer téže komponenty bude vypadat takto (názvy komponent jsem nechal standardní, ač víme, že je to nepěkné a odsouzeníhodné):

procedure wndUkazkaGauge.Timer1Timer(Sender: TObject); begin   Gauge1.Progress := Gauge1.Progress + 1; end;

Můžete si sami vyzkoušet různé tvary, vzhledy a barvy komponenty, naprosto šílenou barvu pruhu v Gauge demonstruje následující obrázek:

Komponenta ProgressBar je podobná, jen má trochu jiné názvy vlastností (místo MinValue, MaxValue a Progress jde o Min, Max a Position). Navíc přináší vlastnost Step, která určuje, o kolik se bude vlastnost Position vždy inkrementovat.

3 domácí úkoly

A na úplný závěr dva domácí úkoly pro pilné Delphaře:

  • Přidejte na formulář (k příkladu Gauge) tlačítko a zkuste vymyslet, jakým způsobem napsat obsluhu události, aby celá akce (postupná inkrementace Gauge) „odstartovala“ až stiskem tohoto tlačítka, nikoliv už při startu aplikace. Je to opravdu jednoduché.
  • Jeden z obrázků v tomto článku demonstruje aplikaci přeloženou pod Delphi 3, ostatní jsou přeloženy pod Delphi 5. Najdete, která to je (a proč zrovna ona)?
  • V příkladu na mřížku (StringGrid) se vyskytují dva for cykly. Víte, proč cykly (pro proměnné i, j) běží až od čísla 1 a nikoliv od nuly, jak by se očekávalo?

Řešení domácích úkolů:

Nejprve si řekneme, jaké je řešení domácích úkolů:

Stačí v návrhu nastavit komponentě Timer (časovači) vlastnost Enabled = False a do události OnClick přidaného tlačítka napsat

Timer1.Enabled := True;

Časovač se pak spustí po stisku tlačítka a vzápětí se „rozjedou“ i Gauge a ProgressBar.

V Delphi 3 byla zkompilována aplikace, kterou ukazuje první obrázek (ListBox). Poznáte to podle odlišné ikonky v rohu :).

Důvodem toho, že oba for-cykly běží od jedničky, je fakt, že nechceme čísla vypisovat do „fixed“ buněk, které je lépe použít třeba k nadpisu apod.

Zásady tvorby hlavního menu

V aplikaci můžeme samozřejmě mít více typů menu. Nejdůležitější bývá hlavní menu, které je umístěno zpravidla úplně nahoře v okně. Zdálo by se, že k hlavním uživatelským menu není obecně co dodat. Opak je však pravdou, neboť jeho vytvoření si komentář rozhodně zaslouží. V následujícím textu shrnu některá fakta, která je vhodné respektovat (nebo se jim naopak vyhnout) při vytváření menu. Pokud patříte k vývojářům, kteří už nějaké ty aplikace vytvořili, asi vás bude následující text trochu nudit, neboť buďto už to všechno víte, nebo se tím řídit stejně nehodláteJ.

Druhy položek menu

Nejprve se podíváme, jaké druhy položek může hlavní menu obsahovat:

  • Příkazy: položky používané k udělení rozkazu pro spuštění akce. Nejsou nijak opticky rozlišeny.
  • Nastavení: položky používané k vypnutí nebo zapnutí volby. Povolení volby je opticky odlišeno zatrhávacím „véčkem“ v levé části položky.
  • Dialogy: položky způsobující objevení dialogu (otevření dalšího okénka). Měly by se odlišovat třemi tečkami za textem položky, což sice není přísně vzato nutné (poněvadž text položky píšeme sami), ale je rozhodně dobré tuto konvenci dodržovat.
  • Vnořené: položky způsobující slouží rozvinutí další úrovně menu – viz dále. Odlišují se trojúhelníčkem v pravé části.

Obecné poznámky a konvence související s návrhem menu:

Položku menu je možné vložit přímo do pruhu s menu (viz obrázek, položka Zavři vše!). Tomuto způsobu je ale vhodné se vyhnout. Uživatelé totiž vybírají součásti pruhu z menu z důvodu prozkoumání struktury. Málokdo předpokládá, že by se mohl rovnou spustit příkaz. V případě, že se přesto rozhodnete vložit příkaz přímo do pruhu, vložte za něho alespoň vykřičník. Stále ale tvrdím, že je lepší vytvořit pro příkaz jedno rozvinutelné menu s jedinou položkou.

Umístění rozvinutelného menu do jiného rozvinutelného menu (rozvinutí ve dvou úrovních) je velmi běžné; ve Windows se v takovém případě objeví vizuální nápověda – malý trojúhelníček na pravé straně. Tento typ položek menu je ale vhodný jen pro zřídka používané příkazy. Častější výběr takové položky je totiž zdlouhavý. Někdy bývá lepší místo vytváření vnořené úrovně umístit skupinu přímo do hlavní úrovně menu a její položky oddělit čarami.

Už vůbec není vhodné používat větší množství zanoření. Nainstalujete-li si někdy aplikaci s více než dvěma úrovněmi zanoření, velmi brzy otestujete kvalitu odinstalačního programu této aplikace.

Větší opatrnost je třeba také při změnách popisů (položek) za běhu programu. Pokud například máte v menu položku Zobraz tabulku, je vhodné poté, co uživatel tuto položku zvolí, změnit její text na Skryj tabulku. Není ale příliš chytré měnit položky „brutálním“ způsobem, tedy nedržet se jejich logického významu a změnit třeba Zobraz tabulku na Zobraz seznam. Pro uživatele neexistuje příliš více matoucích akcí.

Jednou z nich ovšem je skrývání položek menu. Pokud se rozhodnete některou z položek zakázat (což je běžné), udělejte to nastavením Enabled na false (položka bude šedá, nebude ji možno vybrat, ale fyzicky bude na tomtéž místě v menu). Pokud položku zcela skryjete (Visible = false) a pokud něco takového občas periodicky zopakujete, stane se menu zcela nepoužitelným. Uživatelé totiž často hledají položky „popaměti“, jen podle jejich pozice.

Položky v menu by měly být logicky členěny do sekcí (vodorovnými čarami). Maximální počet položek v jedné sekci by rozhodně neměl přesáhnout šest.

Důležité je také dodržování standardní struktury menu. Určitě víte, že ve většině windowsových aplikací je menu jakoby „podle stejného mustru“. V souladu s principem prvořadosti uživatele je nutné uživateli vyjít vstříc používáním takových ovládacích prvků, které dobře zná. Pruh menu by měl začínat položkou Soubor, následovat by měly Úpravy, Zobrazit apod. Menu by mělo končit položkami Možnosti, Nástroje, Okno. Úplně poslední by měla být Nápověda. Navíc každé z těchto podmenu má specifické řazení položek (např. menu Soubor vypadá takto: Nový, Otevřít, Uložit, Uložit jako…, Nastavení tisku, Tisk, Konec (samozřejmě se tato struktura může lišit aplikace od aplikace, ale je dobré dodržovat alespoň jakousi základní linii).

Dalším bodem je používání klávesových zkratek. Jedním typem jsou „standarní“ zkratky typu Ctrl+C, Ctrl+V. Platí zcela totéž, co v předchozím případě (tedy neměnit zaběhnuté scénáře). Pokud v textovém editoru nastavíte pod zkratku Ctrl+C zavření dokumentu, příliš uživatelů nepotěšíte. Druhým typem klávesových zkratek jsou Alt+písmeno, která způsobí vybrání položky menu začínající tímto písmenem.

S tím souvisí další otázka – jazyková. Moje doporučení zní: děláte-li aplikaci pro využití v Česku, udělejte menu české. Jsme Češi, pišme česky a nestyďme se za to. Chcete-li svůj produkt šířit do zahraničí, vytvořte jazykové mutace. Kromě nesporného přínosu pro uživatele to vašemu programu dodá nádech profesionality. Naopak nedoporučuji „překládat“ klávesové zkratky. Uživatelé zpravidla znají ono Ctrl+C, a změníte-li to na Ctrl+K, bude to víc ke škodě než k užitku.

Poznámka pro pokročilé uživatele:

Ještě dodatek ke zmíněným jazykovým mutacím: řešit to lze buď tak, že se text položek menu načte z nějakého souboru (např. textového), nebo je menu v samostatném modulu aplikace, a nebo se vytvářejí spustitelné soubory s menu pro každý jazyk zvlášť, přičemž každý způsob má své výhody a nevýhody.

Konečně Delphi

Dostáváme se k tomu, jak vytvořit menu v naší aplikaci. Je to jednoduché a v Delphi za tím účelem existuje výkonný Menu Designer (viz obrázek, omlouvám se za prohřešek proti štábní kultuře – při vytváření tohoto příkladu jsem zapomněl přejmenovat formulář).

Umístěte na formulář komponentu MainMenu z palety Standard. Umístit ji můžete kamkoliv, protože se stejně zobrazí jen malý čtvereček (který bude samozřejmě za běhu skryt) a menu bude na správném místě. Když na tento čtvereček dvakrát kliknete, otevře se Menu Designer. V něm navrhujete vše podstatné. Kliknete na místo nové položky a napíšete její popis. Tím nastavíte její vlastnost Caption (každá položka menu má své vlastnosti viditelné v Object Inspectoru). Vlastnost Name se také nastaví automaticky, je ovšem na vás, budete-li zachovávat štábní kulturu a položky „správně“ přejmenovávat (v hodně rozsáhlém menu je to samozřejmě poměrně brutální úkol).

Menu vytvářené v Menu Designeru vypadá stejně, jako bude vypadat výsledná (run-time) podoba, s tím rozdílem, že samozřejmě vidíte všechny položky, tedy i položky s vlastností Visible = False, a navíc nevidíte význam vlastnosti Break (viz dále).

Všechny položky menu mají jedinou událost: OnClick. Do jejího těla napíšete kód, který se má provést při vybrání položky.

Chcete-li některou položku použít k rozbalení dalšího podmenu, klikněte na ni pravým tlačítkem a zvolte „Create Submenu“ (je to jen jedna z možností).

Chcete-li položky v menu opticky rozdělit vodorovnou čárou (což vypadá skvěle, pokud to nepřeženete a nebudete oddělovat každou druhou), jednoduše uveďte jako Caption položky pomlčku (-), ve výsledku bude tato položka vypadat právě jako ona čára.

Je dobré vědět, jak nastavíte „aktivní“ písmeno položky (tedy jak např. zařídit, že položka „Otevři“ se vybere stiskem Alt+O). Je to také snadné – jednoduše napište před zvolené písmeno znak & (např. &Otevřít, nebo Uložit j&jako).

Domácí úkol: zkuste vymyslet, co dělat, chceme-li mít znak & přímo v textu položky menu (např. „Vašek & Eva“).

Můžete používat také tzv. šablony menu. Dostanete se k nim také pravým tlačítkem a vybráním „Insert From Template“. Tyto šablony obsahují poměrně dost „standardizovaných“ menu (např. menu Files apod.), takže se nemusíte trápit s vypisováním všech položek. Takto vytvořené menu samozřejmě můžete dále upravovat a následně opět uložit jako šablonu pro příští použití. Jediným problémem tak zůstává, že standardní šablony obsahují samozřejmě pouze anglická menu.

Podívejme se na vlastnosti položky menu:

Vlastnost

Význam

Break

Na tuto vlastnost raději zapomeňte (viz dále).

Checked

Je-li vedle položky zobrazeno zatrhávací „véčko“. Je to velmi elegantní metoda, jak do menu zakomponovat „povolovací“ položky.

Default

Málo používaná, přitom vcelku praktická vlastnost. Nastavení položky v podmenu jako Default znamená, že pokud uživatel poklepe na položku, která toto podmenu rozbalí, bude to bráno jako vybrání této „Default“ položky.

Enabled

Již zmíněná vlastnost „povolující“ položku. Zakážete-li položku, bude šedá a nebude ji možno vybrat.

GroupIndex

Slouží k logickému „rozčlenění“ položek menu.

RadioItem

Slouží k nastavení položky jako „výlučně vybratelné“. Příklad viz níže.

ShortCut

Specifikuje klávesovou zkratku této položky, příklad viz níže.

Visible

Slouží k nastavení položky jako neviditelné. Má mnohdy praktický význam, ne však za účelem opakovaného skrývání a odkrývání položky před uživatelovýma očima.

V tabulce jsem několikrát slíbil, že níže něco blíže vysvětlím či předvedu. Ten okamžik právě nastal:

Vlastnost Break

Ne, prosím, tuto vlastnost raději nepoužívejte… Nastavíte-li u některé položky vlastnost Break na mbBreak, příp. mbBarBreak, zobrazí se tato položka v následujícím sloupci / na následujícím řádku. Je to odporné a používejte to jen k demonstraci toho, jak by menu vypadat nemělo (viz obrázek).

RadioItem, GroupIndex

Nastavíte-li některým položkám stejnou hodnotu GroupIndex a RadioItem = True, bude možno vybrat (zatrhnout, Checked) pouze jednu z nich. V takovém případě budete ale sami muset nastavovat vlastnost Checked, i když ta se u „normálních“ položek nastavuje sama podle toho, kam uživatel klikne.

Klávesové zkratky

V době návrhu nejsnáze vložíte klávesovou zkratku tak, že ji vyberete v Object Inspectoru, kde je k dispozici nepřeberné množství zkratek. Při běhu programu je to trochu obtížnější. Příkladem hodnoty je Ctrl+C. Zadáte ji takto:

MnItmOtevri.ShortCut := ShortCut(Word(`O`), [ssCtrl]);

Zkratka se vedle položky v každém případě objeví (vypíše) automaticky.

Již jsem se zmínil o tom, že není dobré manipulovat s položkami menu za běhu programu (přeskupovat, skrývat, přejmenovávat apod.). Přesto občas nastává situace, kdy potřebujeme použít jiné položky v menu nebo celé úplně jiné menu. Typicky jde o přítomnost dvou typů menu (pro „začátečníky“ a „pokročilé uživatele“). V takovém případě si prostě vytvořte dvě rozdílná menu (na formulář umístěte dvě komponenty MainMenu), jedno z nich nastavte jako implicitní (vlastnost Menu formuláře) a někam (klidně i do menu) přidejte příkaz zobrazující druhé menu.

Položky menu mají poměrně dost slušné množství metod. Jednou z nich je např. Add, která přidá položku na konec menu. Příklad:

procedure Form1.Button1Click(Sender: TSender); var   MnItmNovaPolozka: TMenuItem; begin   mnItmNovaPolozka := TMenuItem.Create(Self);   mnItmNovaPolozka.Caption := `Nová položka`;   MnItmFile.Add(mnItmNovaPolozka); end;

V souvislosti s metodou Add možná stojí za úvahu, co dělat, potřebujeme-li za běhu přidat do menu nějakou položku. Pokud už to potřebujete, máte (hrubě rozděleno) tři možnosti:

  • Dodat položku do programu výše uvedeným postupem (existuje např. metoda Insert, která umožní vložit položku jinam než na konec). Problém může nastat v souvislosti s metodou OnClick, kterou potřebujete ošetřit.
  • Vytvořit několik různých menu obsahující položky podle nějakého klíče a ty pak za běhu zaměňovat – detaily jsou vysvětleny výše.
  • Vytvoříte jedno „velké“ menu, do něho „našlapete“ všechny položky a pak jen „kouzlíte“ s vlastností Visible.

Každopádně se při změnách položek menu příliš nerozšoupněte.

Lokální (pop-up) menu

V poslední době se snad nevyskytuje nová aplikace, která by neobsahovala pop-up menu (lokální menu, kontextové menu, dokonce jsem zaznamenal vpravdě příšerný výraz vyskakovací menu). Otevře se zpravidla po stisku pravé klávesy myši. Jeho kouzlo spočívá hlavně v tom, že obsahuje právě takové položky, které jsou v daném okamžiku aktuální (vztahují se k vybranému elementu).

Delphi poskytuje komponentu PopupMenu. Je dosti podobná komponentě MainMenu, a to jak vizuálně (čtvereček), tak způsobem práce (stejný Menu Designer).

Proto nebudeme zabíhat do podrobností. Jediné, co vysvětlíme, bude zajištění toho, aby se v každém okamžiku otevřelo vždy to „správné“ kontextové menu. Zařídí se to tak, že dané komponentě (např. formuláři, editačnímu oknu, memu…) nastavíme jako hodnotu jeho vlastnosti PopupMenu název odpovídajícího kontextového menu (musíme tedy v aplikaci samozřejmě mít více komponent PopupMenu).

Celá komponenta PopupMenu má navíc (od MainMenu) jednu vlastnost a jednu metodu. Vlastnost se jmenuje AutoPopup a její hodnota True (implicitní) znamená, že se menu objeví po stisku pravého tlačítka myši, tedy tak, jak jsme zvyklí. Zakážeme-li „vynořování“ menu (tedy AutoPopup = False), menu se nezobrazí a vyvolat ho lze jen programově jeho metodou Popup. Přidaná metoda komponenty PopupMenu se jmenuje OnPopup a do jejího těla napíšeme kód, který se má vykonat, „vynoří-li se“ (otevře) kontextové menu.

Poznámka pro pokročilé uživatele:

Událost OnPopup je generována těsně předtím, než se menu rozbalí. Můžete tedy například otestovat nějaké konkrétní nastavení a podle něho upravit položky v menu.

O položkách kontextového menu platí beze zbytku to, co bylo řečeno k položkám hlavního menu.

Řešení domácího úkolu z minulého dílu

Chcete-li mít znak „&“ přímo v textu položky menu, v návrhu musíte ampersand zdvojit, tedy napsat např. „Vašek && Eva“. Chcete-li navíc podtrhnout (učinit aktivním) písmeno k, napište „Vaše&k && Eva“.

Objektově orientovaná architektura

Důvod, proč jsem zařadil popis objektově orientované architektury, spočívá v tom, že si nejsem jist, zda je vhodné toto téma ignorovat a opájet se představou, že jej buď všichni znají, nebo nepotřebují.

Co to je vlastně objektově orientované programování? Často vypadá spíše jako náboženství než jako přístup k programování. Tento programovací styl používá oddělené objekty, které obsahují (zapouzdřují) svá data i kód. Tyto objekty jsou stavebními prvky aplikace. Obvyklým důvodem používání objektů je možnost jednodušších zásahů do programu. Fakt, že data i kód jsou jaksi pohromadě (a že si tedy každý objekt plně za svá data zodpovídá), znamená, že proces odstraňování chyb (a také modifikace vlastností objektu) má minimální efekt na okolní objekty.

Jazyk objektově orientovaného programování obvykle zahrnuje implementaci alespoň tří principů:

  • zapouzdření – kombinace vzájemně svázaných datových položek a metod, které nad nimi pracují;
  • dědičnost – možnost vytvářet objekty vycházející z jiných objektů. Tento princip dovoluje vytvářet hierarchii – nejprve se vytvoří obecný objekt a postupným zpřesňováním vznikají specifičtější následníci;
  • polymorfismus – volání metod proměnné typu třída způsobí volání kódu patřícího k instanci objektu (viz níže), kterou proměnná právě obsahuje.

Pokud bychom se měli pokusit stanovit základní dva termíny objektově orientovaného programování, šlo by zřejmě o třídu a objekt. Třída je datový typ, který si můžete představit jako jakousi šablonu určitých objektů (třeba aut), která popisuje chování konkrétních objektů (aut). Konkrétní auta vytváříme podle této „šablony“. Třída obsahuje nějaká svá (interní) data (tzv. atributy) a své metody (tj. procedury a funkce). Třída by měla charakterizovat chování a vlastnosti několika podobných objektů (např. aut více značek). Objekt je instancí třídy, jakýmsi konkrétním výskytem (konkrétním, fyzicky existujícím exemplářem auta). Jinak řečeno, je to proměnná datového typu, který představuje třída. Objekty při běhu programu zabírají paměť pro svou reprezentaci.

Vztah mezi objektem a třídou si lze představit třeba jako vztah mezi proměnnou a datovým typem.

Abychom si vše názorně předvedli (a také abychom si ukázali, jak to prakticky udělat v Object Pascalu - a tedy také v Delphi), vytvoříme třídu automobil, která bude mít následující atributy:

  • Značka – značka automobilu, typ = řetězec;
  • Rok výroby – typ = celé číslo;
  • Benzín – množství benzínu v nádrži, typ = celé číslo;
  • Kapacita – objem nádrže, typ = celé číslo.

Třída bude mít tyto metody:

  • Vypiš informace – vypíše všechny atributy;
  • Natankuj – naplní nádrž o dané množství litrů. Pokud se dané množství do nádrže nevejde, doplní nádrž na maximum a vrátí false (jako varování).

Abychom hovořili všichni o tomtéž, provedeme to tak: v Delphi vytvoříme novou aplikaci s jedním formulářem, na kterém bude jedno tlačítko.Veškerý níže uvedený kód budeme psát do modulu tohoto formuláře (standardně Unit1.pas).

type   TAuto = class     Znacka: String;     RokVyroby, Benzin, Kapacita: Integer;     procedure VypisInfo;     function Natankuj(Kolik: Integer): Boolean;   end;

Podotýkám, že tento kód se bude vyskytovat v sekci interface příslušného modulu (souboru). Abychom s touto třídou mohli pracovat, je ještě nutné říci, jak budou vypadat těla zmíněných metod. Tato těla budou zapsána v sekci implementation (rozdíl mezi oběma sekcemi vysvětlíme níže), a aby kompilátor věděl, ke které třídě budou těla patřit (lze totiž mít víc různých tříd a v každé např. metodu s názvem Natankuj), používá se v Object Pascalu tzv. tečková notace:

procedure TAuto.VypisInfo; begin   ShowMessage( Format(`%s, %d: %d (%d).`,     [Znacka, RokVyroby, Benzin, Kapacita]) ); end; function TAuto.Natankuj(Kolik: Integer): Boolean;  begin   Result := (Benzin + Kolik) <= Kapacita;   Benzin := Max(Kapacita, (Benzin + Kolik)); end;

Poznámky:

  • Proměnné Result se používá k navrácení hodnoty funkce; v Delphi se příliš nepoužívá klasického „pascalského“ zápisu „název_funkce := návratová_hodnota.“
  • Funkce Max (vrací maximum ze dvou argumentů, návratový typ závisí na typu argumentů) se nachází v jednotce Math, měli byste tedy tuto jednotku zapsat do sekce uses.

Nyní ještě provedeme deklaraci proměnné typu třída a ukážeme si, jak metody volat a jak s proměnnou pracovat. Následující kód bude umístěn v sekci implementation, v jakékoliv proceduře či funkci (např. v metodě ošetřující událost OnClick nějakého tlačítka umístěného na formulář).

procedure TwndHlavni.btnStartClick(Sender: TObject); var   MujBlesk: TAuto; begin // (A)   MujBlesk.Znacka := `Skoda 1000MB`; // (B)   MujBlesk.RokVyroby := 1950;   MujBlesk.Benzin := 0;   MujBlesk.Kapacita := 5;   MujBlesk.VypisInfo;   if not MujBlesk.Natankuj(2) then     ShowMessage(`Nepřehánějte to s tím benzínem!`);   MujBlesk.VypisInfo; end;

Nyní si zkuste program zkompilovat a spustit. Vše bude v pořádku, ale jen do okamžiku, než kliknete na „startovací“ tlačítko. Pak se program zboří. (Tedy – nezboří se úplně, ale fungovat nebude a bude generována tzv. výjimka – viz dále).

Proč tomu tak je? Vysvětlení příčiny je složitější a souvisí se základní myšlenkou objektově orientovaného modelu. Musíme si říci několik informací o vytváření instancí. Následující řádky jsou klíčové pro pochopení OOP.

Základní myšlenka objektově orientovaného modelu spočívá v tom, že proměnná datového typu třída (nemluvíme o instanci objektu, jen o proměnné), jako je např. MujBlesk z předchozího příkladu, neobsahuje 'hodnotu' objektu. Neobsahuje ani objekt auto ani atributy auta. Obsahuje pouze odkaz (ukazatel) na místo v paměti, kde je vlastní objekt fyzicky uložen.Vytvoříme-li proměnnou tak, jak jsme to předvedli o pár řádků výše (pomocí klíčového slova var), nevytvoříme zmíněnou fyzickou reprezentaci objektu (místo pro uložení objektu v paměti), ale jen odkaz na objekt (místo pro uložení tohoto odkazu v paměti)! Vlastní instanci musíme vytvořit ručně zavoláním jeho metody Create, což je tzv. konstruktor (procedura určená k alokování paměti a k inicializaci objektu).

Řešení je tedy prosté: mezi řádky označené (A) a (B) v předchozí proceduře vsuneme volání konstruktoru:

begin // (A)   MujBlesk := TAuto.Create;   MujBlesk.Znacka := `Skoda 1000MB`; // (B)

Kde se vzal konstruktor Create? Je to konstruktor třídy TObject, od něhož všechny ostatní třídy (a tedy i tato) dědí (viz dále).

Když jsme objekt vytvořili, je třeba jej nakonec také zrušit. To provedeme zavoláním metody Free:

  MujBlesk.VypisInfo;   MujBlesk.Free; end;

Konstruktor

Metodu Create jsme volali kvůli přidělení paměti objektu. Často ale objekt potřebujeme také inicializovat. Za tím účelem přidáváme do třídy konstruktor. Lze použít upravenou verzi metody Create nebo definovat konstruktor úplně nový. Není však příliš vhodné pojmenovat konstruktor jinak než Create.

Konstruktor je velmi specifická procedura, protože Delphi samy alokují paměť pro objekt, nad kterým jej spustíte. Použití konstruktoru tedy za vás vyřeší problémy s alokací paměti. Konstruktor se deklaruje užitím klíčového slova constructor. Přidáme tedy do třídy TAuto konstruktor:

type   TAuto = class     Znacka: String;     RokVyroby, Benzin, Kapacita: Integer;     constructor Create(ZZnacka: String; RRokVyroby, BBarva, BBenzin, KKapacita: Integer);     procedure VypisInfo;     function Natankuj(Kolik: integer): Boolean; end;

Musíme také zapsat tělo konstruktoru. To se zapisuje kamkoliv do modulu, klidně mezi další procedury a funkce, ale konvencí je zapisovat jej jako první podprogram modulu, hned na počátku sekce implementation. Správně bychom měli v konstruktoru každé nově vytvořené (odděděné – viz dále) třídy nejdříve vyvolat konstruktor předka a následně uvést své vlastní, specializující příkazy. Pro třídu odděděnou od TObject to jistě není třeba, ale přesto je to vhodné a formálně správné.

constructor TAuto.Create(ZZnacka: String; RRokVyroby, BBarva, BBenzin, KKapacita: Integer); begin   inherited Create;   Znacka := ZZnacka;   RokVyroby := RRokVyroby;    Benzin := BBenzin;   Kapacita := KKapacita; end;

Takhle teď bude vypadat tělo procedury btnStartClick, ve které pracujeme s objektem MujBlesk třídy TAuto:

procedure wndHlavni.btnStartClick(Sender: TObject); var   MujBlesk: TAuto; begin   MujBlesk := TAuto.Create(`Skoda 1000MB`, 1950, 0, 5);   MujBlesk.VypisInfo;   if not MujBlesk.Natankuj(2) then     ShowMessage(`Nepřehánějte to s tím benzínem!`);   MujBlesk.VypisInfo;   MujBlesk.Free; end;

Destruktor

Destruktor je jednoduše řečeno opakem konstruktoru. V předchozím příkladu jsme se s ním již setkali (v řádce MujBlesk.Free). Implicitní název je tedy Free. Jeho funkcí je „zničit“ objekt (uvolnit jej z paměti). Platí, že dynamickou paměť, kterou jsme alokovali v konstruktoru, bychom měli v destruktoru uvolnit.

Typy přístupu k datům

Třída může obsahovat teoreticky jakékoliv množství atributů a metod. Správně by měla být vlastní data třídy skrytá (neboli zapouzdřená) uvnitř třídy. K těmto skrytým datům by měly přistupovat pouze metody téže třídy. Není vhodné nechat kohokoliv manipulovat s daty naší třídy. Jedním z principů objektově orientovaného programování je „každý je zodpovědný za svá data“.

Optimální přístup (z hlediska zásad OOP) je tedy takový: nelze „zvenku“ přistupovat k datům, ale jsou k dispozici veškeré potřebné metody, které tento přístup (a to jak čtení, tak zápis) zajišťují. Tím je zajištěn tzv. autorizovaný přístup k datům.

V Object Pascalu toho dosáhneme používáním specifikátorů přístupu:

  • private – takto označené atributy a metody nejsou přístupné vně třídy;
  • public – označuje položky, které jsou volně přístupné z jakékoliv části programu (ve které je viditelná jejich třída);
  • protected – částečně chráněné položky. Může k nim přistupovat pouze vlastní třída a všichni její potomci (třídy vzniklé dědičností, viz dále).
  • published – položka nebo metoda published je přístupná nejen za běhu, ale i při tvorbě aplikace, z hlediska aplikace je viditelná stejně jako položka public.

Ukážeme si, jak zajistit autorizovaný přístup k datům v naší třídě TAuto (protected atributy využijeme ve zděděné třídě TNakladak – viz dále).

type   TAuto = class     protected       Znacka: String;       RokVyroby, Benzin, Kapacita: Integer;     public       constructor TAuto.Create(ZZnacka: String; RRokVyroby, BBarva, BBenzin, KKapacita: Integer);       procedure VypisInfo;       function Natankuj(Kolik: integer): Boolean;   end;

Poznámka pro pokročilé uživatele:

V rámci pravdomluvnosti je nutné podotknout, že v Delphi, na rozdíl třeba od C++, je trochu jiný význam specifikátoru private. V C++ není sekce private přístupná nikomu jinému než vlastní třídě, zatímco zde můžeme k private položkám přistupovat z celého modulu (zdrojového souboru), ve kterém je daná třída deklarována.

Sekce modulu interface a implementation

Do sekce interface zapisujeme vše, co má být viditelné i v ostatních modulech – datové typy, hlavičky procedur, funkcí apod. V sekci implementation se jednak vyskytují těla funkcí a procedur, jejichž hlavičky jsou v interface, a jednak všechny „soukromé“ prvky (tedy klidně i další funkce potřebné jen v rámci modulu).

Dědičnost

Dědičnost je vlastnost objektově orientovaného programování, kterou mohu využít, pokud potřebuji vytvořit novou třídu podle vzoru konkrétní (již hotové) třídy, ale s dalšími specifickými vlastnostmi (tedy poněkud 'více konkrétní' třídu).

Jako příklad uvedeme vytvoření nákladního auta pomocí existující třídy auto. Třída nákladní auto bude mít navíc atribut nosnost a bude mít vlastní konstruktor. Metoda VypisInfo bude navíc vypisovat i nosnost vozu.

type   TNakladak = class (TAuto) // dědíme od třídy TAuto     private       Nosnost: integer;     public       constructor Create(ZZnacka: String; RRokVyroby, BBarva, BBenzin, KKapacita, NNosnost: Integer);       procedure VypisInfo;   end;

Těla konstruktoru a pozměněné metody:

constructor TNakladak.Create(ZZnacka: String; RRokVyroby, BBarva, BBenzin, KKapacita, NNosnost: Integer); begin   inherited Create(ZZnacka, RRokVyroby, BBarva, BBenzin, KKapacita);   Nosnost := NNosnost; end; procedure TNakladak.VypisInfo; begin   ShowMessage(Format(`%s, %d: %d (%d). Nosnost = %d`,     [Znacka, RokVyroby, Benzin, Kapacita, Nosnost])); end;

Nyní si vyzkoušíme práci s novou třídou:

var   Vejtraska: TNakladak; begin   Vejtraska := TNakladak.Create(`Avia`, 1980, 20, 200, 10);   Vejtraska.VypisInfo;   Vejtraska.Free; end;

Poznámka k typové kompatibilitě v případě dědičnosti: objekt třídy potomka můžeme kdykoliv použít na místě objektu třídy předchůdce. Opačný postup však není možný. Příklad:

MujBlesk := Vejtraska; // lze Vejtraska := MujBlesk; // NELZE, chyba!!!

Polymorfismus, virtuální a abstraktní metody

Bohužel, článek je již teď mnohem delší, než je únosné, proto nezbývá, než další koncepty zmínit jen velmi stručně. Pascalovské funkce a procedury jsou obvykle založeny na tzv. statické vazbě. To znamená, že volání metody je „vyřešeno“ již překladačem (a linkerem). Všechny metody z našich dosavadních příkladů mají statické vazby. Objektově orientované jazyky však umožňují použití jiného druhu vazby, známého jako pozdní vazba (někdy též dynamická).

Výhoda tohoto přístupu je známa jako polymorfismus. Předpokládejme, že naše dvě třídy TAuto a TNakladak definují metodu s dynamickou vazbou. Potom lze tuto metodu aplikovat na všeobecnou proměnnou (jako např. MujBlesk), která může za běhu odkazovat na objekty obou tříd. Určení metody, která bude volána, bude provedeno za běhu, podle konkrétní situace. K definici se používá klíčových slov virtual a override:

type   TAuto = class     procedure VypisInfo; virtual;   end;   TNakladak = class(TAuto)     procedure VypisInfo; override;   end;

Klíčovým slovem abstract deklarujeme metody, které budou definovány až v potomcích současné třídy. Prakticky z toho plyne, že ve třídě nemusíme zapisovat (definovat) tělo metody deklarované jako abstraktní.

type   TAuto = class     procedure Zmen_rok; virtual; abstract;   end;

Běhové chyby mohou vzniknout v důsledku „čehosi prohnilého“ při provádění funkcí, procedur a metod pracujících v rámci knihovny vizuálních komponent (Visual Component Library, VCL), běhové knihovny (Runtime Library, RTL) či v rámci operačního systému. Jakmile tušíme, že by se v daném úseku programového kódu mohla vyskytnout chyba, musíme všechny možnosti správně ošetřit (a to i v případě, že pravděpodobnost vzniku chyby v daném místě je takřka zanedbatelná, neboť Murphyho (i jiné) zákony nás učí, že může-li se něco pokazit, zaručeně se to pokazí).

Vytvoříme proceduru, na které budeme demonstrovat různé přístupy k ošetření chyb. Vytvořte formulář, přidejte na něj tlačítko (Button) a ošetřete jeho kliknutí (OnClick) takto:

procedure wndHlavni.btnTestClick(Sender: TObject); var   A, B, C: Integer; begin   A := 0;   B := 0;   C := A div B;   ShowMessage(IntToStr(C));   btnTest.Caption := IntToStr(C); end;

Vypisujeme výsledek celočíselného podílu dvou čísel (operátor div).

V takto zapsané proceduře neošetřujeme vůbec žádné chyby. Vzhledem k tomu, co jsme si napřed přiřadili do obou proměnných, je zřejmé, že nastane chyba. Tato chyba způsobí výjimku, kterou generuje operátor div. Když si náš program spustíte a kliknete na tlačítko, uvidíte zprávu o výjimce. I když sami nic neošetřujeme, výjimka je zjevně ošetřena. Proč tomu tak je, si povíme níže.

Tradiční způsob ošetření chyb

Tradiční styl spočívá zpravidla v podmínkách a testech, většinou kontrolujeme návratové kódy a „výstupy“ procedur a funkcí a dále operandy u operací apod. Problémy (nevýhody) tohoto postupu jsou (zjednodušeně řečeno) následující:

  • musíme si příslušné návratové kódy pamatovat;
  • každá funkce indikuje „neúspěch“ jinak – vrátí false, 0, -1, apod.;
  • ještě horší je to u procedur, které vrací hodnotu zpravidla přes parametr předaný odkazem (v horším případě v globálních proměnných :-)).

Podívejme se, jak bychom tradičně ošetřili chybu u procedury z příkladu:

procedure wndHlavni.btnTestClick(Sender: TObject); var   A, B, C: Integer; begin   A := 0;   B := 0;   if B <> 0 then   begin     C := A div B;     ShowMessage(IntToStr(C));     btnTest.Caption := IntToStr(C);   end   else begin     ShowMessage(`Chystáte se dělit nulou!`);     btnTest.Caption := `Chyba`;   end; end;

Ošetření chyby s užitím výjimek

Nyní tutéž proceduru přepíšeme s užitím výjimky. Nevadí, že zatím neznáme přesnou syntaxi práce s výjimkami ani definici vlastní výjimky. K tomu se dostaneme vzápětí, nyní jde jen o demonstraci toho, jak bude procedura vypadat.

procedure wndHlavni.btnTestClick(Sender: TObject); var   A, B, C: Integer; begin   A := 0;   B := 0;   try     C := A div B;     ShowMessage(IntToStr(C));     btnTest.Caption := IntToStr(C);   except     on EDivByZero do begin       ShowMessage(`Chystáte se dělit nulou!`);       btnTest.Caption := `Chyba`;     end;   end; end;

Takto zapsaná procedura vypíše chybovou hlášku a do titulku tlačítka napíše slovo „Chyba“ jako indikaci chyby. Pokud změníte hodnotu proměnné B na nenulovou, vypíše se místo toho výsledek podílu (tedy nula) a ten bude také přiřazen do titulku tlačítka.

Tento triviální příklad demonstruje v nejhrubších rysech práci s výjimkami. Celý mechanismus výjimek je postaven na čtyřech klíčových slovech:

  • try – označuje začátek tzv. chráněného bloku kódu, tedy bloku, ve kterém se očekává vznik výjimky a který se má „zkusit“ provést;
  • except – označuje konec chráněného bloku, uvádí příkazy pro obsluhu výjimek, a to ve formátu: on do else
  • finally – začíná blok používaný např. k uvolnění zdrojů alokovaných v bloku try předtím, než je obsloužena výjimka. Tento blok je proveden vždy, ať k výjimce dojde nebo ne!
  • raise – příkaz používaný k vyvolání výjimky. I když se zdá, že je nesmyslné výjimku vyvolávat ručně (že nám bohatě stačí výjimky od systému), opak je pravdou a občas se raise hodí.

Než si ukážeme konkrétní práci s výjimkami, řekneme si něco o výjimkách a stavu programu. Dojde-li k výjimce, hledá se „ošetřující procedura“ (tzv. handler výjimky), tedy procedura, která výjimku ošetří. Není-li nalezena v dané části programu, je výjimka „přenesena“ (tzv. propagována) výše, a to až do okamžiku, kdy se o ni někdo postará. V extrémním případě jde tento postup až k tzv. implicitnímu handleru výjimek v Delphi (proto je nakonec ošetřena každá výjimka). Důležité je, že po ošetření výjimky program pokračuje kódem následujícím po kódu handleru a nikoliv kódem následujícím po kódu, který způsobil výjimku.

Podívejme se nyní blíže na sekci finally. Ta se používá k provedení nějaké činnosti v případě výjimky i v případě normálního provedení (typicky vyčištění paměti, apod.). Kód po finally bude vykonán vždy po opuštění bloku try, ať již k výjimce došlo nebo nikoliv.

Podívejme se na příklad ošetření výjimky bez použití bloku finally (místo čištění paměti budeme měnit titulek formuláře) :

procedure wndHlavni.btnTestClick(Sender: TObject); var   A, B, C: Integer; begin   A := 0;   B := 0;   try     C := A div B;     wndHlavni.Caption := `Nazdar`;   except     on EDivByZero do begin       ShowMessage(`Chystáte se dělit nulou!`);       btnTest.Caption := `Chyba`;     end;   end; end;

V případě dělení nulou nebude nikdy provedeno nastavení titulku formuláře na „Nazdar“. Řešením je užití bloku finally:

procedure wndHlavni.btnTestClick(Sender: TObject); var   A, B, C: Integer; begin   A := 0;   B := 0;   try     C := A div B;   finally     wndHlavni.caption := `Nazdar`;   end; end;

Nyní máme jistotu, že nastavení titulku se vždy provede. Bohužel ale zase nemáme ošetřenou vlastní výjimku, což není úplně šikovné, protože kvůli tomu to všechno děláme. Navíc blok finally toto ošetření neumožňuje (lépe řečeno – ošetření sice umožňuje, ale výjimka se bude šířit (propagovat) dál, protože z hlediska Delphi stále ošetřena nebude). Takže nám nezbývá, než zkombinovat finally a except blok (jde vlastně o zanoření dvou bloků):

procedure wndHlavni.btnTestClick(Sender: TObject); var   A, B, C: Integer; begin   A := 0;   B := 0;   try     try       C := A div B;     except       on EDivByZero do begin         showmessage(` Chystate se delit nulou!`);         btnTest.Caption := `Chyba`;       end;     end;   finally     wndHlavni.Caption := `Nazdar`;   end; end;

Poznámka: pokud chcete tyto příklady přesně opisovat a zkoušet, mějte na paměti, že kompilátor vůbec nezařadí do výsledného programu příkaz c := a div b, pokud hodnotu c někde dále nepotřebujete! A nekamenujte mě prosím za nesmyslnost uvedených příkladů, jsem si jí vědom; chápejte uvedené příklady jen jako ukázku.

Syntaxe bloku except

Blok except umožňuje více možností použití:

try except   on do   on do   else end;

Vidíme, že v sekci else můžeme ošetřit jakoukoliv výjimku, i tu, kterou jsme neočekávali (a tedy nezařadili do výčtu on .. do). V případě ošetřování neznámých podmínek ale buďte maximálně opatrní. Zpravidla je nejlepší nechat ošetření neznámé výjimky na implicitním handleru Delphi. Není dobrý ani nápad výjimku ošetřit (např. indikovat MessageBoxem) a následně znovu vyvolat, protože pak o ní bude uživatel informován dvakrát: vaším MessageBoxem a MessageBoxem Delphi. Takže dostáváme zlaté pravidlo:

Buď výjimku ošetříme, nebo ji necháme bez povšimnutí ošetřit standardně!

Pokud už chcete výjimku sami ošetřit, je možné například použít vyvolání nové výjimky se zadaným chybovým textem:

raise EConvertError.Create(`Nelze zkonvertovat!`);

Poznámky pro pokročilé uživatele:

  • Kdykoliv dojde k výjimce, oznámí se tato skutečnost aplikaci. Kompilátor vygeneruje kód, který ví, jak přerušit provádění aplikace. Obsah zásobníku volání se pak začíná odvíjet od místa přerušení: předávané parametry a lokální proměnné se vyzdvihnou ze zásobníku (a tím uvolní z paměti). Pokud má daná funkce proměnnou typu ukazatel, jejíž paměť je dynamicky alokována, uvolní se při odvíjení zásobníku pouze 4 bajty alokované na zásobníku pro typ ukazatel. Dynamickou paměť je nutno uvolnit ručně!
  • Proces odvíjení zásobníku pokračuje, dokud se nenalezne konstrukce except nebo globální ovladač výjimek.
  • Při vyvolání výjimky se vytvoří objekt chyby, který zůstává v paměti až do uvolnění. Tento objekt se nazývá „instance objektu výjimka“. Konstrukce finally neuvolňuje z paměti tuto instanci, proto výjimku v podstatě „neošetří“.
  • Globální ovladač podmínek je definován v objektu Application, což je instance třídy TApplication, která je definovaná v jednotce Forms. Tento objekt slouží k různým účelům, a jedním z nich je spuštění události OnException v případě, že v programu dojde k výjimce.

Souborová podpora v Object Pascalu

Práci se soubory v Object Pascalu si pouze připomeneme, a to na příkladu. Vytvořte aplikaci, na formulář umístěte jedno tlačítko a jednu komponentu Memo. Stiskem tlačítka se do Memo načte obsah textového souboru DATA.TXT z aktuálního adresáře (ošetříme tedy událost OnClick zmíněného tlačítka):

type   TTextSoub = TextFile; procedure TwndHlavni.btnOPClick(Sender: TObject); var   Soub: TTextSoub;   Radka: String; begin   AssignFile(Soub, `data.txt`);   Reset(Soub);   while not Eof(Soub) do begin     ReadLn(Soub, Radka);     memoSoubor.Lines.Add(Radka);   end;   CloseFile(Soub); end;

Poznámky

Možná jste z Pascalu zvyklí na výrazy Text (pro označení textového souboru), Assign (pro asociování fyzického souboru s proměnnou) či Close (pro uzavření souboru). Ty jsou v Delphi nahrazeny výrazy TextFile, AssignFile, resp. CloseFile. Důvodem je, že původní znění je v Delphi použito k jinému účelu (např. Text je vlastnost některých komponent, např. Memo či Edit). „Staré“ názvy v Delphi sice přesto zůstaly zachovány i s původním významem, je ale nutné uvozovat je kvalifikátorem modulu System. Například místo Assign(F) použijte System.Assign(F).

Využijete-li poznatků předchozí kapitoly, jistě sami snadno přijdete na to, jak provést ošetření chyb. V Delphi je nejefektivnějším způsobem použití mechanismu výjimek (podrobněji viz níže). V „původním“ Pascalu bylo ošetření chyb samozřejmě řešeno trochu jinak (všichni „pascaláci“ si jistě pamatují nezapomenutelné IOResult apod…

Chcete-li pracovat s jiným než textovým souborem, použijte konstrukci file of <typ>, např. file of Integer (soubor s celými čísly).

Souborová podpora v Delphi

Někdy se ovšem nechceme zdržovat vymýšlením konstrukcí vycházejících ze standardního Pascalu. Pak máme k dispozici množství výkonných podpůrných nástrojů pro práci se soubory přímo v Delphi. Asi nejvýznamnějšími prvky jsou metody LoadFromFile, resp. SaveToFile, které dokáží načíst (a zobrazit) obsah souboru zápisem jediné řádky kódu. Nejde přitom zdaleka jen o textové soubory (jak jsme poznali např. v rámci popisu komponenty Memo v kapitole 7).

Metody LoadFromFile, resp. SaveToFile jsou dostupné např. pro třídy TStrings, TPicture, TBitmap, pro některé datové komponenty (TBlobField, TMemoField, TGraphicField), pro grafické formáty (TGraphic, TIcon, TMetaFile) nebo pro OLE zásobníky. Podobné metody jsou k dispozici také u objektů TMediaPlayer.

Poznámka pro pokročilé uživatele:

Jinou třídou týkající se souborů je TIniFile. Ta popisuje speciálně inicializační soubory Windows 3.1. Ve vyšších Windows aplikace zpravidla nahrazují INI soubory systémovými registry. Delphi obsahují i třídy určené pro takovou situaci – TRegistry, resp. TRegistryIniFile. Třídy TIniFile a TRegistryIniFile mají společného předka (TCustoIniFile), proto umožňují kromě jiného změnu přístupu (registry vs. INI soubory) s minimálními změnami kódu.

Modifikujeme nyní příklad uvedený výše, jen použijeme zmíněnou metodu LoadFromFile.

procedure TwndHlavni.btnOPClick(Sender: TObject); begin   memoSoubor.Lines.LoadFromFile(`data.txt`); end;

Myslím, že úspora je více než zřejmá:-). Nemusíme deklarovat žádné typy, definovat žádné proměnné, otvírat žádné soubory, asociovat žádné názvy.

Poznámka pro pokročilé uživatele:

Další zajímavou formou v Delphi je podpora souborových streamů, která vychází z abstraktní třídy TStream. Tato třída má tři následníky: TFileStream, THandleStream a TMemoryStream. Většina jejich metod se týká komponent a používají je zpravidla jejich tvůrci, ale některé jsou použitelné pro kohokoliv (např. ReadBuffer).

Chyby při práci se soubory

Než se vrhneme na standardní dialogy, podíváme se, jak ošetřovat chyby při práci se soubory. Delphi při jakékoliv I/O chybě (tedy chybě vstupu a výstupu) generují výjimku EInOutError. Přesnější specifikace chyby se objevuje (je vracena) v lokální proměnné ErrorCode, která může nabývat následujících hodnot:

Error Code

Význam

File not found

Invalid file name

Too many open files

Access denied

Disk read error – hlášeno, chcete-li číst za koncem souboru - EOF (End of File)

Disk write error – hlášeno, chcete-li zapisovat na zaplněný disk

File not assigned – hlášeno, chcete-li použít proměnnou typu Text nebo TextFile bez předchozího volání Assign()

File not open – hlášeno, chcete-li pracovat se souborem, jenž nebyl otevřen některou z funkcí Reset(), Rewrite(), Append()

File not open for input – hlášeno, chcete-li číst ze souboru otevřeného pro zápis

File not open for output – hlášeno, chcete-li zapisovat do souboru otevřeného pro čtení

Invalid numeric format – hlášeno, chcete-li číst nečíselnou hodnotu z textového souboru do číselné proměnné

Standardní obsluha výjimek (implicitní handler Delphi) dokáže samozřejmě výjimku ošetřit, i když ji sami neodchytnete (viz díl 10), a vypíše MessageBox s odpovídající hláškou podle konkrétní hodnoty ErrorCode. „Nezná“ ovšem všechny hodnoty ErrorCode, a tak se může stát, že uživateli vypíše hlášku typu „I/O Error 103“. Pokud byste tedy chtěli například hlášku počeštit (což u českých programů výrazně doporučuji!), nebo ošetřit opravdu všechny chyby, odchytněte a ošetřete výjimku sami. Podívejte se na následující příklad, který demonstruje ošetření chybových stavů v souvislosti s načítáním obsahu souboru do Memo.

procedure TwndHlavni.btnOPClick(Sender: TObject); var   Soub: TTextSoub;   Radka: String; begin   AssignFile(Soub, `data.txt`);   try     Reset(Soub);     try       while not Eof(Soub) do begin         ReadLn(Soub, Radka);         memoSoubor.Lines.Add(Radka);       end; // while     finally       CloseFile(Soub);     end; // finally except     on E:EInOutError do       case E.ErrorCode of         2: ShowMessage(`Soubor nenalezen!`);       103: ShowMessage(`Soubor neotevřen!`);       else         ShowMessage(`Chyba při práci se souborem: ` + E.Message);       end; // case     end; // except   end; // procedure

Asi byste sami dokázali vysvětlit, že ve vnějším bloku try se pokusíme soubor otevřít, a nepodaří-li se to, ošetříme výjimku (oznámíme chybu) a končíme. Ve druhém vnořeném bloku try (vnitřnějším) se pokusíme soubor načíst do komponenty Memo. Je jisté, že v tuto chvíli je soubor správně otevřen, takže v každém případě soubor posléze zavřeme (sekce finally).

V tomto příkladu tedy máme ošetřeny chyby při otvírání souboru i při vlastním čtení. Pokud by ovšem např. ReadLn generovala při chybě výjimku EReadError, chyby čtení by ošetřeny nebyly (neboť odchytáváme jen EInOutError). To ona ovšem negeneruje).

Poznámky pro pokročilé uživatele:

S problematikou I/O chyb souvisí také výjimky EReadError, resp. EWriteError. První je generována, pokusí-li se aplikace načíst ze streamu větší počet bytů, než je možno. Může být také generována, nedokáže-li Delphi načíst vlastnost při vytváření formuláře (komponenta načítá zdroje – resource – nekorektně, příp. zdroje jsou chybné). Analogicky funguje EWriteError.

Představte si, že byste měli v aplikaci mnoho procedur a funkcí, které budou pracovat se souborem. Opisovat vždy tentýž kód spojený s obsluhou výjimek je jistě nepříjemné a nudné. Naštěstí je to také zbytečné, neboť lze použít událost OnException objektu TApplication, ve které napíšeme ošetřující kód „jednou provždy“. Jednoduchá ukázka, ve které se aplikace po vzniku každé neošetřené výjimky ukončí:

procedure TwndHlavni.FormCreate(Sender: TObject); begin   Application.OnException := AppException; end; procedure TwndHlavni.AppException(Sender: TObject; E: Exception); begin   Application.ShowException(E);  // ukaž chybovou zprávu   Application.Terminate;        // ukonči aplikaci end;

Další příkazy pro práci se soubory

Pouze telegraficky si popíšeme několik dalších metod Delphi, které se soubory (a adresáři) úzce souvisí.

Funkce

Význam

FileExist(jmeno)

Vrací True, existuje-li zadaný soubor

DeleteFile(jmeno)

Smaže zadaný soubor a vrací True, podařilo-li se

RenameFile(stare, nove)

Přejmenuje soubor a vrací True, podařilo-li se

ChangeFileExt(jmeno, pripona)

Změní příponu souboru na zadanou a vrací nový název souboru

ExtractFileName(uplne_jmeno)

Extrahuje název souboru (tj. odstraní případnou cestu). Vrací získaný název.

ExtractFileExt(jmeno)

Vrací příponu daného souboru

Poznámka: nekamenujte mě prosím za nesprávně (stručně) uváděnou syntaxi. Použil jsem ji pro úsporu místa, jistě si sami dokážete odvodit (příp. najít v helpu), že např. „úplný funkční prototyp“ funkce FileExists() vypadá takto:

function FileExists(const FileName: string): Boolean;

Poznámka pro pokročilé uživatele:

Dále existuje celá řada funkcí pracujících s adresáři a se jmény souborů v jednotce SysUtils.

K souborům lze v Delphi přistupovat též pomocí FileCreate(), FileOpen(), FileSeek(), FileWrite(), FileClose(). Tyto funkce tvoří 'samostatnou' množinu funkcí pro práci se soubory za pomoci handlerů.

Úvodem této části se podívejme na další komponenty Delphi, které zajišťují práci se soubory, adresáři a disky. Nejde už o přímou práci, spíše o zobrazení názvů, výběr, apod. Tím se nenápadně dostaneme ke standardním dialogům, které jsou v podstatě jakousi standardní kombinací níže uvedených komponent.

Komponenty jsou sice poněkud zastaralé, přesto je lze použít. Nacházejí se v paletě Win 3.1, jedna také na paletě Samples.

Komponenta

Význam

FileListBox

Jde o specializovaný ListBox (seznam) určený k výpisu všech souborů v aktuálním adresáři.

DirectoryListBox

Jde opět o specializovaný seznam určený k zobrazení adresářové struktury na daném disku.

DriveComboBox

Specializovaný rozbalovací seznam, zobrazuje název aktuálního disku a „sbalený“ seznam dalších dostupných disků.

FilterComboBox

Specializovaný rozbalovací seznam, zobrazuje aktuální souborovou masku (neboli filtr, typicky All Files - *.> a „sbalený“ seznam dalších dostupných masek

DirectoryOutline

Ukazuje hierarchický pohled na adresářovou strukturu, lze zvolit počet úrovní zobrazených adresářů.

Úvodem ke standardním dialogům

Mnoho aplikací má společný požadavek: potřebují otvírat/ukládat soubory, vyhledávat, tisknout, volit barvu (písma, pozadí…), apod. Proto pro tyto činnosti existují tzv. common dialogs, tedy předdefinované dialogové boxy.

Výhody:

  • jednoduchost používání z hlediska programátora;
  • jednost ovládání z hlediska uživatele.

Nevýhody:

  • nelze je (snadno) modifikovat (jen do určité míry), musíte je použít tak, jak jsou, i když některá z obsažených funkcí daného dialogu funkcí je vám k ničemu a některá naopak chybí;
  • jde o standardní Windows dialogy, proto vznikne „zmatení jazyků“, např. máte-li český program v anglických Windows, apod.

V Delphi 5 nacházíme tyto dialogy:

Název dialogu

Modální

Význam

OpenDialog

ano

pro (ruční) nalezení, vybrání, otevření souboru(ů)

SaveDialog

ano

pro (ruční) nalezení, vybrání, uložení souboru(ů)

OpenPictureDialog

ano

pro (ruční) nalezení, vybrání, otevření souboru(ů) s obrázkem. Navíc od předchozích dvou obsahuje náhled vybraného obrázku.

SavePictureDialog

ano

viz OpenPictureDialog, pro ukládání

FontDialo

ano

pro výběr fontu, velikosti, stylu písma

ColorDialog

ano

pro výběr barvy

PrintDialog

ano

pro odeslání tiskového úkolu na tiskárnu

PrinterSetupDialog

ano

pro konfiguraci tiskárny (nastavení tisku)

FindDialog

ne

pro vyhledávání výrazu v textu

ReplaceDialog

ne

pro vyhledávání a nahrazování výrazu v textu

Poznámka: modální okno znamená (zjednodušeně), že se z něj nelze přepnou do jiného okna téže aplikace před jeho uzavřením.

Dialogy se v Delphi nacházejí v paletě Dialogs. V době návrhu se na formuláři zobrazují jako malé ikonky, jejich plný tvar je vidět až za běhu. Otevřou se zavoláním jejich nejdůležitější metody Execute. Tato metoda vrací True, uzavřel-li uživatel dialog stiskem OK (a chce-li tedy výběr použít) a False, pokud dialog ukončil stiskem Cancel nebo stiskem křížku v rohu, apod. Až na PrinterSetupDialog mají vlastnost Options, která se skládá z několika true/false „podvlastností“.

Nyní se na jednotlivé dialogy podrobněji podíváme. Provedeme to tak, že shrneme vždy jejich nejdůležitější vlastnosti, události a metody, má-li to smysl, jinak si jenom uvedeme nejzajímavější rysy.

OpenDialog, SaveDialog

Tyto dva dialogy se tolik podobají, že je probereme najednou. Ještě jednou připomínám, že Delphi tyto (ani jiné) dialogy neimplementují, pouze je zapouzdřují již hotové jako komponenty. Vzhled dialogů bude proto silně závislý na používané verzi operačního systému Windows, a také na systémovém nastavení!

Vlastnosti:

Vlastnost

Význam

DefaultExt

Přípona, která je přidána za zvolený název souboru, pokud uživatel nezadá žádnou (při zápisu názvu souboru z klávesnice).

FileName

Vrací kompletní název (tj. včetně cesty) zvoleného souboru nebo ji lze použít jako vstupní hodnota (jinak se zobrazí naposledy vybraný soubor).

Files

Read-only run-time vlastnost obsahující kompletní (tj. včetně cest) název (názvy) vybraného souboru (vybraných souborů).

Filter

K zadávání filtrů (masek) souborů. K návrhu filtru (lze zadat i více filtrů) použijte Filter Editor (spustí se kliknutím na tři tečky v rohu vlastnosti Filter v Object Inspectoru).

FilterIndex

Který index je zvolen jako „default“ při otevření dialogu.

InitialDir

Počáteční adresář

Options

Možnosti, blíže viz níže

Title

Titulek, místo Caption

Podívejme se na vlastnost Option. Ta obsahuje 20 logických hodnot, kterými lze poměrně značně modifikovat vzhled a vlastnosti dialogu. Příklady:

  • ofOverwritePrompt – zobrazí varovnou hlášku, pokusí-li se uživatel přepsat existující soubor. Doporučuji vždy zapnout!
  • ofHideReadOnly – nezobrazí se možnost otevřít soubor jen pro čtení;
  • ofShowHelp – zobrazí tlačítko „Help“. Pokud jste nápovědu nenapsali, doporučuji buď zakázat, nebo (lépe!) nápovědu napsat;
  • ofAllowMultiSelect – umožní vybrat více souborů najednou. Ty jsou pak vráceny v (run-time) vlastnosti Files typu TStrings;
  • ofEnableSizing – umožňuje uživateli měnit velikost dialogu.
  • ofOldStyleDialog – zobrazí „starý styl“ dialogového okna

Události:

Nejdůležitější jsou OnShow, resp. OnClose, které se objeví, je-li dialog otevřen, resp. uzavře-li jej uživatel. Lze též ošetřit OnCanClose, ve které můžeme zavření dialogu zakázat (např. po provedení nějakého dodatečného testu, apod.). Vyhrát si můžete s událostmi OnFolderChange, OnSelectionChange, resp. OnTypeChange, které se objeví, změní-li uživatel v dialogu adresář, výběr souborů, resp. masku (filtr).

Metody:

Jak již bylo řečeno, nejdůležitější metodou je Execute, kterou se dialog spouští (otvírá).

Obrázek ukáže „nový styl dialogu“ (ofOldStyleDialog = False) OpenDialog ve Windows 2000, se zapnutým zobrazováním položek jako www-odkazů (znovu připomínám, že toto nastavení se provádí ve Windows (Možnosti složky), nikoliv v Delphi!!).

Příklad: chceme do titulku formuláře wndHlavni zapsat jméno otevřeného souboru a zjistit, jestli byl otevřen jen pro čtení. Použitý OpenDialog se jmenuje dlgOpen.

if dlgOpen.Execute then begin   wndHlavni.Caption := dlgOpen.FileName;   if ofReadOnly in dlgOpen.Options then     ShowMessage(`Soubor otevřen pro čtení!`);

Poznámka: vlastnost Options je typu množina, proto se testování jejích prvků provádí s výhodou pomocí operátoru in (prvek je v množině).

OpenPictureDialog, SavePictureDialog

Tyto dva dialogy se velmi podobají předchozím (pokud jde o vlastnosti, události, metody). Podíváme se tedy jen na některé zvláštnosti.

Už jsme zmínili, že součástí dialogu je plocha pro zobrazení náhledu obrázku. Tento náhled je ovšem zobrazen pouze v případě (a to je také největším omezením těchto dialogů), že zvolený obrázek je rozpoznán třídou TPicture. Jde ovšem pouze o bitmapy <.BMP), ikony <.ICO), Windows metasoubory <.WMF) a rozšířené Windows metasoubory <.EMF). Pokud zvolený obrázek nemůže být zobrazen v náhledu, objeví se v oblasti pro náhled nápis „(None)“.

Pokud uživatel zvolí (chce otevřít) soubor nerozpoznaného formátu (tedy jiný než výše uvedené), TPicture vyvolá výjimku EInvalidGraphic. (To ovšem samozřejmě neznamená, že takový soubor pomocí daného dialogu otevřít nejde, tedy že Dialog jeho název nevrátí!)

FontDialog

Tento dialogový box určitě znáte z textových editorů.

Vlastnosti:

Vlastnost

Význam

Device

Určuje, pro které zařízení je font určen (fdScree, fdPrinter, fdBoth)

Font

Pro vstup a výstup informace o fontu (vstup – např. aktuální font, apod.)

MinFontSize, MaxFontSize

Lze definovat rozsah, z něhož může uživatel vybrat velikost písma. Nutno povolit ještě fdLimitSize flag v Options. Ponechte hodnotu 0, nechcete-li výběr omezovat.

Opět se podívejme na některé zajímavé Options:

  • fdAnsiOnly – zobrazí pouze textové fonty (znakové sady Windows), filtruje tedy např. Symbols, atd.
  • fdApplyButton – zobrazí tlačítko „Použít“ (v českých Windows, jinak „Apply“)
  • fdEffects – zobrazí možnosti stylů (podtržené, atd.)
  • fdTrueTypeOnly – zobrazí pouze písma True-Type
  • fdForceFontExist – doporučuji vždy nastavit na True, jinak uživatel může zadat jméno fontu, který neexistuje.

Události:

Oproti předchozím dialogům má navíc událost OnApply, která nastane, když uživatel stiskne tlačítko Použít (Apply).

Metody:

Nejdůležitější metodou je opět Execute.

Podívejte se na příklad konkrétního použití FontDialogu. Chceme změnit písmo v komponentě Memo (nazvané memoText) FontDialog se jmenuje dlgFont.

procedure wndHlavni.btnPismoClick(Sender: TObject); begin if dlgFont.Execute then memoText.Font := dlgFont.Font; end;

Poznámka pro pokročilé uživatele:

Někdy se spíše než přiřazení používá metoda Assign:

memoText.Font.Assign(dlgFont.Font);

Metoda Assign také nastaví vlastnosti fontu, ale s výjimkou vlastnosti PixelsPerInch. Může tedy být použita např. ke kopírování obrazovkového fontu na font tiskárny.

ColorDialog

K tomuto dialogu také není mnoho co dodat, takže jen telegraficky: klíčovou vlastností je Color, ve které se předává a vrací konkrétní barva.Vlastnost CustomColor může obsahovat definici až 16 uživatelských barev. K definici se používá klasický String List Editor, formát je následující:

ColorX=AABBCC,

kde za X dosaďte písmeno A..P a AA, BB, CC jsou hexadecimálně vyjádřené jednotlivé barevné složky.

Vlastnost Options má tentokráte jen 5 položek. cdFullOpen zajistí otevření celého dialogu (tedy i části umožňující definovat uživatelské barvy), naopak cdPreventFullOpen zcela potlačí možnost otevřít sekci uživatelských barev.

Obrázek ukáže kompletní ColorDialog (včetně sekce uživatelských barev):

PrinterSetupDialog, PrintDialog

PrinterSetupDialog zobrazí standardní dialog „Nastavení tisku“. Nemá žádné zvláštní události ani vlastnosti. Vše, co je zpravidla nutné udělat v souvislosti s tímto dialogem, je zareagovat na nějakou událost OnClick a zavolat metodu Execute. Vzhled tohoto dialogu úzce souvisí s nainstalovanou tiskárnou.

PrintDialog již má několik parametrů, které stojí za zmínku. Můžete nastavovat vstupní hodnoty (default) a číst výstupní, jako například počet kopií (vlastnost Copies), způsob kompletace (vlastnost Collage). Lze také omezit počet stran, které bude možné tisknout (MinPage, MaxPage). Zaškrtnete-li volbu PrintToFile, musíte sami od uživatele získat jméno souboru a provést sami příslušnou operaci se souborem!

Některé zajímavé volby lze provádět také pomocí vlastnosti Options, např. poSelection zobrazí RadioButton umožňující uživateli vybrat ručně oblast k tisku (zvýrazněný text se vytiskne). Vždy byste měli nechat zatrženou vlastnost poWarning, která znamená, že uživateli bude zobrazeno varování, bude-li se pokoušet tisknout na nenainstalovanou tiskárnu.

FindDialog, ReplaceDialog

Základním kamenem úrazu komponent Find- a ReplaceDialog je fakt, že jde jen o dialogy! Slouží pouze k zadání toho, co se má hledat (nahrazovat). Vlastní hledání (nahrazování) je třeba naprogramovat ručně. Bohužel.

Text, který se má vyhledat, je ve vlastnosti FindText. Doporučuji při prvním hledání nechat tento řetězec prázdný (případně by mohl obsahovat slovo, na kterém je kurzor), při každém další se tam ponechává naposledy hledaný výraz. ReplaceDialog má pochopitelně ještě vlastnost ReplaceText.

Volby jsou opět ve vlastnosti Options:

  • frDown – směr vyhledávání, hodnota True znamená, že default = dolů
  • frMatchCase – zda se mají rozlišovat malá a velká písmena
  • frWholeWord – zda se mají vyhledávat pouze celá slova

Pokud některou z těchto voleb nechcete uživateli zobrazit vůbec, použijte odpovídající možnost začínající slovem Hide (např. frHideMatchCase, atd.). Můžete také uživateli tyto možnosti zobrazit, ale zakázat mu je nastavit, pak použijte odpovídající možnost začínající slovem Disable (např. DisableMatchCase).

Události:

FindDialog oplývá především událostí OnFind, která nastane, stiskl-li uživatel tlačítko Find Next (Najít další). ReplaceDialog má navíc analogickou událost OnReplace, která následuje po stisku tlačítka Replace nebo Replace All (Nahradit nebo Nahradit vše).

Metody:

Tyto dva dialogy jsou nemodální, mohou tedy zůstat na obrazovce po několik operací hledání / nahrazování. Kromě metody Execute je tedy k dispozici metoda CloseDialog, která dialog uzavře.

Veškerý kód zajišťující vlastní vyhledávání a nahrazení musíte vytvořit ručně! Pokud si na to sami netroufáte, nevadí, v příštím díle seriálu jej vytvoříme společně.

Komponenta RichEdit

Komponenta RichEdit se nachází v paletě Win32. Slouží k poměrně sofistikované práci s textem. Podíváme se na ni blíže a následně ji použijeme v reálné aplikaci – postavíme na ní textový editor.

Komponenta RichEdit obsahuje velké množství vlastností formátu textu RTF (Rich Text Format). Podrobnější informace o formátu RTF můžete nalézt třeba na www.wotsit.org.

„Odlehčenou“ verzí ovládacího prvku RichEdit je komponenta Memo, která je popsána v sedmém dílu našeho seriálu.

Vlastnosti textu (TTextAttributes)

Pomocí komponenty RichEdit můžeme mnoha způsoby pracovat s textem. Můžeme upravovat typ fontu, barvu, velikost, styl apod. Výhodou RichEditu je, že všechny tyto atributy lze nastavovat buď celému textu, nebo jen vybrané části.

Než se podíváme na konkrétní vlastnosti a metody pro práci s textem, musíme si něco říci k třídě TTextAttributes. Tato třída slouží právě k uchování informací o atributech textu a je použita v několika atributech vlastního RichEditu, ve kterých uchováváme informace o textu. Podívejme se na vlastnosti třídy TTextAttributes:

Charset - specifikuje znakovou sadu fontu. Typem jde o celé číslo v rozmezí 0…255. K dispozici je také celá řada předdefinovaných konstant:

  • ANSI_CHARSET (0) - ANSI znaky
  • DEFAULT_CHARSET (1) – font je vybrán pouze na základě specifikovaného jména a velikosti (vlastnosti Name a Size)
  • SYMBOL_CHARSET (2) - standardní symbol
  • OEM_CHARSET (255) – záleží na kódové stránce operačního systému
  • …dále jsou definovány konstanty pro jednotlivé znakové sady (např. CHINESEBIG5_CHARSET (136) – tradiční čínština, RUSSIAN_CHARSET (204), apod.

Color – barva písma. Přípusné hodnoty jsou buď přímo barvy (např. clGreen, clRed, apod.) nebo barvy definované systémem. V takovém případě záleží na nastavení barevného schématu ve Windows (např. clMenuText = barva písma v menu, clHightlightText = barva vybraného textu, apod.). Pokud máte ve svých Windows nastavenu barvu vybraného (tj. označeného) textu např. na zelenou, způsobí přiřazení barvy na clHighlightText zezelenání textu.

ConsistentAttributes – read-only vlastnost říkající, které vlastnosti TTextAttributes jsou konzistentní v celém označeném textu současného výběru v RichEditu. Jistě si dovedete představit využití – pokud bude celý označený text mít stejnou velikost písma, můžete tuto velikost zobrazit. Pokud ne, nezobrazíte nic, apod.

Height – výška (velikost) písma. Je udávána v pixelech.

Name – jméno fontu

Pitch – udává, jakou šířku mají jednotlivé znaky, lépe řečeno, mají-li všechny stejnou šířku nebo nikoliv. Přípustné hodnoty: fpDefault (hodnota záleží na fontu specifikovaném ve vlastnosti Name), fpFixed (všechny znaky mají stejnou šířku), fpVariable (znaky mají různou šířku). Poznámka: nastavení šířky písma nepřinutí Windows roztáhnout nebo smrštit znaky aktuálního fontu:-), i když by to byl jistě zajímavý pohled, ale vyberou font, který danou vlastnost splňuje. Pokud máte např. vybrán font MS Serif a nastavíte fpFixed, zobrazí se Courier.

Protected – logická hodnota indikující, je-li text reprezentovaný v TTextAttributes „protected“. Protected text nemůže být uživatelem změněn. Pokusí-li se uživatel editovat výběr, který obsahuje protected text, RichEdit generuje událost OnProtectChange. V obsluze této události můžeme povolit nebo zakázat editaci textu. Pokud jsme nevytvořili obsluhu události OnProtectChange, protected text je read-only.

Size – velikost písma. Vztah mezi hodnotou v pixelech (vlastnost Height) a hodnotou Size je následující: Size = Height * 72 / ScreenPixelsPerInch. Hodnotu ScreenPixelsPerInch lze získat z proměnné Screen.PixelsPerInch.

Style – styl textu, přípustné hodnoty: fsBold (tučné), fsItalic (kurzíva), fsUnderline (podtržené), fsStrikeout (horizontálně škrtnuté). Typ vlastnosti Style je množina, proto může obsahovat více hodnot (písmo může být zároveň podtržené a tučné, tedy [fsUnderline, fsBold]).

Tolik k vlastnostem třídy TTextAttributes. Tato třída je v RichEditu využívána pro vybraný (označený) text (tj. vlastnost SelAtributes) a ostatní text (tj. vlastnost DefAtributes). K těmto vlastnostem se ještě vrátíme v další podkapitole.

Na závěr se podívejme, jak v programu zmíněné vlastnosti nastavit. Předpokládejme, že máme na formuláři (TwndHlavni) umístěn RichEdit (reEditor). Podívejme se, jak například zjistit, je-li celý označený text tučný (nebo celý netučný):

with TwndHlavni.reEditor do   if caBold in SelAttributes.ConsistentAttributes then     ShowMessage(`Celý text je tučný nebo netučný`)   else     ShowMessage(`Část textu je tučná, část ne`);

Vlastnosti RichEditu

K práci s textem slouží především vlastnosti, které shrnuje následující tabulka. Upozorňuji, že většina jich je runtime, proto je nehledejte v Object Inspectoru.

Vlastnost

Význam

DefAttributes

je typu TTextAttributes (viz předchozí podkapitola) a popisuje „normální“, tedy neoznačený text. V podstatě říká, jak bude vypadat nově přidávaný text.

HideScrollBars

říká, mají-li se skrýt posuvníky, nejsou-li potřeba (celý text se na stránku vejde). Nastavení této vlastnosti nemá žádný význam, pokud je vlastnost ScrollBars (celého RichEditu) nastavena na ssNone.

HideSelection

říká, bude-li vizuální indikace označení textu viditelná i po ztrátě zaměření RichEditu (jednoduše: bude-li text stále vidět jako označený, když se přepnete z RichEditu jinam v rámci aplikace).

Lines

klíčová vlastnost, obsahuje vlastní text. Je typu TStrings.

Paragraph

specifikuje formát odstavce textu, podrobněji viz další podkapitola

PlainText

velmi důležitá vlastnost, kladná hodnota (True) říká, že soubor se bude ukládat jako čistý text <.TXT), kdežto záporná hodnota (false) znamená ukládání ve formátu RTF

SelAttributes

je typu TTextAttributes (viz předchozí podkapitola) a popisuje označený text.

SelLength

délka (tj. počet znaků) označeného textu

SelStart

pozice začátku označeného textu (ve znacích od začátku dokumentu, 0 znamená první znak)

SelText

označený text. Tato vlastnost je typu string (nikoliv TStrings)

WordWrap

říká, budou-li zalamovány řádky, které přesahují viditelnou oblast obrazovky

Než se podíváme na vlastnosti odstavce, povíme si ještě o několika metodách komponenty RichEdit, které jsou velmi praktické:

Metoda

Význam

FindText

Mocná vlastnost, vyhledává text v RichEditu. Parametry: FindText (co: string; odkud, délka: Integer, možnosti: TSearchTypes). Tato metoda je použita v příkladu níže.

Print

Vytiskne obsah RichEditu

ClearSelection

Vymaže označený text

CopyToClipboard

Zkopíruje označený text do schránky

CutToClipboard

Vystřihne označený text do schránky, tj. text z dokumentu vyjme

PasteFromClipboard

Vloží na pozici kurzoru text ze schránky. Je-li označen jiný text, nahradí se.

SelectAll

Označí celý dokument

Odstavec (Paragraph)

Jednou z vlastností komponenty RichEdit je odstavec (Paragraph). Paragraph je runtime vlastností, která specifikuje formátovací informaci aktuálního odstavce. Formátovací informací rozumíme zarovnávání, odrážky, číslování a tabulátory. Aktuálním odstavcem je myšlen odstavec obsahující označený text. Pokud není označen žádný text, je aktuálním odstavcem ten, na (ve) kterém je kurzor. Paragraph je typu TParaAttributes.

Poznámka pro pokročilé uživatele:

Paragraph je read-only vlastnost, protože objekt TCustomRichEdit má pouze jeden objekt TParaAttributes, který nemůže být měněn. Atributy aktuálního odstavce ovšem mohou být měněny, a to nastavováním vlastností objektu TParaAttributes. Mohou být nastavovány jedna po druhé nebo najednou podle existujícího objektu TParaAttributes zavoláním Paragraph.Assign.

Podívejme se tedy na vlastnosti odstavce.

Vlastnost

Význam

Alignment

Specifikuje, jak má být text odstavce zarovnán. Přípustné hodnoty: taLeftJustify (vlevo), taCenter (na střed), taRightJustify (vpravo).

FirstIndent

Říká, jak moc bude první řádka odstavce „odražená“ od levého okraje, udává se v pixelech.

LeftIndent

Říká, jak moc budou ostatní řádky odstavce „odraženy“ od levého okraje. Tato vlastnost je použitelná např. k „vypíchnutí“ některého důležitého odstavce, který má jiné postavení v textu, apod. LeftIndent, stejně jako předchozí FirstIndent, nesouvisí s odrážkami (tedy puntíky).

Numbering

Specifikuje odrážku aktuálního odstavce (tedy onen puntík). Je-li nastavena vlastnost LeftIndent, je odrážka posunuta o uvedenou vzdálenost. Přípustné hodnoty: nsNone, nsBullet.

RightIndent

Analogie LeftIndent. Je efektní zvláště při nastavení Alignment = taRightJustify.

Tab

Určuje počet pixelů, po kterých se bude „poskakovat“ tabulátorem. Tab je indexovaná vlastnost. Pozice prvního „tab stopu“ je tedy Tab[0], dalšího Tab[1], atd.

TabCount

Počet definovaných zastávek tabulátoru („tab stopů“) v tomto odstavci.

Podívejme se na příklad procedury, která definuje pozici zarážek tabulátoru:

procedure NastavTabStops (RichEdit: TRichEdit; TabStops: array of Integer); var   i: Integer;  // indexy oznacujeme zpravidla malymi pismeny begin   with RichEdit.Paragraph do begin     TabCount := High(TabStops) + 1;  // nejvyssi index + 1     for i := 0 to TabCount – 1 do       Tab[i] := TabStops[i];   end; end;

Nyní nadefinujeme zarážky tabulátoru:

NastavTabStops(reEditor, [25, 50, 80, 100]);

Vyhledávání v textu

Tolik tedy obecně k RichEditu. Nyní již jsme schopni vytvořit onen kýžený textový editor, ovšem nejprve vyřešíme restík, který nám zbyl z předchozího dílu. Tam jsme si ukázali dialogy pro vyhledávání a nahrazování v textu – FindDialog, resp. ReplaceDialog. Řekli jsme si, že je ovšem nutné ručně zpracovat vlastní vyhledávání. Na to se tedy nyní vrhneme.

Uvedu možnou obsluhu události pro vyvolání (otevření) FindDialogu (např. po stisku tlačítka, vybrání příkazu v menu apod.). Její součástí bude automatické vyplnění políčka s vyhledávaným textem v případě, že uživatel nějaký text označil.

procedure TwndHlavni.btnNajdiClick(Sender: TObject); begin   dlgFind.FindText := reEditor.SelText;   dlgFind.Execute; end;

Následuje kód ošetřující událost OnFind dialogu FindDialog:

procedure TwndHlavni.dlgFindFind(Sender: TObject); var   Pozice: Longint; begin   with reEditor do begin     Pozice := Pos(dlgFind.FindText, Lines.Text);     if Pozice > 0 then begin       SelStart := Pozice - 1;       SelLength := Length(dlgFind.FindText);     end     else       MessageDlg(`Nenalezeno!`, mtError, [mbOk], 0);     SetFocus;  // jako aktivni nastav reEditor     dlgFind.CloseDialog;   end; end;

Poznámka: tento kód je použitelný k hledání obecně. Pokud ale vyhledáváme konkrétně v RichEditu (což se právě chystáme provádět), je lepší použít vlastnost RichEditu FindText a implementovat vyhledávání takto:

procedure TwndHlavni.dlgFindFind(Sender: TObject); var   Pozice: Longint; begin   with reEditor do begin     Pozice := reEditor.FindText(dlgFind.FindText, 0, Length(text), []);     if Pozice > 0 then begin       SelStart := Pozice;       SelLength := Length(dlgFind.FindText);     end     else       MessageDlg(`Nenalezeno!`, mtError, [mbOk], 0);     dlgFind.CloseDialog;     SetFocus;   end; end;

Vytváříme textový editor

Nyní již nám nechybí vůbec nic k tomu, abychom naprogramovali textový editor. Protože to určitě zvládnete sami, budu psát jen body a podrobněji se zastavím jen u „zapeklitějších“ situací.

Vytvoříme nový projekt. Na formulář umístíme následující komponenty (v závorce uvedu, jak je nazveme): RichEdit (reEditor), MainMenu (mnHlavni), PopupMenu (mnPopup). Dále na formulář umístíme dialogy: OpenDialog (dlgOpen), SaveDialog (dlgSave), FontDialog (dlgFont), ColorDialog (dlgColor), PrintDialog (dlgPrint), PrinterSetupDialog (dlgSetup), FindDialog (dlgFind).

Otevřeme si Menu Designer u mnHlavni a vytvoříme hlavní nabídku programu. V té budou následující položky:

  • Soubor - s podpoložkami Nový, Otevřít, Ulož, Ulož jako, Nastavení tisku, Tisk, Konec
  • Úpravy – s podpoložkami Kopíruj, Vystřihni, Vlož, Vybrat vše, Najít
  • Formát – s podpoložkami Písmo, Barva, Zalamování řádků
  • Nápověda – s podpoložkami O programu a Nápověda.

Poznámka: mějte na paměti, že správně vytvořené menu (viz 9. díl našeho seriálu) by mělo obsahovat možnost zkrácené volby (tj. používání ampersandu & před příslušným písmenem textu položky) a možnost používat klávesové zkratky (např. Ctrl+C by mělo zkopírovat označený text apod.).

V návrhu aplikace je ještě vhodné nastavit „Default“ hodnotu titulku okna. Aby bylo možno kód plně opět využít:-), použijeme konstantu JM_PRG, která bude obsahovat název programu, tedy v našem případě „Textový editor“. Další konstanta (NO_NAME) označuje název souboru „Bez názvu.txt“. Konstanty budou definovány „nahoře“ v modulu, tedy v sekci definic, buď v části interface nebo implementation.

const   JM_PRG  = `dfsd`;   NO_NAME = `Bez názvu.txt`; wndHlOkno.Caption := NO_NAME + JM_PRG;

Nyní přijde důležitý okamžik. Budeme postupně ošetřovat jednotlivé položky menu.

Soubor – Nový: program by se měl zeptat, zda uložit změněný soubor (pokud byl změněný), a pak vymazat celý obsah RichEditu. K tomu účelu je možné např. vytvořit funkci UlozZmeny, která zjistí, zda byl soubor změněn, a podle uživatelova přání jej uloží. Použitelná je vlastnost RichEditu Modified a událost RichEditu OnChange. Použitá procedura UlozSoubor bude definována níže. Budeme udržovat privátní atribut JmenoSouboru, ve kterém bude uloženo jméno právě zpracovávaného souboru. V souladu s poznatky o objektově orientované architektuře víme, že je nutné definovat metodu, která bude toto jméno souboru nastavovat. Ukázka kódu funkce pro nastavení jména:

procedure TwndHlavni.NastavJmenoSouboru(const Jmeno: String); begin   JmenoSouboru := Jmeno;   wndHlavni.Caption := ExtractFileName(JmenoSouboru) + ` - ` + JM_PRG; end;

Poznámka: při každém vkládání nového jména souboru budeme muset volat funkci NastavJmenoSouboru. To je sice poměrně běžný postup, ale není úplně hifi:-), to jaksi všichni cítíme. Ideálním řešením by bylo použití tzv. property. Tato problematika bude popsána v druhém díle zaměřeném na objektové koncepty.

Kód funkcí pro test změny textu uložení změn:

function TwndHlavni.UlozZmeny: TModalResult; begin   if reEditor.Modified then begin     Result:= MessageDlg(`Soubor byl změněn, chcete jej uložit?`, mtConfirmation, mbYesNoCancel, 0);     if Result = mrYes then       UlozSoubor;   end   else     Result := mrOK; end; function TwndHlavni.UlozSoubor; begin   reEditor.Lines.SaveToFile(JmenoSouboru);   reEditor.Modified := False; end;

Kód vlastní položky menu:

procedure TwndHlavni.NovyClick(Sender: TObject); begin   if (UlozZmeny <> mrCancel) then begin     NastavJmenoSouboru(NO_NAME);     reEditor.Lines.Clear;     reEditor.Modified := False;   end; end;

Soubor – Otevři: program by měl nejprve otestovat (stejně jako v předchozím případě), zda je nutné soubor uložit. Pak by měl otevřít dialog OpenDialog. Protože RichEdit jako takový umožňuje práci se soubory TXT a RTF, měl by tento dialog být nastaven pro práci právě se zmíněnými soubory. Ukázka kódu:

procedure TwndHlavni.OtevriClick(Sender: TObject); begin   if (UlozZmeny <> mrCancel) then     if dlgOpen.Execute then begin       NastavJmenoSouboru(dlgOpen.FileName);       reEditor.Lines.LoadFromFile(JmenoSouboru);       reEditor.Modified := False;       reEditor.ReadOnly := ofReadOnly in dlgOpen.Options;     end; end;

Soubor – Ulož: soubor bude (bez ptaní) uložen pod svým aktuálním jménem. Jedinou výjimkou bude situace, kdy se soubor bude jmenovat „Bez názvu.txt“. V takovém případě se otevře dialog Uložit jako.

procedure TwndHlavni.UlozClick(Sender: TObject); begin   if JmenoSouboru = NO_NAME then     UlozJakoClick(Sender)   else     UlozSoubor; end;

Soubor – Ulož jako: bude otevřen dialog SaveFile, ve kterém bude uživatel moci vybrat jméno, pod kterým se bude soubor ukládat.

procedure TwndHlavni.UlozJakoClick(Sender: TObject); begin   if dlgSave.Execute then begin     NastavJmenoSouboru(dlgSave.FileName);     UlozSoubor;   end; end;

Další příkazy musíme z důvodu rozsahu článku probrat již jen povrchně.

Soubor - Nastavení tisku: vyvoláme dialog PrintSetupDialog

Soubor - Tisk: vyvoláme dialog PrintDialog. Ukážeme si kód této procedury:

procedure TwndHlavni.TiskClick(Sender: TObject); begin   if dlgPrint.Execute then     reEditor.Print(JmenoSouboru); end;

Soubor - Konec: nejprve bychom měli opět otestovat, zda je soubor potřeba uložit. Pak ukončíme aplikaci. Není zrovna vhodné provádět další explicitní dotazy typu „Chcete opravdu skončit?“, protože uživatel bude často potřebovat editor opakovaně vypínat a znovu spouštět. Tato situace může nastat třeba při editaci textu, jehož originál je umístěn na vzdáleném stroji. Pak je totiž potřeba editor zavřít, aby byla lokální kopie souboru přenesena zpět (typicky Windows Commander).

Úpravy - Kopíruj: na zkopírování označeného textu do schránky existuje metoda komponenty RichEdit (lépe řečeno zděděná metoda z TCustomEdit).

procedure TwndHlavni.CopyClick(Sender: TObject); begin   wndHlavni.reEditor.CopyToClipboard; end;

Úpravy – Vyřízni, Vlož: k těmto dvěma činnostem existují analogické metody komponenty RichEdit jako pro kopírování do schránky. Popsány jsou v tabulce v předchozí podkapitole.

Úpravy – Vybrat vše: opět existuje metoda, konkrétně SelectAll.

Úpravy – Najít: kód, který uvedeme do obsluhy. Události vybrání této položky v menu jsou uvedeny v předchozí podkapitole, stejně jako kód, který je nutno napsat do obsluhy události OnFind dialogu FindDialog.

Formát – Písmo: nejprve zjistíme, zda je označena nějaká část textu (a zda se tedy bude nastavování týkat atributu SelAttributes nebo DefAttributes). Pak zavoláme dialog FontDialog:

procedure TwndHlavni.PismoClick(Sender: TObject); begin   with reEditor do begin     if SelLength > 0 then begin       dlgFont.Font.Assign(SelAttributes);       if dlgFont.Execute then         SelAttributes.Assign(dlgFont.Font);     end     else begin       dlgFont.Font.Assign(DefAttributes);       if dlgFont.Execute then         DefAttributes.Assign(dlgFont.Font);     end;   end; end;

Formát – Barva: provedeme analogicky jako font

Formát – Zalamování řádků: použjeme vlastnosti RichEditu WordWrap. Praktické je u této položky v menu zobrazovat zatržení říkající, zda je tato volba právě aktivována či nikoliv.

Nápověda – O programu: vytvoříme volbou v menu File – New Form nový formulář. Na jeho designu se můžeme klidně vyřádit; pokud nechceme zůstat u „střízlivého“ vzhledu, zkusíme si třeba zařadit na formulář nějaký posuvný text (námět na domácí úkol – víte, jak na to?).

Nápověda – Nápověda: buďto vytvoříme klasickou nápovědu (soubor *.HLP, což je vcelku „pakárna“ a kromě toho to bude obsahem samostatného dílu našeho seriálu), nebo budeme líní (a u takovéto aplikace by to asi až tolik nevadilo) a nápovědu vytvoříme jako nový formulář s komponentou Memo (nebo RichEdit:-)), do které napíšeme text nápovědy, tedy stručné shrnutí voleb a možností programu.

Pryč s Poznámkovým blokem

Jednoduchý editor máme hotov. Můžete ze svého systému vymazat Poznámkový blok a používat místo něho váš vlastní výtvor – určitě vás to při každém použití potěší. Samozřejmě, nabízelo by se mnoho vylepšení. Iniciativě se meze nekladou. Můžete vylepšit editor o stavový řádek (na kterém by se třeba zobrazovaly údaje o aktuálním souboru, číslo řádky apod). Dále by se daly implementovat statistické údaje, např. počet řádků, znaků; dále aktuální doba psaní apod. Vzhled by mohly vylepšit ikonky (pro otevření a uzavření souboru) – na vylepšování formulářů se podíváme právě v příštím díle. Na závěr dodávám, že podobný příklad je k nalezení v podadresáři DEMOSRICHEDIT adresáře, ve kterém máte nainstalováno Delphi. Je ovšem samozřejmě v angličtině.

A ještě jedna poznámka na závěr: kdo mi jako první pošle vytvořený vlastní zdroják příkladu z tohoto dílu seriálu (s dobrou štábní kulturou:-)), získá čestný titul „Živě’s Best Delphi Programmer“ a nehynoucí slávu plynoucí ze zveřejnění svého jména v příštím díle seriálu…:-)

Řešení domácího úkolu

V minulém díle jsem se vás ptal, jak byste vyřešili vytvoření posuvného textu. Řešením je použití komponenty Timer. Na její událost OnTimer pak posunete text o malý kousek daným směrem. Nezapomeňte testovat, zda je text už na konečném místě :-).

Formuláře nebo okna?

Až dosud jsme používali pojmy „formulář“ a „okno“ v podstatě jako synonyma. Z hlediska uživatele budiž, ale obecně to není úplně přesné.

Pojďme se podívat, co to je vlastně okno z hlediska Windows. Definici rozdělíme podle úhlu pohledu – z pohledu uživatele a z pohledu systému:

  • Z hlediska uživatele je okno částí obrazovky. Je ohraničené, má titulek, je možné s ním pohybovat po obrazovce, většinou měnit velikost, minimalizovat, zavírat. Okna se mohou rozdělovat do dvou kategorií: hlavní okna a dialogové boxy.
  • Z hlediska systému je okno objektem v oblasti vnitřní paměti systému Windows. Často odpovídá oblasti viditelné na obrazovce. Oblast systémové paměti Windows obsahuje seznam všech oken vytvořených všemi aplikacemi. Každému oknu je přiřazen jedinečný identifikátor, číslo nazývané handle. Některá z těchto oken jsou i okny z hlediska uživatele, jiná jsou vytvářena systémem, mají roli ovládacích prvků, nebo mohou být uživateli skryta.

Co to je tedy formulář? Formulář reprezentuje okno z hlediska uživatele. Může být použit k vytvoření hlavních oken, dialogových boxů a MDI oken (viz dále). Na oknech je založeno mnoho dalších komponent, ale jen formuláře definují okna z pohledu uživatele. Další komponenty nebo ovládací prvky mohou být definovány jako okna jen podle definice z technického hlediska.

Když vytvoříte aplikaci, která bude mít jeden formulář, jedno tlačítko a jeden RichEdit, bude samozřejmě z hlediska uživatele existovat jediné okno – onen formulář. Z technického hlediska jsou však vytvořena okna hned čtyři:

  • hlavní okno (overlapped) – formulář,
  • okno aplikace (pop-up) – aplikace,
  • dceřiné okno (child) – tlačítko,
  • dceřiné okno (child) – RichEdit.

Overlapped, child, pop-up

V předchozím příkladu jsem použil názvů overlapped, child a pop-up. Každé z těchto tří označení znamená tzv. styl okna. Podívejme se, oč vlastně u jednotlivých stylů jde:

  • overlapped windows – hlavní okna aplikace
  • pop-up window – dialogové boxy, hlášení, apod. Chování těchto oken se příliš neliší od oken overlapped. Pop-up jsou zřejmě naším dědictvím ze starých verzí Windows. Ve Windows 1.0 se totiž okna nemohla překrývat, mohla ležet jen vedle sebe, a pop-up window byla jedinými okny, která mohla překrývat jiná okna.
  • child windows – používá se pro okna, která se nemohou dostat mimo plochu svého rodiče (rodičovského okna). Původně byla použita pro ovládací prvky uvnitř dialogového boxu.

Můžeme si tedy vysvětlit čtyři okna vytvořená zmíněnou aplikací. Hlavní okno je asi jasné, jde o overlapped okno související s formulářem. Obě child okna jsou vlastně použitými komponentami (tlačítkem, RichEditem). Možná trochu zapeklitější je situace s oknem pop-up. Oč vlastně jde?

Je důležité si uvědomit, že vlastní aplikace je také oknem. Nejen hlavní formulář této aplikace (tedy nejen to, co vidíme na ploše Windows a co my, jako uživatelé, považujeme za aplikaci) je oknem. Oknem je také vlastní aplikace. Abyste na něj nezapomínali, můžete si jej (nepřesně) představit třeba jako ikonu na hlavním panelu.

Okno, vztahující se k objektu aplikace, slouží ke společnému uložení všech oken. Všechny formuláře mají toto (skryté) okno, které je ovládá, proto např. při kliknutí na jedno okno aplikace přenese do popředí všechny dané aplikace.

Z hlediska Windows je okno aplikace typu pop-up. Za domácí úkol zkuste vymyslet, jaké okno je vlastníkem tohoto okna.

V Delphi jsou všechny formuláře okny typu overlapped. Dialogové boxy a další ovládací prvky, které do formuláře umístíte, jsou formulářem vlastněny. Jejich předchůdcem může být buď formulář nebo jedna ze speciálních, tzv. kontejnerových komponent, např. Panel.

Rozdílné pojmy jsou vlastník a rodič (rodičovské okno). Vlastníkem je okno, které má se „svým oknem“ plynulou výměnu zpráv. Rodičovské okno je zpravidla také vlastníkem, ale nutí svá dceřiná okna k pobytu uvnitř vyhrazené (jeho) plochy. Dceřiná okna pak nepoužívají absolutní obrazovkové souřadnice, ale relativní souřadnice z plochy okna rodiče.

Velice šikovným nástrojem na sledování všech oken ve Windows je program WinSight. Dodává se společně s Delphi (např. u Delphi 5 ale jen s verzemi Professional a Enterprise) a naleznete jej v adresáři ..binws32.exe. (Bohužel není součástí Trial verze Delphi, a to ani Enterprise). Po spuštění se objeví seznam všech aktuálně existující oken (bude pěkně dlouhý). Procházíte jej standardně – kliknutím na kosočtvereček, ve kterém je „+“, zobrazíte okna, která jsou daným oknem vlastněna. Dvojité kliknutí na některé okno zobrazí podrobné informace, včetně vlastníka, handle a dalších údajů. Můžete také sledovat zprávy, které si jednotlivá okna posílají, a to pro všechna okna, nebo pro vybraná (hlavní menu, volba Messages). Pokud ovšem chcete něco sledovat či vyhledávat, doporučuji volbu Stop!, která dočasně pozastaví aktivitu programu.

MDI, SDI, cože?

V předchozím textu jste mohli jednou narazit na výraz MDI. Kromě něj se často vyskytuje zkratka SDI. Význam těchto tajuplných zkratek je nakonec vcelku snadno pochopitelný:

  • SDI – Single Document Interface, neboli rozhraní jednoho dokumentu. Představuje aplikaci, do které je možné v jednom okamžiku načíst pouze jeden dokument. Příkladem je třeba Poznámkový blok (nebo náš vytvořený Textový editor z předchozího dílu).
  • MDI – Multiple Document Interface, neboli rozhraní pro více dokumentů). Taková aplikace umožňuje otevření více dokumentů současně. Ukázkou budiž např. MS Word.

Poznámka pro pokročilé uživatele:

Pojem „dokument“ je momentálně již poněkud zastaralý. Aplikace jsou mnohem více orientovány na objekty než na dokumenty. Znamená to, že do SDI aplikace nemusíme nutně vkládat jen dokumenty, ale třeba libovolné objekty OLE (Object Linking and Embedding).

Styly formulářů

V předchozí podkapitole jsme si ukázali styly oken. Jak v Delphi nastavit u daného okna jeho styl?

Formuláře mají vlastnost FormStyle, která je přesně tím, co hledáme:

  • FormStyle = fsNormal: formulář není ani MDI rodičem ani MDI potomkem, je tedy SDI oknem nebo dialogovým boxem;
  • FormStyle = fsMDIChild: formulář je MDI potomkem;
  • FormStyle = fsMDIParent: formulář je rodičovské MDI okno (jakési rámcové okno MDI aplikace);
  • FormStyle = fsStayOnTop: formulář je SDI okno, které zůstává vždy zobrazeno nad všemi ostatními okny (s výjimkou jiných oken fsStayOnTop).

Poznámka: není úplně nejvhodnější měnit tuto vlastnost (FormStyle) za běhu aplikace!

Vytvoření MDI aplikace – prohlížeč obrázků

Aplikaci SDI jsme vlastně vytvářeli v minulém díle seriálu, proto se dnes podíváme pouze na aplikaci MDI. Nejprve velmi jednoduchou aplikaci (prohlížeč obrázků) vytvoříme, pak si povíme o několika důležitých vlastnostech.

Jednou z možností vytvoření MDI aplikace je kliknutí na New… a vybrání položky MDI Application z karty Projects. Pak se vám vytvoří kompletní, funkční MDI aplikace. Touto cestou se ovšem nevydáme a popíšeme si spíše „ruční“ řešení, krok za krokem.

Při vytvářen aplikace nebudu vypisovat zřejmé a triviální kroky, jako např. nastavení titulku komponenty, apod.

  • Vytvořte novou aplikaci (File – New Application).
  • Vlastnost formuláře FormStyle nastavte na fsMDIForm. Jeho jméno (Name, nikoliv Caption) nastavíme třeba na wndRodic.
  • Na tento formulář umístěte jedno tlačítko, pojmenujte jej btnNacti a do titulku napište třeba Načti.
  • Na formulář přidejte také komponentu OpenPictureDialog. Nazveme ji dlgOpenPicture.

Nyní nadefinujeme dceřiný (child) formulář:

  • Vytvořte nový formulář (File – New Form).
  • Vlastnost tohoto formuláře FormStyle nastavíme fsMDIChild. Jméno zvolíme wndPotomek.
  • Na formulář wndPotomek vložíme komponentu Image. Její vlastnost Align nastavíme na alClient a vlastnost Name na imgObrazek.

Máme hotové vytváření uživatelského rozhraní. Nyní projekt uložíme (File – Save Project As). Jednotku Unit1 uložíme jako Rodic, jednotku Unit2 jako Potomek a projekt jako prjMDIApp.

Zbývá napsat programový kód:

Nejprve ošetříme událost OnClick tlačítka btnNacti:

procedure TwndRodic.btnNactiClick(Sender: TObject); begin   if dlgOpenPicture.Execute then     with TwndPotomek.Create(Application) do begin       Caption := dlgOpenPicture.Filename;       imgObrazek.Picture.LoadFromFile(dlgOpenPicture.Filename);     end; end;

Nyní musíme ještě do sekce uses modulu tohoto formuláře připsat odkaz na potomka, tedy

uses   Potomek;

Projekt nyní můžeme směle přeložit a spustit. Zkuste si otevřít více potomků – více oken s obrázky. Jediným „problémem“ zůstává, že při uzavření některého z potomků nedojde k jeho úplnému uzavření (a vymazání z paměti), ale jen k minimalizaci. Kontrolu nad uzavíráním formuláře získáme, pokud ošetříme událost OnClose formuláře wndPotomek:

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

Tím změníme implicitní hodnotu proměnné Action na caFree – formulář se uzavře a uvolní z paměti.

Vzhled aplikace:

Všimněte si, že takto vytvořená MDI aplikace se chová trochu jinak než aplikace, které jsme dosud vytvářeli. Formuláře potomka nemůžeme posouvat mimo formulář rodiče, při minimalizaci se minimalizují jen dolů do oblasti potomka. Všimněte si také chování titulku aplikace.

Formuláře potomků jsou zajímavé ještě z jednoho důvodu. Když si zkusíte některý z nich maximalizovat, zjistíte, že je maximalizován skutečně přes celou plochu svého předka a že jej už nemůžete (jedním kliknutím) ani minimalizovat ani zmenšit na původní velikost. Nemáte zkrátka jeho minimalizační, maximalizační (a vůbec systémová) tlačítka. Víte, jak docílit zpřístupnění těchto tlačítek pro potomky? Námět na domácí úkol!

Automatické vytváření formuláře:

Standardně se oba formuláře (wndRodic, wndPotomek) vytváří automaticky při startu aplikace. Pokud nechcete, aby se automaticky vytvářel i potomek (ostatně to není příliš hezké, je-li vidět prázdné okno bez obrázku), postup je následující:

  • Z hlavního menu vybereme volbu Project, Options. Otevře se dialogové okno Project Options.
  • Ze seznamu AutoCreate na záložce Forms vyberte formulář wndPotomek.
  • Stiskem tlačítka „>“ se tento formulář přesune do seznamu Available Forms.
  • Stiskem OK potvrdíte změnu.

Obecně lze k vytváření formulářů říci toto: MDI aplikace zpravidla potřebují zobrazit více instancí téže třídy formuláře. Každý formulář je objektem, jeho instance se musí vytvořit a po skončení práce opět uvolnit z paměti. Delphi to může zajistit automaticky (viz předchozí příklad), nebo se o to postaráme sami.

Nejprve se podíváme, jak tuto činnost provádějí Delphi automaticky. Důležité jsou tři body:

Definice typu v souboru s jednotkou formuláře: type   TwndRodic = class(TForm)   private   public   end;

Deklarace proměnné typu objekt, která ukazuje na instanci objektu typu TwndPotomek:

var   wndRodic: TwndRodic;

Vlastní vytvoření instance třídy formuláře TwndRodic a přiřazení reference do proměnné wndRodic.

Application.CreateForm(TwndRodic, wndRodic);

Odstranění objektu se provádí až při ukončení aplikace. Formulář vytvořený metodou CreateForm je vlastněn objektem Application. Odstraněním objektu z paměti se zároveň odstraní i všechny objekty, které tento objekt vlastní.

Dynamické vytváření formuláře:

Automatické vytváření formulářů se ovšem uplatní především v aplikacích SDI. V MDI není příliš využitelný, protože tyto aplikace zpravidla obsahují více instancí téže třídy formuláře – viz předchozí příklad s více okny obsahujícími obrázek.

Formulář vytvoříme dynamicky zavoláním jeho konstruktoru Create, jak jsme si řekli v 10. díle seriálu. Tím vytvoříme novou instanci (jméno formuláře ponechávám pro názornost implicitní, i když vím, že je to odsouzeníhodné):

Form1 := TForm1.Create(Application);

Jako parametr konstruktoru předáváme potomka třídy TComponent. Tento prvek se následně chová jako vlastník formuláře. Z konvence obvykle předáváme objekt Application. Jednou z výhod tohoto postupu je, že se instance formuláře automaticky odstraní při ukončení aplikace. Jistě bychom mohli jako parametr předat např. Nil, ale nelze to příliš doporučovat (objekt by neměl vlastníka, odstranění musíme zajistit manuálně, při chybě neošetřené příslušným ovladačem by zůstal formulář v paměti atd).

Poznámka: proměnná Form1 bude ukazovat pouze na poslední vytvořenou instanci (proto v našem příkladu v předchozím textu vůbec žádnou proměnnou nepřiřazujeme a použijeme přímo

with TwndPotomek.Create(Application) do

O odstraňování formuláře z paměti platí totéž co v případě automatického vytváření formulářů. Pokud je formulář vlastněn objektem Application, je při ukončení aplikace automaticky uvolněn. V každém případě jej však můžeme uvolnit ručně. K tomu můžeme použít buď událost OnClose nebo přímo metodu Free.

Událost OnClose má parametr Action, předaný odkazem. Tomuto parametru můžeme přiřadit následující hodnoty:

  • caNone: formulář se nesmí uzavřít
  • caHide: formulář není uzavřen, je pouze skryt. Pokud ovšem v aplikaci nejsou žádné další formuláře, je aplikace ukončena.
  • caFree: formulář se uzavře, uvolní z paměti, a pokud šlo o hlavní okno, je aplikace ukončena.

Čistší možností je ovšem zavolání metody objektu formuláře Free. Tuto metodu bychom měli volat ze stejného modulu, z jakého jsme volali metodu pro vytvoření formuláře, tedy Create. Nejlepší je zavolat ji hned za zavoláním metody Close: Ne vždy ovšem můžeme Free volat (ne vždy dopředu víme o tom, že okno bude uzavřeno). Jméno formuláře ponechávám pro názornost implicitní, ač vím, že je to odsouzeníhodné:

  Form1.Close;   Form1.Free; end;

Vlastnosti, události, metody MDI formuláře

V souvislosti s MDI aplikacemi se podíváme na několik zajímavých vlastností formuláře:

Vlastnost

Význam

ActiveMDIChild

Vrací dceřiný formulář MDI, který má právě zaměření (Focus; jinak řečeno, který je právě aktivní). Pokud nejsou otevřeny žádné dceřiné formuláře, vrací Nil.

MDIChildren

Pole objektů typu formulář. Umožňuje přístup ke všem aktuálně vytvořeným potomkům MDI. První potomek má index 0.

MDIChildCount

Počet dceřiných formulářů (tedy počet prvků v poli MDIChildren).

TileMode

Definuje chování MDI potomků v případě zavolání metody Tile rodičovského formuláře. Při nastavení hodnoty TileMode = tbHorizontal budou uspořádány vodorovně, při TileMode = Vertical svisle.

WinowMenu

Typ této vlastnosti je TmenuItem. Delphi na jejím základě sestaví seznam dostupných dceřiných MDI formulářů, který je vhodné zobrazit v položce hlavního menu Okno.

Podívejme se též na událost OnActivate: ta se vyvolává při přechodu z jednoho dceřiného okna do jiného. Pokud se přepínáte z formuláře, který není MDI, na dceřiný formulář MDI, je vyvolána OnActivate odpovídajícího rodiče MDI.

Na závěr MDI formulářů jsem si nechal několik zajímavých metod:

Metoda

Význam

ArrangeIcons

Uspořádá ikony minimalizovaných dceřiných formulářů podél spodní hranice rodičovského formuláře.

Cascade

Dceřiné formuláře uspořádá do známé kaskády (budou viditelná jejich záhlaví).

Next

Přepne se do dalšího dceřiného formuláře (podle pořadí přepínání klávesou Tab).

Previous

Přepne se do předchozího dceřiného formuláře (podle pořadí přepínání klávesou Tab).

Tile

Uspořádá potomky do „dlaždic“ (mají všechny stejnou velikost, nepřekrývají se). Souvisí s vlastností TileMode – viz výše.

Vytvoření konzolové aplikace

Pryč s grafickým uživatelským rozhraním, s ikonkami, animacemi a dalšími zbytečnostmi, které jen zdržují! Všichni ortodoxní programátoři touží po efektivních programech, které jsou ovládány pouze z příkazové řádky.

Ale vážně: konzolové aplikace (tedy aplikace běžící v módu příkazové řádky) sice momentálně nejsou zrovna nejčastěji vyvíjenými, přesto však (z mnoha důvodů) svůj smysl stále mají. Dříve ovšem existoval velký problém související s vývojem konzolových aplikací: musely běžet v prostředí MS-DOS, a nemohly mít tedy přístup k funkcím Windows API. Delphi ovšem momentálně konzolové aplikace podporují, takže lze vytvořit program běžící v konzolovém módu, ale fungující jako 32bitová aplikace s využitím funkcí Windows API.

Konzolovou aplikaci můžete vytvořit přímo pomocí položky v menu Delphi: New…, Console Application. Bohužel ale ne ve všech verzích (v trojce například tato ikonka chybí; podpora konzolových aplikací v Delphi3 není příliš mohutná). Každopádně nyní vytvoříme konzolovou aplikaci krok za krokem, tak, jak to půjde realizovat i ve starších verzích Delphi.

  • Vytvoříme novou aplikaci (File – New Application).
  • Zvolíme příklad File – Remove From Project.
  • Projekt momentálně obsahuje jedinou jednotku. Protože ale žádný formulář nechceme, vybereme tuto jednotku a klikneme na OK. Na dotaz, zda chceme uložit změny v modulu, odpovíme No.
  • Celý projekt uložíme a nazveme např. prjKonzole.

Bohužel to není vše, co je nutné učinit za účelem vytvoření konzolové aplikace. Zatím máme pouze aplikaci, která nemá žádný formulář. Velikost spustitelného souboru je však např. pro verzi Delphi 3 stále cca 150 kB.

Nyní provedeme editaci zdrojového souboru projektu. Otevřete si buď v některém editoru nebo volbou View – Project Source soubor .DPR. Uvidíme následující kód:

program prjKonzole; uses   Forms; begin   Application.Initialize;   Application.Run; end.

S tímto kódem si nyní trochu pohrajeme

  • V sekci uses je zapsána jednotka Forms. Ta obsahuje metody pro práci s formuláři. V našem programu ale žádné formuláře nejsou. Řešení: vymazat.
  • Direktiva překladače $R zajišťuje „includování“ uvedených souborů, v našem případě souborů *.RES (Resource). Tento soubor slouží k uchování ikony aplikace; konzolová aplikace se bez ikony hravě obejde. Řešení: vymazat
  • Příkazem Application.Initialize inicializujeme externí servery OLE. Naše aplikace však nevyužívá mechanismus OLE. Řešení: vymazat
  • Příkaz Application.Run vlastně zobrazí hlavní okno aplikace. Naše aplikace však neobsahuje žádná okna. Řešení: vymazat
  • Aplikace stále není převedena do konzolového módu. Řešení: převedeme ji direktivou překladače $APPTYPE. Tato direktiva může mít buď parametr GUI (Graphics User Interface, tedy standardní aplikace s grafickým rozhraním) nebo CONSOLE (tedy to, co potřebujeme). Zapíšeme tedy
  • Chceme-li, aby naše aplikace vypsala na příkazový řádek hlášku: “Ahoj, světe! Zdraví Tě konzole!”, mezi begin a end zapíšeme příkaz WriteLn(‘Ahoj, svete! Zdravi Te konzole!’);

Výsledný zdrojový kód tedy bude vypadat takto:

program prjKonzole; begin   WriteLn(`Ahoj, svete! Zdravi Te konzole!`); end.

Pokud chceme použít i jiný příkaz než WriteLn uvedený v příkladu, doporučuji zavést sekci Uses a použít jednotku SysUtils.

Aplikaci uložíme a přeložíme (Ctrl+F9 nebo Project – Compile). Spuštění provedeme z příkazového řádku (ve verzi Delphi 5 lze ovšem i přímo): ve Windows kliknete na Start – Spustit, napíšete Command, v okně najdete vytvořený spustitelný soubor naší aplikace a spustíte jej zapsáním jeho názvu. Všimněte si, že výsledný soubor má velikost pod 20 kB.

Poznámka pro pokročilé uživatele:

V konzolové aplikaci se dostává ke slovu tzv. standardní vstup a výstup, tedy v Pascalu input a output (a v C stdin, stdout, stderr). Použitelné jsou tedy Pascalské příkazy Read, Write, ReadLn, WriteLn.

Řešení domácího úkolu

V minulé kapitole jsem nastínil dvě otázky. První z nich byla, jaké okno je vlastníkem okna aplikace. Pravda je taková, že jde o okno Desktop. Nakonec je to logické, že :-)?

Druhá otázka byla, jak v MDI aplikaci docílit toho, aby okna potomků měla svá minimalizační a maximalizační tlačítka a aby bylo možno takové maximalizované okno uzavřít jedním kliknutím. Kdo z vás na to přišel, může si směle pogratulovat: řešením je vložit na rodičovský (hlavní) formulář komponentu MainMenu, tedy vytvořit v programu hlavní menu. Zmíněná tlačítka potomků se pak vytvářejí jakoby „na onom“ menu.

Co jsou to pouťáky?

A teď už se směle vrhneme do dnešního tématu. Poněkud tajuplný výraz „pouťáky“ neskrývá nic jiného, než pouťové efekty aplikace. Takovým efektem rozumíme třeba barevné tlačítko, obrázek na ploše, změnu kurzoru myši nebo papírek letící do koše při pokusu o výmaz souboru. Tedy vše, čemu se programátoři zpravidla brání, ale čeho si „běžný“ uživatel (a především jeho šéf, který mu bude program kupovat), hned všimne.

Poznámka: Poněkud sporná je otázka, zdali je nápis typu „Systém je zaneprázdněn“ také pouťovým efektem. V tomto ohledu bych vám, drazí čtenáři, dal rád prostor v diskusi pod článkem :-)

Proč se programátoři zuby nehty brání komponování pouťáků do svých aplikací? Nevýhody pouťáků jsou zřejmé:

  • zpomalují aplikaci;
  • ubírají systémové prostředky;
  • berou čas programátorův („proč mám ztrácet čas programováním blikající a pípající animace, když je mnohem důležitější optimalizovat algoritmus?“);
  • nesouvisí bezprostředně s řešeným problémem;
  • dělají z původně svižné aplikace monstrózní, avšak neefektivní monstrum;
  • …atd.

Nevýhod lze najít ještě mnohem víc, ale ať se nám to líbí nebo ne, pouťáky zkrátka aplikaci prodávají. Šéf běžné (ve smyslu nepočítačové) firmy se nebude pídit po tom, kolik geniálních nápadů využil programátor ke zrychlení výpočtu. Když se mu program na první pohled nezalíbí, koupí jej od konkurence, a bude mu jedno, že běží dvakrát pomaleji než ten váš.

Tenhle scénář se netýká jen pouťáků, ale „viditelných“ částí aplikace vůbec, tedy ovládání, struktuře menu, nápovědě, dokumentaci, apod.

Taková je realita. Proto se vší vážností doporučuji v maximální míře dbát na přítomnost pouťových efektů, stejně jako na kvalitu vizuálního rozhraní programu, nápovědy, dokumentace a podobného „balastu“.

Pouťáky v Delphi

Podívejme se, jak snadno a rychle dostat pár pouťáků do aplikace v Delphi a jak obecně vylepšit její vzhled.

Oddělovací čáry – komponenta Bevel

Nejjednodušším, přesto poměrně elegantním způsobem, jak vylepšit vzhled aplikace, je používání oddělovacích čar a rámečků. Pomocí nich vizuálně rozdělíme komponenty na formuláři do logických skupin a celý formulář vypadá mnohem přehledněji.

Komponentu Bevel nalezneme v paletě Additional. Pomocí ní můžeme vytvořit jak pouhou oddělovací čáru, tak rámeček, který navíc může být zapuštěný či vyzdvihnutý. Důležité je, že Bevel (na rozdíl např. od komponenty Panel) se nestává vlastníkem komponent, které na něho umístíme, funguje jen čistě vizuálně.

Důležité jsou dvě jeho vlastnosti: Shape a Style. Vlastnost Shape říká, jak bude Bevel vypadat:

  • bsBottomLine – zobrazí se jen jako čára vespod své klientské oblasti;
  • bsBox – zobrazí se jako box, v závislosti na hodnotě vlastnosti Style bude zapuštěný (lowered) či vyzdvihnutý (raised);
  • bsFrame – klientská oblast bude ohraničena rámečkem (opět lowered nebo raised);
  • bsLeftLine – zobrazí se jako čára na levé straně své klientské oblasti;
  • bsRightLine – zobrazí se jako čára na pravé straně své klientské oblasti;
  • bsSpacer – prázdné místo (nebude vidět nic);
  • bsTopLine - zobrazí se jako čára na horní straně své klientské oblasti.

Vlastnost Style říká, zda tvar bude zapuštěný (bsLowered) nebo vyzdvihnutý (bsRaised) u těch nastavení Shape, kde to má smysl.

Tato komponenta nemá žádné události.

Tlačítko s obrázkem – komponenta BitBtn

Dalším typickým pouťákem jsou tlačítka s bitmapou – komponenty BitBtn. Nalezneme je v paletě Additional. Tato tlačítka jsou pokud jde o funkčnost velmi podobná „klasickým“ tlačítkům (komponentám Button). Liší se od něho pouze svým vzhledem, protože obsahuje bitmapu, která by měla na první pohled charakterizovat účel tlačítka.

V Delphi existuje deset předdefinovaných tlačítek, jak je ukazuje obrázek. Kromě nich je ovšem možné vytvořit libovolný jiný vzhled tlačítka – stačí použít bitmapu vhodné velikosti. Pokud chcete, aby velikost vašich vlastních obrázků korespondovala s velikostí těch standardních, používejte bitmapy o velikosti 18 x 18 bodů.

Podívejme se na důležité vlastnosti tlačítek BitBtn:

Vlastnost

Význam

Glyph

Přiřazuje obrázek (bitmapu) k tlačítku.

Kind

Určuje druh tlačítka, tedy jeho vzhled. Umožňuje vybrat zmíněná předdefinovaná tlačítka, při zadání hodnoty bkCustom můžete použít libovolný (svůj) obrázek.

Layout

Určuje, kde se na tlačítku bitmapa objeví (vlevo, vpravo, nahoře, dole).

Margin

Slouží ke stanovení počtu pixelů mezi levým okrajem tlačítka a obrázkem. Hodnota –1 (která je default) říká, že obrázek i s textem (titulkem) bude centrovaný.

ModalResult

Velmi použitelná vlastnost: říká, zda (a jak) bude uzavřen modální rodičovský (parent) formulář tlačítka. Nevyžadujeme-li jinou činnost, není vůbec nutné ošetřovat události OnClick tlačítek, stačí jim takto nastavit „výsledek“.

NumGlyphs

Říká, kolik obrázků bitmapa obsahuje – viz poznámka pod tabulkou.

Spacing

Určuje počet pixelů mezi obrázkem a titulkem (Caption).

Vlastnost NumGlyphs říká, kolik obrázků obsahuje bitmapa specifikovaná vlastností Glyph. Bitmapa musí splňovat následující podmínky: všechny obrázky musí mít stejnou velikost a musí být umístěny v řadě za sebou. BitButton pak zobrazí jednu z ikon v závislosti na svém stavu:

  • Up – zobrazí se u nevybraného tlačítka, u tlačítka, jež má zaměření, a pokud jiný obrázek není;
  • Disabled – zobrazí se, nelze-li tlačítko vybrat;
  • Clicked – zobrazí se, je-li tlačítko vybráno (je-li zrovan stisknuto);
  • Down – zobrazí se, je-li tlačítko trvale stlačeno (to má význam až u tlačítek SpeedButton, která popisuji v další podkapitole).

Jednu z možností, jak získat bitmapu s více obrázky, popíšeme níže v podkapitole věnované seznamu obrázků (StringList).

Nástrojová lišta poprvé – komponenty SpeedButton

Nástrojové lišty jsou v současných aplikacích velmi časté. Umožňují uživateli možnost rychlé volby bez nutnosti proklikávat se strukturou menu.

V Delphi můžeme nástrojovou lištu vytvořit dvěma způsoby. Komplexnější a flexibilnější je použití komponent SpeedButton z palety Additional. Tyto komponenty sdružíme na komponentu Panel (ta je v paletě Standard).

Komponenta SpeedButton je velmi podobná komponentě BitBtn, popisované v předchozí podkapitole. Zobrazuje bitmapu, ale nemůže zobrazit titulek. Stejně jako u BitButtonu může mít více obrázků pro více svých stavů. Zde se také dostává ke slovu „čtvrtý“ stav – Down (stále vybráno).

Důležitá je vlastnost GroupIndex, pomocí níž můžeme sdružovat tlačítka do skupin. Hodnota 0 (která je default) říká, že jde o nezávislé tlačítko. Pokud chceme vytvořit skupinu, nastavíme všem jejím tlačítkům stejnou (nenulovou) hodnotu GroupIndex. Vlastností Down pak můžeme nastavit, které tlačítko bude zpočátku stisknuto.

Pomocí těchto vlastností můžeme zajistit, aby se tlačítka chovala jako CheckBoxy nebo RadioButtony (tedy aby mohlo být stisknuto právě jedno tlačítko, aby mohla být vybrána libovolná kombinace, nebo aby třeba nemuselo být vybráno žádné tlačítko).

Vytvoření nástrojové lišty:

Shrňme tedy, že pokud chceme vytvořit nástrojovou lištu, vložíme nejprve na formulář komponentu Panel z palety Standard. Pak na ní rozmístíme vlastní tlačítka (komponenty SpeedButton z palety Additional) a vhodně si „pohrajeme“ s jejich vlastnostmi GrouIndex, Down, AllowAllUp.

Cimrmanovský úkrok stranou – komponenta ImageList

Abychom se mohli podívat na další možnost, jak vytvořit nástrojovou lištu, musíme si něco říci o komponentě ImageList. Nalezneme ji v paletě Win32. Jejím účelem je shromažďovat kolekci (seznam) obrázků stejné velikosti. Tyto obrázky (bitmapy, ikony…) jsou následně přístupné pomocí indexu.

ImageList můžeme použít k účinné správě velkých, rozsáhlých množin obrázků a ikon. Navíc všechny obrázky ImageListu jsou reprezentovány jako jedna (široká) bitmapa. Pokud vás napadá souvislost s předchozími podkapitolami (tedy používání bitmap obsahující více obrázků – např. komponenta BitBtn), jste na správné adrese. Kromě tlačítek BitBtn a SpeedButton je seznam obrázků využíván i dalšími komponentami, jako např. TreeView, ListView, CoolBar a ToolBar (viz další podkapitola).

Všechny obrázky jsou následně přístupny pomocí indexu v rozsahu 0 až N-1. V návrhové fázi je nejlepší používat nástroj nazvaný Image List Editor. Ten se vyvolá po stisku pravého tlačítka myši na komponentě ImageList, kterou jsme vložili na formulář (viz obrázek).

Nástrojová lišta podruhé – komponenta ToolBar

Pomocí komponenty ToolBar můžeme také vytvořit nástrojovou lištu. ToolBar se nalézá v paletě Win32. Oproti popisovanému nástrojovému pruhu (vytvořeného z Panelu a ze SpeedButtons) umožňuje navíc některé operace a vyznačuje se poněkud jiným způsobem návrhu. Úzce spolupracujeme s komponentou ImageList popsanou v předchozí podkapitole.

Vše si předvedeme pro změnu na příkladu:

  • Vložíme na formulář komponenty ToolBar (paleta Win32) a ImageList (paleta Win32).
  • Pravým tlačítkem klikneme na komponentu ImageList a zvolíme Image List Editor…
  • V otevřeném okně (viz předchozí obrázek) tlačítkem Add… přidáme do seznamu požadované obrázky (je možné vybírat bitmapy *.BMP a ikony *.ICO). Tlačítkem OK uzavřeme Image List Editor.
  • Levým tlačítkem klikneme na komponentu ToolBar. V Object Inspectoru nastavíme její vlastnost Images na seznam reprezentovaný komponentou ImageList.
  • Pravým tlačítkem klikneme na komponentu ToolBar. Vybereme New Button. Tento krok opakujeme, dokud nevytvoříme kompletní strukturu ToolBaru (nezapomeňte občas vložit New Separator, aby se na vytvořený pruh dalo také dívat:-)).
  • Při opakování předchozího bodu se již automaticky tlačítkům postupně přiřazovaly ikony z ImageListu. Pokud nám tento „default“ stav nevyhovuje, není problém jej libovolně modifikovat, a to nastavením vlastnosti ItemIndex každého z tlačítek na ToolBaru (tedy ToolButtons).

Vidíme, že vytvoření nástrojového pruhu tímto způsobem je velmi flexibilní. Navíc ToolBar umožňuje některé další „bonbónky“, o kterých se ovšem zmíním již jen telegraficky:

  • Pomocí vlastností DisabledImages a HotImages můžeme určit, které obrázky se budou zobrazovat na nepřístupných, resp. aktivních tlačítkách.
  • Můžeme také určovat, jak to bude s možností označovat více než jedno tlačítko zároveň (tedy vytvářet skupiny tlačítek – vlastnost Grouped).
  • Pomocí vlastnosti MenuItem můžeme přiřadit jednotlivým ToolButtonům položky hlavního menu.
  • Vlastností Style můžeme poměrně významně modifikovat styl zobrazení (ale i chování tlačítka).

Záložky – komponenty TabControl a PageControl

V Delphi můžeme se záložkami pracovat dvěma různými způsoby. Buď použijeme komponentu TabControl nebo PageControl. Přestože na první pohled rozdíl nepoznáte, funkčnost je zcela odlišná.

TabControl slouží pouze k definici vlastních záložek. Ve své horní části zobrazuje seznam záložek, ale sám (trochu paradoxně) žádné karty nemá: pokud umístíte na jednu „kartu“ (použil jsem uvozovky, protože fyzicky tam žádná karta vlastně není) jakýkoliv ovládací prvek (komponentu, např. editační pole), bude zobrazeno bez rozdílu na všech „kartách“. Z toho vyplývá, že je nutné naprogramovat reakci na výběr (změnu) záložky.

Naproti tomu PageControl obsahuje vlastní (rozlišné) listy záložek. Ty mohou obsahovat své (vlastní) komponenty a ovládací prvky, jako např. editační okna, tlačítka, apod. Pokud vložíte některou ikonu na jednu kartu, bude skutečně jen na příslušné kartě, nikoliv (zdánlivě) na všech.

Z těchto rozdílů vyplývá také rozdíl v práci a v návrhu těchto dvou komponent.

TabControl U komponenty TabControl se záložky spravují ve vlastnosti Tabs. Tato vlastnost je (nám již dobře známého) typu TStrings, takže v době návrhu je také k dispozici String List Editor. Pro Tabs tedy také fungují všechny metody, které jsme si popisovali v souvislosti s typem TStrings (viz Umíme to s Delphi, 8. díl). Vlastnost TabIndex definuje aktuálně vybranou záložku.

U komponenty TabControl stojí za zmínku také událost OnChanging, v níž je možné zabránit přesunu na jinou záložku (např. můžete otestovat platnost údajů, které uživatel vyplnil do jednotlivých editačních polí, apod.). Používá se parametru AllowChange. V následujícím příkladu např. zakážeme přechod na jinou záložku, pokud je v editačním poli edtUserName slovo „Administrator“, protože nechceme povolit vytvoření uživatele s tímto uživatelským jménem. Komponentě TabControl jsem ponechal původní jméno, ač vím, že je to odsouzeníhodné:

procedure TwndHlavni.TabControl1Changing(Sender: TObject;   var AllowChange: Boolean); begin   AllowChange := (edtUserName <> `Administrator`); end;

PageControl Všimněte si, že jednotlivé listy vytvářejí fyzicky nové komponenty (jmenují se TabSheet a jsou typu TTabSheet). To sice poněkud komplikuje správu, ale zase to umožňuje nastavovat (a navrhovat) individuálně každou z nich.

U komponenty PageControl není k dispozici žádný editor, který by umožňoval editovat seznam záložek. V době návrhu přidáme list se záložkou tak, že na PageControl klikneme pravým tlačítkem a vybereme New Page. S každou vytvořenou stránkou následně můžeme zacházet jako se zcela samostatným, autonomním objektem.

Domácí úkol: zkuste vymyslet, jak přidat novou stránku za běhu programu.



Politica de confidentialitate | Termeni si conditii de utilizare



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 4460
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved