Tips and Tricks on OO GUI Development Using Informix NEW ERA

By Don Udawattage (email: taraka@alphalink.com.au, udadon@hotmail.com)

Having worked as a major developer on a large commercial application developed in Melbourne using the full power of New Era and having many years prior experience of OO GUI Client Server Database application development, I am in a position to identify the strength of this powerful truly OO product, despite its not being well known in Australia. New Era has some inherent problems which stand on the way of serious development and we have found sound solutions to overcome them and made robust applications. For some of you, these findings may look like reinventing the wheel because you may have come across the same problems and found better solutions long time ago. If what I am talking about in this note seems like gibberish and does not make sense to you(not an unreal possibility), please do not hesitate to Email or Phone me. The most up to date copy of this note may be seen in my homepage in the internet(URL: www.alphalink.com.au/~taraka). My home number is +61 3 9395 2637 and my mobile no is +61 0402 044 652 – I am an Australian. If you are viewing this document on a browser, it is convenient get a print and read. At the end of this paper, there is a hypertext link to download software mentioned in this paper.

 Part A: To Make your Application Maintainable and User Friendly Focus on the Following

  A1. Create Your own Base Classes of Visual Objects

Create your own base classes of visual objects(windows, list boxes, text boxes, superfields etc.) derived from New era base classes by simply cloning the 4GLs & 4GHs so that you can add your own common member functions and variables. In this way generic changes, found necessary later, could be implemented easily. When you paste a visual object such as a text box on a window, change its class name property from ixTextbox(for example) to your site’s textbox class. If you develop subsystems, create a separate window base class for each subsystem so that, for example, if your manager wants on the eve of implementation, the windows of different subsystems to have different background colours, you can do this easily, making him happy.

 A2. Implement a Naming Standard

Have a standard for naming visual objects and classes and variables, so that they can be recognisable in 4gl code. For example, you may decide all your list boxes should end with lowercase abbreviation, preceded by underscore(e.g _lb). In addition to being readable, you can write generic functions which, by looking at the name can create, references to them using New Era’s powerful function ixWindow. GetContainedObjectByName() which we have found usefull to build a vector of all the controls in a window at runtime – I will explain later why we had to do this. If your visual object receives data from a column of a database, use the column name as a part of the object name. If the name of the column in the database table is state_code then the list box on your window representing this column can be named state_code_lb. Also name your labels so that the name of the label can be derived from the name of the object. The label of the state_code_lb listbox can be named state_code_la, replacing lb with la. This will help to add the label to error messages in generic functions used to validate data entered into visual objects.

            Reward of Naming Standard

Towards the end of the project we found that some of our users type into data base key fields, illegal characters(such as asterisks) which cause problems. Are we to revisit all the programs to put in validations to prevent this and ask the users to retest ?Where is the time and money ?. Because all our key columns end with _code(e.g. state_code) and our visual objects(eg. Text Boxes) reflects this name, we could simply override the keypress event of the base class to not to accept offensive characters for key fields. However, we could solve this problem more elegantly in the SQL class covered later - a stitch in time saves nine !

A3. Make List Boxes Intelligent

A New Era list box provides basic functions to get/set the whole text on a line but for serious database applications, this is not enough. Provide functions to load data from the database by passing the table and column names and the list box will show the descriptions and the foreign key associated with that description is stored in a vector & when you want to save to database, your functions returns it. You can introduce members to indicate whether a list box is mandatory so that blank entries can be inserted for non-mandatory list boxes automatically. These are only a few of many features.

 A4. Split your Big Applications into a Number of DLLs

