Delphi és Win32 API – Nem csak Delphi felhasználóknak: VI. -

Application Desktop Toolbars - 3.

A Borland Delphi rendszer számos, a Windowsban (és a Linuxban) használt rendszereszközhöz tartalmaz komponenseket, osztályokat. Mégis érhetetlen módon több elem hiányzik belőle, melyek sokszor vagy csak ritkán, de jól jönnének egy program fejlesztése során. Cikksorozatunkban ilyen komponenseket fogunk készíteni. A sorozatot más programnyelvek használóinak is ajánlom, hiszen a bemutatott Windows API függvények bizonyára más nyelveken is elérhetőek.

Ismét elérkeztünk egy nagyobb fejezet végéhez, így most nekilátunk a Delphi komponens megírásához. Elöljáróban annyit, hogy az automatikusan elrejtődő eszköztárakkal most nem foglalkozunk, kicsit ha előrelapoz az Olvasó, láthatja, hogy így is lesz min elszörnyülködni :)

És még valami: a fejlesztés során az derült ki számomra, hogy a Delphi IDE fejlesztése során valamilyen módon „összetűzésbe” kerültek ezekkel az eszköztárakkal, ugyanis a tesztelések után nem mindig állt vissza régi pozíciójába a Delphi főablaka. Ezt egy Előző méret/Teljes méret paranccsal könnyedén ki lehet javítani.

TForm örökítése

Mint azt már a cikksorozat III. részében megtárgyaltuk, most is először azt kell eldöntenünk, hogy honnan származtatjuk le az új komponenst. Kézenfekvőnek tűnik – bár lehet nem :) – hogy valahogy úgy kellene ezt megoldani, hogy a Form létrehozásakor már rendelkezzen azon új képességekkel, amelyekkel az elmúlt két részben már megismerkedtünk. A kérdés csak az, hogyan oldható az meg, hogy már a tervezési idő alatt is megjelenjenek bizonyos tulajdonságok az Object Inspector-ban?

Ha létrehozzuk az összes published tulajdonságot, majd a RegisterComponents-el regisztráljuk a TForm leszármazottat, akkor kapunk egy ikont a komponenspalettán. Márpedig ez nem az, amit mi el szerettünk volna érni. A megoldás kulcsa a RegisterCustomModule eljárás. Deklarációja a dsgnintf.pas unitban:

procedure RegisterCustomModule(ComponentBaseClass: TComponentClass;
CustomModuleClass: TCustomModuleClass);

Az első paraméter az alaposztály, melynek tulajdonságait elérhetővé szeretnénk tenni a tervezési időben. Ide kerül majd a mi új osztályunk. A második paraméter a Form ú.n. szerkesztő osztálya, egy olyan osztály, amelyet a Delphi aktivál a tervezés folyamán. No, miután ismét kellőképpen megijesztettem mindenkit ezzel a mondattal :), megnyugtatásul közlöm, hogy amennyiben az első paraméter a TCustomForm-ból származik (innen származik a TForm onnan pedig a mi osztályunk), akkor a második paraméternek elegendő a TCustomModule értéket adni ahhoz, hogy a történet működjön:

. . .

TModositottForm = class(TForm)
private
FTetszik : Boolean;
published
property Tetszik : Boolean read FTetszik write FTetszik;
end;

. . .

procedure Register;

implementation

provedure Register;
begin
RegisterCustomModule(TModositottForm, TCustomModule);
end;

. . .

Vegyük észre, hogy a paramétereket nem string-ként adtuk át! Az ily módon elkészített TForm leszármazottat regisztrálva a rendszerben, tervezési időben módosíthatjuk a Tetszik tulajdonságát.

