Programmieren unter MS-Windows

Kapitel 9: Ausgabe von Ressourcen

Letzte Änderung: 2.11.97 von B. Tritsch

Überblick

Zurück zum Index "PC- und MS-Windows-Support"

Zurück zum Inhalt


Icons und Cursors

Zum Entwerfen von Symbolen (Icons), Cursors und auch anderer Ressourcen liefern die Compilerhersteller verschiedene Werkzeuge aus:

Das Einbinden von Icons geschieht mit Hilfe des LoadIcon-Befehls.

Für das Einbinden von Cursors ist der LoadCursor-Befehls zuständig.

/*111*/   wc.hCursor       = LoadCursor(hInstance,
/*112*/                                 "hand");
/*113*/   wc.hIcon         = LoadIcon(hInstance,
/*114*/                               "lite");

Sowohl für Icons als auch für Cursors stellt das Windows-System eine Reihe von Standardressourcen zur Verfügung:

Ein Cursor kann jederzeit mit dem Befehl LoadCursor verändert werden. Dies geschieht oft nur vorübergehend, z.B. beim Anzeigen des Uhrglas-Cursors (IDC_WAIT).

HCURSOR vorigerCursor;

vorigerCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
// Aktion ...
SetCursor(vorigerCursor);

Meldungsfelder

Meldungsfelder (Message-Boxen) gehören zwar nicht direkt zu den Ressourcen, sie können aber als Vorstufe zu den Dialogen betrachtet werden.

Eine Message-Box bedeutet die einfachste Methode für einen Programmierer, Text in einem eigenen kleinen Fenster auszugeben:

if (!FilePointer)          // Keine Dateieröffnung
 {
  MessageBox (GetFocus(),
              "Datei konnte nicht eröffnet werden",
              "Fehler-Box", MB_OK | MB_ICONEXCLAMATION);
  return 0;
 }

Dieses Beispiel stellt einen sehr leichten Weg vor, ein aktives Fenster mit der Meldung "Datei konnte nicht geöffnet werden", dem Titel "Fehler-Box", einem "OK"-Button und einem Ausrufezeichen darzustellen. Die GetFocus-Funktion lenkt dabei den Ausgabe-Fokus auf das betreffende Fenster.

Der letzte Parameter kann aus mehreren Teilen bestehen, die durch ein bitweises ODER verbunden sind. Einige wichtige Beispiele sind:

Der Rückgabewert der MessageBox-Funktion ist einer der folgenden Werte, je nach dem wie der Benutzer die Box verläßt:

Dialoge

Richtige Dialogboxen, die Windows-Ressourcen sind, gestalten sich als wesentlich schwieriger zu handhabende Objekte.

Benutzt man zur Entwicklung von Windows-Programmen nur das MS-SDK, so muß man die Dialogboxen alle von Hand "stricken". Zeitgemäßer sind sogenannte "Toolkits", die eine graphische Eingabe mit der Maus erlauben, um dann den ASCII-Source oder gar direkt ein .RES-File daraus zu erzeugen.

Dennoch ist es wichtig, die einzelnen Elemente einer Dialogbox genau zu kennen, um sie richtig einsetzen zu können.

Mit Windows95 wurden eine Reihe neuer Kontrollelemente für Dialogboxen eingeführt:

Das DialogBox-Fenster selbst und seine einzelnen Elemente können eine Vielzahl von Stilen und Attributen besitzen. Dies betrifft sowohl Aussehen und Funktionalität bis ins Detail.

Die einzelnen Elemente müssen mit individuellen Zahlen gekennzeichnet werden, um von der Applikation darauf referenzieren zu können. Zur besseren Programm-Lesbarkeit hat man sich jedoch darauf geeinigt, den Zahlen mit Hilfe eines HEADER-Files und DEFINE-Anweisungen eindeutige Namen zuzuordnen.

#define IDOK        1
#define IDCANCEL    2
#define IDABORT     3
#define IDRETRY     4
#define IDIGNORE    5
#define IDYES       6
#define IDNO        7

