Paradox Community
Search:

 Welcome |  What is Paradox |  Paradox Folk |  Paradox Solutions |
 Interactive Paradox |  Paradox Programming |  Internet/Intranet Development |
 Support Options |  Classified Ads |  Wish List |  Submissions 


Paradox Newsgroups  |  Paradox Web Sites  |  Paradox Book List  |  FAQs From The Corel FAQ Newsgroup  



Subject: FAQ:PdoxWin:Preventing Paradox Events:2001.05.25
Version 1.0 (2001.05.25)
Written by Bertil Isberg (CTech)
edited by Paradox FAQ Team

====================
0. Introduction
====================

This FAQ addresses a common request: "how do I stop 
Paradoxcontinuing to operate as a result of an event, if I 
don't want it to ?".

The reason for wanting to trap this situation is most
commonly:
a. prevention of bad user input
b. the result of finding inconsistent data

This FAQ is appropriate to users of all versions of Paradox
for Windows

-------------------------------
 0.1 Legal Info and Disclaimers
-------------------------------

Paradox is a trademark of Corel.
Borland Database Engine (BDE) is a trademark of Inprise.

The information provided in this FAQ is provided "as is" and
is not warranted in any way. The information provided in
this FAQ is not endorsed or authorized by Corel or Inprise
in any shape, form, or manner. The editors claim NO
responsibility for ANY illegal activity regarding this file,
or as a result of someone reading this file.

You may distribute this file, as long as the copies are
complete, unaltered, and are in electronic form only.

-------------
 0.2 Feedback
-------------

Please send feedback in a Corel Paradox newsgroup or the
news:comp.databases.Paradox newsgroup to any of the FAQ
Team mentioned in the "FAQ: FAQ FAQ" document.
Please preface the subject of your post with the string
"PDXWIN FAQ" to alert Team members to the function of
the message.

Please specify the FAQ name and section number the
comment applies to, if any.

==============================
1. General Information
==============================

Almost everything you do in a Paradox form ends up in an 
action event being called. For example:

Press the Tab key to move to next field
                     active.action(fieldForward)
Press Ctrl+Del keys
                     active.action(dataDeleteRecord)
Leave a record where a field has been changed
                     active.action(dataUnlockRecord)
Toggle the Edit button
                     active.action(dataToggleEdit)
                     active.action(dataEndEdit)
Start editing a record by pressing F9
                     active.action(dataBeginEdit)

When you want to take care of the user's interaction with 
your form, an object's action() event is the place to let 
your code interfere with Paradox' normal behaviour.

==============================
2. Example 1
==============================

If you want to prevent accidental deletions of records, the 
action event at form level should look like

method action(var eventInfo ActionEvent)

if eventInfo.isPreFilter() then
	;// This code executes for each object on the form
else
	;// This code executes only for the form
   switch
     case eventInfo.id()=DataDeleteRecord :
        if msgQuestion("Verify Delete!",
              "Do you want to delete the current record?")
           = "Yes" 
        then
           doDefault
        else
           eventInfo.setErrorCode(UserError)
        endif
   endSwitch
endIf

endMethod

This code takes care of all different possibilities involved 
in deleting a record; Ctrl+Del keys, a custom Delete button, 
and the Paradox menuchoice Record | Delete.

The more you get used to Paradox, the more you will use 
action-constants instead RunTime Library methods. In a 
pushButton(), you'll write the line:

active.action(dataUnlockRecord)

and in the action() event  you'll take care of actions that 
should happen:

method action(var eventInfo ActionEvent)

if eventInfo.id()=DataUnlockRecord then
    if not lgDoMyTestsFirst() then
       eventInfo.setErrorCode(userError)
       return
    endif
endif

endMethod

This way, the call to your custom method lgDoMyTestFirst() 
will execute whenever a record is to be unlocked. No matter 
how the unlock is triggered.

There are two fundamentals to consider when sending actions
a) they prefer to have an object to work on
b) they shouldn't be disabled

