IT SOLUTIONS
Your full service technology partner! 
-Collapse +Expand
Paradox
Search Paradox Group:

Advanced
-Collapse +Expand Paradox To/From
To/FromCODEGuides
-Collapse +Expand Paradox Store
PRESTWOODSTORE

Prestwood eMagazine

May Edition
Subscribe now! It's Free!
Enter your email:

   ► KBDesktop Data...Paradox & Ob...P9 Book: Pow...   Print This     
  From the July 2011 Issue of Prestwood eMag
 
Paradox P9 Book: Power Programming:
Power: Chapter 18, Reusing Code
 
Posted 16 years ago on 3/26/2003 and updated 11/16/2008
Take Away: Chapter 18, "Reusing Code" from Paradox 9 Power Programming by Mike Prestwood. Includes coding custom methods, custom procedures, and using ObjectPAL libraries.

KB100213



Paradox provides several ways for you to reuse code, including custom methods, custom procedures, and scripts:

  • Custom methods The most common way is to create custom methods and add them to a specific objects (for example, a button, form, or library). These custom methods are public and can be accessed from other objects. For example, a custom method on a form can be accessed using dot notation from another form.
  • Custom procedures Custom procedures are very closely related to custom methods but vary in scope. Custom procedures are private to the object. For example, a custom procedure on a form can not be accessed from another form.
  • Scripts Scripts can be called from within an application using play() or run by a user from within the Paradox environment.

When deciding where to place reusable code, think about what object this code belong to. Does it belong to the current Form object? Does it belong to more than one Form object? If it belongs to more than one form, then decide if it belongs to this application or all applications you develop. Finally, decide if the reusable code is reusable only within this object or will be called using dot notation from other objects. If there is a chance it will need to be called from another object, make it a custom method. Make the reusable code a custom procedure only if there is no chance the code will need to be called from another object.

Categorize and name your libraries carefully—perhaps creating one library for each application that stores application-specific information (give it the same name as the main form). Then, create libraries for specific purposes. For example, you might wish to create the following libraries: constants.lsl, strings.lsl, files.lsl, toolbars.lsl, internet.lsl, science.lsl, and printer.lsl. This list is here just for examples and is intended to encourage you to create your own. The better you become at reusing code, the better programmer you will become.

Note: If you create a particularly useful generic routine you wish to make available to others, then send it to me at mike@prestwood.com. Be sure to document each custom method's public interface. Document how to use the custom method, not how you wrote it. Notes on how you wrote it are important and should be integrated as comments within the code itself. I'll integrate the routine with the appropriate library and make it available to others on www.prestwood.com. Also, if you create a whole library of generic routines, then zip it up and send it to me and I'll make it available to others.

Custom Methods and Inheritance

In ObjectPAL, custom methods and procedures are code routines that you add to objects. You can even use the Copy to Toolbar option to make the code (and property changes) part of the current style sheet. In essence, you can inherit from the base class, add functionality, and save the new default object in the current style sheet. If you maintain one style sheet for each application, you can have default application-level code for each object. This isn’t true with inheritance because when you create a new object, the code is simply copied to the new object. If you change the generic routine by altering the code and select the Copy to Toolbar option, the changed code will apply only to newly created objects (the old objects will have the old code). Even with that said, this is still a useful way to customize the code attached to each object for each application you build.

Scope of Custom Methods

A custom method is always public. The object to which the custom method is attached and all the objects contained by it can call the custom method directly. With dot notation, objects can use the custom methods of other objects. For example, if a button has a custom method called cmMsg() on it, it can be called from another button.

Scope of Custom Procedures

Custom procedures, on the other hand, are more limited in scope (they are private to the object to which they are attached). Custom procedures have the same scope as variables. Only the object and objects contained by that object can call the custom procedure directly. In addition, dot notation doesn’t work with custom procedures.

Custom Procedures Private to a Method

A custom procedure can be private to an object or private to a single method on that object. This is important if you want to reuse a bit of code within an event or custom method but not allow other events or objects to have access to the custom procedure. You do this by adding your own proc…endProc statement above or below the method line. When a custom procedure is private to a method, no other objects can call it. Likewise, no other events within the object can call it. The following is an example of a private custom procedure. The proc structure goes either above or just below the built-in method line. For example, create a button and alter the pushButton event as follows (be sure to add lines 2–4 above the method):

1: ;Button4 :: pushButton
2: proc cpMsg()
3: message("A primary index can consist of up to 255 fields")
4: endProc
5: method pushButton(var eventInfo Event)
6: cpMsg()
7: endMethod

Passing by Value Versus Passing by Reference

Custom methods can receive and return values. You can pass a value by value or by reference. When you pass a value to a custom method by value, you pass a copy of the value. If the custom method alters the copy, nothing happens to the original. When you pass a value to a custom method by reference, you pass a reference to the location where the value is currently stored. In other words, you are actually referring to the original value. If the custom method alters it, it is altering the original value.

