Sunteți pe pagina 1din 8

Capitolul 3.

Introducere n MFC 83

3. Introducere n MFC

Dup cum sugereaz i denumirea, Microsoft Foundation Class (sau prescurtat


MFC) este o bibliotec de clase, dezvoltat de firma Microsoft n vederea uurrii
programrii aplicaiilor Windows. Multe dintre clasele MFC ncapsuleaz funciile
Windows API, dar permit o utilizare mai prietenoas a acestora.

Folosind clasele din biblioteca MFC, aplicaiile Windows se pot programa


obiectual de o manier mult mai simpl dect permite API. Acest fapt prezint
urmtoarele avantaje:
crete semnificativ lizibilitatea codului, o aplicaie putnd fi descifrat mult mai
uor;
se simplific programarea aplicaiei, prin folosirea claselor deja implementate de
MFC;
se micoreaz semnificativ timpul de dezvoltare al unei aplicaii Windows;
programatorul are posibilitatea de a se concentra numai asupra aspectelor
particulare ale aplicaiei, lsnd aspectele standard (clase de ferestre, proceduri
fereastr etc.) n seama claselor MFC;
se pot folosi n procesul de programare programele vrjitor (wizards) precum
AppWizard i ClassWizard, care uureaz munca programatorului;

Clasele incluse n biblioteca MFC sunt dispuse n mai multe categorii, dintre care
putem aminti:
clase de uz general - clase pentru manipularea fiierelor (CFile) i irurilor de
caractere (CString), clase excepii (CException), clase pentru reprezentarea unor
zone de pe ecran (CRect, CPoint);
clase de obiecte vizuale - aceste clase manevreaz aproape tot ce este vizibil ntr-
un program Windows. n aceast categorie intr clasele de ferestre (CWnd,
CFrameWnd i clasele derivate din ele), meniuri (CMenu), controale (CButton,
CEdit, CListBox etc. derivate din clasa CWnd) i obiecte de desenare, cum ar fi
creioanele (CPen) i pensulele (CBrush);
clase de aplicaie - n aceast categorie sunt incluse clasele care gestioneaz
obiectele fir de execuie (CWinThread), aplicaie (CWinApp derivat din
CWinThread), precum i clasele care implementeaz arhitectura Document-
Reprezentare (CDocument, CView, i clasele derivate din ele). Arhitectura
Document-Reprezentare este folosit de majoritatea programelor Windows;
clase de tip colecie - aceste clase se folosesc pentru stocarea altor obiecte n
structuri de date de tip tablou (CArray), list (CList) sau hart (CMap);
clase suport OLE2 - aceste clase simplific scrierea aplicaiilor care beneficiaz
de facilitile OLE2;
clase pentru baze de date - aceste clase faciliteaz manipularea bazelor de date
(CDatabase, CRecordset, CDaoDatabase, CDaoRecordset);
clase pentru dezvoltarea aplicaiilor distribuite (n reea) - cteva dintre aceste
clase sunt: CAsyncSocket, CSocket (pentru deschiderea unui canal de comunicaie
ntre dou calculatoare), CFTPConnection i CHTTPConnection (pentru
deschiderea unei sesiuni de lucru FTP, respectiv HTTP);

H. Vlean, 2004
84 Visual C++. Programarea Interfeelor Utilizator

Clasele implementate n biblioteca MFC folosesc motenirea simpl. Majoritatea


claselor MFC sunt derivate (direct sau indirect) din clasa CObject. De asemenea, toate
clasele care reprezint ferestre sau controale de diferite tipuri sunt derivate din clasa
CWnd. Clasele CObject i CWnd utilizeaz funciile virtuale pentru implementarea
polimorfismului n clasele derivate. Aceasta permite obiectelor din program s
acceseze funcii de uz general, prin intermediul unui pointer la clasa de baz.

3.1 Scrierea unei aplicaii simple cu MFC

De obicei, la scrierea unui program MFC, se folosete AppWizard, un program


