Tallózás a mappák
között – 3. |
||
Elérkeztünk ahhoz a ponthoz, hogy immár az elméleti alapokat átvéve a
Delphi komponens hierarchiájába építsük új ismereteinket. Elsőként egy
komponens megírásakor - miután az elmélettel végeztünk - el kell döntenünk,
hogy melyik osztályból kívánjuk leszármaztatni az új komponenst. Erre talán a
már előkészített TCommonDialog osztály lesz a legalkalmasabb. Eldöntendő kérdés még, hogy a
komponens paletta melyik fülére helyezzük el az ikont. Talán ezen se fogunk
összeveszni, ha a Dialogs palettánál maradunk. |
||
Az első lépések |
||
Elsőként hozzunk létre egy új komponenst a Components/New
component… menüpont
segítségével. Itt az Ancestor type mezőbe az ős osztályt, vagyis a TCommonDialog-got írjuk, a Class Name mezőbe TBrowseForFolderDialog, a Palette Page-nek pedig a Dialogs-t adjuk meg. Mivel a jövőben írunk
még pár komponenst, célszerű őket egy csomagba, illetve egy mappába
gyűjteni. Célszerű például a Delphi mappájában, a Lib mappába egy Win32API almappát létrehozni, hogy ide tudjuk
pakolni a készülő és már elkészült fájlokat. A mappa létrehozása után a Unit file name mezőbe írjuk be: C:\Program Files\Borland\Delphi5\lib\Win32API\
BrowseForFolder.pas.
Klikkeljünk az Install gombra. |
A
megjelenő ablakban válasszuk az Into new package fület, majd a File name mezőbe írjuk be: „C:\Program
Files\Borland\Delphi5\lib\Win32API\ Win32API_Dx.dpk”, ahol x a használt Delphi verzió száma (4, 5, 6, 7, stb.). A Description mezőbe kedvünk szerint írjunk
valamilyen leírást. Az OK gombra
klikkelés után a Delphi megkérdezi, hogy újrafordítsa-e a csomagot. Most
egyenlőre válasszuk a No gombot, bár nagy a kísértés, hogy már lássunk
valamit :). |
|
Konstansok létrehozása |
||
A legtöbb használt konstans már deklarálva van a windows.pas unitban. Hiányzik azonban számos, a shell32.dll újabb verzióiban bevezetett konstans.
Most minden, már létező konstanst is létrehozunk, hogy minden meglegyen
egy helyen. Először a
teljesség kedvéért létrehozzuk azokat a szám konstansokat, amelyeket most nem
fogunk használni, de a későbbiek során még jól jöhetnek. Ezeket az SHGetSpecialFolderLocation Windows API függvénynél használhatjuk
fel. Mivel én nem szeretek sokat pötyögni, ezért ollózással a Microsoft
Platform SDK-hoz adott Visual C++ header fájlok közül a ShlObj.h-t hívom segítségül. Ebben megtalálható
az összes szükséges konstans, csakhogy C++ formában. Ezt bizony
konvertálgatnunk kell. A későbbiek során is jól jön, ha szerzünk némi
C++ tudást, legalább szintaktika terén! A fájlban található konstansokat a
következő formában láthatjuk: ... #define
CSIDL_FAVORITES 0x0006 //
<user name>\Favorites #define
CSIDL_STARTUP 0x0007 // Start
Menu\Programs\Startup #define CSIDL_RECENT 0x0008 // <user name>\Recent ... A sorokat
bevezető #define a konstansra utal, Delphiben ezt vegyük ki. A 0x hexadecimális számot jelöl, ezeket $-ra kell lecserélni. Remek, félúton
vagyunk! Kódunk kezd Pascal-os jegyeket ölteni, de még nem az igazi.
Természetesen két dolog hiányzik még: egy = jel a konstansok neve és értéke között, valami
a sorokat lezáró ;
karakterre. Mindezeket a műveleteket könnyedén és gyorsan elvégezhetjük
a Seach&Replace
funkcióval. Ha mindent jól csináltunk, a Pascal-ra átírt kód valahogy így néz
ki: ...
CSIDL_FAVORITES =
$0006; //
<user name>\Favorites
CSIDL_STARTUP =
$0007; //
Start Menu\Programs\Startup
CSIDL_RECENT =
$0008; // <user name>\Recent ... Teljesen analóg
módon most az ablak megjelenítésének testre szabásához szükséges BIF_ kezdetű konstansok jönnek szintén
az ShlObj.h header
fájlból, majd a BFFM_ állandók a megjelenített ablakkal történő kommunikációra. Némi kiegészítést
igényel a BFFM_SETSELECTION
konstans. Windowsban a legtöbb karakterláncot kezelő függvénynek létezik
egy A-ra és egy W-re végződő változata. Az A-ra végződő változatok string
paraméterei ANSI karaktereket tartalmazhatnak, a W-re végződök Unicode karaktereket. Aki
esetleg még nem halott a Unicode-ról, annak röviden annyit, hogy szemben a
DOS-os 8 bites ASCII karakterekkel a Unicode 16 bitet használ egy karakter
eltárolására, megnövelve ezzel a karakterek számát egy betűkészletben.
Ezzel lehetőség nyílt arra, hogy számos más nyelv, mint az arab, cirill,
stb. speciális karaktereit egy és ugyanazon karaktertáblába tárolják. A
Unicode karakterkészleteket csak a Windows NT4/2000/XP verziók támogatják.
Delphiben a Windows API függvények ANSI változatai használatosak, mi is ezt
tesszük. Ez azt jeleni, hogy amikor a BFFM_SETSELECTIONA üzenetet küldjük, akkor majd egy ANSI
karakterláncot kell paraméterként átadni. |
||
Szöveges üzenetek: const vs. Resourcestring |
||
A komponensnek természetesen képesnek kell lennie arra, hogy a
működése során felmerülő problémákat valamilyen módon vissza tudja
jelezni a programnak és/vagy a felhasználónak. Ezt leginkább kivételek és
hibaüzenetek kíséretében teheti meg. A hibaüzeneteket célszerű
kigyűjteni a unit elejére, hogy azokat könnyedén lehessen lefordítani
idegen nyelvre anélkül, hogy végig kellene böngésznünk a fájlt karakterláncok
után. Ha sok üzenet jelenik meg, hasznos lehet ezeket külön unitba kiírni. Lehetőségünk
van még egy módon segíteni a fordítók munkáját. Az esetek többségében ugyanis
nem érhető el egy program forráskódja, esetleg nem minden szükséges
modul áll rendelkezésére és így nem lehet a programot újra lefordítani. Ilyen
esetekre létezik számos erőforrás-szerkesztő, melyek segítségével
bizonyos blokkjait a bináris kódnak újrafordítás nélkül lehet átírni. Ilyen
blokkok tartalmazhatnak karakterláncokat. Delphiben legkönnyebben a resourcestring segítségével készíthetünk ilyen
blokkokat. Teljesen analóg módon kell deklarálni a konstansokat, mintha a const-tal vezettük volna be a blokkot,
sőt az így létrehozott állandókat is pontosan ugyan úgy kell használni.
Összehasonlításképpen tehát elmondhatjuk, hogy a program üzeneteit a resourcestring, az összes többit string, a nem string konstanst pedig a const blokkban érdemes deklarálni. Elnevezési
konvenció persze itt is létezik, melyen nem szükségszerű betartani, csak
éppen ajánlott. String állandókat stílszerűen az S karakterrel vezetjük be (Pl: SCreateError, SInvalidPath, stb.). |
||
Adattípusok |
||
Még mindig nem tartunk ott, hogy elkezdjük megírni magát a komponens
osztályt, szükségünk lesz ugyanis még egy-két új adattípusra. Ilyen például a
TSystemFolder típus. Ez egy felsorolt típus, melynek
létrehozásakor ismét némi trükkhöz folyamodunk. Vegyük a követező kis
példát: type TValamiUj =
(vuElsoElem, vuMasodikElem, vuHarmadikElem); var V : TValamiUj; begin V :=
vuMasodikElem;
ShowMessage(IntToStr( Integer(V) )); end; |
||
A fenti kis
programrészlet egy üzenetablakot jelenít meg, benne az 1-es számmal. Kiváló!
Mivel a TBrowseInfo rekord ulFlags mezőjének és az SHGetSpecialFolderLocation-nak is integer típusú adatra van
szüksége, ezért a fenti példát alkalmazva könnyedén tudunk felsorolt típusból
egész szám típusba és vissza konvertálgatni! Most már csak az a kérdés, hogy
minek ez az egész konvertálgatósdi, miért nem használunk mi is integer mezőket? A válasz egyszerű:
mert az Object Inspectorban így tudunk létrehozni olyan
tulajdonságokat, melyek lehetséges értékeit a Delphi egy lenyíló listában
megjeleníti nekünk! |
Ezzel létrehozhatjuk
a TSystemFolder típust,
de vigyázzunk az adatok sorrendjére! Egy ilyen lenyíló lista egyidejűleg
csak egy érték kiválasztására képes, az ablak testre szabásához viszont
egyszerre több érték is kiválasztható! Ilyen esetekben kell a halmaz típust
felhasználnunk: |
|
TBrowseOption =
(bfReturnOnlyFSDirs, bfDontGoBelowDomain, bfStatusText, bfReturnFSAncestors,
bfEditBox, bfValidate, bfNewStyle, bfIncludeURLs, bfComputer, bfPrinter,
bfIncludeFiles, bfShareable); TBrowseOptions
= set of TBrowseOption; |
||
Az Options mező típusa TBrowseOptions lesz, ezzel pedig már lehetőség
nyílik az Object Inspectorban egy + jelre klikkelve egyszerre több értéket is
kiválasztanunk. |
|
|
Most már tényleg hozzáfogunk a komponens megírására. Ehhez keressük meg a
unitban a Delphi által létrehozott TBrowseForFolder osztály deklarációját. |
||
Published tulajdonságok |
||
Most szeretnék egy-két trükköt megosztani a kedves Olvasókkal, melyeket
gyakorlott komponens írók bizonyára már ismernek. Egy-egy tulajdonság
leírásakor bizony elég sokat kell pötyögni: legrosszabb esetben két metódust
kell teljesen deklarálni, hát még ha egy mezőre is szükségünk van!
Szerencsére a Borland fejlesztői gondoltak erre a billentyűzet-gyilkos
műveletre. Elsőként hozzunk létre egy tulajdonságot az ablak
címének beállítására. Ehhez a published részbe írjuk be: property Title : string read FTitle write
SetTitle; Ezek után
anélkül, hogy bármit is csinálnánk, nyomjuk le a Ctrl+Shift+C gombokat. Látszólag semmi sem történt,
de ha jobban megnézzük, a Delphi létrehozta a private részben az FTitle mezőt és a SetTitle metódust, ha pedig lejjebb gördítjük az
ablakot a forráskódban, láthatjuk a SetTitle kifejtését! Azt hiszem így pillanatok alatt
készen leszünk az alapokkal! Írjuk be a published részbe a három szükséges tulajdonságot
még, majd a fent leírt módon hozzuk létre a szükséges kódokat: property SystemFolder
: TSystemFolder read FSystemFolder write SetSystemFolder; property CustomFolder
: string read
FCustomFolder write SetCustomFolder; property Options : TBrowseOptions read
FOptions write SetOptions; |
||
Új eseménykezelők: eljárások vs.
metódusok; callback függvények |
||
Eddig létrehoztunk már új típusokat, melyekkel valamilyen adatstruktúrát
ábrázoltunk, legyen az rekord, vagy egy egyszerű fixpontos szám.
Egyeseknek bizonyára furcsán hangzik, de lehetőség van eljárás/függvény
típusok létrehozására is. Mit is jelent ez? Egy példán keresztül talán világosabb
lesz: Tegyük fel,
hogy írunk egy függvényt, amely kiszámolja két pont közötti pontok
koordinátáit. A koordinátákat visszaadhatjuk például egy tömbben, de ezek
elemszáma nem tudható előre. Jó lenne valamilyen más módon visszajelezni
a hívó programnak a függvény eredményét. Mi lenne, ha egy paraméterben
átadnánk egy eljárás nevét, amit a számoló függvény meghív. Nem probléma! Már
csak az a kérdés, hogyan? Azon nem változtathatunk, hogy egy függvény
paramétere paraméternév : paraméter_típus alakú legyen, de ezen nem is kell változtatni. Egyszerűen
bevezetünk egy új adattípust, ami nem számot, nem karakterláncot, még csak
nem is rekordot tárol, hanem egy eljárást/függvényt, pontosabban annak címét: type TEljarasTipus = procedure (X, Y : integer); Mint azt
láthatjuk, a TEljarasTipus egy olyan eljárást ír le, amelynek két integer paramétere van.
Visszatérve a koordináta számoló függvényre, most már könnyedén megírhatjuk a
paraméterlistát: procedure
SzakaszPontok(X1, Y1, X2, Y2 : integer; Pontok : TEljarasTipus); var XPont, YPont : integer; begin ... // valamilyen
ciklus XPont := ...; //
Kiszámítjuk az X koordinátát YPont := ...; // Kiszámítjuk
az Y koordinátát // Meghívjuk a
paraméterben átadott függvényt a kiszámított pontokkal Pontok(XPont, YPont); end; Vegyük észre,
hogy a Pontok paraméterben átadott eljárásnak szigorúan megkötött
paraméterlistája van, vagyis két integer szám. Most lássunk egy példát a SzakaszPontok alkalmazására: procedure PontKiir(X, Y
: integer); begin WriteLn(X,Y); end; ... SzakaszPontok(10,10,110,150, PontKiir); ... Azért, hogy ne
csak a cikknek higgyünk, próbáljuk ki, hogy a PontKiir eljárás paraméterében az integer típust
string-re cseréljük. És tényleg! A fordító
máris szól, hogy nem megfelelő a paraméter! Amikről
pedig eddig szó volt, nem mások, mint az ún. callback függvények. Callback függvényekkel a Windows API-ban sokszor
fogunk találkozni. Ez az egyik módja annak, hogy a Windows visszajelezzen
nekünk eseményeket. Ha pedig már
itt tartunk, van még egy alkalmazása Delphiben az eljárás/függvény
adattípusoknak. Alkalmaztuk is, csak legfeljebb nem tudtuk, hogyan
működik. A kulcsszó pedig: eseménykezelők. Egy OnCreate, egy OnKeyPress eseménykezelő mind ilyen
adattípusok egy apró különbséggel. A metódusok mivel objektumon belül vannak,
egy kicsit másfajta bánásmódban részesülnek, ezért egy metódus típus
létrehozásakor az of object kulcsszavakat is használnunk kell: type TMetodusTipus = procedure (X, Y : integer) of
object; A Delphi
osztály hierarchiájának tetején a TObject osztály áll. Eseménykezelőknél íratlan
szabály, hogy az eseménykezelőnek az első paraméterében átadjuk
osztályunk aktuális példányának címét a Self paraméter segítségével. Ez a Sender paraméter az egységesség kedvvért kevés
kivételtől eltekintve TObject típusú. Szintén ilyen szabály, hogy az eseménykezelőket egy Do-val kezdődő virtuális
metódusból hívjuk meg, hogy az osztályban csak ezt a Do… metódust kelljen meghívni, ha jelezni
szeretnénk az adott eseményt. A sok magyarázat helyett ismét lássunk egy
példát: type TOnInitializedEvent = procedure (Sender : TObject; BrowserHWND : HWND) of object; TBrowseForFolderDialog = class(TCommonDialog) private FOnInitialized : TOnInitializedEvent; ... protected procedure DoInitialized(BrowserHWND :
HWND); dynamic; ... published property OnInitialized :
TOnInitializedEvent read FOnInitialized write FOnInitialized; ... end; Ezzel
elkészítettünk egy OnInitialized eseménykezelőt. Először létrehozunk egy metódus típust. A már említett
Sender paraméteren kívül a BrowserHWND paraméterben adjuk majd át a
megjelenített ablak kezelőjét, hogy az eseménykezelőn belül a
különböző egyéb Windows API függvények segítségével üzeneteket tudjunk
küldeni neki. Ezután pedig létrehozunk egyszerűen egy új tulajdonságot a
már bemutatott módon azzal a különbséggel, hogy az új propterty típusa nem ingeter vagy string, hanem TOnInitializedEvent. És ez nagyon fontos. Az Object Inspector pontosan ebből fogja tudni, hogy ezt a
tulajdonságot nem a Properties, hanem az Events fülön jelenítse meg, hiszen ez metódus típusú. Most nézzük meg
a DoInitialized metódus
kifejtését: procedure
TBrowseForFolderDialog.DoInitialized(BrowserHWND : HWND); begin if
Assigned(FOnInitialized) then
FOnInitialized(BrowserHWND); end; Gondolnunk kell
arra is, hogy a használat során nincs eseménykezelő hozzárendelve ehhez
a tulajdonsághoz. Mint az fentebb említettem, egy metódus (eljárás) típus
adattárolás szempontjából valójában egy mutató. Így ha a komponens
felhasználója nem készít eseménykezelőt az OnInitialized tulajdonsághoz, akkor az FOnInitialized mező nil értékű lesz. Ennek vizsgálatát
végzi el az Assigned függvény. A használat
során így már csak a DoInitialized metódust kell meghívnunk, ha jelezni szeretnénk az OnInitialized eseményt. Később az Execute metódus megírásakor még tudnunk kell,
hogy örököltünk egy OnShow és egy OnClose
eseménykezelőt, amit illik majd meghívni a szintén örökölt DoShow és DoClose metódusokkal. |
||
Örökölt metódusok |
||
A TCommonDialog osztálytól
a Create konstruktoron és
a Destroy metóduson kívül
még egy nagyon fontos metódust öröklünk, az Execute-ot. Az Execute meghívásával indul be igazán a gépezet,
jelenik meg és működik az ablak. Mindenek
előtt meg kell néznünk, hogy nem-e látható-e már az ablak, majd
meghívjuk az OnShow eseménykezelőt, hogy még most utoljára módosítani lehessen a
tulajdonságokon, mielőtt feldolgoznánk azok adatait. A metódusból
kiemelném az ulFlags mező feltöltésének módját. Mi a komponensünk megírásakor egy halmaz
típust használtunk, ezt kellene valahogy visszaalakítani egyetlen integer
számmá, amelynek megfelelő bitjei jelentik az egyes állapotokat. Ehhez a
konverzióhoz csinálunk egy tömböt, amivel könnyedén megadhatjuk, hogy melyik
halmaz-értékhez, melyik bit tartozik: const biconst : array[TBrowseOption] of
UINT = (BIF_RETURNONLYFSDIRS, BIF_DONTGOBELOWDOMAIN, IF_STATUSTEXT,
BIF_RETURNFSANCESTORS, BIF_EDITBOX, BIF_VALIDATE, BIF_NEWDIALOGSTYLE,
BIF_BROWSEINCLUDEURLS, BIF_BROWSEFORCOMPUTER, BIF_BROWSEFORPRINTER, BIF_BROWSEINCLUDEFILES,
BIF_SHAREABLE); Ha most például
beírjuk, hogy biconst[bfComputer] akkor a BIF_BROWSEFORCOMPUTER konstanst kapjuk vissza. |
||
A callback függvény |
||
Mint azt az eddigiek során már többször is említettem, szükség van egy callback függvény megírására. Ez
lehetőséget ad arra, hogy az ablak megjelenítése után befolyásolni
tudjuk működését. A cikksorozat első részében bemutatott callback függvényről van szó. El is
kezdhetnénk egy megfelelően paraméterezett metódust írni az osztályba,
de előre elmondom, hogy hibaüzenetet fogunk kapni. Ez pedig azon
egyszerű oknál fogva történik, hogy egy metódus nem függvény és
fordítva. A metódus egy olyan függvény, ami egy osztályon (objektumon) belül
található meg, annak címét csak akkor tekintjük értelmezettnek, ha magából az
osztályból legalább egy példány létezik. Ezzel szemben egy eljárás/függvény a
program teljes futása során rendelkezik egy memóriabeli címmel. Eddig az
okoskodás, most lássuk mi módon tudjuk a helyzetet megoldani. A válaszhoz a TBrowseInfo rekord lParam mezőjén keresztül jutunk. Mint azt
már az első részben említettem, ez egy integer típusú mező, melyet
megfelelő módon konvertálva (szakszóval: kasztolva, vagy cast-tolva) mutatóként is tudunk használni.
És hogy mire? A létező komponens aktuális példányára mutató pointerre,
vagyis a Self paraméter értékét
fogjuk ide átadni. Ezt a mutatót a callback függvény megkapja, amely egy visszaalakítással
vissza tudja adni a megfelelő adatokat az osztálynak. Ez így most
nehézkesnek tűnik, de mielőtt a kedves Olvasó mérgében a sarokba
dobná az újságot, lássuk a konkrét példát. Először is
nézzük meg, hogyan adjuk át a Self
paramétert az osztály Execute metódusában: function
TBrowseForFolderDialog.Execute; ... begin ... bi.hwndOwner := ownerhwnd; bi.pidlRoot := pidRoot; bi.pszDisplayName := @aDisplayName; bi.lpszTitle := @aTitle; bi.iImage := aImageIndex; bi.ulFlags := 0; bi.lpfn := CallBack; bi.lParam := Integer(Self); ... end; Hogyan
működhet ez? Nos ehhez tudnunk kell, hogy amikor egy valamilyen osztály
típusú változót deklarálunk, akkor az csak egy mutató lesz a memóriában
elhelyezkedő osztályra. Egy mutató pedig nem más, mint egy 32 bites
szám, vagyis egy integer. Így ez a típuskonverzió minden probléma nélkül
megengedett. Most készítünk
egy függvényt az osztályon kívül, ami majd fogadja a bejövő
paramétereket. Ez maga a callback függvény, amit a fenti kódrészletben aláhúztam. function CallBack(Wnd:
HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer; stdcall; begin Result :=
TBrowseForFolderDialog(lpData).BFCallBack(Wnd,uMsg, lParam); end; Mit is fog
csinálni az SHBrowseForFolder?
Amikor valami olyasmit csinál a felhasználó, amit ő vissza szeretne
jelezni a hívó programnak, akkor meghívja a TBrowseInfo lpfn mezőjében megadott függvényt. Ez most
estünkben a CallBack nevű
függvény. A Callback pedig semmi
mást nem csinál, mint az lpData felhasználásával visszajuttatja az adatokat az osztályon belülre, ahol
folytatódhat a feldolgozás. function
TBrowseForFolderDialog.BFCallBack; begin BrowserHwnd := handle; Result := 0; case uMsg of BFFM_INITIALIZED :
DoInitialized(handle); BFFM_SELCHANGED : DoSelChanged(handle, Pointer(lParam)); BFFM_VALIDATEFAILEDA : if
DoValidateFailedA(handle,Pointer(lParam)) then Result := 1; end; end; Ez pedig a BFCallBack metódus meghívásával történik. A BFCallBack paraméterlistája az lpData-ot leszámítva megegyezik a CallBack függvény paraméterlistájával. Kiemelném
a BFCallBack metódus
első sorát, amely elmenti a már megjelenített ablak leíróját a
továbbiakhoz. |
||
Új metódusok |
||
Már szinte teljesen készen vagyunk a komponenssel, de hátra vannak még
azok a metódusok, amelyeket az ablak futása során tudunk felhasználni annak
működésének befolyásolására. Ehhez az imént már elmentettük a
megjelenített ablak leíróját a BrowseHWND mezőbe, így lehetőség nyílik a SendMessage Windows API függvénnyel üzeneteket
küldeni az ablaknak. Az ehhez
kapcsolódó metódusok a SetOKEnabled az ablak OK gombjának engedélyezésére/tiltására és a SelectFolder egy tetszőleges mappa
kiválasztására a programból. Ezeket leginkább az OnSelectionChanged eseménykezelőkben érdemes
használni, hogy ezzel tudjuk reagálni a felhasználói bevitelre. |
||
Telepítés, tesztelés |
||
Kész! Kinek sok, kinek kevesebb új ismeretek után elégedetten
hátradőlhetünk a székünkben és megveregethetjük a saját vállunkat.
Megírtuk első Windows API-t használó komponensünket. Nyissuk meg a cikk
elején elkészített Win32API_Dx.dpk fájlt és kattintsunk az Install gombra. Ha nem írtunk el semmit, akkor
a Delphi nyugtázza a komponens sikeres telepítését. Hátra van még a
komponens tesztelése. Persze szubjektív fogalom, hogy mit értünk átfogó teszt
alatt, de mi most egy kisebb programot állítunk össze. Zárjunk be
minden ablakot és kezdjünk egy új projektet. Dobjunk egy TLabel és egy TButton komponenst az ablakra, no meg a friss TBrowseForFolderDialog komponensünket. Új csodánk
tulajdonságait állítsuk be: SystemFolder legyen sfDrives, az Options-ba a következő elemeket tegyük
bele: bfReturnOnlyFSDirs, bfStatusText, bfEditBox, bfValidate, bfNewStyle. Title legyen "Első Windows API
komponens teszt". Készítsünk egy OnInitialized eseménykezelőt: procedure
TForm1.BrowseForFolderDialog1Initialized(Sender : TObject; BrowserHWND :
HWND); begin ShowMessage(’Ablak inicializálva!’); end; Csináljunk még
egy OnSelectionChanged
eseménykezelőt: procedure TForm1.BrowseForFolderDialog1SelectionChanged
(Sender : TObject; BrowserHWND : HWND; ItemID : PItemIDList; const aFolderPath : string); begin ShowMessage(’A kiválasztott útvonal: ’ +
aFolderPath); end; Majd végül egy OnValidationFailed eseménykezelőt: procedure TForm1.BrowseForFolderDialog1ValidationFailed
(Sender : TObject; BrowserHWND : HWND; const
InvalidName : string; var KeepExecuting : boolean); begin KeepExecuting := MessageDlg(’Hibás adat: ’
+ InvalidName + #10’Folytassam?’, mtConformation, [mbYes,mbNo], 0) = mrYes; end; Nincs más
dolgunk, mint a gombra klikkelve megnyissuk az ablakot: procedure
TForm1.Button1Click(Sender: TObject); begin if
BrowseForFolder1.Execute then Label1.Caption
:= BrowseForFolder1.FolderPath else Label1.Caption := ’Művelet megszakítva!’; end; Futtassuk a
programot és gyönyörködjünk fáradságos munkánk szép gyümölcsén. Próbaképpen
változtatgassuk meg a BrowseForFolder1 tulajdonságait. Azt hiszem nyugodtan mondhatom, hogy az ablaknak számos
felhasználási lehetősége van, nem is kezdem el sorolni, inkább zárom
soraimat. Legközelebb a
Windows Asztalon elhelyezhető speciális eszköztárakkal, az ún.
"Application Desktop Toolbar"-ral kezdünk el ismerkedni. |
||
Geiger Tamás |
||