Passing by value and passing by reference are common to custom methods and procedures. Take a look at the following custom method prototypes:

1: method cmCode(var s String) ;Pass by reference.
2: method cmCode(s String) ;Pass by value.
3: method cmCode(Const s String) ;Pass by reference,
4: ;but not changeable.

Line 1 uses the var keyword to pass the s value by reference. In other words, a pointer to the location of the original value is being passed. If the custom method alters s, then it is altering the original value.

In line 2, s is passed by value. In other words, a copy with its own memory address is created and the address to this new memory location is passed. If the custom method alters s, then the original is not altered.

In line 3, the value is passed by reference, but because the value is a constant, the custom method can’t change it. The following examples demonstrate these concepts.

My First Custom Procedure

In this next step-by-step example, you create and use a simple custom procedure. It demonstrates creating and using a simple custom procedure that doesn’t receive or return a value. Use this type of custom procedure to compartmentalize your code into easy to understand segments of code in custom procedures.

Step By Step

  1. Create a new form and place a button labeled Trivia on it, as shown here:
  2. Illustration 1

  3. Open the Proc window for the form. To do this, make sure the form is selected and open the Object Explorer. Select the Methods tab and open the Proc window by selecting Proc, right-click, and select Edit Method, as shown here:
  4. Illustration 2

  5. Alter the Proc window of the form to look like the following. Line 2 prototypes (sets up) the custom procedure. Nothing is in the parentheses, which indicates that the custom procedure expects to be passed nothing. The lack of code after the closing parenthesis indicates that the custom procedure returns nothing. Line 3 does the actual work.
  6. 1: ;Button :: Proc
    2: proc cpMsg()
    3: msgInfo("PDoxWin Trivia",
    "You can use up to 24 tables in a query.")
    4: endProc

  7. Add line 3 to the pushButton event of the myMsg button.
  8. 1: ;Button :: pushButton
    2: method pushButton(var eventInfo Event)
    3: cpMsg()
    4: endMethod

  9. Check the syntax, run the form, and click the Trivia button. When the message appears, click OK:

Illustration 3

Passing By Value to a Custom Procedure

This next step-by-step example demonstrates creating and using a custom procedure that receives, but doesn’t return, a value. This category of custom procedure is important when writing generic custom procedures. For example, rather then referring directly to objects on forms, you can pass a UIObject variable to a custom procedure. Doing this makes the custom procedure work in more situations.

Step By Step

  1. Create a new form (or use the form from the previous example) and place a button labeled Pass by value on it.
  2. Add line 3 to the Proc window and alter line 2 of the "Pass by value" button.
  3. 1: ;Button :: Proc
    2: proc cpMsg(s String)
    3: msgInfo("PDoxWin Trivia", s)
    4: endProc

  4. Add lines 3–7 to the pushButton event of the "Pass by value" button.
  5. 1: ;Button :: pushButton
    2: method pushButton(var eventInfo Event)
    3: var
    4: s String
    5: endVar
    6: s = "Numeric fields are accurate to 15 significant digits"
    7: cpMsg(s)
    8: endMethod

  6. Check the syntax and save your work. Run the form and click the "Pass by value" button. When the message appears, click OK.

The custom procedures are starting to get interesting. This discussion begins with the pushButton event in step 3. Line 4 declares s as a String variable, and line 7 calls the custom method. This time, a string is passed to it.

In step 2, line 2 prototypes the custom procedure. s String, which is between the parentheses, indicates that the custom procedure expects to be passed a string. The lack of code after the closing parenthesis indicates that the custom procedure returns nothing. Line 3 does the actual work of the custom procedure. It uses the variable s.

In this example, both the calling code and the custom procedure use a String variable called s. This shows the connection between the two. In reality, both variables need only to be of the same type; they don’t need to have the same name.

Passing By Reference to a Custom Procedure

This example demonstrates creating and using a custom procedure that receives a reference to a variable but doesn’t return a value.

Step By Step

    1. Create a new form (or use the form from the previous example) and place a button labeled Pass by reference on it.
    2. Add line 3 to the Proc window and alter line 2 of the "Pass by reference" button.
    3. 1: ;Button :: Proc
      2: proc cpMsg(var s String)
      3: msgInfo("Windows Trivia", s)
      4: endProc

    4. Add lines 3–8 to the pushButton event of the "Pass by reference" button.
    5. 1: ;Button :: pushButton
      2: method pushButton(var eventInfo Event)
      3: var
      4: sMsg String
      5: endVar
      6:
      7: sMsg = "The Registration Database replaced INI files."
      8: cpMsg(sMsg)
      9: endMethod

    6. Check the syntax and save your work. Run the form and click the "Pass by reference" button. When the message appears, click OK.

This discussion begins with the pushButton event in step 3. Line 4 declares sMsg as a String variable. Line 8 calls the custom method that uses the variable sMsg. This time, a variable is passed to the custom procedure.

