V minulΘm dφle jsme si pomocφ AppWizardu vygenerovali kostru aplikace. V tomto dφle se dozvφte jak tuto kostru upravit, aby d∞lala to co chcete.
Ale nejd°φve jakß je vlastn∞ struktura MFC aplikace. MFC aplikace se sklßdß z jednΘ a vφce t°φd, jejich₧ p°edky jsou MFC t°φdy. Tyto MFC t°φdy majφ p°edprogramovanΘ urΦitΘ chovßnφ a tφm, ₧e v jejich potomcφch upravujete jednotlivΘ funkce a p°idßvßte novΘ tvo°φte svoji aplikaci.
Pokud jste vytvo°ili svoji aplikaci podle obrßzk∙ z minulΘho dφlu, vygeneroval vßm AppWizard t°φdy, kterΘ vidφte na nßsledujφcφm obrßzku vlevo uprost°ed v okn∞ workspace. Pokud na n∞jakou t°φdu poklepete, otev°e se vßm hlaviΦkov² soubor s deklaracφ tΘto t°φdy a pokud poklepete na n∞kterou jejφ funkci, tak se vßm otev°e soubor se zdrojov²m k≤dem tΘto funkce.
Tak₧e jak vidφte vygenerovalo se vßm p∞t t°φd a jedna globßlnφ prom∞nnß. Nßsleduje struΦn² popis t∞chto t°φd.
CAboutDlg | T°φda dialogu o aplikaci. Jejφm p°edkem je zßkladnφ t°φda vÜech dialog∙ CDialog. Vφce o dialozφch se dozvφte v n∞kterΘ z dalÜφch Φßstφ. |
CMainFrame | T°φda hlavnφho rßmcovΘho okna aplikace. Jejφm p°edkem je zßkladnφ t°φda vÜech oken s rßmy CFrameWnd. Tato t°φda se starß o zobrazenφ titulku aplikace, menu, panelu nßstroj∙ a stavovΘho °ßdku. |
CMfcApp | T°φda aplikace. Jejφm p°edkem je CWinApp. Tato t°φda je ekvivalentem funkce WinMain. Ve skuteΦnosti ji obsahuje. Starß se o vytvo°enφ a zobrazenφ okna a v jejφ virtußlnφ funkci Run je obsa₧ena smyΦka GetMessage, TranslateMessage a DispatchMessage. |
CMfcDoc | T°φda dokumentu. Jejφm p°edkem je CDocument. Starß se o prßci s daty dokumetu jako je uklßdßnφ, naΦφtßnφ a vytvß°enφ novΘho dokumentu. (Nap°. kdy₧ zvolφte z menu polo₧ku nov² dokument.) |
CMfcView | T°φda pohledu. Jejφm p°edkem je CView. Starß se o zobrazovßnφ dat dokumentu. Je to ve skuteΦnosti okno rozta₧enΘ p°es celou klientskou plochu hlavnφho rßmcovΘho okna, tj. plochu kterou nezabφrß menu, panel nßstroj∙ a stavov² °ßdek. Samoz°ejm∞ lze kreslit i p°φmo na klientskou plochu hlavnφho rßmcovΘho okna a toto okno bychom nepot°ebovali, ale ₧ßdnß z mo₧nostφ AppWizardu vßm takovou aplikaci nevygeneruje, tak₧e by jste si ji museli napsat sami a takΘ by neÜly pou₧φvat n∞kterΘ pokroΦilejÜφ mo₧nosti. |
theApp | Toto nenφ t°φda, ale je to globßlnφ prom∞nnß t°φdy aplikace. Jejφm vytvo°enφm vlastn∞ cel² program zaΦφnß. |
V tΘto lekci se soust°edφme na t°φdu pohledu (CMfcView) a jak v nφ odchytßvat zprßvy windows (nap°φklad od myÜi) a jak do nφ kreslit. Vytvo°φme si jednoduch² kreslφcφ program. JedinΘ co bude um∞t bude kreslenφ Φar do okna.
Nov² k≤d budu oznaΦovat tφmto zv²razn∞nφm.
Nejd°φve si musφme p°ipravit mφsto kam budeme nakreslenΘ Φßry uklßdat. Na zaΦßtek hlaviΦkovΘho souboru s deklaracφ t°φdy dokumentu p°ed tuto deklaraci p°idejte nßsledujφcφ °ßdky.
#include <afxtempl.h> // hlaviΦkov² soubor pro Üablony
struct LINE // struktura pro uklßdßnφ jednotliv²ch Φar
{
CPoint first,second;
};
typedef CArray<LINE,LINE&> LINES; // dynamickΘ pole Φar
a do nßsledujφcφ deklarace t°φdy dokumentu p°idejte prom∞nnou dynamickΘho pole Φar (LINES cary;) jak vidφte v nßsledujφcφm v²pisu
class CMfcDoc : public CDocument
{
protected: // create from serialization only
CMfcDoc();
DECLARE_DYNCREATE(CMfcDoc)
// Attributes
public:
LINES cary;
........
........
V p°edchßzejφcφm k≤du je pßr v∞cφ, kterΘ by asi cht∞ly vysv∞tlit. HlaviΦkov² soubor afxtempl.h obsahuje deklaraci a definici Üablon CArray, CList a CMap. Musφme jej vlo₧it, proto₧e pou₧φvßme CArray.
Definice struktury by m∞la b²t jasnß a₧ na typ pou₧it²ch prvk∙. Je to CPoint, co₧ je MFC t°φda, kterß zapouzd°uje strukturu POINT, kterß se pou₧φvß na urΦovßnφ bod∙. Tak₧e struktura vlastn∞ obsahuje dva body, zaΦßtek a konec ·seΦky.
V °ßdku s definovßnφm dynamickΘho pole Φar (typedef CArray......) je CArray Üablona t°φdy, kterß se chovß jako dynamickΘ pole. Pokud neznßte Üablony p°eΦt∞te si Φlßnek èablony v C++ na serveru www.eternal.cz
A nakonec jsme p°idali prom∞nnou do t°φdy dokumentu. Ta je generovanß AppWizardem a m∞li by jste dokßzat pochopit jejφ strukutru, i kdy₧ neznßte MFC. Samoz°ejm∞ je tam pom∞rn∞ dost v∞cφ specifick²ch pro MFC a to Φemu nerozumφte by jste nem∞li m∞nit, proto₧e i zdßnlivΘ komentß°e jsou v MFC d∙le₧itΘ a pokud by jste je odstranili, tak by v nejlepÜφm p°φpad∞ nefungovali n∞kterΘ nßstroje Visual Studia (hlavn∞ ClassWizard).
Tφm mßme p°ipravenou pam∞¥ pro uklßdßnφ jednotliv²ch ·seΦek nßmi kreslen²ch Φar a m∙₧eme se pustit do programovßnφ vlastnφho kreslenφ.
Nejd°φve p°idßme do t°φdy pohledu (CMfcView) dv∞ pomocnΘ prom∞nnΘ "BOOL blbdown", kterß bube uklßdat stav levΘho tlaΦφtka myÜi (zda je stisknuto nebo ne) a "CPoint ptPrev", kterß bude uklßdat p°edchozφ polohu myÜi.
class CMfcView : public CView
{
protected: // create from serialization only
CMfcView();
DECLARE_DYNCREATE(CMfcView)
// Attributes
public:
CMfcDoc* GetDocument();
private:
BOOL blbdown;
CPoint ptPrev;
....
....
Te∩ jeÜt∞ v konstruktoru nastavφme prom∞nnou blbdown na FALSE;
CMfcView::CMfcView()
{
// TODO: add construction code here
blbdown = FALSE;
}
Te∩ musφme odchytit zprßvy od myÜi a to o stisknutφ (WM_LBUTTONDOWN), uvoln∞nφ (WM_LBUTTONUP) levΘho tlaΦφtka myÜi a o jejφm pohybu (WM_MOUSEMOVE). To provedeme pomocφ ClassWizardu. ClassWizard je k tomu p°φmo urΦen a nejen k tomu, ale o jeho dalÜφm vyu₧itφ napφÜi a₧ se dostaneme k dialog∙m.
Zprßvy lze zpracovßvat i jako v API v jednΘ funci (procedu°e okna). V MFC je to virtußlnφ Φlenskß funkce WindowProc t°φdy pohledu (jakΘhokoli okna). Postup ukß₧i na konci tohoto dφlu.
Tak₧e zpßtky k odchytßvßnφ zprßv pomocφ ClassWizardu. Spustφte jej pomocφ menu View - ClassWizard. (Nebo klßvesovΘ zkratky CTRL+W.) V rozbalovacφm seznamu Class name vyberte t°φdu pohledu (CMfcView). PotΘ v seznamu Object IDs vyberte op∞t jmΘno t°φdy pohledu. (DalÜφ polo₧ky v tomto seznamu jsou identifikßtory polo₧ek menu a panelu nßstroj∙, kterΘ umo₧≥ujφ odchytit jejich stisk, ale o tom pozd∞ji.) Potom seznam Messages obsahuje seznam virtußlnφch funkcφ a zprßv se kter²mi umφ ClassWizard pracovat. Pokud se vßm n∞kdy stane, ₧e zde n∞jakou zprßvu nenajdete, tak se nelekejte, ₧e takovß zprßva neexistuje. ClassWizard neumφ zdaleka vÜechny zprßvy, jako dobr² p°φklad m∙₧e slou₧it zprßva WM_SYSCOMMAND, kterou zde nenajdete. Zprßvu namapujete tak, ₧e ji najdete v seznamu Messages, vyberete ji a stisknete tlaΦφtko Add Function. Potom jmΘno tΘto zprßvy v poli Messages bude zobrazeno tuΦn∞ a do seznamu Member functions se vßm p°idß jmΘno funkce, kterß se provede po obdr₧enφ tΘto zprßvy. Po zav°enφ ClassWizardu stiskem OK se vßm tyto funkce p°idajφ i do t°φdy pro kterou jste tyto zprßvy odchytßvali (t°idy vybranΘ v rozbalovacφm seznamu Class Name). Pokud budete chtφt zruÜit odchytßvßnφ n∞jakΘ zprßvy, staΦφ ji najφt v seznamu Messages a stisknout tlaΦφtko Delete Function. To sma₧e funkci z deklarace t°φdy, odstranφ zprßvu z mapy zprßv, ale definici funkce z cpp souboru musφte odstranit ruΦn∞.
Na nßsledujφcφm obrßzku vidφte stav ClassWizardu po namapovßnφ zprßv WM_LBUTTONDOWN a WM_LBUTTONUP a p°ed mapovßnφm zprßvy WM_MOUSEMOVE.
Pokud jste sprßvn∞ namapovali tyto t°i zprßvy m∞li by jste vid∞t odpovφdajφcφ funkce v okn∞ Workspace. Kdy₧ na n∞kterou z nich poklepete, tak se vßm v editoru otev°e jejφ zdrojov² k≤d jak m∙₧ete vid∞t na nßsledujφcφm obrßzku.
Parametry tΘto funkce jsou parametry zprßvy p°evedenΘ z WPARAM a LPARAM na odpovφdajφcφ typy. U t∞chto funkcφ (zprßv) majφ parametry v²znam: nFlags - p°φznaky, nap°. zda je stisknuto CTRL atd. Druh² parametr point je bod ve kterΘm byla myÜ, kdy₧ byla zprßva zaslßna. Bod je udßn v pixelech od levΘho hornφho rohu okna, kladn² sm∞r os je doprava a dol∙.
Tyto funkce upravφme takto:
Funkce OnLButtonDown se volß kdy₧ v okn∞ stiskneme levΘ tlaΦφtko myÜi (zprßva WM_LBUTTONDOWN).
void CMfcView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// Nastavφme pomocnou prom∞nnou blbdown na TRUE -> je stisknuto levΘ tlaΦφtko myÜi.
blbdown=TRUE;
// Pomocnou prom∞nnou ptPrev nastavφme na pozici kurzoru v okam₧iku kdy bylo tlaΦφtko stisknuto.
ptPrev = point;
/*
Nakonec je ponechßno volßnφ funkce OnLButtonDown zßkladnφ t°φdy.
Tφm zajistφme, ₧e pokud mß zßkladnφ t°φda naprogramovanou n∞jakou obsluhu
tΘto udßlosti (stisku levΘho tlaΦφtky myÜi), tato obsluha se provede a tφm se
zachovß funkΦnost i z p°edka naÜφ t°φdy.
*/
CView::OnLButtonDown(nFlags, point);
}
Funkce OnLButtonUp se volß kdy₧ v okn∞ uvolnφme levΘ tlaΦφtko myÜi (zprßva WM_LBUTTONUP).
void CMfcView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// Pomocnou prom∞nnou blbdown nastavφme na FALSE -> levΘ tlaΦφtko nenφ stisknuto.
blbdown = FALSE;
CView::OnLButtonUp(nFlags, point);
}
Funkce OnMouseMove se volß kdy₧ v okn∞ pohneme myÜφ (zprßva WM_MOUSEMOVE).
void CMfcView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
Nejd°φve si nadefinujeme t°i prom∞nnΘ. line je Φßra kterou potΘ p°idßme do naÜeho pole v dokumentu. pDoc je ukazatel na nßÜ dokument. Tento ukazatel zφskßte pomocφ funkce pohledu GetDocument(). dc je instance t°φdy CClientDC jejφmu₧ konstruktoru jsme p°edali jako parametr ukazatel na okno ve kterΘm pracujeme. T°φda CClientDC je MFC t°φda, kterß je potomkem jinΘ MFC t°φdy CDC. T°φda CDC zapouzd°uje HDC (kontext za°φzenφ) z API a pou₧φvß se (bu∩ p°φmo ona nebo jejφ potomci) pro jakΘkoli kreslenφ do okna. Pokud trochu znßte API tak jist∞ vφte, ₧e ne₧ se zaΦne kreslit do okna, m∞ly by se provΘst n∞jakΘ p°φpravy. V MFC za vßs tyto p°φpravy provede tato t°φda (v konstruktoru) a starß se i o uvoln∞nφ kontextu za°φzenφ HDC (co₧ je jejφ Φlenskß prom∞nnß) po skonΦenφ kreslenφ (v destruktoru). Tak₧e pokud chcete kreslit do kterΘhokoli okna vaÜφ aplikace, staΦφ vytvo°it instanci t°φdy CClientDC jejφmu₧ konstruktoru p°edßte ukazatel na toto okno.
LINE line;
CMfcDoc* pDoc = GetDocument();
CClientDC dc(this);
Pokud je stisknuto levΘ tlaΦφtko myÜi, naplnφme strukutru Φßry p°edchßzejφcφ a souΦasnou polohou myÜi, nastavφme p°edchßzejφcφ polohu myÜi na souΦasnou a p°idßme Φßru do dynamickΘho pole v dokumentu. Potom pomocφ funkcφ MoveTo a LineTo vykreslφme Φßru od p°edchßzejφcφ polohy myÜi k souΦasnΘ. Funkce MoveTo nastavφ zaΦßtek Φßry a funkce LineTo nakreslφ Φßru od bodu urΦenΘho p°edchozφm volßnφm MoveTo (nebo LineTo).
if (blbdown)
{
line.first = ptPrev;
line.second = point;
ptPrev = point;
pDoc->cary.Add(line);
dc.MoveTo(line.first);
dc.LineTo(line.second);
}
CView::OnMouseMove(nFlags, point);
}
Pokud by jste te∩ aplikaci p°elo₧ili a spustili tak zjistφte, ₧e u₧ vßm umo₧≥uje kreslenφ. Ale pokud okno minimalizujete, nebo p°ekryjete jin²m, tak zjistφte, ₧e vßm nakreslenΘ Φßry zmizely. To je zp∙sobeno tφm, ₧e okno poka₧dΘ, kdy₧ je p°ekryto, pot°ebuje p°ekreslit. O to se musφte takΘ postarat sami. Poka₧dΘ, kdy₧ okno pot°ebuje p°ekreslit, dostane zprßvu WM_PAINT. Tentokrßt ji vÜak nebudeme odchytßvat pomocφ ClassWizardu (ikdy₧ to takΘ m∙₧ete ud∞lat), proto₧e v zßkladnφ t°φd∞ pohledu (CVIew) je napsßna tak, ₧e volß virtußlnφ funkci OnDraw a jako parametr jφ p°edßvß ukazatel na t°φdu CDC pomocφ kterΘho m∙₧ete do tohoto okna kreslit. To znamenß, ₧e musφme upravit funkci OnDraw tak, aby vykreslovala vÜechny nßmi nakreslenΘ Φßry. AppWizard poΦφtß s tφm, ₧e budeme chtφt n∞co kreslit, tak₧e funkce OnDraw je ji₧ p°ipravena a staΦφ ji pouze upravit tak, aby kreslila to co chceme.
Najd∞te si v t°φd∞ pohledu (CMfcView) funkci OnDraw a upravte ji takto:
void CMfcView::OnDraw(CDC* pDC)
{
CMfcDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
Postupn∞ projdeme vÜechny Φßry v dokumentu a vykreslφme je. PoΦet Φar (prvk∙ pole) zφskßme pomocφ ΦlenskΘ funkce GetSize() Üablony t°φdy CArray. Mo₧nß vßs zarazilo, ₧e k prvk∙m t°φdy p°istupuji pomocφ hranat²ch zßvorek jakoby to bylo obyΦejnΘ pole. To je umo₧n∞no dφky p°etφ₧enφ operßtoru [ ] (hranatΘ zßvorky). Mφsto toho by jste takΘ mohli pou₧φt Φlenskou funkci Üablony t°φdy CArray GetAt(), kterß vßm takΘ poskytne p°φstup k jednotliv²m prvk∙m tΘto Üablony t°φdy dynamickΘho pole, ale oprßtor [ ] umo₧≥uje i p°i°azovat hodnoty do tohoto pole ji₧ existujφcφm prvk∙m. Kreslenφ by jste u₧ takΘ m∞li chßpat. Je to totΘ₧ jako ve funkci OnMouseMove, a₧ na to, ₧e nepou₧φvßme t°φdu CClientDC, ale p°φmo t°φdu CDC (ktarß je jejφm p°edkem) a nemusφme si jejφ instanci vytvß°et sami, ale je nßm p°edßna jako parametr funkce OnDraw.
int i;
for(i=0; i<pDoc->cary.GetSize(); i++)
{
pDC->MoveTo(pDoc->cary[i].first);
pDC->LineTo(pDoc->cary[i].second);
}
}
Te∩ u₧ vßm obsah okna nezmizφ ani kdy₧ jej p°ekryjete jin²m oknem.
Pokud jste zkouÜeli co tato aplikace dovede, jist∞ jste si vÜimli, ₧e nefungujφ povely panelu nßstroj∙ (nebo menu) pro otevφrßnφ, uklßdßnφ a vytvo°enφ novΘho dokumentu. Jak je zprovoznit se dozvφte v p°φÜtφm dφle, kter² se bude v∞novat t°φd∞ dokumentu.
JeÜt∞ jsem vßm slφbil ukßzat jak odchytßvat vÜechny zprßvy v jedinΘ funkci namφsto pomocφ ClassWizardu a mapy zprßv. (Tyto dva zp∙soby lze libovoln∞ kombinovat, jen nemapujte jednu zprßvu jak pomocφ ClassWizardu do mapy zprßv tak do tΘto funkce.) Ukß₧eme si to na odchycenφ zprßvy WM_SYSCOMMAND a tφm, ₧e ji v p°φpad∞, ₧e jejφ parametr wParam bude mφt hodnotu SC_MINIMIZE nezpracujeme (nebo lΘpe °eΦeno neprovedeme nic), zakß₧eme minimalizaci naÜφ aplikace.
Tak₧e zaΦneme tφm, ₧e si pomocφ ClassWizardu p°etφ₧φme virtußlnφ funkci WindowProc, kterß v MFC nahrazuje proceduru okna. Musφme ji p°etφ₧it pro rßmcovΘ okno (CMainFrame), proto₧e budeme odchytßvat zprßvu, kterß mß vliv na zobrazenφ celΘho okna a ne jen pohledu. Postup je stejn² jako u odchytßvßnφ zprßv pomocφ ClassWizardu jen tam kde jsme vybφrali t°φdu pohledu (CMfcView), tentokrßt vybereme t°φdu hlavnφho rßmcovΘho okna (CMainFrame) a mφsto zprßvy vybereme virtußlnφ funkci WindowProc jak je vid∞t na nßsledujφcφm obrßzku.
A potΘ tuto funkci upravφme jako by to byla procedura okna v API.
LRESULT CMainFrame::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
switch (message)
{
case WM_SYSCOMMAND:
if (wParam==SC_MINIMIZE) return 0;
break;
};
return CFrameWnd::WindowProc(message, wParam, lParam);
}
A to je v tΘto lekci vÜe.
Zdrojov² k≤d si m∙₧ete stßhnout zde.