Do not create a single application unless it is small. Group windows belonging to a sub systems into separate dll’s and have a small main executable to drive the dll’s. Windows dll’s are loaded only when functions in them are referenced(really not true in the 16 bit New Era version we used – I hope this will be fixed). Maintain them in a common network drive server and copy them to user’s C: drive when he logs in if the version in his C: drive is older than that of the server. You can do this by writing a program which runs before showing the login/password dialog box(I have written such a program to do this on my home PC, using Visual Basic, Email me for a free copy). This way, loading dll’s will be faster as they are loaded from your C: drive and you have full control of release of dll’s in the server and update them easily knowing very well that when users logs in next time, the latest version is available.

 You must create dll’s for your own visual or non visual classes which your developers are obliged to used so that they do not have access to the source code.

 A5. Provide Functionality to Hide All Windows at Once

Imagine your user has opened 5 windows in your New Era Application and suddenly wants to open up XL without having to exist the application so that once he finishes the XL session, he can resume his work.Your user’s 5 windows have filled the screen. There are so many ways to do this but the best way is to provide a preferences drop down menu in each window and this menu contains an item called "Hide" so that when the user clicks on it, all the windows will be hidden and a small but prominent window appears on the left top corner of the screen giving the entire space to the user. When the user wants to return to the application, he only has to click on this small window and all 5 windows will pop up restored to its original state instantaneously.

 You can do this elegantly by creating a preference drop down menu in the base class window along with a function called within the constructor extension so that every time a new window is created, this function adds the window reference to a global array(i.e. a vector, in a global dll). Then put code in the Hide menu item in the Preferences drop down menu to navigate the window array and call hide event of each window then open up the small prominent window, containing a single button whose activate event navigates the window array and call the show event. All the windows developed by programmers, derived from your base class window, will inherit the preferences drop down menu along with all the functions.

 A6. Provide Facility for User to Print Screen

No GUI application is complete without user being able to print a window to show someone else(specially to the programmer who developed it) a screen filled with data or defects on screens such as spelling mistakes of labels missing labels or visual objects not correctly anchored. Unfortunately, New Era does not provide a function to do this and you have to write this using Window APIs (I have written this program on my PC using Visual Basic, which can be called from new era applications with a system call, please ask for a free copy) You may provide this functionality by adding a Print menu item to the preferences menu we discussed above(Hiding Windows) so that all your project's windows will inherit.

 A7. Use of New Era SuperTables and SuperFields

Although New Era super tables(like MS Access) let you write code less database applications, they cannot be used for complex mission critical application, in addition to the fact that they have limitations and problems – and Big memory overhead(Note: In this paper, I am only referring to free form supertable & not grid form ones which we have decided not to use, for various reasons): 

A superfield, however, has its own strength some of which are built in type checking, and being able to override its validate and data change events in the base class to put your own validations/formatting, saving programmers heaps of coding. I recommend to use superfields only for this purpose, however, as you will see later, you can create a simple frame and use intelligent text boxes and list boxes, to do the same job with much less memory overhead. 

A8. Implement All Database Access via a SQL Class

Despite few weaknesses, Connectivity Class of New Era is a piece of art. None of the projects I have been involved in the past, have I been able to develop database applications with so much ease, with minimum or no need to code SQL. This could be done only with the help of a SQL class providing all the database access functions and members holding the name of the table, columns and the key information and master detail information etc. Such a class can cache data retrieved from the database. In fact, the programmer using such a class need not know how to code SQL. The functions within the class generates the SQL dynamically(without syntax errors) and all the basic checks to the data can be done in one (and the best) place before writing to the database. Such a class must provide a series of simple functions(with minimum or no parameters): 

 

These are the basic minimum interface from the class to the outside world. There are many other functions which you will figure out when you implement, without my having to tell you now and confuse you.