In step 2, line 3 prototypes the custom procedure. In this case, var sMsg String in parentheses indicates that the custom procedure expects to be passed a variable. The lack of code after the closing parenthesis indicates that the custom procedure returns nothing. Line 2 does the actual work of the custom procedure. It uses the variable sMsg.

In this example, the calling code and the custom procedure use different variable names: sMsg and s. This shows that, although the two variables have a direct connection, the connection is by reference to the same value. You could have used the same variable name.

Returning a Value from a Custom Procedure

This next step-by-step example demonstrates how to create and use a custom procedure that doesn’t receive a value by reference but does return a value. After the procedure is created, the return value is used in a message information dialog box.

Step By Step

    1. Create a new form (or use the form from the previous example) and place a button labeled Return a value on it.
    2. In the Proc window of the "Return a value" button, add line 3 and alter line 2.
    3. 1: ;Button :: Proc
      2: proc cpNever() String
      3: return "Never duplicate a line of code!"
      4: endProc

    4. In the pushButton event of the "Return a value" button, add line 3.
    5. 1: ;Button :: pushButton
      2: method pushButton(var eventInfo Event)
      3: msgInfo("Message from guru", cpNever())
      4: endMethod

    6. Check the syntax and save your work. Run the form and click the "Return a value" button. When the message appears, click OK.

Line 2 of step 2 prototypes the custom procedure. The lack of code in the parentheses indicates that the custom procedure expects to be passed nothing. The data declaration after the closing parenthesis indicates that the custom procedure returns a string. Line 3 does the actual work of the custom procedure. It uses the return keyword and passes back a string.

In step 3, line 3 calls the custom procedure and passes it nothing. Because the run-time library procedure msgInfo() expects a string, the custom procedure must return a value.

Sending and Returning a Value from a Custom Procedure

This example demonstrates how to create and use a custom procedure that receives a value by a reference and returns a value. After the procedure is created, the return value is used in a message information dialog box.

Step By Step

    1. Create a new form (or use the form from the previous example) and place a button labeled Pass & return a value on it.
    2. Alter the Proc window of the "Pass & return a value" button as follows:
    3. 1: ;Button :: Proc
      2: proc cpAge(var d Date) Number
      3: var
      4: n Number
      5: endVar
      6:
      7: n = year(today() - d) - 1
      8: return n
      9: endProc

    4. Add lines 3–9 to the pushButton event of the "Pass & return a value" button.
    5. 1: ;Button :: pushButton
      2: method pushButton(var eventInfo Event)
      3: var
      4: dBorn Date
      5: endVar
      6:
      7: dBorn = date("01/08/65")
      8: dBorn.view("Enter your birthdate")
      9: msgInfo("Your age", cpAge(dBorn))
      10: endMethod

    6. Check the syntax and save the form as CUSTPROC.FSL. Run the form and click the "Pass & return a value" button. When the first dialog box appears, type your birthdate and click OK. When the message displays your age, click OK.

In step 2, line 2 prototypes the custom procedure. var d Date in parentheses indicates that the custom procedure expects to be passed a reference to a Date variable. The data declaration after the closing parenthesis indicates that the custom procedure returns a number. Line 4 declares n as a Number variable. It is used in line 7 to accept the result of the calculation of the number of years between today and the date passed to the custom procedure. Line 8 returns n.

In step 3, line 4 declares dBorn as a Date variable. dBorn is given a value in line 7. Line 8 enables the user to change the value with a Date View dialog box. Line 9 calls the custom procedure inside a msgInfo() procedure.

The previous six examples represent the various types of custom procedures. In a nutshell, a custom procedure can be private to the method or public. Values can be passed to the custom procedure by value or by reference. Custom procedures can return a value, or not. After you master these elements, you can begin optimizing your code. Try never to duplicate a line of code. If you need to duplicate code on two objects, then consider putting the common code in a custom method or procedure and call it from both objects.

Custom Methods

Custom methods don’t differ from custom procedures in any way except for scope. Although already stated, it is worth repeating. Custom procedures are private to a method or object and custom methods are always public. A custom method on an object can be called directly from any event on the object or from any event lower in the containership hierarchy. With dot notation, any object can call another object’s custom method.

My First Custom Method

This next step-by-step example demonstrates how to create and use a custom method that simply executes a block of code; the custom method does not accept or return a value. In this case, the custom method asks a question.

Step By Step

  1. Create a new form and place a button labeled A Question on it.
  2. Create a new custom method for the form. To do this, make sure the form is selected and open the Object Explorer. Select the Methods tab and double-click the New Method option or right-click and select Edit Method, as shown here:
  3. Illustration 4

  4. Name the custom method cmQuestion, and select OK.
  5. Add lines 3–7 to the newly created custom method.
  6. 1: ;Form :: cmQuestion
    2: method cmQuestion()
    3: if msgQuestion("Question?",
    "Is ObjectPAL full of features?")= "Yes" then
    4: message("When you’re right, you’re right!")
    5: else
    6: message("How much more do you want?")
    7: endIf
    8: endMethod

  7. Add line 3 to the pushButton event of the A Question button.
  8. 1: ;Button :: pushButton
    2: method pushButton(var eventInfo Event)
    3: cmQuestion()
    4: endMethod

  9. Check the syntax and save the form as CUSTMETH.FSL (you will use this form for the next several step-by-step examples). Run the form and click the A Question button. When the first dialog box appears, answer the question.

