In diesem Tutorial zeige ich dir, wie man schlichten Text auf den Anwendungsbereich zeichnet.
Den Code aus dem letzten Tutorial übernehme ich mit ein paar Änderungen
in dieses Tutorial. Die WndProc
Funktion wird
selbstverständlich erweitert. Den bekannten Code kommentiere ich hier nicht mehr.
Windows teilt uns alle Ereignisse über Nachrichten mit. Da ist auch der Zeichenvorgang
keine Ausnahme. Windows bestimmt also, wann wir zeichnen sollen. Es gibt viele Gründe,
warum der Anwendungsbereich neu gezeichnet werden soll, also ungültig geworden
ist. Um nur ein paar Beispiele zu nennen: Fenster vergrößert, war von einem
anderen Fenster bedeckt oder das Fenster wird gescrollt. Natürlich können wir auch
selber entscheiden, dass wir zeichnen möchten. In dem Zeichenvorgang muss der
Anwendungsbereich wieder als gültig erklärt werden, sonst wird Windows
dein Programm mit WM_PAINT
Nachrichten bombadieren, da es meint, dass dieser
Bereich nochmal neu gezeichnet werden muss.
#define STRICT #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); const char szAppName[] = "Textausgabe im Anwendungsbereich"; int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MSG msg; HWND hWnd; WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wc.lpszClassName = szAppName; wc.lpszMenuName = NULL; RegisterClass(&wc); hWnd = CreateWindow( szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, iCmdShow); UpdateWindow(hWnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) {
Diesmal bearbeiten wir außer der WM_DESTROY
Nachricht noch die
WM_PAINT
Nachricht (im MSDN: WM_PAINT
). Wenn wir diese Nachricht hier nicht
bearbeiten würden, würde DefWindowProc
das übernehmen und nichts
weiter tun, als den ungültigen Bereich wieder als gültig erklären.
case WM_PAINT: {
Damit man in Windows auf dem Bildschirm zeichnen darf, muss man sich erst dafür
"anmelden". Man bekommt dann einen Zeichenbereich zugewiesen. Alle Informationen,
die mit dieser "Anmeldung" zusammen hengen werden in der PAINTSTRUCT
Struktur (im MSDN: PAINTSTRUCT
) gespeichert. Danach deklarieren wir
noch eine Variable vom Typ HDC
, welche den Handle auf unseren Zeichenbereich
speichern soll. hDC
heißt Handle [to a] Device Context, was soviel
bedeutet wie Handle auf einen "Geräte Kontext". hDC
ist eigentlich
redundant (überflüssig), da eine Membervariable der PAINTSTRUCT
Struktur genau diesen Handle speichert. Jedoch versucht man so den Code lesbarer zu
machen.
PAINTSTRUCT ps; HDC hDC;
Die folgende Zeile speichert den Text, den wir ausgeben wollen, in dem Array
szText
.
const char szText[] = "Hallo, dies ist der Text.";
Mit der BeginPaint
Funktion (im MSDN: BeginPaint
) teilen wir Windows mit, dass wir in
unseren Anwendungsbereich zeichnen möchten. Der erste Parameter der
BeginPaint
Funktion legt das Fenster fest, in dem wir zeichnen wollen. Der zweite
Parameter ist ein Zeiger auf unsere PAINTSTRUCT
Funktion, in der Windows die
Daten bezüglich des Zeichenbereiches speichern wird. Der Rückgabewert ist der
Handle auf den Zeichenbereich. Die BeginPaint
Funktion übermalt, wenn
es so gewollt war, den zu erneuernden (ungültigen) Zeichenbereich mit dem Hintergrund
Füllmuster. Der Zeichenbereich wird danach wieder als gültig erklärt.
Dieses als gültig markieren ist wichtig, denn solange noch ein Bereich als ungültig
markiert ist, wird uns Windows immer wieder eine WM_PAINT
Nachricht schicken.
Wenn wir die WM_PAINT
Nachricht nicht bearbeiten, erledigt dies die
DefWindowProc
Funktion für uns.
hDC = BeginPaint(hWnd, &ps); {
Nun ist alles zum Zeichnen vorbereitet. Wir müssen nur noch die TextOut
Funktion (im MSDN: TextOut
) aufrufen, die für uns den Text
auf den Bildschirm bringt. Der erste Parameter ist, wie bei allen Funktionen
der GDI (Graphics Device Interface, also alle Funktionen, die etwas auf einem
Ausgabegerät ausgeben sollen), der Handle auf den Zeichenbereich (Device Context).
Der zweite und dritte Parameter legen wir die X und Y Koordinaten relativ zu der linken,
oberen Ecke des Anwendungsbereiches fest und der linken oberen Ecke des Textes. Würden
wir hier 0, 0
angeben, so wäre der Text ganz oben, links in der Ecke.
Der vierte Parameter ist ein Zeiger auf den Text, der ausgegeben werden soll. Der letzte
Parameter ist die Länge der Zeichenkette.
TextOut(hDC, 50, 50, szText, sizeof(szText) - 1);
Das ist vorerst alles, was wir auf dem Bildschirm ausgeben möchten. Am Ende jeder
Zeichenoperation muss man Windows mitteilen, dass wir nichts mehr zeichnen möchten.
Die EndPaint
Funktion (im MSDN: EndPaint
) macht genau dieses und gibt den
Device Context wieder frei.
} EndPaint(hWnd, &ps);
Da wir die WM_PAINT
Nachricht bearbeitet haben, müssen wir die
Funktion mit return 0;
verlassen.
return 0; } case WM_DESTROY: { PostQuitMessage(0); return 0; } } return DefWindowProc(hWnd, message, wParam, lParam); }
Das obige Programm erstellt ein Fenster und gibt darauf Text aus
(Screenshot).
BeginPaint
ist übriegens nicht die einzige Möglichkeit einen
Devicecontext zu bekommen. Man kann noch GetDC
und CreateDC
benutzen, jedoch ist BeginPaint
die am häufigsten benutzte Möglichkeit.
Die anderen sind nur in speziellen Situationen notwendig und sinnvoll.
TextOut
Funktion nur ein einziges mal.
\n
) und dann jede Zeile einzeln mit TextOut
ausgibt. Schreibe
dazu ein Programm, mit dem du die Funktion testen kannst.