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