Die WinAPI Plattform

Tutorials

(13.) Text im Childfenster

Wenn man im letzten Tutorial im Childfenster einen Text speichern wollte, dann musste man den Speicher für diesen Text dynamisch anfordern und dann den Zeiger auf den Speicherbereich mit SetWindowLong im Fensterspeicher speichern.

Dabe stellt Windows für einen String pro Childfenster eine einfache Methode zur Verfügung. Um diese Technik zu demonstrieren, schreibe ich ein Programm, dass zwei Childfenster besitzt. Das eine Childfenster kümmert sich um die Anzeige eines Textes, während das andere Childfenster - diesmal eine beschriftete - Checkbox darstellt mit der man das andere Childfenster ein- und ausschalten kann. Dementsprechen soll diesmal auch der Titel unseres Hauptfensters angepasst werden.

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK TextChildProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ChoiceProc(HWND, UINT, WPARAM, LPARAM);

const UINT PM_CHECKED      = WM_APP + 1;
const char szTextChild[]   = "TextChild";
const char szChoice[]      = "ChoiceChild";

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow)
{
   MSG         msg;
   HWND        hWnd;
   WNDCLASS    wc;
   
   const char  szAppName[] = "Text im Childfenster";
   
   wc.cbClsExtra           = 0;
   wc.cbWndExtra           = 0;
   wc.hbrBackground        = (HBRUSH) GetStockObject(WHITE_BRUSH);
   wc.hCursor              = LoadCursor(NULL, IDC_ARROW);
   wc.hIcon                = LoadIcon(NULL, IDI_APPLICATION);
   wc.hInstance            = hInstance;
   wc.lpfnWndProc          = WndProc;
   wc.lpszClassName        = szAppName;
   wc.lpszMenuName         = NULL;
   wc.style                = CS_HREDRAW | CS_VREDRAW;
   
   RegisterClass(&wc);
   
   wc.lpfnWndProc          = TextChildProc;
   wc.lpszClassName        = szTextChild;
   
   RegisterClass(&wc);
   
   wc.cbWndExtra           = sizeof(int);
   wc.hbrBackground        = (HBRUSH) GetStockObject(LTGRAY_BRUSH);
   wc.lpfnWndProc          = ChoiceProc;
   wc.lpszClassName        = szChoice;
   
   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)
{
   static  HWND   hText;
   static  HWND   hChoice;
   static  RECT   rect;
   
   switch (message)
   {
   case WM_CREATE:
      {

Der Text, der im Childfenster erscheinen soll, geben wir als zweiten Parameter der CreateWindow Funktion an. Diesen Text können wir dann in der TextChildProc Funktion auslesen.

         hText = CreateWindow(  szTextChild,
                                "Die Letzten werden die Ersten sein.",
                                WS_CHILD | WS_BORDER,
                                0, 0, 0, 0,
                                hWnd,
                                NULL,
                                ((LPCREATESTRUCT) lParam) -> hInstance,
                                NULL);

Obwohl wir als dritten Parameter der folgenden CreateWindow Funktion kein WS_DLGFRAME mit angeben, wird dieses Childfenster so angezeigt werden. Denn in der WM_CREATE Nachricht dieses Fensters werden wir diesen Fensterstiel noch nachträglich setzten. Dies kann man immer dann machen, wenn dieses Fenster auf alle Fälle diesen Style bekommen soll (ausgenommen WS_CHILD, den muss man in der CreateWindow Funktion mit angeben).

         hChoice = CreateWindow( szChoice,
                                 "Ja, ich will diesen Spruch sehen.",
                                 WS_CHILD | WS_VISIBLE,
                                 0, 0, 0, 0,
                                 hWnd,
                                 NULL,
                                 ((LPCREATESTRUCT) lParam) -> hInstance,
                                 NULL);
         return 0;
      }
   case WM_SIZE:
      {
         rect.right  = LOWORD(lParam);
         rect.bottom = HIWORD(lParam);
         
         MoveWindow(hText, rect.right  / 2 - 140, 
                           rect.bottom / 2 - 60, 280,120, TRUE);
                            
         MoveWindow(hChoice, rect.right / 2 - 130, 
                             rect.bottom - 30, 260, 22, TRUE);
         return 0;
      }

Die PM_CHECKED Nachricht bekommen wir von unserem Checkbox Childfenster. Wenn wParam true (also ungleich Null) ist, dann soll das Text Childfenster angezeigt werden. Dies geben wir durch den Aufruf der ShowWindow Funktion an Windows weiter. Mit der SetWindowText Funktion können wir den Text jedes Fensters ändern. In diesem Fall passen wir den Text des Hauptfensters (also die Titelleiste) dem Status an.

Ist wParam gleich Null, dann soll der Text (und damit das Childfenster) nicht angezeigt werden. Dies erreichen wir durch ShowWindow mit dem zweiten Parameter gleich SW_HIDE. Danach setzen wir wieder den Titel des Hauptfensters auf den ursprünglichen Wert zurück.

   case PM_CHECKED:
      {
         if (wParam)
         {
            ShowWindow(hText, SW_SHOW);
            SetWindowText(hWnd, "Text im Childfenster - Der Spruch");
         }
         else
         {
            ShowWindow(hText, SW_HIDE);
            SetWindowText(hWnd, "Text im Childfenster");
         }
         return 0;
      }
   case WM_DESTROY:
      {
         PostQuitMessage(0);
         return 0;
      }
   }
   return DefWindowProc(hWnd, message, wParam, lParam);
}