Egy kérdés még mindenképpen felmerülhet az Olvasóban. Mi a különbség, ha a TForm vagy a TCustomForm-ból származtatjuk le az új ablakot? Delphiben számos osztálynak létezik egy „TCustom…” verziója. Például a TTreeView komponens a TCustomTreeView-ből származik. Minden tulajdonság és eljárás szinte kivétel nélkül a TCustomTreeView-ben kerül kifejtésre annyi különbséggel, hogy ott a tulajdonságok a protected részben szerepelnek. Tegyük fel, hogy egy olyan TTreeView-t akarunk megvalósítani, amelynek elemeit egy adott forrásból (például egy adatbázis táblájából) kívánunk előállítani. Ekkor nincs értelme az Items tulajdonságot tervezési időben elérhetővé tenni. Ezt úgy tehetjük meg, hogy a TCustomTreeView-ből származtatjuk az új komponenst és az Items tulajdonságot nem örököltetjük az új osztály published részébe. Ezáltal ez a tulajdonság protected marad, nem érhető el még a programozó számára sem. Így tehát a „TCustom…” típusú osztályok alkalmasak arra, hogy valóban egyéni igényeink szerint készíthessük el az egyes alapvezérlők testreszabott változatát. A TCustomForm-ból akkor származtatunk, ha bizonyos tulajdonságokat nem szeretnénk elérhetővé tenni az új osztályban. Esetünkben ilyen most nincs, így nyugodtan kijelölhetjük ősosztálynak a TForm-ot.

Fogjunk bele

Készítsünk egy új komponenst, melynek ősosztálya legyen TForm, neve pedig legyen TAppBarForm. Az előző komponensünk írásakor létrehozott Win32API_Dx.dpk csomagba tegyük bele, ezt úgy végezhetjük el, hogy a New Component ablakban az Install gombra megjelenő ablakban az Into existing package fülön kiválasztjuk a már létrehozott Win32API_Dx.dpk fájlt. Most se engedjük lefordítani az ily módon létrehozott fájlt, mert még némi módosításra szorul, mielőtt ezt biztonsággal megtehetnénk.

Görgessünk a Register eljárás kifejtéséhez. Láthatjuk, hogy a Delphi sajnos nem megfelelően hozta létre az osztály regisztrációját végző sort, így azt gyorsan írjuk át a fent leírtak alapján:

procedure Register;
begin
RegisterCustomModule(TAppBarForm, TCustomModule);
end;

Konstansok, unitok, miegymás

Első körben természetesen létrehozzuk a szükséges konstansokat, típusokat, stb.

Mint azt az előző számban már megismertük, szükség lesz egy üzenet konstansra, mellyel majd a Windows jelez nekünk különböző eseményeket.

 WM_APPBARNOTIFY = WM_USER + 1;

Amit itt fontos megjegyezni, hogy amennyiben más olyan komponenseket is használunk, amelyek a Form-nak küldött egyéni üzeneteket dolgozzák fel, könnyen elképzelhető, hogy a WM_USER+1-t ezek is fel szeretnék dolgozni. Ez pedig galaktikus bug-ok sorát idézheti elő. Ennek elkerülésére ezt a konstanst módosíthatjuk WM_USER+100-ra is akár, ha szükség van rá.

Mint már említettük, lehetőség van a Windows Tálca bizonyos tulajdonságait módosítani a ABM_SETSTATE üzenet segítségével. Ezt csak Windows NT/2000/XP alatt tehetjük meg és sajnos nincs a megfelelő konstans definiálva a Delphi alapunitjaiban. Ennek oka valószínűleg az lehet, hogy az üzenet a Microsoft dokumentáció ellenére sem tűnik működőképesnek, a fejlesztés során nem sikerült életet lehetnem bele, de nem csak nekem, a neten még jó pár embernek sem. Ennek ellenére mi beleszerkesztjük a komponensbe, hátha valaki sikerrel jár, esetleg lesz olyan Service Pack, mely korrigálja ezt a problémát :) .

 {$EXTERNALSYM ABM_SETSTATE}
ABM_SETSTATE = $0000000A;

Hátra van még a típusok létrehozása.

Szükség lesz egy típusra, mellyel a regisztrált eszköztár pozícióját határozhatjuk meg, valamint a tálca „automatikus elrejtés” és „mindig felül” tulajdonságainak kezelésére. A megfelelő kivételkezelés megvalósítására most is egy új kivétel osztályt hozunk létre, valamint szükség lesz még két esemény típusra annak jelzésére, hogy a Windows Tálca állapota megváltozott, illetve egy teljes képernyős alkalmazást indítottak.

type
TAppBarPosition = (apLeft, apTop, apRight, apBottom);

EAppBarError = class(Exception);