vrjitor care genereaz automat scheletul programului, particularizat pentru
aplicaia respectiv. Totui, un program MFC se poate scrie i fr a beneficia de
asistena vreunui program vrjitor. Chiar i n acest caz, este mai uor s se scrie un
program utiliznd biblioteca MFC, dect direct, folosind funciile Windows API.

Pentru a scrie un program MFC fr a folosi vreun program vrjitor, trebuie s


parcurgem urmtorii pai:

deschidem un nou proiect de tip Win32 Application, care nu conine nici un fiier
(An empty project);
din meniul Project->Settings, selectm Settings for: all configurations, iar de la
tab-ul General, n caseta combinat Microsoft Foundation Classes: vom alege
Using MFC in a shared library;
introducem codul programului, compilm, editm legturile i lansm n execuie.

S facem un prim exemplu. Vom crea un proiect dup paii descrii mai sus, pe
care-l vom numi PrimulMFC. Vom aduga proiectului un fiier header, numit
PrimulMFC.h:

class CPrimaAplicatie: public CWinApp


{
public:
BOOL InitInstance();
};
class CPrimaFereastra: public CFrameWnd
{
public:
CPrimaFereastra();
};

Acum urmeaz s adugm la proiect fiierul PrimulMFC.cpp, n care vom defini


funciile declarate anterior:

#include <afxwin.h>
#include "PrimulMFC.h"

