Miután dialógusablakokban már profik vagyunk, most a normál ablakok következnek. Az az egyszerű egy menü, egy kis keret, meg a menüből nyíló dialógusablakok. Az ilyen stílusú alkalmazást SDI-nek (Single Document Interface) is szokás nevezni.

Szóval a helyzet úgy néz ki, hogy van egy Frame Window, ami maga az alkalmazás ablaka. Része a rendszermenü, a menüsor, az esetleges ikonsor és a fejléc. Magába foglalja az u.n. Client Area -t. Ez az üres terület az, ahol a dokumentum megjelenik, és a felhasználó dolgozhat vele. A Client-Area-hoz hozzá van rendelve, egy View, ami a megjelenítést végzi. Maga a dokumentum pedig a View-val tart kapcsolatot.

A Frame-hez, pontosabban a menüpontjaihoz pedig Dialógusablakok rendelhetők. Az ilyen menüpontok általában …-ra végződnek.

Az SDI lényege tehát az, hogy egy Frame Window-hoz egy Dokumentum van rendelve.

Ami a példaprogramot illeti, aClient Area-t nem kötelező View-hoz rendelni, a Frame -ből is írhatunk rá. Így természetesen Dokumentum sem lesz, de így a kályhától indulunk el.

A mostani program összehozásához most nem nyújt olyan rugalmas segítséget az AppWizard, de azért még mindig egyszerűbb, mint mindent újból kezdeni.

Tehát MFC AppWizard, Single Document. Az egyszerűség kedvéért különböző extrákkal nem kell agyontuningolni.

Ha ez megvan, tisztogatni kell egy kicsit. Az AppWizard által létrehozott View és Doc Class-okat egy az egyben le lehet szedni. Mivel a ClassView-ból nem lehet törölni, a file-jaikat kell eltávolítani. 

Ezek után a program összesen 3 Class-t tartamaz.
 

  • CSDIApp                  az alkalmazás Class-a
  • CMainFrame             a Frame Window Class-a
  • CAboutDlg                az about ablak.

Az alkalmazás objektumában csak az InitInstance függvényre van szükségünk, és esetleg az AboutBox Message Handler-ére. Az InitInstance tartalmazza a FrameWindow meghívását:

m_pMainWnd=new CMainFrame;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();

A new paranccsal létrehozunk egy FrameWindow-t, amit kirajzoltatunk, és átadjuk rá a vezérlést.

A lényeges dolgok innentől a FrameWindow-ban zajlanak.A Konstruktorban van egy dolog, amit mindenképp meg kell tenni.:

LoadFrame(IDR_MAINFRAME); //Frame betoltese

A Frame-hez tartozó összes resources (erőforrás) itt kerül betöltésre. Ilyenek a menüsor, az Ikonok, a Gyorsbillentyűk, és a string tábla.

A leggyakrabban használt ezek közül a menüsor. A szerkesztése nagyon egyszerű, a Resource View-ban csak rá kell kattintani. Minden menün belül létrehozhatunk menüpontokat, és továbbnyíló Pop-Up menüket. A Pop-Up menükkel semmi dolgunk nincs, azok kezelését a rendszer teljesen magától végzi. A menüpontok az érdekesek. Mindegyikhez tartozik egy ID. Kezelőfüggvényeket rendelhetünk az általuk kiváltott üzenetekhez. A kétféle üzenet lehetséges:

COMMAND
UPDATE_COMMAND_UI

A COMMAND üzenethez Handler-t, azaz kezelőfüggvényt lehet rendelni. Például az Open menüponthoz egy olyan függvény tartozna, ami elindít egy dialógusablakot, amiben bekéri a file nevét, majd megnyitja azt. Szóval ez a függvény a parancs végrehajtására való.

Az UPDATE_COMMAND_UI üzenethez callback függvény rendelhető. A Callback függvény olyan fajta függvény, amit csak az alkalmazáson kívülről lehet hívni. A menüponthoz tartozó CallBack függvény akkor fog meghívódni (szép magyar szóval), ha a menüpont állapota változik. Ami azt jelenti, hogy rányomtunk. Tipikusan arra való, hogy a kiválasztott menüpontot megjelöljük egy pipával, vagy más menüpontokat inaktívvá tegyünk.

Röviden mindkét üzenet a menüpont kiválasztásnál generálódik, de teljesen más céllal. Ezért a hozzájuk rendelt függvényeket sem jó összekeverni.