TTaskbarState = (tsAutoHide, tsAlwaysOnTop);
TTaskbarStates = set of TTaskbarState;

TTaskbarStateChangeEvent = procedure (Sender: TObject; State: TTaskbarStates) of object;
TFullScreenAppEvent = procedure (Sender: TObject; Opened : Boolean) of object;

Windows API hívások rejtése

Mivel a Delphi alapkoncepciója, hogy megkímél minket az API szintű programozástól, így most mi is teszünk a dolog érdekében. Létrehozunk az új osztályon belül olyan metódusokat, melyek az SHAppBarMessage függvényt hívják a megfelelő paraméterrel. Például létrehozunk egy AppBarNew metódust, ami a megfelelően feltöltött rekorddal meghívja az SHAppBarMessage-t az ABM_NEW üzenettel. Ezek után a kód többi részében nekünk már csak az AppBarNew metódust kell meghívnunk, az SHAppBarMessage-el nem kell foglalkoznunk. Ha nem egy olyan függvényt építünk be a Delphi-be, mely egyféle feladatot lát el (mint az első három részben az SHBrowseForFolder), akkor célszerű mindig ilyen metódusokat írni, hogy ezzel is könnyítsük az életünket. A metódusok készítésekor minden szükséges bemenő paramétert elkészítünk, így például az ABM_QUERYPOS-hoz szükséges a képernyőoldal, valamint a kért téglalap adatait megadni, nem kérjük azonban be a Form kezelőszámát/azonosítóját, mert az adott az osztály Handle tulajdonságában. Na de lássuk ezt is a konkrét kódban, úgy talán emészthetőbb:

 TAppBarForm = class(TForm)
. . .
protected
{ Protected declarations }
function AppBarNew : Boolean;
procedure AppBarQueryPos(AEdge : TAppBarPosition; var ARect : TRect);
. . .
end;

. . .

procedure TAppBarForm.FillDefault(var AData: TAppBarData);
begin
AData.cbSize := SizeOf(TAppBarData);
AData.hWnd := Handle;
end;

function TAppBarForm.AppBarNew: Boolean;
var
Data : TAppBarData;
begin
FillDefault(Data);
Data.uCallbackMessage := WM_APPBARNOTIFY;
Result := SHAppBarMessage(ABM_NEW,Data) > 0;
end;

procedure TAppBarForm.AppBarQueryPos(AEdge : TAppBarPosition; var ARect: TRect);
var
Data : TAppBarData;
TempWidth, TempHeight : Integer;
begin
FillDefault(Data);
Data.uEdge := Cardinal(AEdge);

TempWidth := ARect.Right - ARect.Left;
TempHeight := ARect.Bottom - ARect.Top;

. . .

end;

Létrehoztunk egy FillDefault metódust is, mert a cbSize és hWnd mezőket szinte minden SHAppBarMessage híváskor ki kell tölteni. Ezzel nem kell ugyanazt a kódot többször is leírnunk, elég a FillDefault-ot meghívni.

A Windows Tálca kezelése

Mint azt láthattuk, lehetőségünk van a Windows Tálca állapotát, pozícióját lekérdezni. Ezt mi úgy szeretnénk megvalósítani, hogy adott esetben ne kelljen egy TAppBarForm ahhoz, hogy ezt megtegyük. Ennek érdekében egy külön osztályt készítünk számára.

Láthattunk már olyan tulajdonságokat az Object Inspectorban, amelyek mellett egy „+” jelre klikkelve különböző nevű és típusú tulajdonságok jöttek elő. Tipikus példa a HorzScollBar tulajdonsága a TForm-nak. Ez egy TControlScrollBar típusú osztály, mely a TPersistent-ből származik. A TPersistent-ből származtatott osztályok általában egy-egy komponens altulajdonságait tartalmazzák (mint azt az ábra is mutatja), beállított tulajdonságait menteni tudják és vissza tudják olvasni a Form .dfm fájljába/ból.

A tálca kezelésére létrehozott TWindowsTaskBar osztályt így mi is a TPersistent-ből származtatjuk, hogy hozzá tudjuk kapcsolni a TAppBarForm-hoz úgy, hogy tervezési időben is hozzá lehessen férni az – elméletileg – módosítható állapot tulajdonsághoz.

 

 TWindowsTaskBar = class(TPersistent)