As seen above, I have used eventInfo.setErrorCode(UserError) 
to prevent the behaviour instead of disableDefault.

==============================
3. Referring to Objects
==============================

When you want to send an action or trigger the same event 
with RTL methods (action(dataDeleteRecord) or 
DeleteRecord()) you must decide which object it should be 
sent to. During interactive work, all actions are sent to 
the active object. Mostly it's very natural, it is what you 
expect. Sometimes it can be confusing, especially if the 
active object is an undefined field not bound to a table. 

As unbound fields know nothing of DataDeleteRecord, the 
action will bubble up through the containership and reach 
the form. The form will take care of the action and apply it 
to the master table of the form, whatever that happens to be.

For the following examples you could use Customer and Orders 
tables from the Paradox Sample directory. Take a  copy of 
each of the two tables, so you won't destroy the contents of 
the original tables.

==============================
4. Example 2
==============================

Create a form with two tables, Customer  and Orders, and do 
not link the tables in the data model. Pick Customer table 
first, so it will be the table shown in the upper left 
corner of the data model.  

In Design layout, check Style: Tabular. 

As the tables are unlinked, you will only get one table 
frame in your form. To get the second one, you have to use 
the Table tool on the Toolbar. 

When you have placed a second table frame in your form, 
right click it and choose Define tabe from the popup menu, 
and then select the Orders table.

Run the form. Move to Customer and press Ctrl+Del and a 
record in Customer will be deleted. Move to Orders and press 
Ctrl+Del, a record in Orders will be deleted.

Add a button to your form with one line of code: 

deleteRecord(). 

Move to Orders. Press the button and watch what happens. 
A record in Customer is deleted. When no object is specified, 
the action is sent to the form and the form will handle this 
with the Master table of the form as object for the action. 
The Master table is usually the one in the upper left corner 
of the data model; in the case of un-linked tables it is 
usually the first one added to the data model.

Lesson 1: if you want an action to apply to the currently, 
          active object, use active.deleteRecord() or 
          active.action(dataDeleteRecord). Always use an 
          object. It's better that you do it explicitly, 
          rather than letting Paradox handle this 
          automatically. Every action should be directed to 
          a particular object.

==============================
5. DisableDefault
==============================

The Paradox manuals mentions three statements to handle the 
default behaviour:

doDefault: executes the default behaviour. Execution of 
           the default behaviour of the object takes place 
           immediately. After that, the code resumes 
           immediately after the diDefault statement. Note 
           that the default behaviour does NOT happen again 
           after the endMethod is reached.

disableDefault: to inhibit the default behaviour

enableDefault: a programmer's help, as it allows you to 
               first disableDefault as a standard and then 
               enableDefault for some cases.

This description has fooled a lot of ObjectPAL programmers. 
You should NOT use disableDefault to prevent the default 
behaviour. The way to do this is to set an error and thus 
invoke the Paradox errorhandling

------------------------------
5.1. DisableDefault Example
------------------------------
Let's look at this more closely:

You can use the form designed above for the following 
example:

Add some lines in your form's action event, so it looks like:

if eventInfo.isPreFilter() then
	;// This code executes for each object on the form
else
	;// This code executes only for the form
   switch
      case eventInfo.id()=DataDeleteRecord :
	      disableDefault
   endSwitch
endIf

endMethod

In a pushButton method of a button, enter the code

method pushButton(var eventInfo Event)
if not active.action(dataDeleteRecord) then
   msgInfo("Record not deleted", "")
else
   msgInfo("Record deleted","")
endif
endMethod

Run the form,  enter Edit mode and press the button.

With disableDefault, you tell Paradox: Do not execute the 
default behaviour, because my code has taken care of this. 
So Paradox assumes the default behaviour is done and behaves 
accordingly.

Now replace disableDefault in the form's action() event with

eventInfo.setErrorCode(UserError)

and repeat the steps above. Paradox behaves as expected.

Lesson 2: An action event should not be disabled, but 
          prevented with an errorCode.

==============================
6. A chain of events
==============================