Advantages of SQL Class

    1. Since the SQL class uses dynamic SQL, your application is ODBC compliant(at least theoretically) – this may not be a big deal if you will never change the database engine from Informix, however, this gives the IT manager some comfort !
    2. By pasting your visual objects(to which you have built intelligence) on to a simple frame, thereby reducing the overhead of supertables and superfields many times over, the developer can still write "code less" code the same way he can do this using supertables in the same time your having full control over database access.
    3. When you access the database, you can do a lot of generic things in addition reading and updating data user is requires. Some of them are: 
      1. Suppose your site maintains columns in the database for audit purposes(such as who inserted a record and when; who last updated the record and when). And you want to do this without asking the programmer(he may forget or ignore). Every time a record is inserted or updated, you can add the relevant columns to the SQL with complete privacy.
      2. You may decide to enforce row and column level security on database records, without depending on the programmer. An example of row level security is that if the employees of the Sales Department are allowed to maintain records of their employees only and when a sales department employee signs on to your system, his profile contains what group he belongs to (in this case Sales) and what kind of restrictions should be applied when he retrieves/updates records – assume that all these information is stored in the database. By looking at this information, you can, in complete privacy, add a where clause to Select & Update SQL(See also window and table level security explained later)
      3. A super table handles stale data(i.e. concurrent updates) to let you avoid lost updates. Using a time stamp, which comes as a bonus of part of audit information I mentioned above you can implement this. You can read the time stamp of the record, cache it in the class, and add to the filter when user does the update so that if the timestamp has changed in between the update will fail and you know that data is stale and you can inform the user.
      4. You can implement all the database access error and message handling in the class so that even if the developer’s linguistic skills are not the best or even if the SQL class designer’s linguistic skills are in question, text in error messages can be changed in one place. When an error occurs, you have all the information on which field it occurred, what its label is so that you can focus onto the field and add the label to the error message.
      5. You can do type checking of data entered, check entries on mandatory/key fields, illegal characters in key fields and duplicate keys etc just before saving a record to the database.
      6. Suppose a user brings up a record to a screen having few dozens of visual objects then without changing any entry(or changing one entry and changing his mind and reversing the change) tries to save. You can build the update SQL only to include fields he has changed by comparing with what was retrieved from the database, which is kept in the class. If the user has not made any real changes, show a friendly message and do nothing. Otherwise, build the update SQL length of which is proportional to the number of entries he changed, thereby creating a shorter SQL string to be sent to the UNIX server reducing the network traffic.
      7. You can also enforce some special security checks, again in complete privacy, to check if authorised users misuse their power. If Jack has access to run a window which allows to give increment to staff. To make sure that Jack does not exceed his limits and give higher increments to his girl friend thinking that no one will notice, you can build into SQL class to check updates of sensitive columns in a database table such as salary. Every time when this column is updated, SQL class can write to a secret log file in a network drive or log to a database file showing who did and when. 

A9. Implementing Menu, Window and Table Level Security 

Menu Level Security

Jane is a clerical officer and when she signs on to your system, she will get a menu to call windows which she is allowed to call – "Give Salary Increments" is not one of them, so she does not see it on her menu. But Harry is an officer authorised to give annual increment to staff and when he signs on, he can see the "Give Salary Increments" item on the menu and when he clicks on this, salary increment window displays. This is the menu level security on access to windows – what you see is what you are allowed to see. This can be achieved by assigning groups to users and building hierarchies of menus held in the database with information on which groups have access to them. A generic menu driving window which dynamically creates menus using information in the database when a user signs on can be designed to achieve this. To do this using New Era you need some tricks as the driver program does not know what window to open at compile time – it only knows the name of a variable which at run time contains the name of the window to be called. In New Era, you cannot open up a window using a variable and our trick does this as I will explain under opening a window using a variable – I have to tell you that this trick has some problems which I am investigating. 

Table Level Security

A user being allowed to access a maintenance window is one thing and what he is allowed to do inside the window is still another. Mary is answering phone calls from customers and when a customer makes an inquiry about his gas account, Mary opens up a maintenance window, brings up the customer’s account and responds to the query – the story ends there. She cannot change any information on the screen – the data entry fields are display only and save button is hidden. When Marshal, who is a clerical officer, opens the same window, he not only can view the same record but can change data in the fields – the fields are enterable and the Save button is visible. This I call table level security and you can call it whatever you think is better. You can implement this very elegantly by putting code in the constructor extension of your window base class(so that all the windows in your project will inherit). When the window is constructed, code within the constructor looks at the database for permission against the user’s group for the current window and disable fields and hide buttons accordingly. To do this so the programmer will not have to know anything about it, you have to create a vector of all data entry fields, buttons and menus at runtime. I will explain later how to do this. 