private
function GetPosition: TRect;
function GetState: TTaskBarStates;
procedure SetState(const Value: TTaskBarStates);
protected
function AppBarGetTaskbarState : TTaskbarStates;
function AppBarGetTaskBarPos(var ARect : TRect) : Boolean;
procedure AppBarSetState(AState : TTaskBarStates);
public
property Position : TRect read GetPosition;
published
property State : TTaskBarStates read GetState write SetState;
end;

Láthatjuk, hogy itt is létrehozzuk az SHAppBarMessage hívásokat elrejtő metódusokat. Ezzel például a GetPosition a következőképpen néz ki:

function TWindowsTaskBar.GetPosition: TRect;
var
R : TRect;
begin
R := Rect(0,0,0,0);
Result := R;

if AppBarGetTaskBarPos(R) then
Result := R;
end;

Tulajdonságok

Hozzuk létre az alapvető tulajdonságokat a már bemutatott „CTRL+Shift+C varázslat” segítségével.

 TAppBarForm = class(TForm)
. . .
published
{ Published declarations }
property AppBarPosition : TAppBarPosition read FBarPosition write SetBarPosition;
property Registered : Boolean read FRegistered write SetRegistered;
property Taskbar : TWindowsTaskbar read FTaskBar;
property OnTaskbarStateChange : TTaskbarStateChangeEvent read FOnTaskbarStateChange write OnTaskbarStateChange;
property OnFullScreenApp : TFullScreenAppEvent read FOnFullScreenApp write FOnFullScreenApp;
property AfterArrange : TNotifyEvent read FAfterArrange write FAfterArrange;
property BeforeArrange : TNotifyEvent read FBeforeArrange write FBeforeArrange;
end;

Az AppBarPosition adja majd meg, hogy a képernyő mely széléhez kell illeszteni a Form-ot. Az Registered True-ra állításával lehet váltani a dokkolt és lebegő állapot között. A Taskbar tulajdonságon keresztül kérhetünk információkat a Windows Tálcáról.

A négy eseménykezelő a korábban már bemutatott callback üzenetre érkező eseményeket jelzi, de csak dokkolt állapotban, vagyis amikor a Registered = true.

Egy ablak paramétereinek beállítása

Eddigi Delphis munkáink során a TForm BorderStyle, BorderIcons, stb. tulajdonságait használtuk fel arra, hogy viselkedését, kinézetét testre tudjuk szabni. Most nézzünk egy kicsit mélyebben a történetbe és vizsgáljuk meg, hogyan lesznek ezekből a tulajdonságokból Windows API hívások.

Most nem kezdjük el végigboncolni egy ablak létrehozásának folyamatát, az egy teljes cikket kitenne. Ami nekünk szükséges információ, hogy ezek a tulajdonságok akkor kerülnek felhasználásra, amikor a Delphi osztályai beregisztrálják az ablakot a Windows-ba. Ez alatt azt értem, hogy megmondják neki, hogy „mostantól létezik egy ilyen és ilyen ablak, figyelj rá is”. Ekkor azt is megmondják, hogy ez az ablak hogyan nézzen ki.

Minden TWinControl leszármazottnak van egy CreateParams() metódusa, melyben ezeket a paramétereket lehet beállítani. A TForm is egy ilyen leszármazott, így most nézzünk egy élő példát a könnyebb megértés érdekében. Hozzunk létre egy új projektet és hozzunk létre a TForm1 deklarációjában egy CreateParams metódust valahogy így:

 TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
procedure CreateParams(var Params : TCreateParams); override;
end;

A Params paraméter egy rekord, melynek a Style és ExStyle mezői érdekesek számunkra. Mindkét mező DWORD típusú, minden bitje egy-egy stílust engedélyez vagy tilt. Ilyen stílus, hogy legyen-e az ablaknak címsora, vagy engedélyezve legyen-e a maximalizáló gomb, stb.

