Hier soll im folgenden das bereits bestehende Rahmenprogramm um einige Nachrichten und um einige Ausgaben erweitert werden. Zunächst sollte der bereits geschriebene Quelltext wieder hervorgeholt werden und schon mal um die 'WM_PAINT'-Nachricht erweitert werden. Auf dem Bildschirm soll eine grüne Ellipse auf einem roten Rechteck gezeichnet werden. Außerdem soll das Rechteck zum Schluss noch von zwei roten Linien (Liniendicke: 5) durchkreuzt werden. Dieses Gebilde soll bei jedem Neuzeichnen des Fensterinhalts aktualisiert werden. Da 'WinMain(...)' hier nicht verändert wird, beschränken wir uns auf die Fensterprozedur und fügen zunächst die 'WM_PAINT'-Nachricht ein und ergänzen die Fensterprozedur um einige Variablen:
//Windows-API #include <windows.h> //Deklaration der Fensterprozedur LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //WinMain int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { /* --- Programmablauf --- ... */ //Programmende return msg.wParam; } //Implementierung der Fensterprozedur LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMessage, WPARAM wParam, LPARAM lParam) { //Variablen für WM_PAINT-Zweig PAINTSTRUCT ps; //Grafikobjekte HDC hDC; //Gerätekontext HBRUSH hOldBrush, hRedBrush, hGreenBrush; //Pinsel HPEN hOldPen, hRedPen, hGreenPen; //Stifte switch (uiMessage) { case WM_PAINT: return 0; case WM_CLOSE: DestroyWindow(hWnd); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; default: break; } return DefWindowProc(hWnd, uiMessage, wParam, lParam); }
Im folgenden kann man sich dann ausschließlich auf den 'WM_PAINT'-Zweig beschränken.
Zunächst sollen einmal die Stifte und Pinsel erstellt und der Zeichenvorgang eingeleitet werden. Zudem sollen erstellte Objekte am Ende wieder freigegeben und der Zeichenvorgang beendet werden:
case WM_PAINT: //Erstellen der Pinsel hRedBrush = CreateSolidBrush(RGB(255, 0, 0)); hGreenBrush = CreateSolidBrush(RGB(0, 255, 0)); //Erstellen der Stifte hRedPen = CreatePen(PS_SOLID, 0, RGB(255, 0, 0)); hGreenPen = CreatePen(PS_SOLID, 0, RGB(0, 255, 0)); //Beginn der Zeichenoperation hDC = BeginPaint(hWnd, &ps); //Ende der Zeichenoperation EndPaint(hWnd, &ps); //Freigeben der Stifte DeleteObject(hGreenPen); hGreenPen = NULL; DeleteObject(hRedPen); hRedPen = NULL; //Freigeben der Pinsel DeleteObject(hGreenBrush); hGreenBrush = NULL; DeleteObject(hRedBrush); hRedBrush = NULL; return 0;
Nun sind bereits alle Vorbereitungen getroffen worden um die gewünschten Zeichenoperationen, durchführen zu können.
Nun sollten die entsprechenden Aufrufe von 'SelectObject', 'DeleteObject', 'Ellipse', etc. eingefügt werden. Diesmal sollten die Objekte allerdings den gesamten Anwendungsbereich einnehmen. Um dies zu realisieren benötigen wir zunächst einmal die entsprechenden Koordinaten des Anwendungsbereiches. Bekannt ist lediglich die die obere, linke Ecke (0|0). Zu diesem Zweck gibt es die Funktion 'GetClientRect(hWnd, prc)'. Als Parameter erwartet sie neben dem Fenster einen Zeiger auf eine 'RECT'-Struktur, die ganz einfach definiert ist und welche keiner weiteren Erläuterung bedarf :
typedef struct _RECT
{
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT;
Um das entsprechende Rechteck zu bekommen geht man nun wie folgt vor:
RECT rc; GetClientRect(hWnd, &rc);
Die Variable 'rc' sollte dazu als lokale Variable, am besten nach der 'ps'-Variable, innerhalb der Fensterprozedur gespeichert werden.
Dann schließlich kann der 'WM_PAINT'-Zweig um die entsprechenden Funktionsaufrufe erweitert werden:
case WM_PAINT: //Bestimmen der Koordinaten GetClientRect(hWnd, &rc); //Erstellen der Pinsel hRedBrush = CreateSolidBrush(RGB(255, 0, 0)); hGreenBrush = CreateSolidBrush(RGB(0, 255, 0)); //Erstellen der Stifte hRedPen = CreatePen(PS_SOLID, 5, RGB(255, 0, 0)); hGreenPen = CreatePen(PS_SOLID, 5, RGB(0, 255, 0)); //Beginn der Zeichenoperation hDC = BeginPaint(hWnd, &ps); //rotes Rechteck hOldBrush = (HBRUSH)SelectObject(hDC, hRedBrush); //roten Pinsel einsetzen hOldPen = (HPEN)SelectObject(hDC, hRedPen); //roten Stift einsetzen Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom); //rotes Rechteck //grüne Ellipse SelectObject(hDC, hGreenBrush); //grünen Pinsel einsetzen SelectObject(hDC, hGreenPen); //grünen Stift einsetzen Ellipse(hDC, rc.left, rc.top, rc.right, rc.bottom); //grüne Ellipse //rote Linien SelectObject(hDC, hRedBrush); //roten Pinsel einsetzen SelectObject(hDC, hRedPen); //roten Stift einsetzen MoveToEx(hDC, rc.left, rc.top, NULL); LineTo(hDC, rc.right, rc.bottom); //1. Linie MoveToEx(hDC, rc.right, rc.top, NULL); LineTo(hDC, rc.left, rc.bottom); //2. Linie //Zurücksetzen der Objekte SelectObject(hDC, hOldPen); hOldPen = NULL; //Stift zurücksetzen SelectObject(hDC, hOldBrush); hOldBrush = NULL; //Pinsel zurücksetzen //Ende der Zeichenoperation EndPaint(hWnd, &ps); //Freigeben der Stifte DeleteObject(hGreenPen); hGreenPen = NULL; DeleteObject(hRedPen); hRedPen = NULL; //Freigeben der Pinsel DeleteObject(hGreenBrush); hGreenBrush = NULL; DeleteObject(hRedBrush); hRedBrush = NULL; return 0;
Wenn alles soweit geklappt hat, sollte es zu folgendem Fenster kommen:
Wenn sie das Fenster maximieren und wiederherstellen oder auch sonst die Größe ändern, passt sich der Fensterinhalt entsprechend an. Die Grafikobjekte füllen immer den gesamten Anwendungsbereich aus. Wenn Sie mal bei der Deklaration des Fensterklassenstils in der 'WinMain'-Funktion die Werte in 0 abändern (anstatt 'CS_HREDRAW | CS_VREDRAW'), dann bewirkt eine Größenänderung des Fensters kein Neuzeichnen, da definitionsgemäß keine 'WM_PAINT'-Nachricht mehr gesendet wird. Durch diese Veränderung entstehen ungewollte Nebeneffekte.
Hier wurde die GDI erstmalig angewandt und darüber hinaus eine weitere Funktion ('GetClientRect') eingeführt. Nun sollten die Grundlagen der GDI bekannt sein, da die folgenden Lektionen darauf aufbauen werden.
Hier noch einmal der komplette Quelltext dieses Programms:
Winprog_v1_1_src.exe (Quelltext, gepackt)
In den nächsten Lektionen wird es wohl primär darum gehen ein bisschen mehr Benutzeraktivität in das Projekt einfließen zu lassen. Außerdem soll die klassische Windowsprogrammierung langsam auf die Windowsprogrammierung mit der ATL verlagert werden.