Some of the user interactions will trigger not only one 
action, but many. When a record is changed and you press the 
Edit button to end edit,  for instance, the toggleEdit will 
turn into an unlockrecord and an endEdit. When actions are 
disabled, this chain of events will not get the correct 
information. eventInfo.setErrorCode(UserError) will stop the 
chain of events, disableDefault will not.

disableDefault should be used for two things only:
a) when you have replaced the default behaviour completely 
   and you want Paradox to continue as if everything is Ok. 
   E g when you translate a keypress in keyPhysical() event 
   to something else.
b) When you want to prevent bubbling.

In most other situations, you should set an error instead of 
disableDefault.

------------------------------
6.1. After-effects Example
------------------------------
Let's take a look at the effect of disableDefault in a 
changeValue() event.

disableDefault:
      resets the old value and allows the cursor to move 
      to next field

eventInfo.setErrorCode(UserError):
      keeps the new value and prevents you from leaving 
      the field.

You can test this in the Ship Via field in Orders table. 
Write the following lines in the changeValue() event.

method changeValue(var eventInfo ValueEvent)
   if eventInfo.newValue()="UPA" then
       disableDefault
;       eventInfo.setErrorCode(UserError)
   endif
endMethod

Change the value to UPA in a record and Tab out of the field. 
As you'll see, you the new value will not be entered, but 
you can move off the field.

Make a comment out of disableDefault, and let 
eventInfo.setErrorCode(UserError) execute by removing the 
semicolon.

When disableDefault is used, the event chain will continue 
to execute as if everything is Ok,   and canDepart() is 
allowed. eventInfo.setErrorCode(userError) will block the 
rest of events due to Paradox builtin errorhandling.

==============================
7. Internal and external builtin methods (events)
==============================

As this article has been a comment to a part of the Paradox 
manuals, where basics of the event model is described, there 
is one more thing to be said. Built-in methods are classified 
into three categories, internal, external and special events. 
The last group contains only the pushButton(), changeValue(), 
and newValue() events.

The manual states that internal events do not bubble, and 
reading carefully you will find that disableDefault should 
not be used to prevent the default behaviour for internal 
events, e g don't use disableDefault in an open() event. 
I recall having a hard time to understand why mouseEnter() 
and mouseExit() belonged to the category internal events and 
why Status() was an External event. 

The explanation is: 
      The names of the categories, Internal and External, 
      are just names. The classification is based upon the 
      following: 

      a. Events that do not bubble are called internal
      b. Events that may bubble are called external.  

That's why mouseEnter() and mouseExit() are internal events. 
They do not bubble. Status() may bubble. As you see, there 
is nothing to understand, only to accept.

==============================
8. Conclusion
==============================

One of the most misused statements in the ObjectPAL language 
is disableDefault. With the examples shown, you have got an 
explanation why you should avoid it and use 
eventInfo.setErrorcode() when you want to prevent the 
default behaviour of a Paradox event (builtin method).

You have always been shown the effects of not deciding the 
object which an action should be applied to. The 
recommendation is to always specify objects when actions and 
RunTimeLibrary methods are called.

==============================
9. Lessons
==============================

Lesson 1: if you want an action to apply to the currently,
          active object, use active.deleteRecord() or
          active.action(dataDeleteRecord). Always use an
          object. It's better that you do it explicitly,
          rather than letting Paradox handle this
          automatically. Every action should be directed to
          a particular object.

Lesson 2: An action event should not be disabled, but
          prevented with an errorCode.


Paradox Community Newsgroups


 Feedback |  Paradox Day |  Who Uses Paradox |  I Use Paradox |  Downloads 


 The information provided on this Web site is not in any way sponsored or endorsed by Corel Corporation.
 Paradox is a registered trademark of Corel Corporation.


 Modified: 15 May 2003
 Terms of Use / Legal Disclaimer


 Copyright © 2001- 2003 Paradox Community. All rights reserved. 
 Company and product names are trademarks or registered trademarks of their respective companies. 
 Authors hold the copyrights to their own works. Please contact the author of any article for details.