Tutorial
Lutz Ifers WinAPI Tutorial
von MOD | Seite 11 von 16 |
Kapitel 4.1: Zweitfenster
Ein Fenster ist gut, zwei Fenster sind besser und wenn man weiß, wie man sich beliebig viele Fenster macht, ist das am aller besten. Da der olle Lutz ein fauler Sack ist, demonstrieren wir hier nur, wie man die Fenster erzeugt - ansonsten machen die Fenster im Moment noch gar nichts, als CallBack-Funktion tragen wir in die Fensterklasse nämlich direkt die Default-Window-Procedure ein.
Nochmal zur Unterscheidung zwischen Fenster und Fensterklasse: Mehrere Fenster können die selbe Fensterklasse haben, aber kein Fenster mehrere Fensterklassen. Die Fensterklasse bestimmt die Callback-Funktion der Fenster, soll heißen: Selbe Callbackfunktion = selbe Funktion der Fenster. Trotzdem kann der Inhalt der Fenster unterschiedlich sein. Ein Beispiel: Buttons sind auch nur Fenster, wie wir später lernen werden. Sie teilen sich alle die selbe Fensterklasse, schicken beim Klicken also alle die selben Nachrichten ab. Ihr Inhalt (die Beschriftung, bzw. defacto ist es bei Buttons der Fenstertitel) kann unterschiedlich sein.
Für unserere Zweitfenster legen wir also eine eigene Fensterklasse an, die neben der anderen Callback-Funktion einen weiteren Unterschied zu Fensterklasse unseres (Haupt-)Fensters hat: Bei Style definieren wir zusätzlich "CS_NOCLOSE" - das kleine X wird also ausgegraut, ansonsten wäre es möglich, die Nebenfenster (einzeln) zu schließen.
''' Lutz Ifers WinAPI-Tutorial
''' Lizenz: WTFPL
'''
''' Kapitel 4.1 - "Zweitfenster"
#include "windows.bi"
const ProgrammName = "Zweitfenster"
const NebenfensterName = "Nebenfenster"
declare function Fenster(byval hWnd as HWND, byval message as UINTEGER,_
byval wParam as WPARAM, byval lParam as LPARAM) as LRESULT
dim as WNDCLASS wcMeinFenster
with wcMeinFenster
.style = CS_HREDRAW or CS_VREDRAW
.lpfnWndProc = ProcPtr(Fenster)
.cbClsExtra = 0
.cbWndExtra = 0
.hInstance = GetModuleHandle(NULL)
.hCursor = LoadCursor(NULL, IDC_ARROW)
.hIcon = LoadIcon(NULL, IDI_APPLICATION)
.hbrBackground = GetStockObject(WHITE_BRUSH)
.lpszClassName = StrPtr(ProgrammName)
.lpszMenuName = NULL
end with
RegisterClass @wcMeinFenster
dim as WNDCLASS wcNebenFenster
with wcNebenFenster
.style = CS_HREDRAW or CS_VREDRAW or CS_NOCLOSE
.lpfnWndProc = ProcPtr(DefWindowProc)
.cbClsExtra = 0
.cbWndExtra = 0
.hInstance = GetModuleHandle(NULL)
.hCursor = LoadCursor(NULL, IDC_ARROW)
.hIcon = LoadIcon(NULL, IDI_APPLICATION)
.hbrBackground = GetStockObject(WHITE_BRUSH)
.lpszClassName = StrPtr(NebenfensterName)
.lpszMenuName = NULL
end with
RegisterClass @wcNebenFenster
dim as HWND hMeinFenster = CreateWindow(_
ProgrammName, "Titelzeile", WS_OVERLAPPEDWINDOW,_
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,_
NULL, NULL, GetModuleHandle(NULL), NULL)
ShowWindow hMeinFenster, SW_NORMAL
UpdateWindow hMeinFenster
dim as MSG msg
do while getmessage(@msg, NULL, 0, 0) <> 0
DispatchMessage @msg
loop
end msg.wParam
Trotz mehrerer Fenster und Fensterklassen benötigen wir nur eine Nachrichtenschleife: GetMessage empfängt alle Nachrichten eines Programmes (mit all seinen Fenstern); DispatchMessage sortiert die Nachrichten nach Fenster und leitet sie an die zugehörigen Callback-Funktionen weiter.
function Fenster(byval hWnd as HWND, byval message as UINTEGER,_
byval wParam as WPARAM, byval lParam as LPARAM) as LRESULT
static as HWND hFensterA, hFensterB, hFensterC, hFensterD
static as POINT ptStart, ptEnde
dim as HDC hDC
select case message
case WM_DESTROY
PostQuitMessage 0
return 0
case WM_CREATE
hFensterA = CreateWindow(NebenfensterName,_
"Vollwertiges Zweitfenster",_
WS_OVERLAPPEDWINDOW or WS_VISIBLE,_
CW_USEDEFAULT, CW_USEDEFAULT, 200, 100,_
hWnd, NULL, cast(LPCREATESTRUCT,lParam)->hInstance, NULL)
hFensterB = CreateWindow(NebenfensterName,_
"Ohne alles",_
WS_VISIBLE,_
CW_USEDEFAULT, CW_USEDEFAULT, 100, 100,_
hWnd, NULL, cast(LPCREATESTRUCT,lParam)->hInstance, NULL)
hFensterC = CreateWindow(NebenfensterName,_
"Vielleicht Menübar",_
WS_VISIBLE or WS_OVERLAPPED or WS_SYSMENU,_
CW_USEDEFAULT, CW_USEDEFAULT, 300, 150,_
hWnd, NULL, cast(LPCREATESTRUCT,lParam)->hInstance, NULL)
hFensterD = CreateWindow(NebenfensterName,_
"Selbstständig",_
WS_OVERLAPPEDWINDOW or WS_VISIBLE,_
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,_
NULL, NULL, cast(LPCREATESTRUCT,lParam)->hInstance, NULL)
return 0
end select
return DefWindowProc( hWnd, message, wParam, lParam )
end function
Wir erzeugen die Fenster mit der gleichen Funktion, CreateWindow, wie unser Hauptfenster. Theoretisch hätten wir das auch im Hauptprogramm machen können, allerdings werden die Rückgabewerte der Funktion, die Handles zu den einzelnen Funktionen zunehmend interessanter (wir werden später lernen, wie man die Fenster bewegt, versteckt, wieder anzeigt, minimiert, maximiert, Nachrichten schickt usw.), weshalb wir diese Werte in der Callbackfunktion benötigen, daher werden die Variablen auch als static definiert.
Als Parentwindow geben wir bis auf FensterD das "große" Hauptfenster an - damit gehören diese Fenster zum Hauptfenster, FensterD ist eigenständig, und erscheint deshalb auch in der Taskleiste als eigenständiger Eintrag.
Aber das eigentlich interessante am ganzen Fenstererstellen sind die verschieden Styles, die wir für die Fenster verwenden können. WS_VISIBLE erzeugt die Fenster aktiv, wir müssen sie nicht wie das Hauptfenster erst mit ShowWindow aktivieren. OVERLAPPEDWINDOW, eine Kombination aus mehreren anderen Style-Flags steht für die "normalen" Fenster, mit verschiebbarer Größe, Systemmenü (dem Icon links oben), Maximieren- und Minimieren-Icon. Die Namen der Styles sind soweit selbsterklärend - eine vollständige Liste findet sich in der MSDN unter Windowstyles.
Links:
In der MSDN: CreateWindow, Windowstyles
Zusätzliche Informationen und Funktionen | |||||||
---|---|---|---|---|---|---|---|
|