Anwendung der GDI

Einführung

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. 

Einfügen der Objekte

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.

Einbinden der Objekte in den Gerätekontext und ausführen der Zeichenoperationen

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.

Zusammenfassung

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)

Die nächsten Lektionen

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.

Zurück Nach oben Weiter