Das Anzeigen der (modalen) Dialogbox erfolgt dann folgendermaßen (innerhalb der Fensterprozedur des aufrufenden Fensters):

FARPROC     lpfnProc;  // Long Pointer auf eine Funktion
int         nResult;   // Rückgabewert
HANDLE      hInst;     // Instanzenhandle der App.
HWND        hwMain;    // Handle des Hauptfensters
...
...
case MI_LOADFILE:      // Öffnet die Dialogbox
  // Pointer auf die Callback-Fkt. erzeugen:
  lpfnProc = MakeProcInstance(SampleDlgProc, hInst);
  // Dialog anzeigen und aktivieren:
  nResult = DialogBox (hInst, "TESTDLG",
                       hwMain, lpfnProc);
  // Instanzen wieder freigeben (Sandwich)
  FreeProcInstance (lpfnProc);
  break;

Schon kurz bevor eine Dialogbox auf dem Bildschirm erscheint, schickt sie eine Message WM_INITDIALOG. Das gibt dem Programmierer Gelegenheit, bestimmte Initialisierungsarbeiten in der Dialogbox vorzunehmen (Vorbelegung der Editierfelder etc.).

Die Funktion DialogBox hat der Reihe nach vier Parameter:

  1. Handle der aktuellen Instanz
  2. Ressource-Name der Dialogbox
  3. Parent Window
  4. Instanzadresse der Dialogbox-Prozedur

Dialogboxen werden in "modal" und "nicht-modal" unterschieden. Diese werden etwas unterschiedlich behandelt, wobei der tiefere Sinn folgender ist:

Praktisch verwendbar innerhalb dem Dialog-Box-Callback (der Fensterprozedur für den Dialog) sind die Steuer-Elemente dann über die WM_COMMAND-Message:

switch (msg)
 {
  case WM_INITDIALOG:   // Message beim Start
    SetDialogItemText(hDlg, ID_TEXT, "Wait...");
    break;

  case WM_COMMAND:      // Message von den Steuerungen
    switch (wP)         // wP gibt nähere Auskunft
     {
      case IDOK:
        .
        .
        break;

      case IDCANCEL:
        .
        .
        break;

      default:
        return FALSE;
        break;
     }
    break;

  default:
    return FALSE;
    break;
 }

Common Dialogs

Die Win32-Umgebung stellt dem Entwickler eine Reihe von allgemein verwendbaren Standarddialogen zur Verfügung, die eine erneute Implementation bei jeder Applikation vermeiden sollen. Diese Common Dialogs sind jedem Windows-Benutzer wohlbekannt. Sie beinhalten Dialoge zum:

Das folgende Beispielprogramm demonstriert alle Common Dialogs der Reihe nach.

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg,
                         WPARAM wParam, LPARAM lParam)
 {
  switch(uMsg)
   {
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    default:
      return DefWindowProc(hwnd, uMsg, wParam, lParam);
   }
  return 0;
 }

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                                        LPSTR d3, int nCmdShow)
 {
  MSG msg;
  HWND hwnd;
  WNDCLASS wndClass;

  OPENFILENAME ofn;
  CHOOSECOLOR cc;
  CHOOSEFONT cf;
  PRINTDLG pd;
  PAGESETUPDLG psd;
  FINDREPLACE fr;
  COLORREF crCustColors[16];
  LOGFONT lf;
  char szFindWhat[80];
  char szReplaceWith[80];
  HWND hdlgFt, hdlgFr;

  if (hPrevInstance == NULL)
   {
    memset(&wndClass, 0, sizeof(wndClass));
    wndClass.style = CS_HREDRAW | CS_VREDRAW;
    wndClass.lpfnWndProc = WndProc;
    wndClass.hInstance = hInstance;
    wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
    wndClass.lpszClassName = "COMMDLGS";
    if (!RegisterClass(&wndClass)) return FALSE;
   }
  hwnd = CreateWindow("COMMDLGS", "Common Dialogs Demonstration",
                      WS_OVERLAPPEDWINDOW,
                      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
                      NULL, NULL, hInstance, NULL);
  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);

  // Hier beginnen die Common Dialogs
  // Immer wenn ein Dialog beendet wird, startet der naechste
  memset(&ofn, 0, sizeof(ofn));
  ofn.lStructSize = sizeof(OPENFILENAME);
  GetOpenFileName(&ofn);
  memset(&ofn, 0, sizeof(ofn));
  ofn.lStructSize = sizeof(OPENFILENAME);
  GetSaveFileName(&ofn);
  memset(&cc, 0, sizeof(cc));
  memset(crCustColors, 0, sizeof(crCustColors));
  cc.lStructSize = sizeof(cc);
  cc.lpCustColors = crCustColors;
  ChooseColor(&cc);
  memset(&cf, 0, sizeof(cf));
  memset(&lf, 0, sizeof(lf));
  cf.lStructSize = sizeof(cf);
  cf.lpLogFont = &lf;
  cf.Flags = CF_SCREENFONTS | CF_EFFECTS;
  ChooseFont(&cf);
  memset(&pd, 0, sizeof(pd));
  pd.lStructSize = sizeof(pd);
  PrintDlg(&pd);
  memset(&psd, 0, sizeof(psd));
  psd.lStructSize = sizeof(psd);
  PageSetupDlg(&psd);
  memset(&fr, 0, sizeof(fr));
  memset(szFindWhat, 0, sizeof(szFindWhat));
  memset(szReplaceWith, 0, sizeof(szReplaceWith));
  fr.lStructSize = sizeof(fr);
  fr.hwndOwner = hwnd;
  fr.lpstrFindWhat = szFindWhat;
  fr.lpstrReplaceWith = szReplaceWith;
  fr.wFindWhatLen = sizeof(szFindWhat);
  fr.wReplaceWithLen = sizeof(szReplaceWith);
  hdlgFt = FindText(&fr);
  hdlgFr = ReplaceText(&fr);

  while (GetMessage(&msg, NULL, 0, 0))
   {
    if(!IsDialogMessage(hdlgFt, &msg))
     {
      if(!IsDialogMessage(hdlgFr, &msg))
       {
        DispatchMessage(&msg);
       }
     }
   }
  return msg.wParam;
 }

Menüs

Genau wie Dialogboxen sind Menüs Ressourcen. Wir wollen uns wieder nur um die Erstellung von Menüs über "Toolkits" und nicht über das SDK kümmern.

Wurden ein Menü über das Toolkit erzeugt, die einzelnen Menüpunkte mit individuellen Zahlen versehen und diesen Zahlen über ein HEADER-File Namen zugeordnet, kann das Menü in die Applikation integriert werden. Dies geschieht beim Registrieren der Fensterklasse oder mit einem LoadMenu-Befehl.

/*110*/   wc.lpszMenuName  = "AppMenu";

Der Handle zu einem Menü, der für viele Menüoperationen gebraucht wird, läßt sich mit der GetMenu-Funktion erfahren.

Es gibt nun zwei einfache Möglichkeiten, Menüpunkte optisch zu verändern:

Ein Check Mark wird mit der folgenden Funktion an einen Menüpunkt angefügt:

BOOL CheckMenuItem (HMENU hMenu, WORD wCheckItem, WORD wMode)

Ein Menüpunkt läßt sich auch grau einfärben und deaktivieren. Dies geschieht über die EnableMenuItem-Funktion. Die Parameter haben prinzipiell die gleiche Bedeutung, nur für den Modus gibt es entsprechend der Funktionalität andere Schlüsselwörter:

switch (msg)
 {
  case WM_CREATE: // vor Erscheinen des Fensters
    ...
    ...
    EnableMenuItem (hMenu, MI_LINENUM, MF_GRAYED | MF_BYCOMMAND);
    EnableMenuItem (hMenu, MI_ANSI, MF_ENABLED | MF_BYCOMMAND);
  break;
 }

Zum nächsten Kapitel