Tutorial
Erste Schritte in der WinAPI mit Freebasic und FBEdit
von stephanbrunker | Seite 12 von 13 |
Menü
Um dem Fenster ein Menü hinzuzufügen, hat FBEdit einen halbwegs guten Assistenten. Man erzeugt das Menü mit "Ressourcen->Menü hinzufügen" und wählt das Menü IDR_MENU1 im Editor aus, woraufhin sich der passende Editor öffnet:
Das Menü hat jetzt nur zwei Hauptpunkte: Datei und Hilfe, die stehen linksbündig. Die Untereinträge sind dann mit der "->" Taste eingerückt. Während die Hauptauswahl keine ID und keinen Namen hat, bekommen die Menüeinträge einen Namen und eine ID wie andere Windows Controls auch, z.B. IDM_SAVE 10001. Mit "Preview" bekommen wir eine animierte Voransicht unseres Menüs. Die Keyboardfunktionen für das Menü sind in Windows fest integriert, die kommen automatisch (Alt für das Menu und Navigation mit den Pfeiltasten). Wenn wir für die Haupteinträge Buchstaben zum Aufrufen verwenden wollen, geht das mit einem "&" vor dem Buchstaben, also statt "Datei" "&Datei" oder "D&atei". Die Funktion "Accelerator" im Editor ist nicht lokalisiert, brauchen wir auch nicht. Es geht um den Hilfetext, der hinter den Menüeinträgen steht, und das können wir auch manuell mit einem "\t". So bewirkt also "Öffnen\tStrg + S" das als Hinweistext "Strg + S" angezeigt wird. Dann gibt es noch die Möglichkeit, einen Querstrich zwischen Menüeinträgen zu erzeugen, den erzeugen wir mit der Caption "-" und sonst nix. Checked sind Menüeinträge, die mit einem Haken davor erscheinen sollen, und Grayed-Einträge sind deaktiviert. Wie man das zur Laufzeit ändert, erkläre ich zusammen mit den Bitmaps, die man in den Menüs zeichnen kann.
Die Namen und ID's müssen dann wie gehabt in unsere Dialog.bi und die Message in der Callbackfunktion ist WM_COMMAND / BN_CLICKED wie bei Buttons auch. Wir verbinden das Menü mit unserem Fenster, indem wir den Namen des Menüs (IDR_MENU1) in der Property "Menu" bei unserem Dialog eintragen. Wenn wir Fensterklassen verwenden, können wir das auch bei der WNDCLASSEX Struktur für alle Fenster machen.
Kontextmenüs
Wenn wir bei den Menüs sind, können wir auch gleich das Kontextmenü abhandeln. Wir erzeugen dies als neues Menü in der Rescource, das auf Menuleisten-Ebene nur einen Eintrag mit der Caption "dummy" hat. Die Subeinträge sind dann die Einträge unseres Kontextmenüs. Alles wieder in die *.bi verlinken. Dann brauchen wir nur noch den Code, den man von C nach Basic übersetzt aus der MSDN direkt übernehmen kann (manchmal ist Microsoft auch mal nett!):
'If the mouse coordinates are in the client area: display the context menu
Function OnContextmenu(ByVal hWin As HWND, ByVal x As Integer, ByVal y As Integer) As BOOL
Dim rc As RECT 'client area of window
Dim pt As Point = (x,y) 'location of mouse click
Dim As HMENU hmenu, hmenuTrackPopup 'handle to the menu
'Get the bounding rectangle of the client area
GetClientRect(hWin,@rc)
'Convert the mouse position to client coordinates
ScreenToClient(hWin,@pt)
'If the position is in the client area, display a shortcut menu
If PtInRect(@rc,pt) Then
ClientToScreen(hWin,@pt)
'loads the Top-level Menu
hmenu = LoadMenu(hInstance,Cast(ZString Ptr,IDM_CONTEXTMENU))
'get a handle to the first subitem
hmenuTrackPopup = GetSubMenu(hmenu,0)
'display the menu at the mouse position
TrackPopupMenu(hmenuTrackPopup,TPM_LEFTALIGN Or TPM_RIGHTBUTTON, pt.x, pt.y, 0, hWin, NULL)
DestroyMenu(hmenu)
Return TRUE
EndIf
Return FALSE
End Function
Aufgerufen wird die Funktion bei der WM_CONTEXTMENU-message:
Case WM_CONTEXTMENU
'If the mouse coordinates are in the client area, display the context menu
If OnContextMenu(hWin, LoWord(lParam), HiWord(lParam) ) Then
Return DefWindowProc(hWin, uMsg, wParam, lParam)
End If
Auch wieder: kurz und schmerzlos, wenn gewusst wie.
Menüeinträge ändern: Bitmaps hinzufügen
Wie ich schon erwähnt habe, kann man existierende Menüs zur Laufzeit ändern, zum Beispiel um sie zu aktivieren oder ihren Status von "checked" auf "unchecked" zu ändern. Das geht mit der Funktion SetMenuItemInfo. Die dazugehörige MENUITEMINFO Struktur in der winuser.bi ist aber verbuggt (die letzte Zeile hbmpitem as HBITMAP fehlt). Entweder also die Definitionsdatei ändern oder schnell mal unsere eigene Struktur definieren:
Type MENUITEMINFOC
cbSize as UINT
fMask as UINT
fType as UINT
fState as UINT
wID as UINT
hSubMenu as HMENU
hbmpChecked as HBITMAP
hbmpUnchecked as HBITMAP
dwItemData as DWORD
dwTypeData as LPWSTR
cch as UINT
hbmpitem as HBITMAP
End Type
dabei definieren wir schnell noch einen HMENU für unser Menu, ein HBITMAP für die Bitmap und integrieren die Bitmap folder.bmp als BMP_FOLDER in unsere Recource, aber das hatten wir ja schon alles.
Dann packen wir das in unsere Fenster-Initialisierung (WM_INITDIALOG geht mal wieder nicht, da das Menu da noch nicht gezeichnet ist):
'get the handle of the hMain-Window menu:
hMenu=GetMenu(hMain)
'load the bitmap for the menuitem @16x16 pixels:
hBmp1=LoadImage(hInstance,Cast(ZString Ptr,BMP_FOLDER),IMAGE_BITMAP,16,16,LR_DEFAULTCOLOR)
Dim As MENUITEMINFOC mii
mii.cbSize = SizeOf(MENUITEMINFOC)
'set Bitmap for IDM_FOLDER
mii.fMask = MIIM_BITMAP
mii.hbmpItem = hBmp1 'Bitmap fot the item
SetMenuItemInfo(hMenu,IDM_FOLDER,FALSE,Cast(MENUITEMINFO Ptr,@mii) )
'set IDM_ABOUT to Checked and Radiobutton style
mii.fMask = MIIM_FTYPE Or MIIM_STATE
mii.fType = MFT_RADIOCHECK
mii.fState = MFS_CHECKED
SetMenuItemInfo(hMenu,IDM_ABOUT,FALSE,Cast(MENUITEMINFO Ptr,@mii))
Die Funktion kann natürlich immer noch viel mehr, man könnte auch die Images für die "Checked" und "Unchecked" - Version ändern. Das wir einfach eine neue Struktur erzeugen können und die zur Funktion passt, wird richtig interessant, wenn es zu den Drag&Drop Interfaces kommt. Kurz gesagt erwartet Windows ab der mit dem Pointer übergebenen Speicherstelle die Variablen in der Reihenfolge wie sie im TYPE definiert sind - und dabei ist es im Prinzip egal, wie die Elemente oder der Type eigentlich heißen.
Der ganze Code bevölkert jetzt die Tutorial7.zip.
Zusätzliche Informationen und Funktionen | |||||||
---|---|---|---|---|---|---|---|
|
|