Column Level Security at Window Level

Suppose in this case the salary of an employee is on the employee maintenance window which also contains other information such as his name and address. When Dick opens this window from a menu, he can see the salary of an employee and can change it as he is authorised to do so. Tom has access to the same window and he can change other things(such as address) but not salary which is display only. When Harry opens the same window he not only cannot change the salary but he cannot see it. Under SQL class, I explained how to enforce column level security in a guaranteed manner. However, visual effects like this make it more user friendly. This can be implemented in similar ways to table level security by building into the base class constructor of the window. 

A 10. Provide Generic Windows

The more windows you create, the more windows you have to maintain and bigger will be your machine code which will take more disk space and longer to load. Whenever possible, provide generic windows whose controls are dynamically created at run time. A good example is that if user enters a wild card for a client name which brings several records, which will be displayed on a separate window for user to select the one he is after, use a generic window to which all the display information can be at runtime. Other examples of generic windows are pure table maintenance windows, menu drivers and Report Running windows etc.

A 11. Development Standards.

Development standards are wonderful on paper but implementing them on a large project requires code or peer reviews which is no fun. Programmers will follow standards, without being forced, if the standards make their life easy. If a programmer can develop a maintenance widow in a day by following standards and if the same window requires 3 days if he does not follow standards, he will certainly stick to standards. Furthermore, due to non conformance to standards, the programs will not function as expected or non standard appearance of a window is readily noticeable, then again the programmer will follow the standards. This is the philosophy that we have adopted, by creating visual/SQL classes and generic interfaces and this has paid dividends.

A 12. Compiling Nightmare

Imagine your site has a few dozen applications involving hundreds of windows derived from a single base class. Now if you have made a change to the base class which results in changes to the header file, all the applications should be opened and recompiled. You can avoid manual compilation by writing a batch program which does all the compilation, which you can run overnight. The best solutions to these type of problems is to have automatic source code control system to allow users to check in/out programs and an overnight program takes care of compilation and copying the dll’s to proper places. 

A 13. Don’t Do’s in New Era Coding

The list I give below is only a few of many things which I advise you to avoid – this list will grow in time. However, I have to tell you that these are my personal views derived from my own experience and you may have valid, logical reasons to do what I tell you not to do. 

VARAIBLE MyRecord LIKE MyDatabaseTable.*

You make a declaration like this and later in your code you access the database by building the sql:

                        SELECT * FROM MyDatabaseTable

After executing this statement and getting an ixRow record, MyRow, you may do this.

                        LET MyRecord = UNPACKROW MyRow

These 3 statements save a lot of typing(beware: All that Shines is not Gold !). But nobody, may be not even you(after few months) will be able to figure out what is in MyRecord. This kind of code is not readable, which is not the end of the world - the worst is still to come. If some one adds a new column to MyDatabaseTable, which your program need not know, your program has to be forced to recompiled by making some dummy entry such as a blank line to change the time stamp of the 4gl file, otherwise, this code will crash. That is not all, the programmer who has written this code may have left and no one knows which programs to recompile - hunt down all programs or simply wait until the program crashes, when your favourite customer calls for a critical inquiry, and the person answering the phone has to say "Sorry sir, our system is down". The data independence of relational databases will be thrown out of the window with this kind of coding, which converts your OO program to a COBOL program. 

INSERT INTO MyDataBaseTable VALUES(1,’Harry’, ‘Smith’,…,…)

Instead of specifying the column names like this:

INSERT INTO MyDataBaseTable (Id, FirstName, SurName,…,…)

VALUES(1, ’Harry’, ‘Smith’,…,…) 