BOOL CPrimaAplicatie::InitInstance()
{
m_pMainWnd = new CPrimaFereastra;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
Capitolul 3. Introducere n MFC 85

CPrimaFereastra::CPrimaFereastra()
{
Create(NULL, "Prima mea aplicatie MFC");
}

CPrimaAplicatie primaApp;

Dac vom compila i executa programul, va apare o fereastr! Dar, nu avem


nicieri o funcie main() i totui programul se execut corect! De aceea, se impun
cteva explicaii pe marginea acestui program:
pentru a putea folosi clasele MFC, este necesar editarea legturilor ntre fiierele
cu cod obiect ale bibliotecii MFC i fiierele proiectului (selectarea opiunii
Project->Settings etc.). Declaraiile claselor din biblioteca MFC este fcut n
fiierul afxwin.h, deci acest fiier trebuie inclus n codul surs al programului;
orice program MFC este un program executabil. El trebuie s conin o clas
derivat public din clasa CWinApp, care este o clas care ncapsuleaz programe
Windows. Orice program care se execut sub Windows este un obiect de clas
CWinApp sau de o clas derivat public din aceasta. Obligatoriu, clasa derivat
public redefinete metoda InitInstance() a clasei CWinApp. Aceast metod, dup
cum sugereaz i numele su, este apelat automat de infrastructur la pornirea
unei instane a aplicaiei (la crearea unui obiect aplicaie), pentru a-l iniializa. Ea
este declarat ca virtual n cadrul clasei CWinApp, aa nct se va apela varianta
corect (cea redefinit n noua clas aplicaie). Este neaprat necesar redefinirea
funciei InitInstance() cu semntura:

BOOL InitInstance();

pentru a nu se pierde caracterul de funcie virtual.


De obicei, funcia InitInstance() are rolul de a crea fereastra principal a
aplicaiei i de o a afia pe ecran. Pentru aceasta, n funcia InitInstance() este
utilizat pointerul CWnd* m_pMainWnd, care este o variabil membru al clasei
CWinApp i motenit de clasa aplicaie (CPrimaAplicatie n cazul nostru), pentru
a crea un nou obiect fereastr, asociat programului aplicaie. Pentru aceasta, se
scrie o linie de cod ca mai jos:

m_pMainWnd = new CPrimaFereastra;

unde n operatorul new se apeleaz constructorul unei clase derivat, direct sau
indirect, n mod public, din clasa CWnd sau CFrameWnd. Aceasta permite
conversia explicit de la un obiect de tip CPrimaFereastra* (returnat de operatorul
new, n exemplu) la un obiect de tip CWnd* (respectiv m_pMainWnd).
Dup crearea obiectului de tip fereastr, acesta este afiat pe ecran folosind
funciile ShowWindow() i UpdateWindow(), membre ale clasei CWnd. Funcia
ShowWindow() primete ca parametru un ntreg (int m_nCmdShow, definit n clasa
CWinApp) care specific modul de afiare iniial a ferestrei aplicaiei (normal,
minimizat, maximizat,etc.).
Dup succesiunea normal a operaiilor sus-menionate, funcia InitInstance()
returneaz TRUE, semnaliznd faptul c aplicaia poate continua normal.
folosirea unei clase derivate (direct sau indirect) din clasa CWnd nu este
obligatorie. Se pot folosi una dintre clasele fereastr furnizate de MFC: CWnd,
CFrameWnd, sau alt clas fereastr. Totui, exemplul de mai sus definete o nou
clas fereastr, bazat pe clasa CFrameWnd. Motivul utilizrii acestei clase ca i

H. Vlean, 2004
86 Visual C++. Programarea Interfeelor Utilizator

clas de baz este prezentat n paragraful urmtor. Clasa CFrameWnd este


derivat direct din clasa CWnd, i reprezint o fereastr cadru a unei aplicaii.
Constructorul clasei CPrimaFereastraWnd trebuie s apeleze funcia Create()
(motenit din clasa CFrameWnd). Primul parametru transmis funciei se refer la
clasa de ferestre pe care dorim s o folosim pentru fereastra cadru a aplicaiei. El
este NULL, deci se va folosi o clas de ferestre nregistrat de MFC. Cel de-al
doilea parametru este titlul ferestrei principale a aplicaiei. Ceilali parametri ai
funciei CFrameWnd::Create() nu mai sunt transmii explicit, ei lund valori
implicite.
orice program MFC posed un obiect (i numai unul) derivat din clasa aplicaie
(CPrimaAplicatie primaApp, n cazul exemplului de mai sus). Declararea unui
asemenea obiect (sub forma unei variabile globale) duce la declanarea
mecanismului MFC de desfurare a unei aplicaii (apelul funciei InitInstance()
n constructorul clasei aplicaie, care creeaz un nou obiect fereastr, apelndu-se
astfel constructorul clasei fereastr, care apeleaz funcia Create() pentru a crea
fereastra, iar apoi aceasta este afiat, etc.). Putem considera acest obiect aplicaie
ca fiind echivalent funciei main() dintr-un program consol, sau funciei WinMain()
dintr-un program Windows scris folosind Windows API.

Observaie: i n acest caz simplu, la fel ca i n capitolul 2, utilizarea funciei


UpdateWindow() nu este necesar.

3.2 Manipularea mesajelor in MFC

Se poate observa ct de mult se simplific scrierea unei aplicaii Windows


folosind MFC. Totui, fcnd comparaie cu aplicaiile Windows din capitolul
precedent, aceast aplicaie nu are o facilitate: ea nu trateaz nici un mesaj Windows,
ci las tratarea mesajelor n seama procedurilor implicite.
S modificm programul anterior, astfel nct s trateze mesajele Windows. Vom
intercepta i trata mesajele WM_CLOSE i WM_KEYDOWN, la fel ca i n capitolul 2. n
primul rnd, va trebui s modificm fiierul header:

class CPrimaAplicatie: public CWinApp


{
public:
// redefineste functia InitInstance a clasei CWinApp
BOOL InitInstance();
};
class CPrimaFereastra: public CFrameWnd
{
public:
CPrimaFereastra();
protected:
afx_msg void OnClose();
afx_msg void OnKeyDown(UINT nChar);
DECLARE_MESSAGE_MAP()
};

Pentru a putea trata mesajele adresate unei ferestre, este necesar definirea unei
hri de mesaje. O hart de mesaje este utilizat pentru a stabili legturi ntre mesajele
transmise unei ferestre i funciile destinate procesrii acestor mesaje. Declararea unei
Capitolul 3. Introducere n MFC 87

hri de mesaje se face n interiorul unei clase derivate din clasa CWnd (uzual n
fiierul header), folosind macroul DECLARE_MESSAGE_MAP, astfel:

DECLARE_MESSAGE_MAP()

Observaie: Nu se pune ; dup acest macrou!

Declararea hrii de mesaje se face n clasa derivat din clasa fereastr, deoarece,
s ne reamintim, Windows trimite toate mesajele ctre fereastra asociat programului
aplicaie. Declararea hrii de mesaje i a funciilor de tratare a mesajelor se face n
cadrul unei seciuni protejate a clasei fereastr. Funciile de tratare a mesajelor au
semnturi predefinite i declararea lor ncepe ntotdeauna cu cuvntul afx_msg. Acest
cuvnt semnalizeaz compilatorului faptul c este vorba despre o funcie ce trateaz
mesaje. Ceea ce urmeaz dup acest cuvnt cheie, este specific fiecrui mesaj tratat.
Nu este necesar nvarea semnturilor tuturor funciilor de tratare a mesajelor.
Visual C++ posed un program vrjitor, numit ClassWizard, care permite inserarea
automat a funciilor de tratare a mesajelor n cadrul unei clase. Pentru folosirea
ClassWizard, proiectele trebuie s fi fost generate cu AppWizard, un alt program
vrjitor. Din pcate, nseamn c nu putem folosi ClassWizard n exemplul de mai sus.
Din fericire ns, uzual proiectele MFC se genereaz folosind AppWizard, tocmai n
ideea de a beneficia de suportul oferit de ClassWizard (i alte utilitare).

Observm c funcia de tratare a mesajului WM_CLOSE are semntura

afx_msg void OnClose();

i este de fapt o funcie membr a clasei CWnd. Aceast funcie este redefinit de
clasa CPrimaFereastraWnd. Este necesar ca funcia OnClose() redefinit s apeleze
versiunea sa de baz, prin instruciunea CFrameWnd::OnClose(), pentru c aceasta din
urm distruge obiectul fereastr i dealoc memoria. Dac nu am apela aceast
funcie, ar trebui s avem noi grij de aceste operaii.
Semntura funciei de tratare a mesajului WM_KEYDOWN este

afx_msg void OnKeyDown(UINT nChar);

Este necesar ca aceast funcie s primeasc un parametru de intrare. Acest


parametru este utilizat pentru transmiterea codului ASCII a tastei apsate. De
completarea lui se va ocupa biblioteca MFC.
S vedem cum modificm fiierul surs:

#include <afxwin.h>
#include "PrimulMFC.h"

BOOL CPrimaAplicatie::InitInstance()
{

return TRUE;
}

BEGIN_MESSAGE_MAP(CPrimaFereastra, CFrameWnd)
ON_WM_CLOSE()
ON_WM_KEYDOWN()
END_MESSAGE_MAP()

H. Vlean, 2004
88 Visual C++. Programarea Interfeelor Utilizator

CPrimaFereastra::CPrimaFereastra()
{
Create(NULL, "Prima mea aplicatie MFC");
}
void CPrimaFereastra::OnClose()
{
MessageBox("Aplicatie terminata", "Gata", MB_OK|MB_ICONSTOP);
CFrameWnd::OnClose();
}
void CPrimaFereastra::OnKeyDown(UINT nChar)
{
CString text;
switch(nChar){
case VK_BACK: text="Ati apasat tasta BackSpace"; break;
case VK_TAB: text="Ati apasat tasta Tab"; break;
case VK_LEFT: text="Ati apasat tasta SageataStinga";break;
case VK_RIGHT: text="Ati apasat tasta SageataDreapta"; break;
case VK_UP: text="Ati apasat tasta SageataSus"; break;
case VK_DOWN: text="Ati apasat tasta SageataJos"; break;
case VK_RETURN: text="Ati apasat tasta Enter"; break;
case VK_ESCAPE: text="Ati apasat tasta Escape"; break;
case VK_CONTROL: text="Ati apasat tasta Control"; break;
default: text="Ati apasat alta tasta"; break;
}
MessageBox(text, "Mesaj",MB_OK|MB_ICONEXCLAMATION);
}

CPrimaAplicatie primaApp;

Harta de mesaje declarat n fiierul header este definit n fiierul surs.


Definirea hrii de mesaje trebuie fcut n afara clasei. Se folosesc macrourile
BEGIN_MESSAGE_MAP i END_MESSAGE_MAP, care marcheaz nceputul, respectiv
sfritul hrii de mesaje.

Atenie! Harta de mesaje nu conine nici un caracter ;!

Macroul BEGIN_MESSAGE_MAP primete doi parametri: numele clasei fereastr


derivate i numele clasei fereastr de baz. Macroul END_MESSAGE_MAP nu primete
nici un parametru. Aceste dou macrouri apar la nceputul, respectiv sfritul oricrei
hri de mesaje. Iat la ce folosesc cei doi parametri transmii macroului
BEGIN_MESSAGE_MAP: cnd se recepioneaz un mesaj, se ncearc tratarea lui printr-o
funcie definit n clasa fereastr derivat. Dac aceasta nu furnizeaz o funcie de
tratare a mesajului, tratarea mesajului este lsat n seama clasei fereastr de baz, n
care exist implementate funcii de tratare pentru toate mesajele. Acest mecanism se
repet n cazul clasei fereastr de baz (dac aceasta este la rndul ei derivat dintr-o
alt clas fereastr). Cu alte cuvinte, la recepionarea unui mesaj, de exemplu
WM_CLOSE, programul va cuta funcia OnClose() (s ne reamintim, declarat ca i
funcie afx_msg) n clasa CPrimaFereastra. Dac nu o va gsi, nu e nici o problem,
va trata mesajul prin intermediul funciei implicite din clasa CFrameWnd. Este un fapt
oarecum ateptat. i nainte de a intercepta mesajul WM_CLOSE explicit n program,
acesta putea fi nchis.
Implementarea funciilor este acum cunoscut. Este totui demn de remarcat c la
implementare, cuvntul afx_msg nu mai apare. Funcia OnClose() apeleaz funcia
MessageBox(), dup care distruge fereastra.
Capitolul 3. Introducere n MFC 89

Funcia MessageBox() apelat aici nu este totuna cu funcia MessageBox() apelat n


exemplul Windows API. Aici apelm funcia CWnd::MessageBox(), care este o funcie
membru a clasei CWnd. De altfel, cele dou funcii au semnturi diferite:

int MessageBox(HWND, LPCSTR, LPCSTR, UINT);


int MessageBox(LPCSTR, LPCSTR, UINT);

Prima semntur aparine funciei API, iar a doua funciei CWnd. Dac am fi dorit
s apelm funcia Windows API MessageBox(), ar fi trebuit s scriem:

::MessageBox(this->m_hWnd, ..., ..., MB_OK);

Funcia API MessageBox() solicit ca prim parametru o variabil de tip HWND. De


remarcat construcia this->m_hWnd. Aceasta se refer la o variabil membru de tip
HWND, numit m_hWnd, motenit de clasa CPrimaFereastraWnd din clasa CWnd,
reprezentnd tocmai identificatorul ferestrei principale asociate aplicaiei.
De reinut faptul c orice funcie Windows API care este redefinit ntr-o clas
MFC se poate apela (n interiorul acelei clase, sau al unei clase derivate din ea)
folosind construcia

::Nume_Functie_API(lista_parametri_actuali);

Funcia OnKeyDown() primete ca parametru valoarea UINT nChar. n funcie de


valoarea transmis de Windows acestui parametru, se afieaz mesajul corespunztor.
Tipul UINT este de fapt un ntreg fr semn. Diferena dintre UINT i unsigned int, sau
BYTE, sau WORD sau DWORD, este c acestea au lungime de reprezentare fix, n timp
ce dimensiunea UINT depinde de mediul software n care se execut programul: n
Windows 3.1, 3.11 este WORD, iar pentru platformele 9.x i NT este DWORD.
S revenim puin la harta de mesaje. Aceasta conine ntre macrourile de nceput
i sfrit, intrri care specific ce mesaje sunt tratate de clasa respectiv. Intrrile n
harta de mesaje sunt de fapt tot nite macrouri. Fiecare mesaj general Windows are
asociat n MFC un astfel de macrou. Regula de numire a macroului asociat este
urmtoarea: se adaug la numele mesajului (scris cu litere mari) prefixul ON_. Rezult
astfel macrouri de forma:

ON_WM_CLOSE()
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()

Pentru fiecare dintre aceste macrouri (folosite n harta de mesaje), clasa fereastr
trebuie s declare i s defineasc o funcie de tratare a mesajului respectiv (cu o
semntur predefinit). Pentru mesajele WM_CLOSE, WM_PAINT i WM_LBUTTONDOWN,
exemplificate mai sus, aceste funcii au semnturile:

afx_msg void OnClose();


afx_msg void OnPaint();
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

Toate aceste funcii sunt funcii membre ale clasei CWnd. Datorit faptului c ele
sunt redefinite i incluse n harta de mesaje a clasei CPrimaFereastraWnd, se
apeleaz versiunea corect a lor (fr a fi nevoie de declararea lor ca funcii virtuale).
Nu se folosete mecanismul funciilor virtuale din urmtorul considerent: dac o clas

H. Vlean, 2004
90 Visual C++. Programarea Interfeelor Utilizator

de baz declar o funcie virtual, toate clasele derivate din acea clas de baz vor
conine cte un tablou de pointeri la versiunea corect de funcie virtual care trebuie
apelat. Avnd n vedere faptul c sunt cteva sute de mesaje Windows, dimensiunea
acestui tablou de funcii virtuale ar fi fost foarte mare i de aceea s-a recurs la soluia
hrilor de mesaje.
Din cele artate mai sus, putem trage concluzia c MFC lucreaz cu dou tipuri de
funcii:
funcii obinuite, care pot fi fie funcii membre ale unei clase, fie funcii din afara
clasei. Pentru aceste funcii sunt necesare urmtoarele operaii:
1. declararea lor, uzual n fiiere header;
2. definirea lor, uzual n fiiere surs;
3. apelarea funciei n cadrul programului;
funcii care rspund la mesaje, care se execut la transmiterea unui mesaj de
ctre sistemul de operare ctre program. Pentru aceste funcii, sunt necesare
urmtoarele operaii:
1. declararea lor, uzual n fiiere header, obligatoriu ca i funcii afx_msg;
2. inserarea n harta de mesaje a macroului ON_... corespunztor, pentru a
specifica programului ce mesaje sunt interceptate;
3. definirea lor, uzual n fiiere surs;

Funciile afx_msg, spre deosebire de funciile obinuite, nu trebuie apelate n


program pentru a fi lansate n execuie. Ele sunt lansate n execuie automat, la
interceptarea mesajului asociat! Totui, nu este nici o greeal dac le apelm din
program. Ele se vor executa n acest caz normal, ca i orice alt funcie.

ntrebri i probleme propuse

1. Implementai i executai exemplele propuse n capitolul 3;


2. Care este deosebirea ntre modul n care trateaz mesajele API i MFC?
3. Implementai o funcie care afieaz coordonatele prompterului la apsarea
butonului drept al mouse-lui.
Indicaii:
inserai n harta de mesaje macroul ON_WM_RBUTTONDOWN();
declarai funcia

afx_msg void OnRButtonDown(UINT nFlags,CPoint point);

implementai funcia ca mai jos:

void CPrimaFereastra::OnRButtonDown(UINT nFlags,CPoint point)


{
CString text;
text.Format("Am apasat butonul la coordonatele [x=%d , y=%d]", point.x, point.y);
MessageBox(text);
}

CPointeste o clas avnd structura de date format din variabilele publice long x
i long y.Este utilizat pentru a ncapsula coordonatele unui punct de pe ecran.
CString este o clas care ncapsuleaz iruri de caractere. irurile pot fi formatate
respectnd tehnica de formatare din funcia printf(). Aceste clase vor fi descrise mai pe
larg n capitolele urmtoare.

S-ar putea să vă placă și