In step 2, line 3 uses the message question dialog box to ask a question. If the user clicks Yes, the condition of the if structure is satisfied and the message in line 4 appears in the status bar. If the user clicks No, the message in line 6 appears.

In step 3, line 3 starts the whole process. The syntax for calling a custom method is the same as the syntax for calling a custom procedure. Therefore, you can’t use the same name for a custom procedure and for a custom method in the same object. You can, however, use the same name for both as long as they are in different objects; the scope determines whether the procedure or method is called.

For example, if you put a custom procedure named cmQuestion on the page level of the form created in this example, it’s called from the button rather than the cmQuestion form-level custom method. If you look at the Object Tree, you’ll notice that the page is closer to the button than the form is.

Adding Private proc and var Structures to Custom Methods

As we have already discussed, custom procedures have the same scope as variables. They are private to the object or method and can be called from any event, custom procedure, or custom method contained within that object. This next example demonstrates adding both a private custom procedure and a private static variable to a custom method.

1: ;Form :: cmUsingProc
2: var
3: siCounter SmallInt
4: endVar
5:
6: Proc cpCounter()
7: siCounter = siCounter + 1
8: endProc
9:
10: method cmUsingProc()
11: siCounter = 9
12: cpCounter()
13: siCounter.view()
14: endMethod

After you create this custom method at the form level, you can call it from a button as follows:

;Button :: pushButton
2: method pushButton(var eventInfo Event)
3: cmUsingProc()
4: endMethod

At this point, you should be understanding that reusing code using both custom procedures and custom methods is an important part of your ObjectPAL programming. To create truly reusable code (custom methods), you need to plan your application design well. When stepping back from your specific programming task and delving into application design, keep the application objects in mind (primarily forms and libraries).

Using subject to Dereference an Object

You can pass a subject to a custom method. With dot notation, you can tell a custom method to act on another object. To do this, you use the keyword subject. The code inside the custom method can use subject to refer to the object that preceded the calling of the custom method using dot notation. This next step-by-step example demonstrates how to use subject and dereference an object.

Suppose that you want to toggle the pattern of one of two ellipses, depending on the value in a field. This next step-by-step example dereferences a field and uses subject in a custom method to refer to the dereferenced value.

Step By Step
    1. Create a new form with two ellipses on it named Ellipse1 and Ellipse2 and name the page Pge1.
    2. Add a radio button field named choice with the two values Ellipse1 and Ellipse2.
    3. Add a button labeled Toggle pattern, as shown here:
    4. Illustration 5

    5. Create a custom method at the form level called cmToggleBackground() and add lines 3–7 to it.

1: ;Form :: cmToggleBackground
2: method cmToggleBackground()
3: if subject.Pattern.Style = BricksPattern then
4: subject.Pattern.Style = WeavePattern
5: else
6: subject.Pattern.Style = BricksPattern
7: endIf
8: endMethod

    1. Add lines 3–7 to the pushButton event of the button.

1: ;Button :: pushButton
2: method pushButton(var eventInfo Event)
3: if choice.isBlank() then
4: return
5: else
6: pge1.(choice).cmToggleBackground()
7: endIf
8: endMethod

  1. Check the syntax and save your work. Run the form. Select an ellipse from the choice field and click the button. Experiment with this form:

Illustration 6

In step 2, line 2 prototypes the custom method. Line 3 checks whether the pattern of subject is a brick pattern. If it is, line 4 changes it to a weave pattern. If it isn’t, line 6 sets it to a brick pattern. Note the use of subject. By not hard-coding the name of the object, you can make your routines more generic and flexible.

In step 3, line 3 ensures that the choice field has a value. Otherwise, an error would occur whenever the field is left blank. If the choice field is blank, the method returns control. If a choice was made, line 6 calls the custom method. Parentheses are used around the field name, and an object is specified before the first parenthesis.

Passing Arrays and Dynamic Arrays

To pass an array or a dynamic array to a custom method or procedure, you need to set up a Type statement. The syntax checker doesn’t enable you to declare an array directly, so you have to create a custom type of the array or dynamic array. For example:

Type
ctPassDyn = DynArray[] String
endType

Once you create the custom type, you can use the custom type when you prototype your custom method (usually the first line of your custom method). For example:

method (dynSystem ctPassDyn)
;Use dynSystem here.
endMethod

This next step-by-step example demonstrates how to pass an array to a custom method.

Passing an Array to a Custom Method