This will suffer from same flaws as in the case of Record Like(explained above), but in addition, you need to make code change to the insert statement, in addition to just recompiling every time a new column is added to the table, even though the new column is maintained elsewhere - beware of shortcuts, which can be very long.

VARIABLE MyVector ixVector() -- suppose to instantiate here

When the executable(compiled C code) program containing this line of code ran well on Computer A instantiating the MyVector correctly but the same program run on Computer B did not instantiate leaving MyVector NULL (crashing the program later). The moment we changed the code as follows and recompiled, the program ran successfully on both machines:

VARIABLE MyVector ixVector

                        LET MyVector = NEW ixVector()                       

After this experience, I prefer not to use the second type of declaration. 

RETURN A, B, C,………..

            The calling program receives the parameters as:

CALL ANY FUNCTION () RETURNING A, B, C,………..

I found this a nuisance when the number of returned values is big and you are required to remember the order. That is my problem but imagine that the function needs to return an additional parameter later which your program is not interested in receiving. Your program needs to be changed and recompiled. To overcome this and many other problems, I declare the function to return an ixVector which contains pairs consisting of parameter name and value and provide another short functions to extract values from the vector. This has made the function, and the code calling it, relatively independent of each other and has proven to be efficient and less cumbersome to programmers. 

LET BoolVar = IsClientOk(VarClienId, "A", "01/01/1997")

Call Like this:

LET BoolVar = IsClientOk(

ClientId : VarClienId,

Sataus : "A",

DueDate : "01/01/1997"

)

The former is dull(lazy), not readable and doomed to fail, specially if you do not include the function prototype, which the compiler will not insist. The compiler will make its own assumptions and if the prototype is not included, the parameters should exactly match by number and position of the actual function.

With the second syntax, code is readable, compiler will force you to include the 4gh file containing function prototype. The order and the number of parameters of the called function is not important as long the parameters you name are present. The greatest advantage is that the called function can add new parameters with defaults(so that you need not pass a value to it) at any position and change the order of the parameters. When this happens, your code will be recompiled due to change in 4gh file with no code changes required.

A variable declared in the prebody section of a window (or a module variable in an include file which is included in the prebody section) is in effect a shared variable of the window (although you are not required to use the key word SHARED) and multiple instances of the same window will have only one value for this variable. Unless it is truly shared (in which case declare it as a shared variable or constant within the class extension) do not use it so that you can reference it from within member functions included in the prebody - to save coding. Suppose for instance, to avoid using calls to getWindow() more than once in the "Find Customer" window, you use a variable which holds reference to the window (by calling getWindow() once when the window opens). You declare this variable in the prebody section. Assume your program opens up a second instance of the same window, to find another customer, leaving the first window open. The variable holding the reference now points to the second instance corrupting the first. This example is not very good but gives you the idea. I had heaps of problems with this type of implementations with menu driving generic windows which have multiple instances. To avoid this, use member variables

A14. Allow Runtime Debugging and Diagnostics

Helga calls you and says that when she tries to bring up the customer Paul Klugman, the system says the customer does not exist and she knows that he exists. The programmer who had designed the customer maintenance window has left the project and you are new. You access the database interactively(say using dbaccess) and finds that the PauL Klugman is in the database. How do you know why the SQL in the program fails ? - because the programmer quite correctly added a restriction to the select filter that customer status is active and some one accidentally made Klugman inactive. Problems like this as well as bugs went undetected during testing can be caught by putting a debugging facility. In the preference menu mentioned earlier, you can allow a debug toggle which a user can turn on/off thereby setting up a global debug flag in the memory. In your SQL class or any other base class which implements the database access, you can check if the debug flag is on, if so, display the entire sql which Helga can read out to you or copy and paste into a text file on a public network drive for you to pick up and run interactively. I have implemented this and found it very useful. If you do not want the user to see the SQL for security reasons, you can write it to a temporary file in the public network drive. Because everything is built into the base class, you can add this functionality even after project has been implemented.

 A15. Monitor Window Resources

