VB 6 praktika: a
jolly joker „object” objektum lehetőségei |
|
A VB
objektumai, objektumgyűjteményei hatalmas lehetőségeket rejtenek, melyek
azonban sokszor rejtve maradnak a tapasztalatlanabb felhasználók előtt. A
cikk a legáltalánosabb objektumot, az objectet mutatja be. A téma tárgyalásán keresztül a
cikk arra az alap VB-ben nem megoldott problémára is megoldást nyújt, mint a
form és a rajta lévő vezérlők automatikus átméretezése, áthelyezése. |
|
Kiknek, miért? |
|
A Visual
Basic (VB) az egyik legnépszerűbb programozási nyelv mind a hivatásos, mind pedig
a hobbiprogramozók között. Úgy sejtem, meglehetősen nagy az utóbbiak száma,
sőt a VB egyszerűségénél fogva a hobbiprogramozók nagy része előbb-utóbb itt
köt ki. Részben ez lehet az oka annak, hogy a magyar és külföldi fórumokon
néha elképesztően naiv kérdésekkel találkozom. A kérdezőnek a kérdésből ítélve fogalma sincs a VB
objektumairól, azok egymásból történő leszármaztatásáról, különböző
objektumok közös tulajdonságairól – általában olyan programozási egyszeregyről,
amivel egy C++ vagy Java programozó az első naptól kezdve tisztában kell,
hogy legyen. Mert ilyen a VB, ezért ilyen népszerű! Csodálatos programokat
készíthetünk mindenféle különösebb előképzettség nélkül, szinte azonnali
sikerélménnyé változtatva a tanulás nehéz, feszültséggel teli folyamatát. A
VB egyik nagy hátránya azonban éppen az egyszerűségében rejlik: sok "nagy, komoly" programozási nyelvet beszélő fejlesztő
lenézően, jobb esetben sajnálkozva tekint a VB-s kollégáikra, mondván, igazán
nagy dolgokra a VB nem alkalmas. Való igaz, szinte a VB az egyetlen nyelv,
melynek a forráskódját megnézve, a programot betöltve és böngészve a teljesen
kezdőnek is halvány fogalma támad, mi miért van, hiszen minden magától
értetődik, a VB elrejti előlünk a problémásabb részeket, de az is igaz, hogy a
VB az a nyelv, ahol el lehet kontárkodni. Sajnos a VB igen könnyen el tudja
rejteni előlünk lehetőségeit, és ebben a súgója is ludas. Ami azonnal
szembeötlik egy C++ súgó oldalán az adott objektum öröklődési ábrája: ezt
igen gyakran hiába keressük a klasszikus VB súgójában, pedig itt is van,
persze jóval egyszerűbb, mint például a Microsoft C++ MFC könyvtára. Igaz az
is, hogy a Microsoft Office VBA súgójában igen jól nyomon követhetjük az
objektumok származtatását. Ezzel azonban egy a baj: sok VB programozó olyan
szinten nem tanulta meg az objektumokban gondolkodást, hogy az öröklődési
ábrát tökéletesen figyelmen kívül hagyja, esetleg egy futó pillantással
letudja. Cikkem az ilyen programozók számára készült. A VB
objektumai, objektumgyűjteményei hatalmas lehetőségeket rejtenek, más
nyelvekhez hasonló öröklődési sémát követnek. Lehetővé teszik például, hogy
olyan látszólag teljesen különböző dolgokat, mint a form és a rajta
elhelyezett vezérlőelemek, együtt kezeljük (tudom, tudom egy C++
programozónak ez teljesen természetes, de ez most VB), gyűjteménybe
helyezzük, a gyűjtemény elemein, pl. a form vezérlőelemein végiglépkedjünk és
a típusától függően műveletet végezzünk rajta stb. Tapasztalatom szerint
éppen ezek az alkalmazási módok a leggyakrabban használtak a gyakorlatban.
Cikkemben mindhárom esetre nyújtok példát, hiszen egy példa többet ér ezer
szónál. A cikk keretein belül az általános object-ről szóló tudnivalókat egy olyan általános, minden komolyabb programban
elengedhetetlen, az alap VB-ben mégsem megoldott probléma megoldásán
keresztül mutatom be, mint a form és a rajta lévő vezérlők automatikus
átméretezése, áthelyezése. A probléma megoldásához éppen a VB általános
objektuma ad kulcsot, mellyel mind a form, mind a rajta lévő vezérlők azonos
módon kezelhetők - ideális példa hát a VB általános objektumának
bemutatásához. |
|
Miért a VB 6 |
|
Pályafutásom
során több kitérőt tettem más programnyelvek felé, végül mégis visszatértem a
VB-hez. Elismerem más nyelvek felsőbbrendűségét több kérdésben is, de megkapó
az az egyszerűség, lényegretörőség, mellyel a VB a feladatokat kezeli.
Iszonyatosan gyorsan lehet vele (nemcsak asztali) alkalmazásokat készíteni,
nem hiszem, hogy más nyelvek a VB-t akár csak megközelítő fejlesztési időt
tudnának produkálni. Egy jól elkészített eljáráskönyvtár segítségével szinte
órákon belül lehet SOS projekteket készíteni, ahol a rendszerterv
párhuzamosan halad a projektfejlesztéssel, ahol emiatt a kezdeti terv és a
végállapot alig-alig van köszönőviszonyban egymással. A VB jól elboldogul
ilyen körülmények között. Ha lesz rá lehetőségem, egy későbbi cikkben
körvonalazom egy ilyen könyvtár főbb paramétereit, komponenseit. A Debug
rendszere pedig utolérhetetlen, hiszen futás közben szerkeszthető a
forráskód! Szívemhez nőtt
a VB az évek alatt. Hagy sirassam el ezért most egy kicsit a feltörekvő
utódnak kikiáltott mindentudó VB.NET fényében! Mert valljuk meg, a VB.NET
igencsak kevéssé hasonlít az elődjeihez, talán nem is VB többé. Tiszteletem
és elismerésem a VB.NET felé, valóban szinte határtalanok a lehetőségei, jól
megírt és elég jól átgondolt fejlesztői környezetnek tűnik az eddigi
tapasztalataim alapján, de hosszú az a lista mely azt taglalja, miben más, és
főleg miben nem támogatja az elődjeit. Az alábbi weboldalon található ilyen
lista 116. bejegyzésből állt a cikk írásának idején: http://www.mvps.org/vb/index2.html?rants/vfred.htm
De ami nekem
személy szerint, mint volt hobbiprogramozónak leginkább hiányzik, az a
klasszikus VB tovatűnt egyszerűsége, barátságos felülete. Attól tartok, hogy
a VB.NET-tel a VB túllépett azon a határon, ahol egy lelkes amatőr egy-két
könyvvel felfegyverkezve izgalmas programokat tudott megírni. De éppen emiatt
hiszek abban is, hogy a klasszikus VB még sokáig fennmarad a programozók egy
részének nagy örömére, és titokban abban is bízom, hogy valamelyik cég
felkarolja a .NET-tel elejtett egyszerű fejlesztési filozófiát. |
|
A probléma |
|
No, de
térjünk vissza a problémához. A programozók az első programjuk elkészítése
után tisztában vannak azzal, hogy a vezérlőelemek pozícióját az őket befogadó
nagyobb egység, általában a form, vagy egy másik vezérlőelem, pl. egy keret
bal felső sarkából kell számítani. Méretüket általában a tervezési fázis
során megadjuk, és bár kódból is változtathatjuk, többnyire a program futása
során a méret és a pozíció fix marad. Sajnos, így a vezérlőelemek a form
átméretezése során is megtartják mind a pozíciójukat, mind pedig a méretüket,
ami igen nagy hátrány. A Java igen jó
megoldást nyújt a problémára oly módon, hogy a formot szekciókra osztja
(közép, felső, alsó, jobb és bal), ahová a vezérlőelemek elhelyezhetők. A
form átméretezésekor a vezérlőelemek mérete és helyzete együtt változik a
form szekcióival, így igen tetszetős kinézetet biztosít a formnak. A
Microsoft Access ezzel szemben megtartja a vezérlőelemek méretét és
pozícióját, de egy függőleges és egy vízszintes gördítősávval bejárható a
teljes form. Mindez azon az áron, hogy a teljes form nem látható egyszerre.
Az új VB.NET egy másfajta, de a Javához hasonlóan elegáns megoldást ad a
problémára: a vezérlőelemeket a form, vagy az adott vezérlőelemet befogadó
nagyobb egység egy vagy több oldalához köti, horgonyozza (Anchor to).
Ezáltal átméretezéskor a vezérlőelem követheti az őt befogadó elem
jobb oldalát (Anchor to Right), az alját (Anchor to Bottom), vagy a szélessége nő (Anchor to Left And to Right). Nos, ez utóbbi megoldást a VB korábbi
verzióival is könnyen megtehetjük, és általánosan alkalmazhatjuk a VB-s
formjainkra. Erre mutat megoldást a cikk keretében bemutatásra kerülő
példaprogram. |
|
A példaprogram
bemutatása |
|
A cikkhez
egy a fentebb leírt általános átméretezést végző demo programot mellékeltem a
forráskóddal együtt. A példaprogram egy példaformot és az általános
átméretezési műveletet végző modult tartalmaz. A formra egy általános
adatbázis egy adatlapjának megfelelő vezérlőelemeket tettem fel,
parancsgombot, gridet, keretet, szövegablakot, címkét. A programot futtatva a
form átméretezésekor látható az eredmény, mint azt az alábbi két ábra
mutatja: a keret szélessége követte a form szélességét, a Frissítés gomb
pozíciója változott és a keret jobb oldalán maradt, a gridnek pedig mind a
szélessége, mind a magassága követte a form méretének változását. A vezérlőelemek teljes ablakméret esetén A vezérlőelemek normál ablakméret esetén |
|
A megoldás
körvonalazása |
|
Mielőtt a
kódokba ugranánk, röviden nézzük át a megoldást, hogy aztán a részletekkel
tudjunk törődni! A célunk az,
hogy a form átméretezésekor az általunk kiválasztott vezérlőelemek pozíciója
és/vagy mérete igazodjon a form új méretéhez. Például ha a form szélességét
100 egységgel megnöveljük, akkor a jobbra igazítandó vezérlőelemeket is
szeretnénk ugyanannyival jobbra mozgatni. Ha a form magasságát 200 egységgel
csökkentjük, akkor a form aljához és tetejéhez igazított vezérlőelem, pl.
grid, magasságát szeretnénk 200 egységgel csökkenteni. A megoldást
nehezítik azok a vezérlőelemek, melyek maguk is más vezérlőelemeket
tartalmaznak, akár több szinten egymásba ágyazva. Az egyes vezérlőelemek
pozíciója, mint azt említettük korábban az őket befogadó elem bal felső
sarkából számítandó, így az adott vezérlőelemnek meg kell határozni az őt
befogadó másik vezérlőelemét, és annak méretbeli változását kell követni. A megoldás
sémája ezek után a következő: A form
betöltésekor elmentjük a form méretét. Átméretezéskor az új és a régi méret
különbsége adja azt az értéket, mellyel a vezérlőelemek pozícióját és/vagy
méretét változtatni akarjuk. Átméretezés után egyenként végiglépkedünk a form
vezérlőelemein, és beállítjuk az új méretüket és pozíciójukat. Azt az
információt, hogy az adott vezérlő mely oldalra igazított, a jelenlegi
példában az egyes vezérlőelemek Tag
tulajdonságában tároljuk. Amennyiben a vezérlő önmaga is tartalmaz más
vezérlőket, akkor az előbbi műveletsort a tartalmazott vezérlőkre is
végigfuttatjuk és így tovább. A műveletek végén ismét elmentjük a form
jelenlegi méretét, felkészülve a következő átméretezésre. Ennyi! Egyszerű,
igaz? Egy kissé több munkával egy tömbbe, vagy gyűjteménybe is menthetjük az
igazítási információt, ekkor a Tag tulajdonságot
egyéb műveletekre fenntarthatjuk. |
|
A kódolás |
|
Nos, akkor
nézzük hát a kódot, ahol egyben a szükséges tudnivalókról is szó esik! A kód a
következő kódrészletekben bemutatott technikákra épül: 1. Minden VB-beli
objektum közös őse az általános object objektum. A címben így írtam: jolly
joker. A legegyszerűbb, ha így is tekintünk rá. Ha két valamely specifikus
objektum közös tulajdonságát egy művelettel kívánjuk kezelni, akkor ezt az object objektummal biztosan megtehetjük. Ezért egy olyan függvényfejléc, mely
bemeneti paraméterként egy object elemet tartalmaz, mind a formot, mind a
vezérlőelemeket elfogadja bemeneti paraméternek. Private Sub DoWriteObjectName(ByRef InputObject As Object) 'Az objektum típusának kiíratása Debug.Print
TypeName(InputObject) 'Az objektum nevének kiíratása Debug.Print InputObject.Name End Sub Ha ezt a függvényt egy létező parancsgomb vezérlőelem paraméterrel hívjuk
meg: Private Sub Command1_Click() DoWriteObjectName Command1 End Sub akkor a következő eredményt kapjuk a debug ablakban: CommandButton Command1 A form (a
formból hívva a Me)
paraméterrel meghívva ugyanazt a függvényt: Private Sub Command1_Click() DoWriteObjectName Me End Sub az eredmény a
következő Form1 Form1 A következő, a
példaprogramban is szereplő DoResizeChildControls függvény a ParentObject paraméterével mind form, mind pedig az adott form bármely vezérlőelemét
átveheti: Private Sub DoResizeChildControls(_ ByRef ContainerForm As Form, _ ByRef ParentObject As Object, _ ByVal OldWidth As Long, _ ByVal OldHeight As Long) Ebben a
függvényben például a ParentObject.Width tulajdonság a paramétertől
függően bármely vezérlőelem, vagy magának a formnak a Width tulajdonságát hordozhatja. Így tudjuk
elérni és változtatni egyetlen függvénnyel egy megadott form minden
komponensének pozícióját és méreteit.
2. Az egyes
vezérlőelemeket a form egy Controls gyűjteményben tárolja. Egy For …
Each ciklussal végiglépkedhetünk az adott
form összes vezérlőelemén, amennyiben egy általános Control objektumot definiálunk: Public Sub DoWriteControlsName(ByRef ContainerForm As Form) Dim ctr As Control For Each ctr In ContainerForm 'A vezérl típusának kiíratása Debug.Print TypeName(ctr) 'A vezérl nevének kiíratása Debug.Print ctr.Name Next End Sub A
példaprogrambeli form esetén az eredmény a debug ablakban a következő eredményt hozza: DataGrid xgrdDataGrid TextBox xtxtRogzito TextBox xtxtRowver Frame xfmeFilter CommandButton xcmdFrissit TextBox xtxtSorszam Label xlblSorszam Label xlblRogzito Label xlblRowver Label FormBorder Ezt a technikát
többször is használjuk a példaprogramban, pl. annak megállapítására, hogy
van-e az adott vezérlőelemnek tartalmazott vezérlőeleme (IsControlHasChildControl funkció). Az eddigiekből
már az is világos, hogy a ContainerForm As Form bemeneti paraméter helyett ContainerForm As Object paramétert is használhattunk volna, a kód
ugyanúgy működött volna, hiszen minden form egyben object is! Sőt, ha az átméretező modult egy külön dinamikus könyvtárba (dll)
tesszük, akkor csak ez lehet a megoldás, mivel oda nem adhatjuk át a
formunkat form paraméterként. |
|
További felhasznált
technikák |
|
Rekurzív
függvényhívással oldható meg a tartalmazott vezérlőelemek átméretezésének és
áthelyezésének kérdése. Röviden arról van szó, hogy egy függvény meghívja
önmagát. A rekurzív hívás technikájával most nem foglalkozom, általában
minden VB könyvben megtalálható a könyv első harmadában. Bármely
vezérlőelem befogadója, legyen az a form, vagy egyéb más vezérlőelem
meghatározható a Container tulajdonság lekérdezésével. A Parent tulajdonság ellenben a befogadó form nevét adja
vissza. A funkciónak
egyértelmű okokból csak akkor szabad lefutnia, ha normál, vagy maximalizált a
form nézet. Ezt könnyen eldönthetjük a form WindowState tulajdonságával. Ezen kívül a Form_Resize esemény a form betöltésekor
automatikusan meghívódik, de az lenne célszerű, hogy csak akkor fusson le az
átméretezési funkció, amikor a form már inicializálva van, és a felhasználó
maga változtatja a méretét. Annak eldöntésére, hogy egy objektum üres-e,
vagyis Nothing az értéke, (ez
a VB.NET-nél az <object> Is Nothing hívással egyenértékű), egy kis funkciót
kellett megírni (IsObjectInitialized). Ezek után úgy
gondolom, hogy a teljes kód
megértése nem okozhat problémát. |
|
Az átméretezési
funkció meghívása |
|
Az
átméretezési funkciót az adott form Form_Resize eseményéhez kötjük, ezáltal érjük el, hogy a
funkció automatikusan lefusson a form átméretezésekor. A funkció meghívásakor
azonban figyelemmel kell lenni néhány dologra. Egy bizonyos
méretnél kisebbre nem engedhetjük összehúzni a formot, hiszen a vezérlőelemek
egymásra csúsznának. Praktikus megoldásként egy a tervezési időben a jobb
alsó sarokban lévő kis piros négyzettel állíthatjuk be a form minimális
méretét, majd a form betöltésekor ehhez igazítjuk a form méreteit. Ugyancsak a
form betöltésekor rendeljük a vezérlőelemek Tag tulajdonságához az irányultság (Anchor) értékeket. Ezek az értékek a modulban Enum-ként lettek definiálva. Ezek után
nézzük a form minimális kódját a függvény meghívásához: 'Példaform a resizing modul meghívásához 'Filenév: frmResizeDemo.frm 'Készítette: Balogh Tamás '2002. októberében 'szabadon felhasználható Option Explicit 'Itt tároljuk a form átméretezés eltti méretét Public FormOldWidth As Long Public FormOldHeight As Long Private Sub Form_Load() 'A form méretének beállítása 'Tervezési idben a jobb alsó
sarokban lév kis piros négyzettel 'állítható be a minimális méret Me.Width = Me.FormBorder.Left +
(Me.Width - Me.ScaleWidth) Me.Height = Me.FormBorder.Top +
(Me.Height - Me.ScaleHeight) Me.Top = 0 Me.Left = 0 Me.FormOldHeight = Me.Height Me.FormOldWidth = Me.Width 'Anchor beállítása az egyes
vezérlelemet Tag tulajdonságába Me.xfmeFilter.Tag =
enmAnchorTo.eAnchorTo_TopLeftRight Me.xcmdFrissit.Tag =
enmAnchorTo.eAnchorTo_TopRight Me.xgrdDataGrid.Tag =
enmAnchorTo.eAnchorTo_All Me.xlblRogzito.Tag =
enmAnchorTo.eAnchorTo_BottomLeft Me.xtxtRogzito.Tag =
enmAnchorTo.eAnchorTo_BottomLeft Me.xlblRowver.Tag = enmAnchorTo.eAnchorTo_BottomRight Me.xtxtRowver.Tag =
enmAnchorTo.eAnchorTo_BottomRight End Sub 'Ez az esemény indítja el az automatikus átméretezést Private Sub Form_Resize() 'Ha normál a form nézet, 'egy bizonyos méretnél kisebbre
nem engedi az ablakot 'Tervezési idben a jobb alsó
sarokban lévő kis piros négyzettel 'állítható be a minimális méret If Not Me.WindowState =
vbMinimized Then If Me.Height <
FormBorder.Top + (Me.Height - Me.ScaleHeight) Then Me.Height = FormBorder.Top +
(Me.Height - Me.ScaleHeight) End If If Me.Width <
Me.FormBorder.Left + (Me.Width - Me.ScaleWidth) Then Me.Width =
Me.FormBorder.Left + (Me.Width - Me.ScaleWidth) End If End If 'A funkció meghívása DoResizeControls Me End Sub Lám ennyi az egész. Pár sor a formon, egy modul és a programunk
vezérlőelemei szabadon méretezhetők! Remélem kis cikkem sokaknak hasznos
információkat nyújtott. Hozzáfűzéseket, esetleges helyesbítéseket, egyáltalán
bármilyen javító szándékú hozzászólást szívesen fogadok. |
|
Balogh Tamás |