Suppose that you need to pass an array with two elements in it to a custom method for display. To do this, you have to create a custom type of an array. Then, use the custom type in the prototype line (usually the first line) of the custom method.

Step By Step

  1. Create a new form and put a button labeled Pass Array on it.
  2. Add line 3 to the Type window of the Pass Array button.
  3. 1: ;Button :: Type
    2: Type
    3: ctPassAr = Array[2] String
    4: endType

  4. Create a custom method called cmDisplayArray() on the Pass Array button and alter as follows:
  5. 1: ;Button :: cmDisplayArray
    2: method cmDisplayArray(var ar ctPassAr)
    3: ar.view("Childhood friend")
    4: endMethod

  6. Add lines 3–9 to the pushButton event of the Pass Array button.
  7. 1: ;Button :: pushButton
    2: method pushButton(var eventInfo Event)
    3: var
    4: arName Array[2] String
    5: endVar
    6:
    7: arName[1] = "Grant"
    8: arName[2] = "Winship"
    9: cmDisplayArray(arName)
    10: endMethod

  8. Check the syntax and save your work. Run the form and click the button. The name "Grant Winship" appears in an Array view dialog box, as shown here:

Illustration 7

In step 2, line 3 declares a new data type: a two-element array that is ready to receive string elements.

In step 3, line 2 uses the new data type in the custom method’s prototype. The var keyword indicates that the array is passed by reference. If you leave out the var keyword, the array is passed by value. Line 3 displays the array.

In step 4, line 4 declares arName as a two-element private array that is ready to receive string values. Lines 7 and 8 populate the array. Line 9 passes an array reference to the custom method.

With the knowledge you gain in this chapter, you can start to reuse and compartmentalize your code. Remember to try never to duplicate a line of code and to put often used code into custom procedures. These techniques work well for reusing code in a form. For reusing code across forms, ObjectPAL provides an object that is used only for storing code—the library.

Review of Custom Methods and Custom Procedures

Is there a good rule of thumb to use as to when to use a custom procedure? Functionally, the rule of thumb should be to always use custom procedures unless the code is called from outside of the containership hierarchy. Unfortunately, however, the real-world reality is that it’s too much of a pain for most people to do this because of Paradox’s incapability to present a list of custom procedures and jump directly to a specific one. Any procedure or method window is limited to 32K, which is dedicated to one method as opposed to being shared among many. This limits the wide use of custom procedures to just 32K per Proc window.

One advantage custom procedures have over custom methods is that they are faster. The extra speed of procedures is not execution speed, but access speed. This implies that the advantage of procedures decreases as the size of the procedure increases. The biggest advantage of custom procedures—and a motivation for their use—is their slightly faster access speed. However, speed is not that much of an issue in the decision because the difference in access time between custom methods and custom procedures is negligible. In addition, the scope is easily controlled by containership.

Libraries

A library is a Paradox object that stores custom ObjectPAL code. A library is useful for storing and maintaining frequently used routines and for sharing custom methods and constants among forms, scripts, and other libraries.

In a library, you can store custom methods, custom procedures, variables, constants, and user-defined types. Think of a library as a place to store often-used routines. It’s the key to reusing your code and therefore, is a timesaver. The more you use libraries, the more time and energy you will save. You code a library similar to the way in which you code a form. In fact, you can think of a library as being a form with no user interface.

Characteristics of a Library

You never run a library. With ObjectPAL, you open and use the custom methods in a library, but you never run a library. A library doesn’t contain objects. It contains compiled code.

The object variable self has different meanings, depending on when it’s used. When it’s used in a library, self refers to the calling object. When it’s used in a form, self refers to the object to which the code is attached.

self refers to the calling object, not the library. This makes sense, although it’s different than when another object’s custom methods use self.

The scope of a library is determined by where you declare its variable and by how you open it (with or without the constant PrivateToForm). Although you can open a library from any point in the containership path, the placement of the Library variable determines which objects can see it. The form’s Var window is a good place to declare a library’s variable.

Every library has a built-in open, close, and error event. The open and close methods work just like a form’s open and close methods. open is a good place to initialize variables. error is called when code in the library generates an error. The error event is a good place to trap for errors that the library itself might generate. By default, the error event calls the built-in error event of the form that called that particular custom method.

A library is a complete unit, just like a form. With a library, however, external objects have access only to the library’s custom methods. Although a library can’t contain objects, it has Var, Const, and Type windows for declaring, setting, and defining variables. A library also has Procs and Uses windows for writing procedures and accessing other libraries and DLLs. You access all these items by writing custom methods that utilize them.

The custom methods you write in a library can be self-contained—that is, each method contains all the commands within its own code. By using custom methods, you can access variables, constants, types, and procedures inside a library. You can even get to a library’s Uses window to call functions from other libraries or DLLs.

Libraries enable you to do the following:

  • Reuse your code
  • Centralize your code
  • Set and retrieve variables in the Var window
  • Call custom procedures in the Procs window