Every window your users open takes up windows resources. The situation becomes worse when they, in addition, have opened other window applications such as Microsoft XL and Microsoft word. When New Era cannot find enough resources when your program instantiates a new window using the NEW operator, New Era closes the application instead of returning a NULL window reference informing the programmer that it runs out of resources. The programmer cannot inform the users that the window cannot be opened(unless in future releases New Era fixes this problem by retruning a NULL reference instead of closing the application.) A good practice, in any event, is for you to monitor the resources and warn the user to close some windows when the amount of free resources goes below a lower limit. You can use the window API's to monitor the available resources every time a new window is opened. The functionality can be built into you window base class in the constructor extension.

 

Part B: Problems and Solutions  - New Era Development

B1. Calling a New Era Function using a variable

"Why on earth does one want to call a function via a variable ? – simply call it by name". There are situations, specially when you write generic functions such as running batch programs or jobs using a generic despatcher which at compile time does not know what function to call. It only knows the database table and column name which holds the name of the function. Many GUI products have recognised this problem and provided means to call a function using a variable. The previous GUI product I worked with can call a function using a variable by simply putting a colon (: ) in front of the variable telling the compiler that the name of the real function is the content of the variable - e.g. CALL :FunctionName(). Unfortunately, New Era does not provide any means of doing this. However, thanks to Windows APIs, I have been able to do this very successfully with some tricks which I can tell you. Some of these I discovered accidentally and I am not able to give a logical explanation. First, you must know the dll in which the function resides and the function must reside in a dll and not in an exe. I have been able to implement an automatic job scheduler which calls functions via a variable. If you need sample code, please do not hesitate to email me. To implement this you must know C/C++ & APIs – refer the famous book "Windows API by James L. Conger) 

When a New Era global function is converted to C code all its parameters are taken away by the code generator and replaced with a single integer parameter, which is a number and my guess is that this number tells the number of original parameters to the function. New Era passes parameters to the function by pushing to the stack and the function reads them by popping from it. When the function returns, the return values are again pushed to the stack from which the calling function pops – this is my understanding and I may be completely wrong. To not to complicate matters, all the functions, to be called using a variable, must not have any parameters and must return void. If you want to pass data to the function, use either globals(created in a global dll) or the database. 

Make sure that the function is called when the user clicks on a button. This is not the whole story. The button you click must be created dynamically on the window using a button class you have created and whose activate event is handled with your own handler and the code to call the function should be inside the handler and never in a function which the handler calls. If you do it in any other way, still everything works well but alas ! When the function which contains the C code containing APIs returns, the system crashes and you will have to restart windows.

B2. Instantiating a Window Using a Variable

This is another why-on-earth… type of question. As I explained under A9, to design a generic menu driving window which reads the menu from the database and create buttons dynamically so that when the user clicks on a button, the appropriate window will be displayed. The code behind the button does not know at compile time what window to be used in the NEW command:

                        LET WindowVar = NEW MyWindow(…)

Where MyWindow has to be the actual class name of the window which cannot be replaced with a variable. New Era Provide following command:

                        LET WindowVar = NEW ixWindow(…..) as MyWindowVar

Where MyWindowVar is a variable containing the name of MyWindow class, This line of code creates a blank window – not MyWindow.

The trick that I have found is that in each window, create a global function at the end of 4gl file which accepts no parameters and returns void. All it contains is 3 lines of code to instantiate the window. The name of this function can be made unique by adding some suffix(such as _Instatiate) to the end of the name of the window. If the name of the window is mywin and its class property is MyWinClass and the names of the 4gl is mywin.4gl, the function you create at the end of 4gl file is:

                        FUNCTION MyWin_Instantiate() RETURNING VOID

                                    VARIABLE TempWin MyWinClass                               

                                    LET TempWin = NEW MyWinClass(……….)

                                    CALL TempWin.open()

                        END FUNCTION 