Ez eddig így még nem poén, hiszen ezeket be tudjuk állítani az Object Inspectorban, csakhogy mi most olyan bitkombinációkat is létrehozhatunk, amit az Object Inspectorban lehetetlen. Például készítsünk egy olyan ablakot, amelynek ugyan nincs címsora, de a széleit lehet méretezni. Ehhez állítsuk át bsNone-ra a Form1 BorderStyle tulajdonságát és írjuk be a következőt az implementációs részbe:

procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);

Params.Style := Params.Style or WS_THICKFRAME;
end;

Először meghívjuk az ősosztály azonos metódusát. Ez mindent beállít nekünk a Params rekordban, nekünk egyedül azzal kell foglalkozni, hogy a megfelelő bitet beállítsuk. A WS_THICKFRAME hatására egy méretezhető keretet ad a Windows az ablakunknak. A további beállítható bitekről bővebb információt itt találhat az Olvasó:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/Windowing/Windows/WindowReference/WindowStyles.asp

A Style mezőhöz tehát a WS_xxxx konstansokat használhatjuk. További lehetőségek tárháza kínálkozik az ExStyle mező használatával. Ehhez a WS_EX_xxxx konstansok tartoznak. Itt lehet például beállítani, hogy egy MDI gyerek ablakot hozunk létre. Bővebben az ebben a mezőben használható konstansokról:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/Windowing/Windows/WindowReference/
WindowFunctions/CreateWindowEx.asp

Térjünk vissza komponensünkhöz. Ezt a kis kitérőt azért tettük, mert az eszköztár működéséhez mindenképpen szükséges, hogy az ExStyle mező WS_EX_TOOLWINDOW bitjét beállítsuk, a WS_EX_APPWINDOW-t pedig tiltsuk függetlenül attól, hogy mit állított be a programozó (jaj, de hát mi is azok vagyunk :), csak most programozóknak programozunk ;) ).

Ehhez a TAppBarForm osztályban létrehozunk egy CreateParams metódust a fentiek alapján a következő tartalommal:

procedure TAppBarForm.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);

Params.ExStyle := (Params.ExStyle or WS_EX_TOOLWINDOW) and not WS_EX_APPWINDOW;
end;

„A méret a lényeg”

Huh, na kérem haladunk, haladunk, egyre közelebb a cél fele, és még mindig van…

(Ugye mondtam, lesz min szörnyülködni).

Amikor átméretezünk egy Formot akár egérrel, akár a Left, Top, Width, Height tulajdonság állításával, mindig meghívódik a SetBounds metódus, mely a tényleges méretezést végzi el egységesen. Négy bemenő paramétere meglepő módon a bal és felső koordinátája, valamint szélessége és magassága az ablaknak. Ezt fogjuk felülírni, hogy dokkolt esetben jelezni tudjunk a Windows-nak, hogy rendezze el az asztalokon az ikonokat, ne kerüljenek az eszköztárunk alá.

procedure TAppBarForm.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);

var

 R : TRect;

begin

 if not FRegistered then

 inherited

 else

 begin

 R := Rect(Left, Top, Left + Width, Top + Height);

 AppBarQueryPos(FBarPosition, R);

 AppBarSetPos(FBarPosition, R);

 MoveWindow(Handle, R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top, TRUE);

  end;

end;

Ha nem dokkolt állapotban vagyunk, akkor egyszerűen meghívjuk az ősosztály metódusát. Dokkolt esetben készítünk egy R változót, melybe a kapott paraméterek alapján az új méretadatokat helyezzük, majd meghívjuk az SHAppBarMessage függvényt az ABM_QUERYPOS-al, hogy megmondja nekünk, ehhez képest az adott oldalra történő dokkoláshoz milyen koordináták kellenek. Ezt szó nélkül el is fogadjuk és jelezzük a rendszernek, hogy mi most oda betelepülünk. Mint azt korábban már említettem, a rendszer csak az Asztal ikonjait rendezi el, ablakunk helyremozgatását nekünk kell elvégezni. E célt szolgálja a MoveWindows API függvény meghívása, mely szerintem rendszeres olvasóinknak már nem igényel külön magyarázatot. Talán csak az utolsó TRUE érték, mely azt mondja meg, hogy a mozgatás után rögtön újra is rajzolja az ablakot.

A kódban több helyen is előfordul ilyen sor:

 SetBounds(Left, Top, Width, Height);