When should you use a library? Whenever you repeat code, create a custom method. Whenever you want to use a custom method on more than one form, put it in a library.

Opening a Library with

open()

Use open() to give access to a library. You must declare a library first. As soon as you declare a variable, you use the open() command to open it. The syntax is as follows:

libvar.open( libName String [ GlobalToDesktop | PrivateToForm] ) Logical

Following are two examples:

1: lib.open("LIBRARY")
2: lib.open("LIBRARY", PrivateToForm)

GlobalToDesktop Is the Default

If you leave the second parameter of the open() method blank, Paradox opens the library global to the desktop (the default). This is important because this means you can share code and variables among the forms of an application. You could, for example, have one form set a variable in a library and another read that variable—in effect, setting and getting the variable as needed. We’ll revisit passing variables through a library later in this chapter.

Closing a Library with

close()

Closing a library frees up memory. Although it’s not always necessary, you can close a library with the close() method. For example, the following closes the lib library variable:

1: lib.close()

Steps in Using a Library

    This next section takes you on a short guided tour of how to create and use a library.

    1. Create a custom method in a library. By means of custom methods, you can access all the various components of a library.
    2. Declare the Library variable. You need to declare a Library variable on every form with which you want to use a particular library. A good place to declare a Library variable is on the Var method of the form, as in the following:

    1: ;Form :: Var
    2: var
    3: libStrings Library
    4: endVar

  1. Open the library. After you declare the Library variable, you can use it to open the library. The syntax for opening a library is as follows:
  2. library.open(Filename)

    A good place to open a form’s library is in the form’s init method, as in the following:

    1: ;Form :: init
    2: method init(var eventInfo Event)
    3: lib.open("Strings")
    4: endMethod

    Note that this statement doesn’t specify an extension. Paradox automatically looks for LIBRARY.LSL first and then for LIBRARY.LDL.

    Note: Use the open() command on all the forms in an application. Libraries don’t have an attach() command. It is implied by open().

  3. Declare the custom method or methods in the Uses window or use the extended Uses syntax discussed later in this chapter. If you don’t use the extended Uses syntax, then every method you want to use from a library must be declared in the Uses window of the object you want to use it in. The syntax to prototype each custom method follows:
  4. Uses ObjectPAL
    methodName( [[var | const] argList]) [returnType]
    endUses

    A good place for this is in the form’s Uses window:

    1: Uses ObjectPAL
    2: cmMsg()
    3: endUses

  5. To use Paradox’s extended Uses, all you need to do is this:
  6. Uses ObjectPAL
    "LIBRARY1.LSL" "LIBRARY2.LSL"
    endUses

      All the custom method prototypes, types, and constants from both LIBRARY1 and LIBRARY2 are bound at compile time. More information on extended Uses later in this chapter.

    1. Use the custom method. After the custom method is set up for use, you must use proper dot notation to call it. The syntax for using a custom method from a library is as follows:

1: lib.cmMsg()

Note: The syntax of object.method() is consistent throughout ObjectPAL. If an object with the name box has code on its mouseClick event, you can access that code with box.mouseClick() method from any other object. When this code executes, the UIObject method mouseClick() calls the mouseClick event.
How do you know when you can call the code in a built-in method of an object? Easy, if the run-time library has a method equivalent, then you can use it—for example, mouseClick() and pushButton().

Passing eventInfo

Just like you can pass variables to a custom method, you can pass the eventInfo variable to a custom method. You can use this technique to centralize your code further by passing eventInfo to a custom method in a library. For example, you can pass the eventInfo up to a library routine to handle all errors.

Inspecting

eventInfo

You can use the technique of passing eventInfo to inspect the eventInfo by creating your own custom method. For example, the following custom method displays information about an ActionEvent when passed eventInfo. Create a form based on the Customer table, shown next, and add the following custom method to the form level (name the custom method cmActionEvent()).

Illustration 8

1: ;Form :: cmActionEvent
2: method cmActionEvent(var eventInfo ActionEvent)
3: var
4: ui UIObject
5: dynEventInfo DynArray[] Anytype
6: endVar
7: eventInfo.getTarget(ui)
8:
9: dynEventInfo["actionClass"] = eventInfo.actionClass()
10: dynEventInfo["errorCode"] = eventInfo.errorCode()
11: dynEventInfo["getTarget Name"] = ui.name
12: dynEventInfo["getTarget Value"] = ui.value
13: dynEventInfo["getTarget Container Name"] = ui.container.name
14: dynEventInfo["id"] = eventInfo.id()
15: dynEventInfo["isFirstTime"] = eventInfo.isFirstTime()
16: dynEventInfo["isprefilter"] = eventInfo.isprefilter()
17: dynEventInfo["isTargetSelf"] = eventInfo.isTargetSelf()
18: dynEventInfo["Reason"] = eventInfo.reason()
19:
20: dynEventInfo.view("View Eventinfo")
21: endMethod

