Know What Behaviors You Want
A program can be defined by its behaviors. It’s up to you to decide what these behaviors should be. There is a tendency to place objects together and hope that the objects will "just do the right thing." Often they will. When they do not, you need to know what the "right" behaviors are. A data-entry program's behaviors can be divided into two types: data-entry and navigational. In a large event-driven application, these can get complicated. Fortunately, the two behaviors do not interact much and can therefore be treated separately.
Navigation Behaviors
Event-driven programming makes controlling navigation a far greater challenge. You have two types of navigation devices: the mouse and the keyboard. With a mouse, users can navigate to just about any item in just about any object from just about any object. In most cases, you want the mouse to be free to roam the desktop (non-modal navigation). In some limited cases, you will want to restrict the mouse's movements (modal navigation). The keyboard is a far more limited and less intuitive navigational device. You are provided with a limited number of navigation keys (Next control, Previous control, etc.). It is your job to make sure that they act sensibly. You control this with:
- Object placement (object order and nesting)
- Setting navigation properties (for example,
ring_stateandscope_state) - Augmenting navigation-message handlers (for example,
switchandswitch_back)
The high-level classes you will be using are designed to make the navigation between objects as simple and automatic as possible. A large number of conditions are handled properly without any programmer intervention required. More complicated views will require customization.
Consider, as an example, a check-writing view consisting of a check-header entry-form object and a check-detail grid object. We can define the following navigational needs and conditions. These may not agree with what you would want to see in your application. This is not important. It is important that you can define what your requirements are.
We will define our navigation needs for the check view as:
- The mouse should support moving back and forth between the header object and the grid object. The
switchandswitch_backkeys will permit navigation between these objects with the keyboard. - Before entering the detail grid, we must make sure that the current check header record is valid and saved. If it is not, do not allow entry into the grid.
- When a row-change navigation occurs or when grid-to-header navigation occurs, the grid record should be saved. If the save fails, do not allow the user to navigate out of the row.
- Users should be able to navigate out of either the grid or the header object into a different view without any kind of save or validation.
Once you have defined your navigational needs, you can now determine which needs will require custom code. Needs 1 and 4 are handled automatically. Needs 2 and 3 will require customization. It will take a while to be able to think like an event-driven programmer.
For example, you might try to solve need 2 by sending a request_save in the exiting procedure of the header object. It would make sense that the header object should save its data before the detail object is entered. This will not work all the time. The header object can be exited on the way to any other object, and the detail object can be entered from any object (it's that darn mouse again).
The proper approach is to perform the save in the grid's entering procedure. The grid object will send a message to the header object asking it to save itself. The header object should return a value letting the grid object know if the record was saved. If it was not, the grid will not allow entering to proceed. This may seem convoluted, but this is what is required to make event-driven programs work properly.
Data-Entry Behaviors
Data-entry objects perform four data-entry tasks: save, delete, find, and clear. That's it. You must make sure that all these operations behave properly. Before you can do that, you need to know what these behaviors should be. This will determine how you should connect your DSOs (with the ddo_server property), how to set your finding filters (inside of OnConstrain), and where in the Data Dictionary structure you connect your data-entry objects (with set server).
If you know what behaviors you want and expect from your application, it becomes much easier to test your program. You simply test that navigation works the way you desire and that for each object the four data-entry behaviors are proper. If navigation is improper, you will want to make adjustments in your DEOs (nesting, property settings, message handlers). If data-entry behaviors are incorrect, you will want to adjust your DDOs (the DDO_Server dependencies, constraints, message handlers) and the DEO-to-DDO connections (the Server dependencies).