Verwenden von Fenstern
Die Beispiele in diesem Abschnitt beschreiben, wie die folgenden Aufgaben ausgeführt werden:
- Erstellen eines Hauptfensters
- Erstellen, Aufzählen und Anpassen von untergeordneten Fenstern
- Löschen eines Fensters
- Verwenden von überlappenden Fenstern
Erstellen eines Hauptfensters
Das erste Fenster, das eine Anwendung erstellt, ist normalerweise das Hauptfenster. Sie erstellen das Hauptfenster, indem Sie die CreateWindowEx-Funktion verwenden und die Fensterklasse, den Fensternamen, die Fensterformatvorlagen, die Größe, die Position, das Menühandle, das Instanzhandle und die Erstellungsdaten angeben. Ein Hauptfenster gehört zu einer anwendungsdefinierten Fensterklasse. Daher müssen Sie die Fensterklasse registrieren und eine Fensterprozedur für die Klasse bereitstellen, bevor Sie das Hauptfenster erstellen.
Die meisten Anwendungen verwenden in der Regel die WS_OVERLAPPEDWINDOW-Formatvorlage, um das Hauptfenster zu erstellen. Mit dieser Formatvorlage wird dem Fenster eine Titelleiste, ein Fenstermenü, ein Rahmen zur Größenanpassung und das Minimieren und Maximieren von Schaltflächen zugewiesen. Die CreateWindowEx-Funktion gibt ein Handle zurück, das das Fenster eindeutig identifiziert.
Im folgenden Beispiel wird ein Hauptfenster erstellt, das zu einer anwendungsdefinierten Fensterklasse gehört. Der Fenstername, Hauptfenster, wird in der Titelleiste des Fensters angezeigt. Durch die Kombination der Formatvorlagen WS_VSCROLL und WS_HSCROLL mit der Formatvorlage WS_OVERLAPPEDWINDOW erstellt die Anwendung zusätzlich zu den Komponenten, die von der Formatvorlage WS_OVERLAPPEDWINDOW bereitgestellt werden, ein Hauptfenster mit horizontalen und vertikalen Bildlaufleisten. Die vier Vorkommen der CW_USEDEFAULT-Konstante setzen die anfängliche Größe und Position des Fensters auf die systemdefinierten Standardwerte fest. Durch Angeben von NULL anstelle eines Menühandles wird im Fenster das Menü für die Fensterklasse definiert.
HINSTANCE hinst;
HWND hwndMain;
// Create the main window.
hwndMain = CreateWindowEx(
0, // no extended styles
"MainWClass", // class name
"Main Window", // window name
WS_OVERLAPPEDWINDOW | // overlapped window
WS_HSCROLL | // horizontal scroll bar
WS_VSCROLL, // vertical scroll bar
CW_USEDEFAULT, // default horizontal position
CW_USEDEFAULT, // default vertical position
CW_USEDEFAULT, // default width
CW_USEDEFAULT, // default height
(HWND) NULL, // no parent or owner window
(HMENU) NULL, // class menu used
hinst, // instance handle
NULL); // no window creation data
if (!hwndMain)
return FALSE;
// Show the window using the flag specified by the program
// that started the application, and send the application
// a WM_PAINT message.
ShowWindow(hwndMain, SW_SHOWDEFAULT);
UpdateWindow(hwndMain);
Beachten Sie, dass im vorherigen Beispiel die ShowWindow-Funktion aufgerufen wird, nachdem das Hauptfenster erstellt wurde. Dies geschieht, da das System das Hauptfenster nach der Erstellung nicht automatisch anzeigt. Durch Übergabe des SW_SHOWDEFAULT-Flags an ShowWindow ermöglicht die Anwendung dem Programm, das die Anwendung gestartet hat, den anfänglichen Anzeigezustand des Hauptfensters festzulegen. Die UpdateWindow-Funktion sendet dem Fenster seine erste WM_PAINT-Nachricht.
Erstellen, Aufzählen und Anpassen von untergeordneten Fenstern
Sie können den Clientbereich eines Fensters mithilfe von untergeordneten Fenstern in verschiedene Funktionsbereiche unterteilen. Das Erstellen eines untergeordneten Fensters ist wie das Erstellen eines Hauptfensters: Sie verwenden die CreateWindowEx-Funktion Zum Erstellen eines Fensters einer anwendungsdefinierten Fensterklasse müssen Sie die Fensterklasse registrieren und eine Fensterprozedur bereitstellen, bevor Sie das untergeordnete Fenster erstellen. Sie müssen dem untergeordneten Fenster die Formatvorlage WS_CHILD zuweisen und beim Erstellen ein übergeordnetes Fenster für das untergeordnete Fenster angeben.
Das folgende Beispiel unterteilt den Clientbereich des Hauptfensters einer Anwendung in drei Funktionsbereiche, indem drei untergeordnete Fenster gleicher Größe erstellt werden. Jedes untergeordnete Fenster hat die gleiche Höhe wie der Clientbereich des Hauptfensters, aber jedes ist nur ein Drittel seiner Breite. Das Hauptfenster erstellt die untergeordneten Fenster als Reaktion auf die WM_CREATE-Nachricht, die das Hauptfenster während seines eigenen Fenstererstellungsprozesses erhält. Da jedes untergeordnete Fenster die Formatvorlage WS_BORDER aufweist, verfügt jedes über einen dünnen Linienrand. Da außerdem die WS_VISIBLE-Formatvorlage nicht angegeben ist, wird jedes untergeordnete Fenster zunächst ausgeblendet. Beachten Sie auch, dass jedem untergeordneten Fenster ein Bezeichner für untergeordnete Fenster zugewiesen ist.
Die Größe und Position des Hauptfensters richtet sich nach der WM_SIZE-Meldung, die das Hauptfenster erhält, wenn sich seine Größe ändert. Als Reaktion auf WM_SIZE ruft das Hauptfenster die Dimensionen des Clientbereichs mithilfe der GetClientRect-Funktion ab und übergibt die Dimensionen dann an die EnumChildWindows-Funktion. EnumChildWindows übergibt das Handle an jedes untergeordnete Fenster und wiederum an die anwendungsdefinierte EnumChildProc-Rückruffunktion. Diese Funktion skaliert und positioniert jedes untergeordnete Fenster, indem sie die MoveWindow-Funktion aufruft. Größe und Position basieren auf den Abmessungen des Clientbereichs des Hauptfensters und der Kennung des untergeordneten Fensters. Anschließend ruft EnumChildProc die ShowWindow-Funktion auf, um das Fenster sichtbar zu machen.
#define ID_FIRSTCHILD 100
#define ID_SECONDCHILD 101
#define ID_THIRDCHILD 102
LONG APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
RECT rcClient;
int i;
switch(uMsg)
{
case WM_CREATE: // creating main window
// Create three invisible child windows.
for (i = 0; i < 3; i++)
{
CreateWindowEx(0,
"ChildWClass",
(LPCTSTR) NULL,
WS_CHILD | WS_BORDER,
0,0,0,0,
hwnd,
(HMENU) (int) (ID_FIRSTCHILD + i),
hinst,
NULL);
}
return 0;
case WM_SIZE: // main window changed size
// Get the dimensions of the main window's client
// area, and enumerate the child windows. Pass the
// dimensions to the child windows during enumeration.
GetClientRect(hwnd, &rcClient);
EnumChildWindows(hwnd, EnumChildProc, (LPARAM) &rcClient);
return 0;
// Process other messages.
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
BOOL CALLBACK EnumChildProc(HWND hwndChild, LPARAM lParam)
{
LPRECT rcParent;
int i, idChild;
// Retrieve the child-window identifier. Use it to set the
// position of the child window.
idChild = GetWindowLong(hwndChild, GWL_ID);
if (idChild == ID_FIRSTCHILD)
i = 0;
else if (idChild == ID_SECONDCHILD)
i = 1;
else
i = 2;
// Size and position the child window.
rcParent = (LPRECT) lParam;
MoveWindow(hwndChild,
(rcParent->right / 3) * i,
0,
rcParent->right / 3,
rcParent->bottom,
TRUE);
// Make sure the child window is visible.
ShowWindow(hwndChild, SW_SHOW);
return TRUE;
}
Löschen eines Fensters
Sie können die DestroyWindow-Funktion verwenden, um ein Fenster zu löschen. In der Regel sendet eine Anwendung die WM_CLOSE-Nachricht, bevor ein Fenster gelöscht wird, und gibt dem Fenster die Möglichkeit, den Benutzer zur Bestätigung aufzufordern, bevor das Fenster gelöscht wird. Ein Fenster, das ein Fenstermenü enthält, empfängt automatisch die WM_CLOSE-Nachricht, wenn der Benutzer im Fenstermenü auf Schließen klickt. Bestätigt der Benutzer, dass das Fenster gelöscht werden soll, ruft die Anwendung DestroyWindow auf. Das System sendet die WM_DESTROY-Nachricht an das Fenster, nachdem es vom Bildschirm entfernt wurde. Als Reaktion auf WM_DESTROY speichert das Fenster seine Daten und gibt alle zugeordneten Ressourcen frei. Ein Hauptfenster schließt die Verarbeitung von WM_DESTROY ab, indem es die PostQuitMessage-Funktion zum Beenden der Anwendung aufruft.
Das folgende Beispiel zeigt, wie Sie den Benutzer zur Bestätigung auffordern, bevor Sie ein Fenster löschen. Als Reaktion auf WM_CLOSE wird ein Dialogfeld angezeigt, das die Schaltflächen Ja, Nein und Abbrechen enthält. Wenn der Benutzer auf Ja klickt, wird DestroyWindow aufgerufen. Andernfalls wird das Fenster nicht gelöscht. Da die Anwendung die WM_CLOSE-Nachricht verarbeitet, wird 0
in allen Fällen zurückgegeben. Da es sich bei dem zu löschenden Fenster um ein Hauptfenster handelt, wird im Beispiel PostQuitMessage als Reaktion auf WM_DESTROY aufgerufen.
case WM_CLOSE:
// Create the message box. If the user clicks
// the Yes button, destroy the main window.
if (MessageBox(hwnd, szConfirm, szAppName, MB_YESNOCANCEL) == IDYES)
DestroyWindow(hwndMain);
return 0;
case WM_DESTROY:
// Post the WM_QUIT message to
// quit the application terminate.
PostQuitMessage(0);
return 0;
Verwenden von überlappenden Fenstern
Damit ein Dialogfeld als durchsichtiges Fenster angezeigt wird, erstellen Sie zunächst das Dialogfeld wie gewohnt. Legen Sie dann in WM_INITDIALOG das überlappende Bit der erweiterten Formatvorlage des Fensters fest, und rufen Sie SetLayeredWindowAttributes mit dem gewünschten Alpha-Wert auf. Der Code kann etwa so aussehen:
// Set WS_EX_LAYERED on this window
SetWindowLong(hwnd,
GWL_EXSTYLE,
GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
// Make this window 70% alpha
SetLayeredWindowAttributes(hwnd, 0, (255 * 70) / 100, LWA_ALPHA);
Beachten Sie, dass der dritte Parameter von SetLayeredWindowAttributes ein Wert ist, der zwischen 0 und 255 liegt, wobei 0 das Fenster vollständig transparent und 255 es vollständig undurchsichtig macht. Dieser Parameter imitiert die vielseitigere BLENDFUNCTION der AlphaBlend-Funktion.
Um dieses Fenster vollständig undurchsichtig zu machen, entfernen Sie das WS_EX_LAYERED-Bit, indem Sie SetWindowLong aufrufen und dann das Fenster bitten, neu zu formatieren. Das Entfernen des Bits ist erwünscht, um dem System mitzuteilen, dass es etwas Speicher freigeben kann, der mit der Schichtung und Umleitung verbunden ist. Der Code kann etwa so aussehen:
// Remove WS_EX_LAYERED from this window styles
SetWindowLong(hwnd,
GWL_EXSTYLE,
GetWindowLong(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
// Ask the window and its children to repaint
RedrawWindow(hwnd,
NULL,
NULL,
RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
Um überlappende untergeordnete Fenster verwenden zu können, muss sich die Anwendung im Manifest als Windows 8-fähig deklarieren.
Für Windows 10/11 kann dieser Kompatibilitätsausschnitt in app.manifest
eingefügt werden, damit es Windows 10-fähig ist:
...
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 GUID -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
...
Weitere Informationen zum Ändern des App-Manifests finden Sie hier: Anwendungsmanifeste