Hi,
At various times, there have been issues raised about the GECLOSE event not triggering validations on forms. What happens then is that the validation runs after the form is closed, and if it fails it crashes the GUI environment by trying to activate a control on the closed form. See for example:
http://forum.asent.com/topic2466.html http://forum.asent.com/topic872.html http://forum.asent.com/topic993.html
I have now written a subroutine to handle this situation, based on the code in posts 872 and 993. This is shown below (see the notes following the subroutine):
SUBROUTINE Q.ATGUI.CHECKCHANGED(ischanged, guierrors, guistate) ***************************************************************************** * Bp.Q Q.Atgui.CheckChanged - Check whether a control has changed data when * leaving a form, and post the validation if required. * * Author : BSS * Created: 13 Apr 2015 * Updated: 13 Apr 2015 * Version: 1.0.0 * * Based on code in the AccuTerm forum: * http://forum.asent.com/topic993.html * http://forum.asent.com/topic872.html * * Use this subroutine in the form close event handler as follows: * * CALL Q.ATGUI.CHECKCHANGED(ischanged, guierrors, guistate) * IF (guierrors<1> GE ERR.FAIL) THEN RETURN TO progexit (or gui error handler) * IF (ischanged) THEN RETURN * * Close the form * * ------------------------------------------------------------------------- * * $INCLUDE GUIBP ATGUIEQUATES EQUATE ERR.FAIL TO 2
ischanged = @FALSE * * Get active control * CALL ATGUIGETPROP(GXROOT, '', '', GPSTATUS, 1, 0, actid, guierrors, guistate) IF (guierrors<1> GE ERR.FAIL) THEN RETURN
actapp = OCONV(FIELD(actid, '*', 1), 'MCU') actfrm = OCONV(FIELD(actid, '*', 2), 'MCU') actctl = OCONV(FIELD(actid, '*', 3), 'MCU') * * Has it changed? * CALL ATGUIGETPROP(actapp, actfrm, actctl, GPCHANGED, 0, 0, ischanged, guierrors, guistate) IF (guierrors<1> GE ERR.FAIL) THEN RETURN
IF (ischanged) THEN * * Get the event mask and changed value of control * CALL ATGUIGETPROP(actapp, actfrm, actctl, GPEVENTMASK, 0, 0, eventmask, guierrors, guistate) IF (guierrors<1> GE ERR.FAIL) THEN RETURN CALL ATGUIGETPROP(actapp, actfrm, actctl, GPVALUE, 0, 0, avalue, guierrors, guistate) IF (guierrors<1> GE ERR.FAIL) THEN RETURN * * Check event mask for events that we should respond to * eventlist = '' argslist = '' ;* Note: args are LOWER'ed in this list gridrow = '' gridcol = '' CALL Q.GUI.EVENTS.CONVERT('OS', events, eventmask) ;* Convert mask to event names; Sort names.
LOCATE 'CHANGE' IN events<1> SETTING epos THEN eventlist<-1> = GECHANGE argslist<-1> = avalue END LOCATE 'VALIDATECELL' IN events<1> SETTING epos THEN CALL ATGUIGETPROP(actapp, actfrm, actctl, GPROW, 0, 0, gridrow, guierrors, guistate) IF (guierrors<1> GE ERR.FAIL) THEN RETURN CALL ATGUIGETPROP(actapp, actfrm, actctl, GPCOLUMN, 0, 0, gridcol, guierrors, guistate) IF (guierrors<1> GE ERR.FAIL) THEN RETURN eventlist<-1> = GEVALIDATECELL argslist<-1> = gridcol:@SM:gridrow:@VM:avalue END LOCATE 'VALIDATEROW' IN events<1> SETTING epos THEN IF (gridrow EQ '') THEN CALL ATGUIGETPROP(actapp, actfrm, actctl, GPROW, 0, 0, gridrow, guierrors, guistate) IF (guierrors<1> GE ERR.FAIL) THEN RETURN END eventlist<-1> = GEVALIDATEROW argslist<-1> = gridrow:@VM:LOWER(avalue) END LOCATE 'DEACTIVATEROW' IN events<1> SETTING epos THEN IF (gridrow EQ '') THEN CALL ATGUIGETPROP(actapp, actfrm, actctl, GPROW, 0, 0, gridrow, guierrors, guistate) IF (guierrors<1> GE ERR.FAIL) THEN RETURN END eventlist<-1> = GEDEACTIVATEROW argslist<-1> = gridrow END LOCATE 'VALIDATE' IN events<1> SETTING epos THEN eventlist<-1> = GEVALIDATE argslist<-1> = @VM:LOWER(avalue) END LOCATE 'DEACTIVATE' IN events<1> SETTING epos THEN eventlist<-1> = GEDEACTIVATE argslist<-1> = '' END * * If we don't need to respond to the event, then set the changed flag to false * IF (eventlist EQ '') THEN ischanged = @FALSE END ELSE * * Otherwise, clear any existing events, and post the new events to the queue * Note: We can't repost the GECLOSE here in case the validation fails and we set up a loop! * CALL ATGUICLEAREVENTS(actapp, '', '', 0, 1, guierrors, guistate) IF (guierrors<1> GE ERR.FAIL) THEN RETURN
dce = DCOUNT(eventlist, @AM) FOR eventno = 1 TO dce event = eventlist<eventno> args = RAISE(argslist<eventno>) CALL ATGUIPOSTEVENT(actapp, actfrm, actctl, event, args, -1, guierrors, guistate) IF (guierrors<1> GE ERR.FAIL) THEN RETURN NEXT eventno END END
RETURN * * ------------------------------------------------------------------------- * * END
Notes:
You will need to write your own subroutine to convert the eventmask to a list of events. In this subroutine, this is the call to: Q.GUI.EVENTS.CONVERT
This subroutine will need testing in your own situation to make sure it picks up all relevant events - AND TO MAKE SURE THAT THE EVENTS ARE HANDLED IN THE CORRECT ORDER.
I hope this helps a few people.
Cheers,
Brian
|