Első ránézésre nincs értelme átméretezni az ablakot a jelenlegi helyére a jelenlegi méreteivel, azonban dokkolt esetben ha például megváltozik az oldal, ahová dokkoltunk, ezzel újra lekérjük a Windows-tól a helyes adatokat és ezzel megfelelő helyre kerül az eszköztár.

Az AppBarQueryPos metódust érdemes egy kicsit jobban megvizsgálni:

procedure TAppBarForm.AppBarQueryPos(AEdge : TAppBarPosition; var ARect: TRect);

var

 Data : TAppBarData;

 TempWidth, TempHeight : Integer;

begin

 FillDefault(Data);

 Data.uEdge := Cardinal(AEdge);

 TempWidth := ARect.Right - ARect.Left;

 TempHeight := ARect.Bottom - ARect.Top;

 if (FBarPosition in [apLeft, apRight]) then

 begin

 if FBarPosition = apLeft then

 begin

 ARect.Left := 0;

 ARect.Right := TempWidth;

 end

 else

 begin

 ARect.Left := Screen.Width - TempWidth;

 ARect.Right := Screen.Width;

 end;

 ARect.Top := 0;

 ARect.Bottom := Screen.Height;

 end

 else

 begin

 if FBarPosition = apTop then

 begin

 ARect.Top := 0;

 ARect.Bottom := TempHeight;

 end

 else

 begin

 ARect.Top := Screen.Height - TempHeight;

 ARect.Bottom := Screen.Height;

 end;

 ARect.Left := 0;

 ARect.Right := Screen.Width;

 end;

 Data.rc := ARect;

 SHAppBarMessage(ABM_QUERYPOS,Data);

 ARect := Data.rc;

 case FBarPosition of

 apLeft : ARect.Right := ARect.Left + TempWidth;

 apRight : ARect.Left := ARect.Right - TempWidth;

 apTop : ARect.Bottom := ARect.Top + TempHeight;

 apBottom : ARect.Top := ARect.Bottom - TempHeight;

 end;

end;

Annak tudatában, hogy mely oldalra fogunk dokkolni, már mi megpróbálunk olyan adatokat átadni a Windows-nak, melyeket valószínűleg ő fog visszaadni. Erre azért van szükség, hogy ha például bal oldalra dokkolunk, akkor a Form szélessége ne változzon, csak pozíciója és magassága. Mivel a TRect nem szélességi és magassági adatokat tartalmaz, így ezelet nekünk kell a (Bal, Fent, Szélesség, Magasság) adatokat (Bal, Fent, Jobb, Lent) adatokká alakítani. Az SHAppBarMessage meghívása után az előtte elmentett szélesség és magasság adatot állítjuk vissza attól függően, hogy melyik oldalra dokkolunk. Ezzel például ha meg is nyúlik az ablak a képernyő teljes magasságára, szélessége nem változik oldalra dokkolás esetén.

Méretezés tiltása

Amikor egy oldalhoz dokkolt állapotban vagyunk, az ablaknak csupán egy oldalán engedhető meg, hogy az egérrel átmérethető legyen. Ezt sokféleképpen meg lehet oldani, mi most stílszerűen egy kis „Windows-os” megoldást használunk.

Amikor a felhasználó az egérrel egy ún. nem-kliens területre klikkel (ilyen a címsor, a méretező szegély, vagyis minden, ami nem a tényleges munkaterülethez tartozik), a Windows egy WM_NCHITTEST üzenetet küld az ablaknak. Ebben megkapjuk, hogy a felhasználó a nem-kliens terület mely pontjára klikkelt és mely területére (oldalt, lent, címsoron, stb.). Ezzel az üzenettel nyugtázhatjuk a rendszernek, hogy valóban oda klikkeltek, nem tévedett (miért is tévedne? :) ) és mi sem akarunk változtatni az eredményen. Ugyanakkor ha tiltani szeretnénk, hogy bizonyos területekre klikkelni lehessen, akkor ebben az üzenetben kell beleszólni a dolgok menetébe. Ha HTCLIENT konstanst adunk vissza az üzenetben visszatérési értékként, akkor ezzel azt mondjuk a rendszernek, hogy kliens területen történt az esemény, így annak feldolgozása nem szükséges. Így dokkolt állapotban és annak függvényében, hogy mely oldalon vagyunk, tiltani tudjunk a szükséges oldalakra való klikkelést.

 TAppBarForm = class(TForm)

 private

 procedure WMNCHitTest(var Msg : TWMNcHitTest); message WM_NCHITTEST;

