Override 'Cancel' in event procedures - forms

There is data validation in my MS Word user form which returns the focus to the textbox where the user entered something incorrect. Now I am trying to accommodate the user's change of mind: instead of correcting the entry, I want him to be able to exit the form (click the Exit command button), in which case the entry would be discarded. I suppose that a solution would start with not using the text box's exit event. I little help from someone who knows the answer would save me a lot of testing time, perhaps to find out that I can't do it.
Does anyone know?

I understand that you are handling the Exit event of the Textbox, setting the Cancel output parameter if the data is not valid.
There's a tricky but simple solution that permits to keep that working and still have an Exit button. It permits to activate the handler of the Exit button without requiring the focus to leave the Textbox. This way you can unload the Form safely in this handler.
Try this it works pretty smoothly:
1- Set the property TakeFocusOnClick of the Exit command button to False. You can do that at design time in the property-sheet, or at run-time i.e. at UserForm_Activate
2- just unload the form when the Exit button is clicked:
Private Sub ExitButton_Click()
Unload Me
End Sub

#A.S.H provided the key to the solution below. His point is that it is possible to call another event procedure while Cancel is active in the Exit procedure of a control. That other procedure can be used to rectify the condition in the first control which is triggering the Cancel, thereby enabling an orderly exit. The all-enabling condition is that the control on whose click event the rectifying procedure is to run must not take the focus when clicked (meaning it can run without triggering an exit from the control stuck on Cancel). I have added code to the exit procedure to set CmdExit.TakeFocusOnClick = False when a Cancel condition arises there. Now, ...
Private Sub CmdExit_Click()
' 12 May 2017
' if CmdExit can't take the focus it can't be the ActiveControl
If Not ActiveControl Is CmdExit Then
Select Case ActiveControl.Name
Case "Cbx107"
Cbx107.Value = ""
Case "Tbx53"
Tbx53.Value = "0"
End Select
With CmdExit
If Not .TakeFocusOnClick Then
.TakeFocusOnClick = True
.SetFocus
End If
End With
End If
' now CmdExit is the ActiveControl
MsgMe "Cmd Exit: ActiveControl = " & ActiveControl.Name
Me.Hide
End Sub

Related

Why Seting the SetMode to orbit disables custom KeyPressFcn event handlers, the callback

