Lekce 9 - Bitmapy a vlastní malování
Vítejte u další části návodu. Budeme pokračovat v kreslení. Začněme ulehčením, které jistě při kreslení využijete:
WITH!
with Canvas do
begin
end;
Způsobí to, že můžete psát položky plátna jako např. Canvas.Pen ale jako pouhé Pen. Stejně jako u okna. Nepíšete Form1.Width, ale stačí Width. Stejně tak můžete využít with. Ještě jeden příklad:
with PaintBox1.Canvas do begin Rectangle(0, 0, 150, 200); Pen.Color := Panel1.Color; Ellipse(1,1,149,199); end;
Na úvod si ještě řekněme, jak můžeme napsat text bez pomoci Labelu. Samozřejmě pouze na Canvas. Použijeme příkaz:
TextOut(X, Y, Napis);
Kde X bude umístění zleva a Y odshora. Napis bude jakýkoli string, který chceme vypsat. Př.:
Canvas.TextOut(100, 150, '<- Zde je pozice 100x150!');
A bude! OK, postupme dál!
Chci vás naučit kreslit na Canvas bitmapy (soubory s koncovkou BMP) a k tomu potřebujete znát nový typ proměnné:
TBitmap
Co umí?: Skoro nic, ale je potřeba. Umí hlavně vytvořit v paměti místo na obrázek a to místo nějakým obrázkem z disku zaplnit. K vytvoření místa slouží tento příkaz:
Obrazek := TBitmap.Create;
Obrazek je proměnná typu TBitmap. A teď potřebujeme do místa pro bitmapu nazvaného Obrazek nějakou tu BMP načíst! Jak? Klasicky:
Obrazek.LoadFromFile(Adresa);
A za Adresa dosadíte adresu nějakého souboru s koncovkou BMP. Př.:
Obrazek := TBitmap.Create;
Obrazek.LoadFromFile('C:\Obrázky\KukíkPoNičení.bmp');
A je to! Poté, co TBitmap použijete a víte, že už ji používat nebudete, měli byste uvolnit místo v operační paměti tím, že TBitmap proměnnou zrušíte:
Obrazek.Destroy;
Nakonec ještě dvě zajímavosti. TBitmap má Canvas (stejnou jako Picture -
nelze ji překreslit tak, že by se smazal její obsah), takže si na ní
můžete klidně kreslit!
A za druhé: Můžete si zvolit průhlednou barvu (stejně jako u okna).
Položkou Transparent nastavíte, zda TBitmap bude, či nebude moct mít
průhlednou část a položkou TransparentColor nastavíte, která barva bude
průhledná.
Př.:
Obrazek.Transparent := True; Obrazek.TransparentColor := clWhite;
A to je k proměnné TBitmap asi vše! Nebo ne?
POZOR! ZAJÍMAVOST!
Chcete si vytvořit soubor BMP? Jde to! Nastavíte velikost Obrazku (typu
TBitmap) a kreslíte si na něj. Jak určit velikost? Pomocí položek Width a
Height. A jak potom uložit do souboru? Pomocí příkazu
SaveToFile(Adresa);
Př.:
Obrazek.Width := 100; Obrazek.Height := 150; Obrazek.SaveToFile('%USERPROFILE%\Desktop\HokusPokus.bmp');
A obsah Obrazku se vám uloží na plochu, %userprofile% je zkratka WIndows, která vede do složky aktuálního uživatele a desktop vede podle názvu na plochu. HokusPokus.bmp! Ukládejte
Dialogy pro práci s obrázky
K načítání obrázků se může hodit okno k otevírání obrázků. To naleznete v části horního menu nazvané Dialogs. Třetí zleva.:
OpenPictureDialog
Zklamu vás, ale vše je stejné, jako u OpenDialogu. Jediný rozdíl je ten, že u dialogu obrázků máte nastavené Filtry pro obrázky, které Delphi zvládá. Ty si samozřejmě ale můžete upravit.
A pro ukládání slouží dialogové okno napravo od OpenPictureDialogu nazvané:
SavePictureDialog
- Stejně jako u OpenPictureDialogu je i zde změna pouze v náhledu na obrázek a Filtrů pro obrázky.
Když už jsme u objektů, povězme si o jednom, který vám určitě vylepší program (můžete si třeba ozvučit různé akce). Zajeďte do Additional a tam naleznete:
ApplicationEvent
- Prostě skvělá věcička! Co když se maximalizuje okno? To byste chtěli třeba změnit rozměry nějakých objektů, ne? Ano, jde použít procedura OnResize, kterou má samotné okno, ale to je jen jedna z výhod, které vám dovoluje ApplicationEvent. No, posuďte sami, při jakých akcích s oknem můžete naprogramovat, co se má stát:
- OnActivate - POZOR! Není to to samé, co má okno tohle se spustí VŽDY, když se okno zaktivuje (má modrý vršek-je aktivní), zatímco u okna pouze poprvé!
- OnDeactivate - Když ze okno deaktivuje (má šedý vršek-je neaktivní)
- OnException - Při výjimce (Co se například stane, když dělíte nulou)
- OnHelp - Při požádání o Help
- OnHint - Jakmile ukážete myší na objekt, který má nadefinovanou položku Hint
- OnIdle - Když je program nečinný (zrovna nic nevypisuje, nebo nepočítá)
- OnMessage - Při vypsání nějaké zprávy (výstražné, informační atd.)
- OnMinimize - Jakmile se minimalizuje okno
- OnRestore - Když se okno vrátí z minimalizace
- OnShowHint - Jakmile se zobrazí pomocný text (Hint)
P.S.k OnShowHint - uvnitř procedury můžete využít místní proměnnou HintStr, která obsahuje text, který se zobrazuje.
Vraťme se zpět k plátnu... Minule jsme probírali čáru. Ale jak udělat
bod nebo křivku? Také z čáry! Vše se musí rozpočítat na spousty malých
čáreček (třeba jednopixelových (pixel je jeden bod na obrazovce, ale to už
snad víte ). Abyste viděli,
že to tak opravdu funguje, uvedu příklad křivek (a bude záležet jenom na
vás, jak budou křivky křivé), kde není potřeba nic počítat:
Takže nejprve pojmenujte okno! Bude to tak lepší! Třeba Okno! Teď najděte proceduru OnMouseDown, do níž pište:
Canvas.LineTo(X, Y);
if ssRight in Shift then
Invalidate;
Zde vidíte krásný příklad využití proměnných procedury OnMouseDown! Nejprve se nakreslí čára na místo, kde stisknete tlačítko myši a pokud stisknete pravé, vše se následně překreslí (a protože není nic v proceduře OnPaint, vše se smaže). Teď vám program dělá čáry mezi bodem, kde jste klikli naposledy a kam jste kliknuli nyní! Tím vám ale kreslení křivek nepředvedu. Názornější bude použít navíc proceduru OnMouseMove, kde napíšete:
if ssLeft in Shift then
Canvas.LineTo(X, Y);
A teď můžete trénovat kreslení obrázků jednou čarou. A co když chcete zvedat pero? V proceduře OnMouseDown změňte LineTo na MoveTo a to je celá změna! Ale co když chcete udělat jen tečku? OK, napište do procedury OnMouseDown ještě:
Canvas.MoveTo(X, Y+1);
Ale mezi první dva řádky! NE NA KONEC! A pokud nenapíšete + nebo -1 za X
nebo Y, nic se nenakreslí!
A ještě: Pokud se vám nelíbí tenoučká čára, kterou píšete, do
rocedury OnCreate vložte:
Canvas.Pen.Width := 5;
A nebo ještě lépe: Vložte TrackBar a pojmenujte ho Sire. Najděte v něm proceduru OnChange a pište:
Canvas.Pen.Width := Sire.Position;
Nyní si už můžete nastavovat šířku podle rozsahu TrackBaru!
A konečně můžeme kreslit bitmapy na plátno. Je na to příkaz: DRAW, Napíšete:
Canvas.Draw(Zleva, shora, bitmapa);
Zleva bude rozměr X, kam se nakreslí obrázek (jakýkoli Integer), shora je rozměr Y (také Integer) a bitmapa je obrázek, který se bude kreslit (TBitmap). Můžete však také zakreslovat nejdřív na nějakou TBitmap a jí potom vložit na plátno. A jako vždy vám teď ukážu příklad:
Nejprve vytvoříme proměnné:
Obr : TBitmap; Ykz, Ypz : Integer;
Přejmenujeme Form1 na Okno, barvu pozadí (Color) na clWhite a následně pouze vyplníme procedury. Nejprve OnCreate:
Obr := TBitmap.Create; Obr.Width := Okno.Width; Obr.Height := Okno.Height;
Vytvořili jsme tedy Obr typu Tbitmap, který má rozměry Okna. Nyní proceduru OnMouseDown:
Ykz := X; Ypz := Y;
Tím určíme rozměry pro začátek kreslení. A nyní OnMouseMove:
if ssLeft in Shift then begin Canvas.Draw(0,0,Obr); Canvas.Ellipse(Ykz,Ypz,X,Y); end;
Neboli pokud je při pohybu stisknuté levé tlačítko myši, nakresli nejprve pozadí (TBitmap Obr, která obsahuje předchozí pozadí) a poté přes pozadí nakresli od Ykz,Ypz po pozici myši elipsu. A teď závěrečnou (důležitou proceduru OnMouseUp:
Obr.Canvas.Ellipse(Ykz, Ypz, X, Y);
Jakmile pustíte myš, nakreslí se elipsa do TBitmap (prostě pomyslný obrázek) a tím se vlastně uloží to operační paměti. A vy můžete kreslit další přes tuhle. Tím máme základ a zbývá jen dodělat dvě drobnůstky. Co když změníme rozměr okna? Vyplníme proto proceduru OnResize:
if Obr.Width < Okno.Width then Obr.Width := Okno.Width; if Obr.Height < Okno.Height then Obr.Height := Okno.Height;
Pokud okno zvětšujeme přes rozměry TBitmap Obr, změní se i její rozměry. Pokud, ne, tak se nezmění (a to chceme! - kdybyste okno zvětšili, kreslili na něj, zmenšili a následně zase zvětšili, ořízly by se okraje původního obrázku!). A ještě musíme počítat s tím, že se okno někdy překresluje. A k tomu máme proceduru OnPaint, do které dáme akorát:
Canvas.Draw(0,0,Obr);
A máme program, kde se kreslí bílé elipsy na bílé pozadí! Doufám, že vám takový příklad stačí!
Uznávám, že tento návod je celkem nacpaný příklady, ale je to nejlepší způsob, jak něco vysvětlit! K dalším pracím potřebujete ale znát další typ proměnné a to: TRect
Klasicky se zeptám co umí? A opět standartně odpovím nic, ale hodí se. K čemu? Rect je zkratka pro Rectangle a pokud se učíte angličtinu, nebo máte dobrou paměť, tak Rectangle je anglicky obdélník. A co je tohle za obdélník? Opět pomyslný (neviditelný). Udává nám pouze rozměry jakéhosi obělníku na ploše. Proměnné typu TRect mají tyto podproměnné (Integery):
- Left - souřadnice X (počet pixelů zleva) počátečního bodu
- Right - souřadnice X cílového bodu
- Top - souřadnice Y (počet pixelů odshora) počátečního bodu
- Bottom - souřadnice Y cílového bodu
A nebo můžete použít zbylé dvě proměnné typu TPoint
- TopLeft - souřadnice počátečního bodu
- BottomRight - souřadnice cílového bodu
Vy ale neznáte typ proměnné: TPoint
Ten už je vůbec jednoduchý. Má pouze dvě souřadnice nějakého místa:
- X - zleva (Integer)
- Y - shora (Integer)
A to je protentokrát z typů proměnných vše! Teď vám řeknu další příkaz Canvasu, který se hodí. A to: PIXELS
Určí vám barvu, která je na Canvasu v určitém místě. Užití: Pixels[Zleva, Shora]; Místo Zleva zadáte rozměr X a místo Shora rozměr Y. Př.:
Panel1.Color := Canvas.Pixels[150, 486];
Ale teď se vraťme k TRect! Proč jsem vám o něm vůbec říkal, že? Z
jednoho důležitého důvodu! V Canvasu je totiž potřeba neustále! Tvůrci
Delphi si tím snížili počet údajů v závorkách (no uznejte! Např
RoundRect má 6 údajů. To je o nervy! ) a tak pro někoho
programování prodloužili, ale pro nějaké případy zase zkrátili! Mno,
prostě vám teď můžu říct dalěí příkazy, kde je použit TRect: Ellipse
Ano, možná se divíte, ale Ellipse má 2 alternativy. Jednu se čtyřmi
rozměry v závorce a druhou s TRectem v závorce.
Př.:
Canvas.Ellipse(Ctverec);
Rectangle
Ano, stejně tak i Rextangle má 2 alternativy.
Canvas.Rectangle(Ctverec);
Příkazů s TRect je spoustu a tak vám tentokrát napíšu tři. A ten poslední je nový: CopyRect.
Zkopíruje obdélníkovou část z nějakého Canvasu a vloží do toho, ve kterém příkaz vyvoláme. Vzor psaní:
CilovaCanvas.CopyRect(CilovyTRect, ZdrojovaCanvas, ZdrojovyTRect);
CilovyTRect bude TRect, podle kterého se určí místo, kam se do CilovaCanvas nakreslí to, co se v místech, která určuje ZdrojovyTRect(také typu TRect), ze ZdrojovaCanvas vyřízne. Př.:
Bitmapa.Canvas.CopyRect(Ctverec1, Canvas, Ctverec2);
A pokud chcete využít TPoint, můžete místo MoveTo(X,Y); používat PenPos(Misto);, kde Misto bude TPoint!
A teď si dovolím něco, co jsem ještě nikdy v těchto návodech neudělal a to že s vámi vyrobím kvalitní program. Ale nedoděláme ho dnes, ale až jindy. Dneska na to totiž máme pouhé 3 body a to bude zatím stačit... Důležité pro vás asi bude, jaké jsou objekty a jaké procedury. Pro jistotu si můžete zdrojový kód programu dole pod článkem. Když už jsme u přenášení zdrojových kódů Delphi, měl bych vám říct, jaké soubory musíte zkopírovat: Dva se jménem Projektu a dva se jménem každého okna. Ty se jménem projektu musí mít příponu DPR a RES. A soubory oken musí mít PAS a DFM. Co které obsahují? DPR obsahuje postup otevírání oken apod. V RES jsou uložené např. ikony. PAS obsahuje ten text, co vidíte v okně na psaní programu a DFM informace o objektech (obsah Object Inspectoru). A teď pokročme. Nejprve vám řeknu nadřazenost objektů (vždy napíšu nejprve název typu objektu a do závorky jeho jméno):
Do okna (pojmenujte ho Okno!) vložte kamkoli: OpenPicturDialog(Open), SavePictureDialog(Save), ColorDialog(VyberBarvu), PaintBox(Plocha) a dva Panely(VyberBarvyAStylu a VyberNastroje).
Začněme vkládáním věcí do panelu VyberBarvyAStylu (opět vložte kamkoli-později pozice nastavíte!): Panel(BgBarva), 2xCheckBox(Obrys a Vypln), TrackBar(Sirka) a 3xLabel(Pozice, SirVys, Velikost). Do panelu BgBarva ještě vložte Panel, pojmenujte FwBarva a můžeme postoupit k vkládání objektů do panelu VyberNastroje: 2x Button(Otevrit, Ulozit) a 5xRadioButton(Cary, Elipsy, Linie, Obdelniky, Rozmer). A teď se připravte na šílenou tabulku:
Name | Caption | Left | Top | Align | Width | Height | Checked | Color | BevelOuter |
Okno | Kreslení | ? | ? | alNone | ? | ? | X | clGray | X |
Plocha | X | 66 | 10 | alNone | 320 | 240 | X | clWhite | X |
VyberBarvu | X | X | X | X | X | X | X | clBlack | X |
VyberBarvyAStylu | 0 | 267 | alBottom | auto | 50 | X | clBtnFace | bvRaised | |
BgBarva | 8 | 5 | alNone | 40 | 40 | X | clBlack | bvLowered | |
FwBarva | 8 | 8 | alNone | 24 | 24 | X | clWhite | bvNone | |
Obrys | Obrys | 136 | 8 | X | 49 | 17 | True | clBtnFace | X |
Pozice | X, Y | 255 | 24 | alNone | 20 | 13 | X | clBtnFace | X |
Sirka | X | 64 | 8 | alNone | 73 | 33 | X | X | X |
SirVys | š., v. | 255 | 8 | alNone | 23 | 13 | X | clBtnFace | X |
Velikost | 320x240 | 327 | 16 | alNone | 47 | 13 | X | clBtnFace | X |
Vypln | Výplň | 136 | 24 | X | 49 | 17 | True | clBtnFace | X |
VyberNastroje | 0 | 0 | alLeft | 56 | auto | X | clBtnFace | bvRaised | |
Cary | Tužka | 3 | 51 | X | 49 | 17 | False | clBtnFace | X |
Elipsy | Elipsy | 3 | 3 | X | 49 | 17 | False | clBtnFace | X |
Linie | Linky | 3 | 35 | X | 49 | 17 | False | clBtnFace | X |
Obdelniky | Obdél. | 3 | 19 | X | 49 | 17 | False | clBtnFace | X |
Otevrit | Otevřít | 4 | 96 | X | 48 | 20 | X | X | X |
Rozmer | X, Y | 6 | 165 | X | 46 | 17 | True | clBtnFace | X |
Ulozit | Uložit | 4 | 120 | X | 48 | 20 | X | X | X |
Vysvětlivky:
X = neobsahuje tuto položku
? = zadejte si, co chcete
auto = vypočítá se samo ze zadaných hodnot
To bohužel není vše. Ještě nastavte u fontu Rozmeru tučné písmo (Bold), u FwBarva a BgBarva nastavte ShowHint na True a Hint na Obrys(BgBarva) a Výplň(FwBarva). Ještě jsem vám neříkal o položce (proměnné) okna, která je:
ActiveControl - Zvolíte objekt, který bude po spuštění aktivní (bude mít okolo sebe rámeček, jako kdyby to bylo poslední, na co jste kliknuli!). A my si jako ActiveControl zvolíme Rozmer! A teď k samotnému textu programu (co co dělá budete muset vyčíst z poznámek v {}:
public { Public declarations } Obraz : TBitmap; Iks, Yps : Integer; end; var Okno: TOkno; implementation {$R *.dfm} procedure TOkno.PlochaMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin {Zazanamenání pozice kurzoru:} Pozice.Caption := IntToStr(X) + ', ' + IntToStr(Y); {Šířka obrázku:} if ssLeft in Shift then SirVys.Caption := IntToStr(X - Iks) + ', ' + IntToStr(Y - Yps); {Kreslení elipsy:} if (Elipsy.Checked) and (ssLeft in Shift) then begin Plocha.Canvas.Draw(0,0,Obraz); Plocha.Canvas.Ellipse(Iks, Yps, X, Y); end; {Kreslení obdélníku:} if (Obdelniky.Checked) and (ssLeft in Shift) then begin Plocha.Canvas.Draw(0,0,Obraz); Plocha.Canvas.Rectangle(Iks, Yps, X, Y); end; {Kreslení linie:} if (Linie.Checked) and (ssLeft in Shift) then begin Plocha.Canvas.Draw(0,0,Obraz); Plocha.Canvas.MoveTo(Iks, Yps); Plocha.Canvas.LineTo(X, Y); end; {Kreslení čar:} if (Cary.Checked) and (ssLeft in Shift) then Plocha.Canvas.LineTo(X, Y); end; procedure TOkno.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin {Zazanamenání pozice kurzoru:} Pozice.Caption := '(' + IntToStr(X - 66) + ', ' + IntToStr(Y - 10) + ')'; end; procedure TOkno.FormCreate(Sender: TObject); begin {Nastavení TBitmap Obraz:} Obraz := TBitmap.Create; Obraz.Width := 320; Obraz.Height := 240; end; procedure TOkno.PlochaPaint(Sender: TObject); begin {Nakreslení při překreslení :-)} Plocha.Canvas.Draw(0,0,Obraz); end; procedure TOkno.PlochaMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin {Zapamatování souřadnic:} Iks := X; Yps := Y; Plocha.Canvas.MoveTo(X,Y); {Šířka a výška je 0:} SirVys.Caption := '0, 0'; {Změna rozměru papíru:} if (Rozmer.Checked) and (ssDouble in Shift) and (X*Y > 0) then begin Plocha.Width := X; Plocha.Height := Y; Obraz.Width := X; Obraz.Height := Y; Velikost.Caption := IntToStr(X) + ' x ' + IntToStr(Y); end; end; procedure TOkno.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin {Změna rozměru papíru:} if (Rozmer.Checked) and (ssDouble in Shift) and (X > 66) and (Y > 10) then begin Plocha.Width := X-66; Plocha.Height := Y-10; Obraz.Width := X-66; Obraz.Height := Y-10; Velikost.Caption := IntToStr(X-66) + ' x ' + IntToStr(Y-10); end; end; procedure TOkno.OtevritClick(Sender: TObject); begin {Otevření obrázku} if Open.Execute then Obraz.LoadFromFile(Open.FileName); Plocha.Width := Obraz.Width; Plocha.Height := Obraz.Height; Plocha.Canvas.Draw(0,0,Obraz); Velikost.Caption := IntToStr(Obraz.Width) + ' x ' + IntToStr(Obraz.Height); end; procedure TOkno.PlochaMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var Celek : TRect; begin {Zrušení šířky a výšky:} SirVys.Caption := 'š., v.'; {Nakreslení ťupky:} if (X = Iks) and (Y = Yps) and (Rozmer.Checked = False) and (Obrys.Checked = False) then Plocha.Canvas.LineTo(X + 1,Y); {Nakreslení elipsy:} if Elipsy.Checked then Obraz.Canvas.Ellipse(Iks, Yps, X, Y); {Nakreslení obdélníku:} if Obdelniky.Checked then Obraz.Canvas.Rectangle(Iks, Yps, X, Y); {Nakreslení linie:} if Linie.Checked then begin Obraz.Canvas.MoveTo(Iks, Yps); Obraz.Canvas.LineTo(X, Y); end; {Nakreslení čar:} Celek.Left := 0; Celek.Top := 0; Celek.Right := Plocha.Width; Celek.Bottom := Plocha.Height; if Cary.Checked then Obraz.Canvas.CopyRect(Celek, Plocha.Canvas, Celek); end; procedure TOkno.UlozitClick(Sender: TObject); begin {Uložení obrázku} if Save.Execute then Obraz.SaveToFile(Save.FileName); end; procedure TOkno.FwBarvaDblClick(Sender: TObject); begin {Výběr barvy výplně:} if VyberBarvu.Execute then begin FwBarva.Color := VyberBarvu.Color; Plocha.Canvas.Brush.Color := FwBarva.Color; Obraz.Canvas.Brush.Color := FwBarva.Color; end; end; procedure TOkno.BgBarvaDblClick(Sender: TObject); begin {Výběr barvy obrysu:} if VyberBarvu.Execute then begin BgBarva.Color := VyberBarvu.Color; Plocha.Canvas.Pen.Color := BgBarva.Color; Obraz.Canvas.Pen.Color := BgBarva.Color; end; end; procedure TOkno.SirkaChange(Sender: TObject); begin {Nastavení šířky pera:} Plocha.Canvas.Pen.Width := Sirka.Position; Obraz.Canvas.Pen.Width := Sirka.Position; end; procedure TOkno.ObrysClick(Sender: TObject); begin {Nastavení viditelnosti obrysu:} if Obrys.Checked then begin Plocha.Canvas.Pen.Style := psSolid; Obraz.Canvas.Pen.Style := psSolid; end else begin Plocha.Canvas.Pen.Style := psClear; Obraz.Canvas.Pen.Style := psClear; end; end; procedure TOkno.VyplnClick(Sender: TObject); begin {Nastavení viditelnosti výplně:} if Vypln.Checked then begin Plocha.Canvas.Brush.Style := bsSolid; Obraz.Canvas.Brush.Style := bsSolid; end else begin Plocha.Canvas.Brush.Style := bsClear; Obraz.Canvas.Brush.Style := bsClear; end; end; end.
A můžete kreslit elipsy, obdélníky, úsečky a čáry. Také můžete měnit barvu výplně a obrysu, šířku a viditelnost obrysu, průhlednost výplně a velikost kreslicí plochy. Příště ten program ještě zdokonalíme...

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 501x (394.7 kB)
Aplikace je včetně zdrojových kódů v jazyce Delphi