This function can be called by the menu driving program the same way I have explained under B1(calling a function using a variable) as the menu program knows the name of the window and its dll. The menu driver is able to construct the name of the function to call by adding "_Instantiate" to the window name. Obviously, creating extra functions is an unnecessary burden on the programmer who develops the window. The best way is to add this function automatically just before compilation by putting in code in the make file. Write a small program which processes the 4gl file, just before New Era’s compiler processes it and add the function. Call to this function can be put in the appropriate place in your Application Builder .INI file.(Again I have written this program in Visual Basic - why VB ? it is easier for file access – and if you need the source code, which obviously has to be changed as I have hardcoded our naming standard to identify the class name, please do not hesitate to ask.

I have to confess that this approach is not as bullet proof as calling a function using a variable. I have noticed that opening some windows(specially very big ones) through the instantiation function causes the application to hang for no apparent reason. I will continue to investigate and any suggestions from you are welcome. In the meantime, if you want to implement this type of menu interface, I have another method, which is so elegant but does the job.

B3. Creating an Array of Visual Objects of the Window

Again what for ? As I explained under table level security under A9, in order to hide/disable visual objects to(or not to) allow a user to make changes, without expecting every programmer to code for this, the functionality has to be built into the window base class. The base class window, however, does not have access to the visual objects of the windows derived from it. The base class must have a function which is called in the constructor to capture all the controls when the real window is constructed. There is a function GetNextTabEnabledControl() in the ixWindow class which allows you to capture all visual objects starting from a known one such as Exit button which every window must have. However this function cannot catch objects which are disabled or not tab enabled. I have found a better way of doing this. In the .4gh file all the controls are listed in one place. And as in the case of creating an instantiating function(described under B2), you can write a small program to go through the 4gh file and grab the names and build up a string having names of all controls which helps the base class build the vector of visual objects. The program I have written to instantiate the window also does this job in addition - killing two birds with one stone. This vector, along with security information can be used to control access to data entry fields and push buttons. 

B3. Some Problems with New Era Superfields

We have noticed that the data changed event of a superfield is triggered only if the user clicks on a list box or a button, immediately after entering data in the superfield. This sometimes is a problem, specially when there is code in the data changed to populate the very list box the user clicked on. The solution to this problem is to create a hidden dummy superfield which is used to set the current cell by calling the SetCurrentCell() function. The code to do this has to be manually put in the focus in event of the non superfield object(list box or text box), which is a nuisance.  

The second problem is disabling or hiding the superfield which has the focus. New Era brings an error message "Cannot disable/hide the current superfield" and does not disable/hide the superfield. The solution to this is to create a second dummy superfield which should not be hidden and should not be disabled. This superfield needs to be anchored off the screen so that the user will not see it and it should not be tab enabled so that user cannot tab into it. 

These two problems can be solved by putting code into the constructor of the supertable to create the two dummy superfields automatically increasing the number of columns property(numDisplayCols) of the supertable by 2. Having done this, put code in the list box base class’s focus in event to set the current cell to the hidden dummy super field to solve the first problem. To solve the second problem, override the hide and disable events of the superfield base class to set current cell to the second dummy superfield.  An obvious side effect of this to the application programmer - to whom the dummies built into the supertable base class must be transparent - is that the supertable member function getNumCols() now returns a number which is more than the number of real non dummy super fields the application developer created. The solution to this is to override this function in the base class to return the number of real superfields after decrementing the total number by the number of dummies. In this way, the problem created in the base class is solved in the base class without allowing it to spread to the descendants.

Conclusion

In conventional, imperative programming, the cost of correcting mistakes made in the early stages of a project and detected in the later stages is exponential. Object Oriented development eliminates this exponentiality, only if you know what you are doing. Spend more time on the design create the necessary building blocks first, thinking of all possibilities – as carpenters say measure twice and cut once !

"Download Software Mentioned in this Paper"