Next, pass it an ActionEvent eventInfo variable. For example, type the following code into the action event of the form:

1: ;EVENT1 :: Form :: action
2: method action(var eventInfo ActionEvent)
3: if eventInfo.isprefilter() then
4: ;// This code executes for each object on the form
5: ;//
6: if eventInfo.id() = DataUnlockRecord or
7: eventInfo.id() = DataPostRecord then
8: DoDefault
9: if eventInfo.errorCode() = peKeyViol then
10: msgStop("Warning",
"Key violation\n\nLet’s inspect the action event eventInfo.")
11: cmActionEvent(eventInfo)
12: endIf
13: endIf
14: else
15: ;// This code executes only for the form
16: ;//
17: endIf
18: endMethod

Next, run the form and cause a key violation (see Figure 18-1).

Figure 1: PassingEventInfo.fsl showing the contents of eventInfo

Passing Variables Through a Library

Passing variables from form to form and from form to library comes close to making up for the lack of a true global variable in ObjectPAL. Occasionally, you need a system-wide control mechanism, or you need to store a piece of data from a form in a library for later use. A variable in a library enables you to emulate a true global variable. The next example shows you how to put variables into and get variables from a library.

Passing a Variable Between Forms

Suppose that you want to pass a value from one form to another through a library. Because you are using a library, both forms don’t have to be open at the same time.

Step By Step
    1. Create two forms and save the forms as LIB1.FSL and LIB2.FSL, respectively. Put a button labeled Put on the LIB1.FSL and a button labeled Get on the LIB2.FSL. Create a library and save it as LIB.LSL (see Figure 18-2).
    2. Figure 2: Set up forms, library, and custom methods

    3. Create a new library and add lines 3 and 4 to the Var window of the library.
    4. 1: ;Library :: Var
      2: Var
      3: sFirstName String
      4: sLastName String
      4: endVar

    5. Create a new custom method in the library called cmPutFirstName() and add line 3 to it.
    6. 1: ;Library :: cmPutFirstName
      2: method cmPutString1(AFirstName String)
      3: sFirstName = AFirstname
      4: endMethod

    7. Create a new custom method in the library called cmPutLastName() and alter it as follows:
    8. 1: ;Library :: cmPutLastName
      2: method cmPutString2(ALastName String)
      3: sLastName = ALastName
      4: endMethod

    9. Create a new custom method in the library called cmGetFirstName() and alter as follows:
    10. 1: ;Library :: cmGetFirstName
      2: method cmGetFirstName() String
      3: return sFirstName
      4: endMethod

    11. Create a new custom method in the library called cmGetLastName() and alter it as follows: Save the library as LIB.LSL.
    12. 1: ;Library :: cmGetLastName
      2: method cmGetLastName() String
      3: return sLastName
      4: endMethod

    13. Create a new form and add lines 3 and 4 to the Uses window of the first form.
    14. 1: ;Form :: Uses
      2: Uses ObjectPal
      3: "lib.lsl"
      4: endUses

    15. Add line 3 to the Var window of the first form.
    16. 1: ;Form :: Var
      2: Var
      3: lib Library
      4: endVar

    17. Add lines 3–5 to the pushButton event of the Put button on the first form. Save the form as LIB1.FSL.
    18. 1: ;Button :: pushButton
      2: method pushButton(var eventInfo Event)
      3: lib.open("LIB")
      4: lib.cmPutFirstName("Keith")
      5: lib.cmPutLastName("Kinnamont")
      6: endMethod

    19. Add lines 3 and 4 to the Uses window of the second form. This is an example of using ObjectPAL’s extended uses, which is discussed in the next section.
    20. 1: ;Form :: Uses
      2: Uses ObjectPAL
      3: "lib.lsl"
      4: endUses

    21. Add line 3 to the Var window of the second form.
    22. 1: ;Form :: Var
      2: Var
      3: lib Library
      4: endVar

    23. Add lines 3 and 4 to the pushButton event of the Get button on the second form. Save the form as LIB2.FSL.
    24. 1: ;Button :: pushButton
      2: method pushButton(var eventInfo Event)
      3: lib.open("lib.lsl")
      4: msgInfo("Childhood friend", lib.cmGetFirstName() + " " + lib.cmGetLastName())
      5: endMethod

    25. Check the syntax and save all the various elements (the forms and the library). Close the library and run both forms. Click the Put button on the first form, then click the Get button on the second form, as shown here:

Illustration 9

If you close LIB1.FSL before you open LIB2.FSL, the library is closed and reopened, losing any variables that were set. Also, if you press the Get button without first pressing the Set button, you’ll get an error. You can avoid this error by initializing the variables in the library to Null in the library’s open event.

Note: Since custom methods are public, a library can access the custom methods in other libraries.

Extended Uses Syntax

