So, ich möchte euch das Ergebnis natürlich nicht vorenthalten, evtl. kann es jemand gebrauchen (so wie ich gerade

)
Dieser Custom-Messagerequester unterstützt bis zu 9 Antwortbuttons.
Als Ergebnis wird die Indexnummer des gedrückten Buttons zurückgegeben, beginnend mit 0 oder aber -1, wenn der Requester mit dem [X] geschlossen wurde.
Ich habe versucht das Verhalten so nah wie möglich an die Windows System-Messagebox() zu bringen.
Der Custom-Requester unterstützt mehrzeiligen Text und die Standard MessageBox()-Icons (#MB_ICONASTERISK, #MB_ICONINFORMATION usw...).
Die Antwortbuttons werden am unteren Rand des Requesters zentriert dargestellt und es ist ein Defaultbutton definierbar.
Der Requester öffnet sich Screencentered und er ist modal, d..h. er blockiert die Eingaben seines Parent-Fensters solange er geöffnet ist.
Das einzige, was ich nicht hinbekommen habe ist, daß der aktuell aktive Button statt mit SPACE auch mit der ENTER-taste auswählbar ist. Vielleicht hat hier noch jemand eine Idee.
Die Ausmaße des Requester-Fensters werden auf Basis der API-Funktion GetTextExtentPoint32_() berechnet. Diese ermittelt anhand eines Strings und des aktuellen DeviceContexts, in dem der String "gedrawt" werden würde, die Gesamtbreite in Pixeln.
Das ganze scheint mir aber nicht sonderlich genau zu erfolgen, daher das Gefummle mit den Multiplikatoren.
Außerdem steht der DeviceContext des Requesterfensters zum Zeitpunkt der Berechnung noch nicht zur Verfügung (das muß vor dem OpenWindow() passieren), deshalb nutzt die Berechnung den DeviceContext des Parent-Fensters.
Über Verbesserungsvorschläge würde ich mich freuen.
EDIT:
- Auswahl per Returntaste geht jetzt auch
- Wechsel zwischen den Buttons mittels Cursortasten geht ebenfalls
(das Verhalten ist hier dem originalen Windosrequester angepaßt = der DefaultButton wird dadurch ebenfalls verschoben)
- MessageBeep gemäß Messageart implementiert
Aufruf und Parameter:
MessageRequesterCustom(sTitle.s, sText.s, sButtonsText.s, iDefaultButton.i, iIcon.i, iParentWindow.i)
sTitle : Titel des Requesters
sText : Text des Requesters
sButtonstext : Text der in den Buttons erscheint (mehrere Texte/Buttons werden mit "|" getrennt, max. 9 Buttons)
iIcon : Konstante für die Darstellung des RequesterIcons (IDI_APPLICATION, IDI_ASTERISK, IDI_EXCLAMATION, IDI_HAND, IDI_QUESTION, IDI_WINLOGO)
iParentWindow : Handle des übergeordneten Fensters, dessen Eingaben blockiert werden
Hier ein Bild eines normalen Systemrequesters:
Und hier ein paar variationen des Custom Requesters:
Und hier der zugehörige Code:
Code: Alles auswählen
EnableExplicit
Procedure.i MessageRequesterCustom(sTitle.s, sText.s, sAllButtonsText.s, iButtonMinsize.i, iDefaultButton.i, iIcon.i, iParentWindow.i)
; +-----------------------------------------------------------------
; |Description : Darstellen eines benutzerdefinierten, modalen Requesters mit bis zu 10 verschiedenen Antwort-Buttons
; |Arguments : sTitle : Titel des Requesters
; | : sText : Text des Requesters (TABs werden nicht unterstützt!)
; | : sAllButtonsText: Text der in den Buttons erscheint (mehrere Texte werden mit "|" getrennt)
; | : iButttonMinsize: Minimale Breite eines Buttons in Pixeln (Standard = 75)
; | : iDefaultButtton: Nr. des Defaultbuttons, der optisch hervorgehoben wird und mit Return auswählbar ist
; | : iIcon : Konstante für die Darstellung des RequesterIcons (IDI_APPLICATION, IDI_ASTERISK, IDI_EXCLAMATION, IDI_HAND, IDI_QUESTION, IDI_WINLOGO)
; | : iParentWindow : Handle des übergeordneten Fensters, dessen Eingaben blockiert werden
; |Result : Nr. des Knopfes, der gedrückt wurde (beginnend bei 0) bzw. -1, wenn der Requester mit [X] oder ESC geschlossen wurde
; +-----------------------------------------------------------------
Protected iRequester.i, iTextGadget.i, iImageGadget.i
Protected iParentWindowDC.i
Protected iEvent.i, iEventMenu.i, iEventWindow.i, iEventGadget.i, iEventType.i, iCount.i, iPosition.i, iLastPosition.i, iLongestLinePosition1.i, iLongestLinePosition2.i, iQuit.i = -1
Protected iAllButtonsWidth.i, iButtonPositionX.i, iMaxButtons.i = -1, iTextLen.i, iTextPositionX.i, iWindowWidth.i, iWindowHeight.i
Protected sButtonText.s, sLongestLineText.s
Protected stTextsize.SIZE
Protected Dim iButton.i(9), Dim iButtonWidth.i(9), Dim sButtonText.s(9)
#MRC_ESC = 1
#MRC_Return = 2
#MRC_CursorLeft = 3
#MRC_CursorRight = 4
; Evtl. vorhandenen #CR$ löschen, so daß nur noch #LF$ als Zeilenumnbruchzeichen bleibt
; ReplaceString(sText, #CR$, "")
; Icon Konstante umwandeln und Textoffset ermitteln
iTextPositionX = 60
Select iIcon
Case #MB_ICONASTERISK, #MB_ICONINFORMATION
iIcon = #IDI_ASTERISK
Case #MB_ICONEXCLAMATION
iIcon = #IDI_EXCLAMATION
Case #MB_ICONERROR, #MB_ICONHAND, #MB_ICONSTOP
iIcon = #IDI_HAND
Case #MB_ICONQUESTION, #MB_ICONWARNING
iIcon = #IDI_QUESTION
Case #IDI_WINLOGO
;Bleibt so
Case #IDI_APPLICATION
;Bleibt so
Default
iTextPositionX = 10
EndSelect
; Die längste Zeile aus sText ermitteln
iTextLen = Len(sText)
Repeat
iPosition = FindString(sText, #LF$, iPosition + 1)
If iPosition = 0
iPosition = iTextLen
EndIf
If (iPosition - iLastPosition) > (iLongestLinePosition2 - iLongestLinePosition1)
iLongestLinePosition1 = iLastPosition
iLongestLinePosition2 = iPosition
EndIf
iLastPosition = iPosition
Until iPosition = iTextLen
sLongestLineText = Mid(sText, iLongestLinePosition1, (iLongestLinePosition2 - iLongestLinePosition1))
iParentWindowDC = GetDC_(WindowID(iParentWindow))
; Maximal benötigte Breite des Fensters ermitteln
iTextLen = Len(sLongestLineText)
GetTextExtentPoint32_(iParentWindowDC, sLongestLineText, iTextLen, @stTextSize.SIZE)
iWindowWidth = stTextsize\cx + iTextPositionX - iTextLen * 1.7
; Breite der Buttons in Pixeln ermitteln (auf Basis des DCs des übergeordneten Fensters) und Buttontexte extrahieren
For iCount = 0 To 9
sButtonText = StringField(sAllButtonsText, iCount + 1, "|")
If sButtonText <> ""
iTextLen = Len(sButtonText)
GetTextExtentPoint32_(iParentWindowDC, @sButtonText, iTextLen, @stTextSize.SIZE)
iButtonWidth(iCount) = stTextsize\cx + 10 - iTextLen * 1.7
If iButtonWidth(iCount) < iButtonMinsize : iButtonWidth(iCount) = iButtonMinsize : EndIf
iAllButtonsWidth + iButtonWidth(iCount) + 10
sButtonText(iCount) = sButtonText
iMaxButtons + 1
EndIf
Next iCount
; Falls die Buttons breiter sind als die errechnete Fensterbreite, dann muß diese angepaßt werden
If iAllButtonsWidth > iWindowWidth
iWindowWidth = iAllButtonsWidth + 10
iButtonPositionX = 10
Else
iButtonPositionX = (iWindowWidth - iAllButtonsWidth) / 2
EndIf
; Maximal benötigte Höhe des Fensters ermitteln (Zählen der Zeilenumbrüche und multiplizieren mit Texthöhe mal Faktor)
iWindowHeight = CountString(sText, #LF$)
If iWindowHeight > 2
iWindowHeight = 105 + (iWindowHeight - 2) * (stTextsize\cy * 0.8)
Else
iWindowHeight = 105
EndIf
ReleaseDC_(iParentWindow, iParentWindowDC)
iRequester = OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, iWindowWidth, iWindowHeight, sTitle, #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_Invisible, WindowID(iParentWindow))
If iRequester
DisableWindow(iParentWindow, 1)
AddKeyboardShortcut(iRequester, #PB_Shortcut_Escape, #MRC_ESC)
AddKeyboardShortcut(iRequester, #PB_Shortcut_Return, #MRC_Return)
AddKeyboardShortcut(iRequester, #PB_Shortcut_Left, #MRC_CursorLeft)
AddKeyboardShortcut(iRequester, #PB_Shortcut_Right, #MRC_CursorRight)
AddKeyboardShortcut(iRequester, #PB_Shortcut_Tab|#PB_Shortcut_Shift, #MRC_CursorLeft)
AddKeyboardShortcut(iRequester, #PB_Shortcut_Tab, #MRC_CursorRight)
; Buttons platzieren
For iCount = 0 To iMaxButtons
iButton(iCount) = ButtonGadget(#PB_Any, iButtonPositionX, iWindowHeight - 35, iButtonWidth(iCount), stTextsize\cy + 7, sButtonText(iCount))
iButtonPositionX + iButtonWidth(iCount) + 10
Next iCount
SendMessage_(GadgetID(iButton(iDefaultButton)), #BM_SETSTYLE, #BS_DEFPUSHBUTTON|#BS_TEXT|#BS_PUSHBUTTON, #False)
SetActiveGadget(iButton(iDefaultButton))
; Messagebeep erzeugen
MessageBeep_(iIcon)
; Icon und Text platzieren
If iTextPositionX > 10
iImageGadget = ImageGadget(#PB_Any, 10, 10, 32, 32, LoadIcon_(#Null, iIcon))
EndIf
iTextGadget = TextGadget(#PB_Any, iTextPositionX, 20, iWindowWidth - iTextPositionX , iWindowHeight - 55, sText)
; Fenster darstellen
HideWindow(iRequester, 0)
Repeat
iEvent = WaitWindowEvent(25)
iEventType = EventType()
iEventWindow = EventWindow()
iEventGadget = EventGadget()
iEventMenu = EventMenu()
If iEventWindow = iRequester
If iEvent = #PB_Event_Menu
Select iEventMenu
Case #MRC_ESC ; Taste ESC wurde gedrückt
iEvent = #PB_Event_CloseWindow
Case #MRC_Return ; Taste Return wurde gedrückt
iQuit = iButton(iDefaultButton)
Case #MRC_CursorLeft ; Cursor Links wurde gedrückt
If iDefaultButton = 0
iDefaultButton = iMaxButtons
Else
iDefaultButton - 1
EndIf
Case #MRC_CursorRight ; Cursor Rechts wurde gedrückt
If iDefaultButton = iMaxButtons
iDefaultButton = 0
Else
iDefaultButton + 1
EndIf
EndSelect
; Buttons refreshen
For iCount = 0 To iMaxButtons
SendMessage_(GadgetID(iButton(iCount)), #BM_SETSTYLE, #BS_TEXT|#BS_PUSHBUTTON|#BS_PUSHLIKE, #True)
Next iCount
SendMessage_(GadgetID(iButton(iDefaultButton)), #BM_SETSTYLE, #BS_DEFPUSHBUTTON|#BS_TEXT|#BS_PUSHBUTTON|#BS_PUSHLIKE, #True)
SetActiveGadget(iButton(iDefaultButton))
EndIf
If iEvent = #PB_Event_Gadget And iEventType = #PB_EventType_LeftClick And iEventGadget <> iImageGadget
iQuit = iEventGadget
EndIf
EndIf
Until iQuit > -1 Or iEvent = #PB_Event_CloseWindow
; Ermitteln des gedrückten Buttons
If iEvent <> #PB_Event_CloseWindow
For iCount = 0 To iMaxButtons
If iButton(iCount) = iQuit
iQuit = iCount
EndIf
Next iCount
EndIf
RemoveKeyboardShortcut(iRequester, #PB_Shortcut_Tab)
RemoveKeyboardShortcut(iRequester, #PB_Shortcut_Tab|#PB_Shortcut_Shift)
RemoveKeyboardShortcut(iRequester, #PB_Shortcut_Right)
RemoveKeyboardShortcut(iRequester, #PB_Shortcut_Left)
RemoveKeyboardShortcut(iRequester, #PB_Shortcut_Return)
RemoveKeyboardShortcut(iRequester, #PB_Shortcut_Escape)
DisableWindow(iParentWindow, 0)
CloseWindow(iRequester)
EndIf
ProcedureReturn iQuit
EndProcedure
; ------------------------
; --- Requester-Demo -----
; ------------------------
Define iMainWindow.i, sLoadText.s
iMainWindow = OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, 600, 600, "Ich bin das übergeordnete Fenster", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
If OpenFile(0, "D:\Test.txt") ; Zu Demonstrationszwecken einen 20 - 30 Zeiligen Text einlesen
While Eof(0) = 0
sLoadText + ReadString(0) + #LF$ + #CR$
Wend
CloseFile(0)
EndIf
MessageRequesterCustom("CUSTOM-REQUESTER", "Erste Zeile." + #LF$ + sLoadText, "Mach dies!|Mach jenes|Ach nee, mach doch lieber das", 0, #MB_ICONERROR, iMainWindow)
MessageRequesterCustom("CUSTOM-REQUESTER", "Erste Zeile.", "Mach dies!|Mach jenes|Ach nee, mach doch lieber das", 0, #MB_ICONASTERISK, iMainWindow)
MessageRequesterCustom("CUSTOM-REQUESTER", "Erste Zeile." + #LF$ + "3" + #LF$ + "3", "Mach dies!|Mach jenes|Ach nee, mach doch lieber das", 0, #MB_ICONEXCLAMATION, iMainWindow)
MessageRequesterCustom("CUSTOM-REQUESTER", "Erste Zeile." + #LF$ + "4" + #LF$ + "4" + #LF$ + "4", "Mach dies!|Mach jenes|Ach nee, mach doch lieber das", 0, #MB_ICONMASK, iMainWindow)
MessageRequesterCustom("CUSTOM-REQUESTER", "Erste Zeile." + #LF$ + "5" + #LF$ + "5" + #LF$ + "5" + #LF$ + "5" + #LF$ + "5", "1|2|3|4|5|6|7|8|9|Abbrechen", 0, #IDI_APPLICATION, iMainWindow)
End
Gruß Kurzer
Edit die X-te = Aktuellen, verbesserten Code eingefügt.