LRESULT CALLBACK TextChildProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   switch (message)
   {

Für dieses Childfenster müssen wir nur die WM_PAINT Nachricht bearbeiten. Da der Text, den dieses Childfenster anzeigen soll, von Windows gespeichert wird, müssen wir diesen Text erst in ein Array kopieren. Mit der GetWindowText Funktion wird der Text des Fensters in das szText Array kopiert. Diese Funktion liefert die Anzahl der kopiert Zeichen zurück. Die DrawText Funktion zeichnet schlißlich den Text formatiert in das Fenster.

   case WM_PAINT:
      {
         RECT        rect;
         HDC         hDC;
         PAINTSTRUCT ps;
         char        szText[200];
         
         GetClientRect(hWnd, &rect);
         
         hDC = BeginPaint(hWnd, &ps);
         {
            DrawText(hDC, szText, GetWindowText(hWnd, szText, sizeof(szText)),
                          &rect, DT_WORDBREAK | DT_CENTER);
         }
         EndPaint(hWnd, &ps);
         
         return 0;
      }
   }
   
   return DefWindowProc(hWnd, message, wParam, lParam);
}

LRESULT CALLBACK ChoiceProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   switch (message)
   {

In der WM_CREATE Nachricht müssen wir einmal noch den Fensterstiel WS_DLGFRAME setzen und dann noch den Fensterspeicher mit Null vorbelegen. Als erstes speichern wir den in CreateWindow angegebenen Fensterstiel in der Variable style. Diesen Wert bekommen wir durch den Aufruf von GetWindowLong und als zweiten Parameter GWL_STYLE. Danach verknüpfen wir diesen Wert mit WS_DLGFRAME und speichern ihn mit SetWindowLong wieder ab. Zum Schluß belegen wir noch den ersten long Wert des Fensterspeichers mit 0 vor.

   case WM_CREATE:
      {
         long style = GetWindowLong(hWnd, GWL_STYLE);
         SetWindowLong(hWnd, GWL_STYLE, style | WS_DLGFRAME);
         SetWindowLong(hWnd, 0, 0);
         
         return 0;
      }

In der WM_PAINT Nachricht zeichnen wir die Checkbox und den Text, den wir vorher wieder mit GetWindowText in ein Array kopiert haben. Wenn die Checkbox gerade ausgewählt sein soll, dann wird auch noch ein X gezeichnet.

   case WM_PAINT:
      {
         HDC          hDC;
         PAINTSTRUCT  ps;
         char         szText[100];
         
         hDC = BeginPaint(hWnd, &ps);
         {
            SetBkMode(hDC, TRANSPARENT);
            Rectangle(hDC, 6, 2, 18, 14);
            
            TextOut(hDC, 24, 0, szText, GetWindowText(hWnd, szText, sizeof(szText)));
            
            if (GetWindowLong(hWnd, 0))
            {
               MoveToEx(hDC, 6, 2, NULL);
               LineTo(hDC, 17, 13);
               MoveToEx(hDC, 6, 13, NULL);
               LineTo(hDC, 17, 2);
            }
         }
         EndPaint(hWnd, &ps);
         
         return 0;
      }

In der WM_LBUTTONDOWN Nachricht prüfen wir, ob das Checkbox Feld getroffen wurdel. Wenn ja, dann senden wir dem Hauptfenster die Nachricht und lassen den Bildschirm neu zeichnen.

   case WM_LBUTTONDOWN:
      {
         if (LOWORD(lParam) >=  6   &&
             LOWORD(lParam) <  18   &&
             HIWORD(lParam) >=  2   &&
             HIWORD(lParam) <  14)
         {
            SetWindowLong(hWnd, 0, !GetWindowLong(hWnd, 0));
            SendMessage(GetParent(hWnd), PM_CHECKED, 
                           GetWindowLong(hWnd, 0), (LPARAM)hWnd);
            InvalidateRect(hWnd, NULL, FALSE);
         }
      }
   }
   
   return DefWindowProc(hWnd, message, wParam, lParam);
}

In diesem Tutorial hast du gelernt, wie man den Text eines Fensters schreib und liest. Du kannst jetzt einem Childfenster auch nachträglich noch Fensterstiele dazufügen.

Nach diesem Tutorial schrieb ich den Source Code für das 14. Tutorial. Als Beispiel entwickelte ich ein Childfenster, dass sich ziemlich genau so verhält, wie die Windows Buttons. Als alles fertig programmiert war, merkte ich, dass ich mich mit diesem Tutorial zu sehr in Einzelheiten verlieren würde. Also wurde es nicht das 14. Tutorial, sondern man kann den gut kommentierten Code nur downloaden.

Wenn du meinst, dass irgend eine Stelle in diesem Tutorial ungenau ist oder etwas nicht erklärt wird, dann schreibe mir einfach eine E-Mail.

webmaster@win-api.de