. . .

 end;

. . .

procedure TAppBarForm.WMNCHitTest(var Msg: TWMNcHitTest);

begin

 inherited;

 if FRegistered and (Msg.Result in [HTLEFT..HTBOTTOMRIGHT]) then

 begin

 case FBarPosition of

 apLeft : if Msg.Result <> HTRIGHT then

 Msg.Result := HTCLIENT;

 apTop : if Msg.Result <> HTBOTTOM then

 Msg.Result := HTCLIENT;

 apRight : if Msg.Result <> HTLEFT then

 Msg.Result := HTCLIENT;

  apBottom : if Msg.Result <> HTTOP then

 Msg.Result := HTCLIENT;

 end;

 end;

end;

„Energiát”

Már csak egyetlen dolog van hátra, meg kell még írni a SetRegistered metódust. Ez a Registered tulajdonság beállító metódusa.

procedure TAppBarForm.SetRegistered(const Value: Boolean);

begin

 if csDesigning in ComponentState then

 exit;

 if Value <> FRegistered then

 begin

 if Value then

 begin

 if not AppBarNew then

 raise EAppBarError.Create(SRegisterFailed);

 SaveState;

 end

 else

 begin

 if not AppBarRemove then

 raise EAppBarError.Create(SUnRegisterFailed);

 end;

 FRegistered := Value;

 if FRegistered then

 begin

  SetWindowLong(Handle,GWL_STYLE,FSavedStyle and (not WS_CAPTION));

 SetBounds(Left, Top, Width, Height);

 end

 else

 RestoreState;

 end;

end;

Amennyiben most kell egy eszköztárat regisztrálni, akkor először is elküldjük a rendszernek az ABM_NEW üzenetet. Ha ez sikeresen lefutott, elmentjük a Form jelenlegi pozícióját, hogy később vissza tudjuk állítani azt.

Nem sokkal feljebb már beszéltünk az ablakok stílusát megadó Style és ExStyle konstansokról és hogy ezeket a CreateParams-al tudjuk beállítani. Igen ám, de mi van, ha futás közben szeretnénk olyan mutatványokat csinálni, amit kis kitérőnkben tettünk. Ehhez nyújt segítséget a SetWindowLong API függvény, mely a Handle paraméterben megadott ablak Style (GWL_STYLE) vagy ExStyle (GWL_EXSTYLE) tulajdonságát állítja be. Mi ezt most arra használjuk, hogy eltüntessük az ablak címsorát anélkül, hogy a méretezhető keret eltűnne.

A SaveState metódus menti az aktuális ablakpozíciót és stílust egy-egy private mezőbe, míg a dokkolás befejezése után a RestoreState állítja vissza.

És itt a vége…

Ezzel megtárgyaltunk minden fontosabb metódust a komponensben, a többit a CD-n megtalálja az Olvasó, jöhet az installálás, tesztelés, gyönyörködés, vállveregetés. Igen, pontosan, elkészültünk második komponensünkkel.

A teszteléshez nyissunk egy új projektet, majd a TForm1 deklarációjában írjuk át az ősosztályt TAppBarForm-ra és az interface uses részébe vegyük fel az AppBarForm unitot.

Jövő héttől a Windows NT/2000/XP szolgáltatásainak kezelésével kezdünk el foglalkozni.

Addig is sikeres kódolást, és azért azt se feledjük el, hogy a nyár közepén vagyunk (a cikk írásakor pl. szakad az eső :) ), így néha-néha nem árt felállni a gép elől és strandolni egyet, ha tehetjük.

Megj.: A mellékelt forráskódban a main.pas állomány TForm1.AppBarFormFullScreenApp metódusában szereplő két sort kikommenteztük, mert Windows XP rendszeren megakadályozta a helyes futást.

Geiger Tamás - info@gsc.hu