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ó:
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:
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