Ezeknek a hozzárendeléseknek a megvalósítása legkönnyebben a ClassWizard-dal érhető el. Kiválasztjuk a menüpont ID-jét, az üzenetet (COMMAND/UPDATE_COMMAND_UI), és egy Add Function-nal már meg is jelent.

Ennek megfelelően egy példa a Handler függvényre:

void CMainFrame::OnSettext() { CNewTextDlg dialog; dialog.m_NewText=Text; if (dialog.DoModal()==IDOK) Text=dialog.m_NewText; MyUpDate(); }

Minden dolog ismert benne, meghív egy dialógusablakot, ami bekér egy stringet, és a saját változójában eltárolja. Onnan kiszedhetjük, hiszen attól, hogy az ablak már eltűnt a memóriából, a változói még megvannak.

Egy példa a CallBack függvényre:

void CMainFrame::OnUpdatePos(CCmdUI* pCmdUI) {   pCmdUI->SetCheck(pCmdUI->m_nID == Pos); }

Amint látható a függvény kap paraméterként egy CCmdUI Class-ra mutató pointert. Ennek segítségével tudjuk a menüt változtatni. Változás esetén a régi menüponttól leszedi a pipát, az újra meg felteszi. Csak azért lehetséges, mert az összes menüpont CallBack függvénye egy személyben, akit változtatni kell az adott Pop-Up menün belül. 

A program egy stringet pozícionál a képernyőn a menüben kiválasztott helyre. Ehhez írni kell a Client Area-ra. 

Ilyenkor leggyakrabban az WM_PAINT üzenethez rendelt függvénnyel lehet célba érni. Az WM_PAINT üzenet akkor generálódik, ha az ablak újrarajzolódik. Ebben az estben pedig nyugodtan írhatunk.

void CMainFrame::OnPaint() {   CPaintDC dc(this); // device context for painting   dc.SetTextAlign(TA_BASELINE | TA_CENTER);   dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));   dc.SetBkMode(TRANSPARENT);   dc.TextOut(TextX, TextY, Text); }

A CPaintDC Class segítségével írhatunk ki nem az ablakhoz (most a Client Area-hoz) tartozó szöveget. Tetszőleges koordinátákra, tetszőleges színnel. 

A koordinátatengelyek:

Természetesen a helyes koordináták kiszámolásához ismernünk kell az ablak méretét, a szöveg méreteit képpontokban. Ezen kívül minden alkalommal, amikor újraméretezzük az ablakot, újra kell számolni a koordinátákat. 

Egy példa a Client Area méreteinek lekérdezésére:

CRect rect; //negyzet koord CSize WindSize; GetClientRect(rect);//Ablak koord. lekerd WindSize.cx=rect.right; WindSize.cy=rect.bottom;

A Crect Class egy négyzet jellemző két koordinátáját tárolja. A bal felsőt és a jobb alsót. 
Hasonló Class a CSize is, ez x és y méretet tárol.

A GetClientRect függvény egy CRect Classba adja vissza a ClientArea méreteit, innen pedig kényelmesen ki lehet pakolni. 

Egy példa a szöveg méreteinek lekérdezésére:

CPaintDC dc(this); // device context for painting CSize TextSize; TextSize=dc.GetTextExtent(Text);

Szintén a CPaint segítségével. A GetTextExtent függvény képernyőpontokban adja vissza a string méretét.

Említettem, hogy az újraméretezésnél is mindent előröl kell kezdeni.

Kell egy Handler-t írni a WM_SIZE üzenetre. Akkor hívódik meg, mikor az ablak új méretet kapott.

Példa:

void CMainFrame::OnSize(UINT nType, int cx, int cy) {   CFrameWnd::OnSize(nType, cx, cy);   WindSize.cx=cx;   WindSize.cy=cy;   MyUpDate(); }

A függvény kapásból megkapja az új méretet, csak el kell tárolni, és újraszámolni a szöveg koordinátáit.

Úgy érzem minden részletre kitértem. Azt inkább nem írom le, hogy kell kiszámolni egy négyzet közepét, meg a széleit.

Azt hiszem eddig ez a leghosszabb VisualC-s cikkem. Nem baj úgyis jubilálunk, mert ez a 20. (vagy a 21.) PCX-USER. [Ez a 24. Szerk.]