Extended Uses enables ObjectPAL to use compile time binding. Compile-time binding enables the compiler to resolve code at the time the code is compiled. Extended Uses enables you to utilize libraries easier as well as do some things you couldn’t do before. Following are the three things it enables you to do:

  • You can utilize the custom types you have defined in a library or form in another library or form without having to write the code again.
  • You can utilize the constants you have defined in a library or form in another library or form without having to write the code again.
  • You can utilize the custom methods from a library or form from another library or form without having to prototype the custom methods.

In C, compile-time binding is accomplished with the #include command. In Pascal, it is accomplished with the Uses statement (similar in nature to ObjectPAL’s Uses statement). Following is the syntax for doing compile-time binding in ObjectPAL:

Uses ObjectPAL
["fileName"]*
endUses

For example, assume that you have a library you use just for constants, a library just for custom types, and a library for custom methods. Following is the code you need in the Uses window of the form to give your code the capability to utilize all the constants, types, and custom methods from the libraries:

1: ;Form :: Uses
2: Uses ObjectPAL
3: "constants.lsl"
4: "types.lsl"
5: "methods.lsl"
6: endUses

Now in your code, you can utilize the constants from the CONSTANTS.LSL library, and the types from the TYPES.LSL library. In addition, although the two files, CONSTANTS.LSL and TYPES.LSL, need to be around while you deliver the form that utilizes them, the libraries do not need to be around after you deliver the form. The types and constants have been bound at delivery time.

The custom methods from the METHODS.LSL library are not quite that easy to use, but they almost are. You still have to open the library just as you learned earlier in this chapter; however, you do not have to prototype each method—the compiler does that for you at compile time. Following is an example of using an imaginary cmMyMethod() custom method in METHODS.LSL:

Uses ObjectPAL
;Step 1 - specify library.
"methods.lsl"
endUses
method pushButton(var eventInfo Event)
var
libMethods Library
endVar

;Step 2 - Open library.
libMethods.open("methods") ;Can be methods.lsl or methods.ldl.
;Step 3 - Call method using dot notation.
libMethods.cmMyMethod()
endMethod

When you deliver your application, you still need to distribute a copy of the library (either .LSL or .FDL).

Creating a Library from Scratch

The last example in this chapter will demonstrate creating a library from scratch and then using it. This example is included to show you the flexibility and power of ObjectPAL. Following is the pertinent code from the pushButton event:

On The Net: http://prestwood.com/forums/paradox/books/official/files/CreateLibrary.fsl.

1: ;Button :: pushButton
2: method pushButton(var eventInfo Event)
3: var
4: lib Library
5: endVar
6:
7: ;Create library.
8: lib.create()
9: lib.methodSet("cmMessage", "method cmMessage() msgInfo(\"From new library\", \"Hello World!\") endMethod")
10: lib.save("test")
11: lib.close()
12:
13: ;Use library.
14: lib.open("test")
15: lib.cmMessage()
16: endMethod

Now, here is the pertinent code from the button’s Uses method:

1: ;Button :: Uses
2: Uses ObjectPAL
3: cmMessage()
4: endUses

Summary

In this chapter, you learned that custom procedures, custom methods, libraries, and scripts are devices you can use to compartmentalize and organize your code. When your code is well organized, you can reuse more of it.

In ObjectPAL, custom methods and procedures are attached to objects. ObjectPAL comes with a set of methods and procedures already attached to objects, called the run-time library. A custom method consists of methods and procedures from the run-time library, as well as registered functions from dynamic link libraries (DLLs). Think of the set of custom methods and custom procedures that you develop as either enhancements you make to existing objects (like forms and buttons) and as your own private run-time library of code.

A custom procedure is similar to a custom method, except that a custom procedure is private to the object or method. A custom method is always public.

A library is a Paradox object that stores custom ObjectPAL code. A library is useful for storing and maintaining frequently used routines and for sharing custom methods, constants, and custom types among forms, scripts, and other libraries.

More Info


Comments

0 Comments.
Share a thought or comment...
 
Write a Comment...
...
Sign in...

If you are a member, Sign In. Or, you can Create a Free account now.


Anonymous Post (text-only, no HTML):

Enter your name and security key.

Your Name:
Security key = P1272A1
Enter key:
KB Post Contributed By Mike Prestwood:

Mike Prestwood is a drummer, an author, and creator of the PrestwoodBoards online community. He is the President & CEO of Prestwood IT Solutions. Prestwood IT provides Coding, Website, and Computer Tech services. Mike has authored 6 computer books and over 1,200 articles. As a drummer, he maintains play-drums.com and has authored 3 drum books. If you have a project you wish to discuss with Mike, you can send him a private message through his PrestwoodBoards home page or call him 9AM to 4PM PST at 916-726-5675 x205.

Visit Profile

 KB Article #100213 Counter
14010
Since 4/2/2008
Go ahead!   Use Us! Call: 916-726-5675  Or visit our new sales site: 
www.prestwood.com


©1995-2019 Prestwood IT Solutions.   [Security & Privacy]