1-The code below displays the properties of the pressed key.Try it by pressing a key and observe the results.
figure('Name','Press keys to put event data in Command Window',...
'KeyPressFcn',#(obj,evt)disp(evt));
you will see outputs like this( e.g upon pressing space bar)
Character: ' '
Modifier: {1x0 cell}
Key: 'space'
2-Now simply add the following line of code to above ( or simply execute it before clearing the workspace)
cameratoolbar('SetMode','orbit');
Now press any key and nothing happens! the control will no longer be transferred to your costume call back function! ( here:#(obj,evt)disp(evt)).
same thing happens for WindowButtonDownFcn, WindowButtonUpFcn too.
How can I get around this? I wanna be able to handle KeyPressFcn or WindowButtonDownFcn after executing cameratoolbar('SetMode','orbit').
I found the answer: Once the cameratoolbar('SetMode','orbit') is called one of these two happens:the handle to the figure is lost or the event handler gets its default value. I am not sure which one though. Therefore we can add the following code to re-assign the lost handler back to our own call back function:
set(gcf,'KeyPressFcn',#(obj,evt)disp(evt))

Using REPEAT a block till an event occurs?

This is my issue:
On triger of a START button i wan to execute one block. And it should stop executing when I
press STOP button.
Eg
on triger of start button:
REPEAT:
message "hai".
END.
and when I press STOP button It should stop. What additional condition should I give to REPEAT block?Preferably I dont want to write the condition in STOP button Triger..Please give suggestion .
Progress is not multi-threaded.
So you have to carefully think through which bits of code need to execute in what context in order to fake it. (More carefully than my initial "it cannot be done" response.)
As "firhang" points out "PROCESS EVENTS" can be used to check for events queued and ready to be acted on.
So you could run your loop inside an event handler and have that event handler listen for additional events.
I believe that the following satisfies the original question and works in both GUI and Character clients:
define variable i as integer no-undo.
define variable bStop as logical no-undo.
define button butStart label "Start".
define button butStop label "Stop".
form butStart butStop with frame but-frame row 1.
on choose of butStop bStop = true.
on choose of butStart do:
bStop = false.
do while bStop = false:
i = i + 1.
process events.
message i.
end.
message "Paused".
end.
enable butStart butStop with frame but-frame.
pause 0 before-hide.
wait-for window-close of current-window.
PROCESS EVENTS statement
Processes all outstanding events without blocking for user input.
Syntax
PROCESS EVENTS
Example
This procedure counts to 1,000 until you choose STOP:
r-proevs.p
DEFINE VARIABLE ix AS INTEGER NO-UNDO.
DEFINE VARIABLE stop-sel AS LOGICAL NO-UNDO.
DEFINE BUTTON stop-it LABEL "STOP".
DISPLAY stop-it.
ON CHOOSE OF stop-it
stop-sel = TRUE.
ENABLE stop-it.
DO ix = 1 TO 1000:
DISPLAY ix VIEW-AS TEXT.
PROCESS EVENTS.
IF stop-sel THEN LEAVE.
END.
On each pass through the loop, the procedure displays the new value of ix and then checks whether any events are waiting to be processed. If no events have occurred, execution continues and the loop iterates. If the STOP button has been chosen, that event is processed changing the value of stop-sel. When execution continues, the program exits the loop.
If the loop does not contain the PROCESS EVENTS statement, the choose event never processes and the loop iterates until ix equals 1,000.
Notes
The WAIT-FOR statement processes all pending events and blocks all other execution until a specified event occurs. The PROCESS EVENTS statement processes all pending events and immediately continues execution with the next statement.
If there are any asynchronous requests for which PROCEDURE-COMPLETE events have been received but not yet processed, this statement processes these events as described for the WAIT-FOR statement.
You cannot call the .NET method system.Windows.Forms.Application:DoEvent( ) in ABL. The PROCESS EVENTS statement performs the function of this method.
.NET can raise exceptions in the context of an ABL session when this statement executes.
DEFINE VARIABLE bStop AS LOGICAL NO-UNDO.
DEFINE BUTTON butStop LABEL "Stop".
FORM butStop WITH FRAME but-frame ROW 1.
ON CHOOSE OF butStop bStop = TRUE.
ENABLE butStop WITH FRAME but-frame.
REPEAT:
PROCESS EVENTS.
MESSAGE "hai".
IF bStop THEN LEAVE.
END.
MESSAGE "stoped".
WAIT-FOR WINDOW-CLOSE OF CURRENT-WINDOW.

Using pstimer to run an event

I want to execute a function or a procedure or a block in regular intervals of 60seconds.
Do i have to use PS timer for that?
let this be my block
MyString = myEditor.Screen-Value.
Message MyString.
//myEditor is my Editor widget in the frame.
//My String is a string which i will use to display
I want to repeat this in each 60 seconds.. So that everytime it should display whatever I typed
inside the editor.? How can I do this with using PSTimer or without using it?
You must generate a Tick trigger block.
In this block can you write your code.
PSTimer has a property "Interval", which means interval for Tick event in miliseconds.
It has also other property "Enabled", witch starts and stopts timer.
When you run some longer code from your Tick event, schould be better switch that property "Enabled" on FALSE and on end of the trigger over on TRUE. Otherwise you can become a conflict with your code and new Tick event resp. your program will making never other, only showing runing your trigger script.

MsgBox made with wscript.shell won't close automatically in this case

I am writing an .mdb file in ms-access, and with a form inside.
I've made a Subroutine to show a msgbox which will close automatically after some secs.
Sub TimedMsgBox(Message As String)
CreateObject("wscript.shell").PopUp _
Message & vbCrLf & vbCrLf & _
"This message self-closes in 2 seconds...", 2, "Message"
End Sub
When the msgbox popup, I wait and it close automatically, however, if I change my focus to other application such as firefox, the message will just wait for my attention and didn't close after the selected seconds.(i.e: it is not counting the seconds.) When I change my focus back to it, then it will count the seconds then exit.
How can I made it automatically disappear after some seconds no matter what I am doing?
If it is possible my suggestion would be to create a form instead of a msgbox. Then you can use the On Timer event on the form to automatically close after a period of time.
In my database I have a mainform that gets loaded by the users but when that form opens, I also open a hidden 'ExitForm'. This exit form has a timer event that closes the database at a certain time of night.
You might be able to do something similar.

Messagebox.show fires twice after button click (MVVM)

I am using Galasoft MVVMLight. I have a button bound to a command which sends a message to the view to display a messagebox asking for confirmation. If I click either the Yes or No on the messagebox it performs the necessary actions then shows up again. However if I step through the program instead I only get the messagebox once. Is this a bug or is something else going on?
EDIT: I modified the messagebox.show line by adding an Icon and default result and now I can't reproduce this behavior... I'm stumped... if it happens again I'll try a counter like airplaneman19 suggested.
Try tracking the amount of times the MessageBox shows up with an integer, like so:
int counter = 0;
if(counter == 0){
MessageBox.Show();
counter++;
}
else if (counter == 1)
/*Do something that won't alter the program just to escape the if....else statement
like "x++";
I had a similar problem once, I mean, with MessageBox firing twice. It was due to focus changes, and ListView in WinForms fired another selection changed event when running the app; but when debugging - some focus change was missing, and there was no bug :)
I hope this atleast gives you some ideas...