VERSIONS: All ? - written for Paradox 5
SUBJECT: disableDefault
Comment A - BY: Randy Magruder - Borland
EventInfo.setErrorCode() is almost always the preferable way to block an event. The difference is somewhat internal but here it is in an
example form. Lets say you have canDepart code. You use disableDefault. What this means is: "Whatever your default behavior is for canDepart,
don't do it". In the case of canDepart, that's tantamount to saying "Don't ask permission to depart the field". DisableDefault therefore bypasses
Paradox's logic which controls whether a field is given permission to be departed. If, however, you set an error code on the event, the built-in
default code DOES execute. Any code WITHIN that default behavior which checks the errorcode will cause an internal clean-up and return from
the canDepart process.
In almost all cases, the error code is the first thing that the default behavior code checks, and if its a non-zero value, it immediately returns.
However, if you want to block the default behavior COMPLETELY, you can use disableDefault. The reason *I* wrote disableDefault in the code
block I handed you is because I'm not sure at what point the error code is checked for every single dataAction, so I used the far more restrictive
"disableDefault" than the somewhat liberal and forgiving "setErrorCode". If you have some time, you should try setErrorCode first and see
whether it meets your needs. If not, use disableDefault. In most cases setErrorCode is what you want to try first, because it is a guaranteed SAFE
method of letting Paradox know an error condition exists. With disableDefault, you deny Paradox the chance to do a safe cleanup.
Comment B - BY: Mark Pauker
Question:
Do you have any good examples of situations where disableDefault would be correct? I.e. where you would want to block the default behavior
but still tell Paradox that the behavior has been handled without an error? So far I have been using setErrorCode exclusively and have not found
any obvious need for disableDefault, but there must be some cases where it is needed.
Reply:
There are lots of situations where I'd use disableDefault instead of setErrorCode(). One very frequent use is to stop bubbling. For example, if I
issue a "ui.action(UserAction)", the UserAction is going to be sent to the uiObject in question, and then bubble all the way up to the form. As soon
as an object handles the action, though, I want the bubbling to stop, and I want the action to return True.
Without issuing either disableDefault or setErrorCode, the event would continue to bubble. This may be alright in some circumstances, but
makes it nearly impossible to create reusable objects (since the actions will bubble up through other objects that shouldn't be seeing them, and
those objects might have completely different meanings associated with the UserAction constants).
Issuing a setErrorCode in this situation should stop the bubbling, but would also cause the action to return False (which by convention means
that the action didn't take place). As we stated earlier, though, the action _did_ take place.
Another example of where I use disableDefault is in conjunction with hotkeys. If someone presses a key that my application defines as a
hotkey, my code deals with it. For example, let's say I defined the F12 key as a hotkey which exits edit mode. When that key gets pressed, I would
issue an "active.action(DataEndEdit)". Since I have taken responsibility for the functionality of the keystroke, I want to disableDefault to allow the
method to return True and yet not do what it would normally do in response to F12. (Actually, I would most likely check the value returned by
the action; if True I would disableDefault, and if False I would issue a setErrorCode showing that the keystroke did not accomplish its task.)
As you can see, there is some leeway here in terms of how we define success and failure for a method. The place where people need to be
careful is when dealing with actions and menuActions. As my last message pointed out, it doesn't make sense to issue a disableDefault in
conjunction with a DataUnlockRecord because that would mean that _we_ had unlocked the data model's record, which is not possible. (Only
Paradox's default behavior can do that.)
By using setErrorCode exclusively, you have avoided the more significant problem of telling Paradox that you've done something when in
fact you haven't. If one were to err on either side, this would be the right way to go. That's why I tell people that they should use setErrorCode
exclusively until they feel comfortable with the differences between them.
I find that once people grasp these concepts, Paradox seems several orders of magnitude less difficult.
< Quote >
In retrospect, it is kind of surprising that so much vague and even misleading information was circulated around this issue.
< /Quote >
I think the problem is that it's very difficult to factor out the simple rules which make up complex systems, and the Paradox manuals don't
really describe the theory behind the event model at all. Thus the community was left to describle the event chain with rules based only on
preconceptions and observations.
The problem is that most people's conclusions have been wrong. (I know that mine were for the first two or three go-'rounds.)
The reason why I am so emphatic when describing the difference between disableDefault and setErrorCode is that that understanding
removes the mystery (and thus 95% of the complexity) of the event model. While I may not have memorized all of the RTL methods, and I
certainly don't know the default behavior of every object in every situation, the fact is that I find Paradox a relatively straightforward environment
to work in now.
Comment C - BY: Mark Pauker
< Quote >
if eventInfo.isPreFilter() then
else
switch
case eventInfo.id() = DataToggleEdit :
disableDefault
disableDefault
{any other things to be disabled}
endswitch
endif
< /Quote >
While both disableDefault and eventInfo.setErrorCode(...) will prevent the toggling of the edit mode here, they will do it for different reasons.
When you say disableDefault, you are saying: "I have done what needed to be done to successfully complete the current operation, so Paradox doesn't need to do anything further." In effect, you are telling Paradox that you have successfully toggled the edit mode. This is why when you issue:
action(DataToggleEdit)
the method will return True (leading you to believe that edit mode _was_ toggled when in fact it wasn't).
Using eventInfo.setErrorCode(...) explicitly tells Paradox not to do the action it was asked to perform, thus Paradox knows that the edit toggle
didn't take place and acts accordingly (returns False, among other things).
Even now, many people believe that there is a bug in Paradox because when they issue an action, Paradox returns True (telling them that the
action completed successfully) when in fact the action didn't complete at all. In reality, it is not Paradox, but rather they themselves who are
causing this to occur. The value returned when one issues:
action(DataPostRecord)
describes whether or not Paradox encountered a _problem_ when calling the built-in. It does not tell you whether or not the record was posted. A
"problem" is defined as an error which occurs when attempting to do the default behavior. If you were to issue a disableDefault within the action
method, then the default behavior would not be attempted, and thus no error could occur. In this case, as far as Paradox is concerned there would
be no problem, and thus Paradox would return True.
On the other hand, setErrorCode explicitly tells paradox that an error occurred within the default behavior and thus would cause the method
to return False. The offshoot of all of this is that if we use setErrorCode properly, then the return value we receive when we attempt to post a
record really _will_ tell us whether or not the record was successfully posted.
The ramifications of using disableDefault incorrectly go beyond return values as well. Do you remember early on in v1.00 when a record
sometimes remained locked after the user moved off of it? This Paradox bug would leave records locked and give you no way of unlocking them
without exiting Paradox. Turns out, this was not a bug at all. What was happening was that developers were using disableDefault when
attempting to stop the records from being unlocked. Unfortunately, what they were in fact telling Paradox was that _they_ had unlocked the
record so Paradox didn't have to bother. Paradox took them at their word and moved off of the record assuming that it had been successfully
unlocked. (Paradox was young then... it didn't understand that people were going to try to mislead it.
) The developers should have been
using setErrorCode().
Because the ramifications of the problem were so severe, and because most people perceived this as a bug in Paradox, Borland quickly "fixed"
this by placing some extra checks to make sure that no matter what we told it to do, Paradox would not leave a record object while it was still
locked. If you use the tracer to look at what happens when disabling the DataUnlockRecord using both techniques, you will see how clean and
efficient it is when using setErrorCode(), and how confusing and inefficient it is when using disableDefault.
I think of the above example often when deciding whether to use setErrorCode or disableDefault. The fact is that in many circumstances, they
can be used interchangeably without effecting the functionality of an app. The problem remains, though, that only one of them is strictly correct
in any given circumstance, and if we "lie" to Paradox, there's always a chance that something unexpected might occur (like the records being left
locked in v1).