IV.
GDI en device context
GDI is een afkorting van windows Graphic Device Interface. Het bevat klassen en structuren om grafische vormen, teksten lijnkleur, klijndikte, enz. te definiëren. De device context is de mogelijk om te tekenen op scherm of printer, zonder de eigenschappen van dat apparaat te kennen.
A. Grafische vormen Er zijn vele grafische vormen mogelijk. Een klein overzicht hebben we samengebracht in onderstaande oefening Oef04a Oef04a: grafische vormen Maak een leeg project aan. Alleen de OnPaint() methode is nodig, vermits we hier alle tekenopdrachten plaatsen. De andere bestanden zijn dus weer hetzelfde als voorheen.
En hier is de code: CMainFrame.h: /********************************** Oef04a.h - CMainFrame.h ***********************************/ #include
class CMainFrame: public CFrameWnd { private:
Visual C++ en MFC
ir. C. Daniels
28
public: CMainFrame(); //constructor //afhandeling van de window-berichten afx_msg void OnPaint(); //handelt WM_PAINT af DECLARE_MESSAGE_MAP(); };
CMainFrame.cpp: /***************************************** Oef04a - CMainFrame.cpp ******************************************/ #include "CMainFrame.h" BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd) //het bericht //roept deze functie aan ON_WM_PAINT() //void OnPaint() END_MESSAGE_MAP() CMainFrame::CMainFrame() //constructor { Create(NULL, "Oef04a Grafische vormen", WS_OVERLAPPEDWINDOW, CRect(0,0,400,500) ); CenterWindow(); } void CMainFrame::OnPaint() { CPaintDC dc(this); dc.SelectStockObject(NULL_BRUSH); /********** lijn **********/ dc.MoveTo(10,50); dc.LineTo(125,25); dc.TextOut(10,20,"Line"); /****************** open structuren *******************/ //Polylijn *********************************** CPoint pl[]={ CPoint(220,80), CPoint(270,120), CPoint(320,50), CPoint(370,100),}; dc.Polyline(pl,4); dc.TextOut(220,50,"Polyline"); //PolyBezier ******************************** //aantal punten moet 3*n+1 zijn // n = aantal 'splines'
Visual C++ en MFC
ir. C. Daniels
29
CPoint pb[]={
CPoint(140,180), CPoint(190,220), CPoint(240,120), CPoint(290,200), CPoint(330,280), CPoint(370,120), CPoint(300,200),}; dc.PolyBezier(pb,7); //dc.Polyline(pb,7); dc.TextOut(200,200,"PolyBezier"); /************************* gesloten structuren *************************/ //rechthoeken ******************************** CRect r(10,60,190,150); //methode 1 dc.Rectangle(r); dc.Rectangle(30,90,100,250); dc.TextOut(15,70,"Rectangle");
//methode 2
//afgeronde rechthoek ************************ CRect r1(10,300,140,450); CPoint p(30,30); dc.RoundRect(r1,p); //dc.Rectangle(r1); dc.TextOut(30,370,"RoundRect"); //veelhoek ************************************* CPoint pp[]= { CPoint(200,250), CPoint(250,230), CPoint(300,300), CPoint(290,350), CPoint(210,320) }; dc.Polygon(pp,5); dc.TextOut(170,330,"Polygon"); //Arc, Chord, Pie ******************************* CRect r2(CPoint(200,370),CPoint(350,450)); //omsluitende rhoek //dc.Rectangle (r2); CPoint cpt = r2.CenterPoint(); //centrum rechthoek CPoint startpt = cpt + CPoint(2,-16); //beginpunt boog dc.MoveTo(cpt);dc.LineTo(startpt); CPoint eindpt = cpt + CPoint(-20,10); //eindpunt boog dc.MoveTo(cpt);dc.LineTo(eindpt); //kies een van de 3 onderstaande: dc.Arc(r2,startpt,eindpt); //dc.Chord(r2,startpt,eindpt); //dc.Pie(r2,startpt,eindpt); dc.TextOut(280,420,"Arc Chord Pie"); }
Test het programma uit!
Visual C++ en MFC
ir. C. Daniels
30
Als u sommige commentaar (bij PolyBezier, of Arc bv.) kunt u beter zien, hoe de figuur wordt bepaald door de omsluitende rechthoeken of lijnen.
B. Lijnstijlen, dikte en kleuren Om het uiterlijk van een lijn aan te passen moeten we een CPen-object aanmaken. Daarvoor gebruiken we: a - de methode CreatePen(lijnstijl, dikte, kleur) b - een stockobject met SelectStockObject(object). Stockobjecten zijn een klein aantal voorgedefinieerde kleur- (en brush-) stijlen. Oef04b Lijnstijlen, kleuren en dikte Volgende oefening geeft enkele voorbeelden. Zoek aanvullende informatie op in de help!
Alleen de code van CMainFrame moet aangepast: CMainFrame.cpp: /***************************************** Oef04b - CMainFrame.cpp lijn: stijl, kleur, dikte ******************************************/ #include "CMainFrame.h" BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd) ON_WM_PAINT() //void OnPaint() END_MESSAGE_MAP() CMainFrame::CMainFrame() //constructor { Create(NULL,"Oef04b lijnen",WS_OVERLAPPEDWINDOW, CRect(0,0,270,200)); CenterWindow(); }
Visual C++ en MFC
ir. C. Daniels
31
void CMainFrame::OnPaint() { CPaintDC dc(this); // lijnen CPen aPen1; dc.SelectStockObject(BLACK_PEN); //zwarte lijn dc.MoveTo(10,140); dc.LineTo(250,0); //pen creatie in 2 stappen aPen1.CreatePen(PS_SOLID,20,RGB(255,0,0)); //dik rood dc.SelectObject(aPen1); dc.MoveTo(10,10); dc.LineTo(250,50); dc.SelectStockObject(WHITE_PEN); //stock-pen wit dc.MoveTo(10,10); dc.LineTo(250,50); //pen creatie in 1 stap CPen aPen2(PS_DOT,1,RGB(0,120,0)); dc.SelectObject(aPen2); dc.MoveTo(10,40); dc.LineTo(250,80);
//Groen puntjes
CPen aPen3; aPen3.CreatePen(PS_DASH,1,RGB(0,0,180)); dc.SelectObject(aPen3); dc.MoveTo(10,70); dc.LineTo(250,110); dc.SelectStockObject(BLACK_PEN); dc.MoveTo(10,150); dc.LineTo(250,10);}
//blauw streepjes
//zwarte lijn 2
C. Brush Een brush (Engels voor ‘borstel’) vult de achtergrond op van gesloten figuren. We kunnen zowel een kleur als een stijl kiezen. De stijl en kleur van de randlijn worden bepaald door een CPen-object. Oefening Oef04c: Brush In deze oefeningen vindt u enkele voorbeelden van de CBrush class terug. Alleen de code in CMainFrame.cpp gaan we weer aanpassen.
CMainframe.cpp:
Visual C++ en MFC
ir. C. Daniels
32
/******************************* Oef04c - CMainFrame.cpp Brush ********************************/ #include "CMainFrame.h" BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd) ON_WM_PAINT() //void OnPaint() END_MESSAGE_MAP()
CMainFrame::CMainFrame() //constructor { Create(NULL,"Oef04c Brush voorbeelden", WS_OVERLAPPEDWINDOW,CRect(0,0,270,290) CenterWindow(); }
);
void CMainFrame::OnPaint() { CPaintDC dc(this); CRect r1(10,10,110,60); CBrush aBr1; aBr1.CreateSolidBrush(RGB(220,0,180)); default pen dc.SelectObject(aBr1); dc.Rectangle(r1);
//paarse rechthoek en
r1 += CPoint(120,0); CPen aPen1; aPen1.CreatePen(PS_SOLID,5,RGB(180,230,0)); rand dc.SelectObject(aPen1); dc.Rectangle(r1);
//paars met dikke
r1 += CPoint(0,70); //HATCH met rode rand CPen aPen2; //pen - object voor de rand aPen2.CreatePen(PS_SOLID,10,RGB(255,0,0)); CBrush aBr2; //brush - object voor opvulling aBr2.CreateHatchBrush(HS_CROSS,RGB(0,220,50)); //gekruist dc.SelectObject(aBr2); dc.SelectObject(aPen2); dc.Rectangle(r1); r1 -= CPoint(120,0); //HATCH met blauwe rand CPen aPen3; //pen - object voor de rand aPen3.CreatePen(PS_SOLID,10,RGB(0,0,240)); CBrush aBr3; //brush - object voor opvulling aBr3.CreateHatchBrush(HS_FDIAGONAL ,RGB(200,200,0)); //gearceerd dc.SelectObject(aBr3); dc.SelectObject(aPen3); dc.Rectangle(r1); CPoint p[]={CPoint(50,150), CPoint(210,180), CPoint(30,220)};
Visual C++ en MFC
ir. C. Daniels
//veelhoek
33
CPen aPen4; //pen - object voor de rand aPen4.CreatePen(PS_SOLID,5,RGB(0,220,150)); CBrush aBr4; //brush - object voor opvulling aBr4.CreateHatchBrush(HS_VERTICAL, RGB(220,100,200)); //gearceerd dc.SelectObject(aBr4); dc.SelectObject(aPen4); dc.Polygon(p,3); }
D. Penstijlen PS_NULL en PS_INSIDEFRAME Als u in een figuur dikke randlijnen heeft, kunt u zelf bepalen, hoe deze randlijn getekend wil: ofwel op de rand, of echt ‘in’de figuur. Onderstaand voorbeeld maakt dit duidelijk. Oef04d: PS_NULL en PS_INSIDEFRAME Hieronder een voorbeeld dat ook dit aspect duidelijker maakt:
CMainFrame.cpp: /***************************************** Oef04d - CMainFrame.cpp PS_NULL PS_INSIDEFRAME ******************************************/ #include "CMainFrame.h" BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd) //het bericht //roept deze functie aan ON_WM_PAINT() //void OnPaint() END_MESSAGE_MAP() CMainFrame::CMainFrame() //constructor { Create(NULL, "Oef04d PS_NULL PS_INSIDEFRAME", WS_OVERLAPPEDWINDOW, CRect(0,0,350,280) ); CenterWindow(); }
Visual C++ en MFC
ir. C. Daniels
34
void CMainFrame::OnPaint() { CPaintDC dc(this); // default dc.SelectStockObject(LTGRAY_BRUSH); CRect r(20,20,160,100); dc.Rectangle(r); dc.TextOut(60,50,"Default"); CPen p1(PS_NULL,0,RGB(0,0,0)); r += CPoint(170,0); dc.SelectObject(p1); dc.Rectangle(r); dc.TextOut(230,50,"PS_NULL"); CPen p2(PS_SOLID,20,RGB(255,0,0)); r += CPoint(-170,100); dc.SelectObject(p2); dc.Rectangle(r); dc.TextOut(60,150,"PS_SOLID"); CPen p3(PS_INSIDEFRAME,20,RGB(255,0,0)); r += CPoint(170,0); dc.SelectObject(p3); dc.Rectangle(r); dc.TextOut(200,150,"PS_INSIDEFRAME"); }
E. Tekstkleuren Ook de tekstkleuren kunnen aangepast worden. De tekstkler met SetTextColor() en de achtergrondkleur van de tekst (niet van de client area!) met SetBkColor()
Visual C++ en MFC
ir. C. Daniels
35
Oef04e SetTextColor() en SetBkColor() En hieronder de code: CMAinFrame.cpp: /******************************* Oef04e - CMainFrame.cpp Tekstkleuren ********************************/ #include "CMainFrame.h" BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd) //het bericht //roept deze functie aan ON_WM_PAINT() //void OnPaint() END_MESSAGE_MAP() CMainFrame::CMainFrame() //constructor { Create(NULL, "Oef04e Tekstkleuren", WS_OVERLAPPEDWINDOW, CRect(0,0,400,250) ); CenterWindow(); }
void CMainFrame::OnPaint() { CPaintDC dc(this); CPen p(PS_NULL,0,RGB(0,0,0)); dc.SelectObject(p); dc.SelectStockObject(LTGRAY_BRUSH); dc.Rectangle(50,10,100,200); dc.TextOut(2,30,"AaBbWwIi 012345 - Default Font"); dc.SetTextColor(RGB(255,0,0)); dc.SetBkColor(RGB(180,255,180)); CFont* pFont=(CFont *)dc.SelectStockObject(SYSTEM_FONT); dc.TextOut(2,55,"AaBbWwIi 012345 - System Variable-pitch Font"); dc.SetTextColor(RGB(255,255,0)); dc.SetBkColor(RGB(80,80,255)); dc.SelectStockObject(SYSTEM_FIXED_FONT); dc.TextOut(2,80,"AaBbWwIi 012345 - System Fixed-pitch Font"); dc.SetTextColor(RGB(192,0,0)); dc.SetBkColor(RGB(192,255,192)); dc.SelectStockObject(ANSI_VAR_FONT); dc.TextOut(2,105,"AaBbWwIi 012345 - ANSI Variable-pitch Font");
dc.SetBkMode(TRANSPARENT); dc.SelectStockObject(ANSI_FIXED_FONT); dc.TextOut(2,130,"AaBbWwIi 012345 - ANSI Fixed-pitch TRANSPARENT");
Visual C++ en MFC
ir. C. Daniels
36
dc.SelectObject(pFont); dc.SetBkMode(OPAQUE); dc.SelectStockObject(SYSTEM_FIXED_FONT); dc.TextOut(2,155,"AaBbWwIi 012345 - OPAQUE"); }
F. Fonts gebruiken Een tekstfont is een vrij ingewikkeld object. De CreateFont() methode vraagt niet minder dan 14 parameters. Zoek deze eens op in de help.
Oef04f Oefening over letterfonts Nu passen wel zowel CMainFrame.h als CMainFrame.cpp aan: CMainFrame.h /********************************** Oef04f.h - CMainFrame.h Fonts ***********************************/ #include class CMainFrame: public CFrameWnd { private: CFont fontTimesNR; CFont fontArial; CFont fontComic; public: CMainFrame(); //constructor afx_msg void OnPaint(); DECLARE_MESSAGE_MAP(); };
Visual C++ en MFC
ir. C. Daniels
37
CMainFrame.cpp: /******************************* Oef04e - CMainFrame.cpp Tekstkleuren ********************************/ #include "CMainFrame.h" BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd) //het bericht //roept deze functie aan ON_WM_PAINT() //void OnPaint() END_MESSAGE_MAP() CMainFrame::CMainFrame() //constructor { Create(NULL, "Oef04f Fonts", WS_OVERLAPPEDWINDOW, CRect(0,0,350,250) ); CenterWindow(); // Verschillende manier om fonts te definiëren // Manier 1 (eenvoudig) fontTimesNR.CreatePointFont(220,"Times New Roman"); // manier 2 (velden in een LOGFONT specifiëren) // een LOGFONT heeft 14 velden! LOGFONT lfArial; //Zet alle velden op nul om te straten ::ZeroMemory (&lfArial,sizeof(lfArial)); lfArial.lfHeight=250; //puntgrootte =25.0 lfArial.lfWeight=FW_BOLD; //=700 FW_MEDIUM=500 lfArial.lfItalic=TRUE; lfArial.lfUnderline=TRUE; lfArial.lfEscapement=-3500; //draaien over -35,00 graden ::lstrcpy(lfArial.lfFaceName,"Arial"); //vgl. strcpy) fontArial.CreatePointFontIndirect(&lfArial); //we kunen de grootte ook in pixels geven ipv in punten //met CreateFontIndirect LOGFONT lfComic; ::ZeroMemory (&lfComic,sizeof(lfComic)); lfComic.lfHeight=30; //30 pixels hoog lfComic.lfWeight=FW_LIGHT; lfComic.lfItalic=FALSE; lfComic.lfUnderline=FALSE; lfComic.lfEscapement=-200; //draaien over 40,0 graden ::lstrcpy(lfComic.lfFaceName,"Comic Sans MS"); fontComic.CreateFontIndirect(&lfComic); } void CMainFrame::OnPaint() { CPaintDC dc(this);
Visual C++ en MFC
ir. C. Daniels
38
dc.SetBkMode(TRANSPARENT); //manier 1 CFont* pFontDefault = dc.SelectObject(&fontTimesNR); dc.TextOut(5,5,"Times New Roman"); // bereken tekstgrootte CSize size=dc.GetTextExtent("Times New Roman"); CRect r = CRect(CPoint(5,5), CPoint (5+size.cx,5+size.cy)); dc.SelectStockObject(NULL_BRUSH); dc.Rectangle(r); //manier 2 met LOGFONT dc.SelectObject(&fontArial); dc.TextOut(12,50,"Arial"); //grootte in pixels dc.SelectObject(&fontComic); dc.TextOut(25,100,"Comic Sans MS"); dc.SelectObject(pFontDefault); dc.TextOut(100,90,"Default font!"); //Informatie over het font opvragen TEXTMETRIC tm; //heeft 20 member variabelen... dc.GetTextMetrics(&tm); CString s; s.Format("Hoogte: %d, Gem.letterbreedte: %d", tm.tmHeight,tm.tmAveCharWidth); dc.TextOut(100,110,s); }
G. Een windowklasse registreren Misschien heeft u zich al afgevraagd waarom we nog niet de achtergrondkleur van de cleint area veranderd hebben, maar wel bijna alle andere kleuren. Dit komt omdat we gebruik maken van de default-‘look’van een window-applicatie, aangeduid door de parameter NULL in de Create-methode vanhet venster: Create(NULL, “Oef04.....”,WS_OVERLAPPEDWINDOW,....);
Als we zelf onze eigen layout willen bepalen, moeten we een windowklasse registreren, zoals dat heet. Eigenlijk is dat geen klasse maar een structuur, maar wordt altijd ‘windowclass’ genoemd. Pas de CMainFrame-constructor aan als volgt, om de achtergrondkleur te wijzigen: CMainFrame::CMainFrame() //constructor { //een window'klasse' registreren CString MijnWinClass; //naam van de kalsse MijnWinClass =AfxRegisterWndClass( CS_DBLCLKS | CS_HREDRAW, AfxGetApp()->LoadStandardCursor(IDC_ARROW), ::CreateSolidBrush(RGB(100,255,100)),
Visual C++ en MFC
ir. C. Daniels
39
AfxGetApp()->LoadStandardIcon(IDI_APPLICATION) ); //Creëer nu het window object gebaseerd op 'onze' klasse Create( MijnWinClass, "Oef04f Fonts", WS_OVERLAPPEDWINDOW, CRect(0,0,350,250) );
...enz. En nu heeft ons programma een mooie lichtgroene achtergrondkleur:
H. Vijf manieren om een DC te creëren Voor we kunnen tekenen, moeten we eerst de device context instantiëren. Er bestaan hiervoor vijf manieren, naargelang de plaats waar je wil tekenen. In OnPiant() In de functie OnPaint(). Deze verwijdert het WM_PAINT bericht uit de berichtenstroom. Methode 1 void CMainFrame::OnPaint() { CPaintDC dc(this); ...// doe al het tekenwerk... }
Methode 2 void CMainFrame::OnPaint() { PAINTSTRUCT ps; CDC* pDC = BeginPaint(&ps); //ps bevat belangrijke info over //invalid – area // doe al het tekenwerk... pDC->TextOut(1,2,”Hallo”);
Visual C++ en MFC
ir. C. Daniels
40
//of in een zelfgemaakte functie Teken() : Tekenen(pDC); //geeft pointer door EndPaint(&ps); //niet vergeten }
In alle ander functies van CMAinFrame Hier zijn ook twee mogelijkheden: Methode 3 void CMainFrame::AndereFunctie() { CClientDC dc(thsi); dc.TextOut(1,2,“...... }
Methode 4 void CMainFrame::AndereFunctie() { CDC* pDC = GetDC(); pDC->dc.TextOut(1,2,“...... ReleaseDC(pDC); }
In een andere klasse als CMainFrame Stel dat we gegevens willen uivoeren naar de client area vanuit de klasse CAndere met de methode Display(): void CAndere::Display() { CWnd* pWnd = AfxGetMainWnd(); CDC* pDC = pWnd->GetDC(); pDC->Textout(1,2,”Hallo”); pWnd->ReleaseDC(pDC); }
Visual C++ en MFC
ir. C. Daniels
41