FORM Objects + EVENTS Updated: July 11, 2009 The system may report a number of events with a simple mouse click. Therefore, internal variable nCode (notify code) may be examined in event subroutines to filter desired events (HBguidef.inc). For example, COMBOBOX and LISTBOX events may be filtered as shown in hotwin.bas source code. Note: Based on available information, a HotBasic application will most often set hWnd to the handle of the message sender upon calling event subroutines. All event procedures have up to five useful arguments, in HotBasic internal variables hWnd, uMsg, wParam, lParam and nCode. hWnd is the FORM object handle. uMsg is the message type. E.g., uMsg indicates the mouse button used: Left Right Middle OnMouseDown &H201 &H204 &H207 OnMouseUp &H202 &H205 &H208 OnDblClick &H203 &H206 &H209 wParam and lParam provide additional data specific to the message type. nCode is the specific event evoking the message, where applicable, described in HBguidef.inc in your "inc" subdirectory. An event procedure cannot assume that these values will not be overwritten by startup of other event procedures, since even a single GUI application may have multiple threads running. Thus, if one or more of the event arguments are needed, they should be saved immediately upon entry into your procedure: DEFINT vKey MyKeyDownProc: vKey = wParam 'save wParam As above, event procedures may start with a LABEL and end with RETURN. Or DECLARE SUB MyKeyDownProc 'no arguments listed here 'code MyForm.OnKeyDown = MyKeyDownProc 'code SUB MyKeyDownProc vKey = wParam 'save wParam 'code END SUB EVENTS Defines subroutine called on event ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OnClick MyButton.OnClick = RunMyCode .OnClick is a generic user-select event, which might be called ".OnSelect" instead. For example, a user LISTBOX selection occurs with doubleclick. Keyboard inputs may also cause a user-select event. If you want mouse only, try .OnMouseDown. Most .OnClick events arise from WM_COMMAND (uMsg = &H111) messages. nCode is the "notify code" -- the HIWORD of wParam. The LOWORD of wParam is the .ID of the object originating the message. MyID = LOWORD(wParam) lParam is the handle of the object which is most often that of the main form, since child objects send messages through the parent. For SCROLLBAR and TRACKBAR, lParam is .Position for appropriate nCodes. Some .OnClick events arise from WM_NOTIFY (uMsg = &4E) messages where lParam is not a handle, but rather a pointer to a NMHDR structure. Generally, the .OnClick event (WM_COMMAND) is processed only by FORM or SPLASH parents. That is, if a BUTTON is set as child of something else, such as a TABCONTROL, PANEL, GROUPBOX, etc, .OnClick will not produce any response for the simple reason that these objects *do not* process WM_COMMAND messages. For such "child of child" objects, use .OnMouseDown instead (please see example in hotchild.bas in the HotToys download). OnClose For forms, the close button in the title bar normally closes the form displayed with a .ShowModal statements allowing subsequent code in the application to run. If you prefer that additional FORMs are "closed" without closing the main form, try using .OnClose: form2.OnClose = form2_close 'code form2_close: form2.visible=false 'hide form2 instead of exiting application return For the main form, a "blank" .OnClose procedure prevents the user from closing the main form: form_close: return OnDblClick wParam and lParam as in .OnMouseDown OnKeyDown OnKeyUp .OnKeyDown and .OnKeyUp trap WM_KEYDOWN (uMsg = &H100) and WM_KEYUP (uMsg = &H101) messages where wParam = the virtual-key code of the non- system key (alt not pressed) and lParam contains details: Bits Description 0-15 repeat count = LOWORD(lParam) 16-23 scan code = HIWORD(lParam) AND &HFF 24 1 if the key is an extended key else 0 30 previous key state: 1 if the key is down before the message is sent for WM_KEYDOWN 1 for WM_KEYUP 31 transition state: 0 for WM_KEYDOWN, 1 for WM_KEYUP OnMessage (1) Usage with FORM events Traps Windows messages not processed by other HotBasic .On... events. This powerful event allows coders to process such messages to customize and expand HotBasic coding capabilities. Used with main FORM. =====onmsg.bas $APPTYPE GUI: $TYPECHECK ON SHOWCONSOLE DIM form As FORM form.onmessage = PrintThis form.ShowModal END PrintThis: 'normally you select your message here PRINT HEX$(hWnd);space;HEX$(uMsg);space;HEX$(wParam);space;HEX$(lParam) RETVAL zero 'Default Windows Procedure called as usual RETURN =====onmsg.bas To use .OnMessage, "RETVAL zero" or "RETVAL one" are essential to control whether the Default Windows Procedure is or is not called. With "RETVAL zero", window object behavior might remain the same, because its Default procedure is called as normal (without .OnMessage). With "RETVAL one", you have essentially bypassed the Default procedure and "replaced" it with yours, which in effect is a sort of "sub-classing" done in the HotBasic way -- simple and clean. In sum, your .OnMessage procedure (1) does what it wants for particular messages (uMsg) and (2) decides whether the Default Windows Procedure for the object and message is also run (RETVAL zero) or not (RETVAL one). (2) Usage with FORM child Objects (e.g., GRID, TABCONTROL, TREE, etc) An .OnMessage routine for a FORM child object is equivalent to .OnNotify and thus, the uMsg parameter is always WM_NOTIFY. "Something happened to me," is the event for a child object .OnMessage procedure. On entry, the wParam variable should equal the .ID property of the child object. lParam points to a NMHDR structure, where BYREF(lParam) equals the object's .Handle property and BYREF(lParam+8) is the notification code which tells you what happened. Coders can look up the codes that apply for the Object type. For error checking, note that wParam should equal BYREF(lParam+4); both values are the object's .ID property. OnMouseDown OnMouseMove OnMouseUp .OnMouseDown, .OnMouseMove and .OnMouseUp trap button events. wParam key flags are: $DEFINE MK_LBUTTON &H1 'left mouse button $DEFINE MK_RBUTTON &H2 'right mouse button $DEFINE MK_SHIFT &H4 'SHIFT key is down $DEFINE MK_CONTROL &H8 'CTRL key is down $DEFINE MK_MBUTTON &H10 'middle mouse button LOWORD(lParam) is mouse x and HIWORD(lParam) is mouse y position. OnPaint No message parameters. MyForm.OnPaint = form_paint 'code form_paint: MyForm.Circle 40,40,30,&HFFFFFF 'white return OnReSize wParam = a SIZE_... value; client width = LOWORD(lParam) and client height = HIWORD(lParam). OnShow wParam = 1 (show) or = 0 (hide) To run GUI programs from the command-line or from a .bat file, there may be no user key or mouse inputs. Thus, the main form .OnShow procedure will be called with FORM .Show (not .ShowModal): (1) Parse the command-line and set up your job, by setting EDIT text, CHECKBOX states, etc, anything a real-time user might otherwise do with key and mouse inputs in a "regular" run of the program. (2) Call the procedure that starts the job. For example, this may be an .OnClick procedure that would be called if a user had clicked a button. (3) Exit the program. If you watch the program run, it will "magically" appear with all the "right" settings, start running and close itself when the job is done. You never get out of the .OnShow procedure, unless it detects that certain command-line parameters are absent. If so, the .OnShow procedure is done and the next main program statement may be FORM .ShowModal for a regular user-controlled run. Since .Show does not start the GUI message loop, be sure to add DOEVENTS as needed in your .OnShow and procedure code. hbLoop ~~~~~~ hbLoop is an internal variable created in GUI apps. If hbLoop is non-zero, its value is assumed to be the entry address for a routine, e.g., hbLoop = codeptr(MyRoutine) which will process *all* messages before any further normal processing occurs. Here is a template for a hbLoop routine: MyRoutine: 'your code RETVAL zero ret The above code does nothing. Upon a message, before anything is done, hbLoop is checked and if non-zero, MyRoutine is called with hbMsg containing the address of the Windows' message structure which would contain all the info you need. E.g., BYREF(hbMsg) is the handle of the Window's object sending the message. Finally, with RETVAL X in MyRoutine, if X is not zero, then any further default processing of the message is aborted. In the above template, RETVAL zero assures that "normal" default processing of the message occurs. In sum, hbLoop provides an easy way to "preprocess" all app messages in a custom manner with the option to then run the default event processing (RETVAL zero) or not (RETVAL one). With the latter return value, you have the option to completely replace HotBasic's and Windows' default message processing with your own code. ~~~~~~~~~~~ Note: The ZERO keyword may be used to "turn off" any of the above events: MyObject.OnClick = zero At run-time, an application can change the procedure that handles an event: MyObject.OnClick = MyObjProc2 'was MyObjProc1 at startup + Penthouse (registered) version Copyright 2003-2009 James J Keene PhD Original Publication: Nov 18, 2003