Lookups in C4

by Steve Parker

Published 1998-08-01    Printer-friendly version

TopSpeed gives us three different ways to do lookups: falling-down-easy, a-tad-harder-but-more-customizable and tear-off-your-shirt-pound-your-chest-and-roll-your-own. But lookups themselves come in only two flavors. They are differentiated by where the lookup is called. Lookups can be called when a control is selected or when it is accepted. That's it.

If called when a control is selected, the end user has no choice but to select a record (or, if you decide to permit it, enter a new record and then select one) or cancel. Why does the user have no choice? Because as soon as the control is selected, the lookup is (immediately) called.

If called when a control is accepted, the user has a chance to make an entry first. In this case, the lookup only validates what the user entered. Some purists refer to the first case, when the control is selected, as a lookup and the accepted case as a validate.

While material to the end user (and it most certainly is), as far as you and I are concerned, the fact is that the primary difference is where the code is placed in the final source file. In the first case, the code generates into ThisWindow.TakeSelected for the control (ABC templates) or the control's Event:Selected (Clarion templates). In the second, it generates into ThisWindow.TakeAccepted or the control's Event:Accepted.

The code generated is, with only minor differences (as noted), the same in both cases.

!Code generated on Selected (in ThisWindow.TakeSelected):
ReturnValue = PARENT.TakeSelected()
CASE FIELD()
OF ?PEO:State
  STA:State = PEO:State
  IF Access:States.Fetch(STA:KeyState)
    IF SELF.Run(1,SelectRecord) = RequestCompleted
      PEO:State = STA:State
    END
  END
  ThisWindow.Reset
END
RETURN ReturnValue
!Code generated on Accepted (in ThisWindow.TakeAccepted):
ReturnValue = PARENT.TakeAccepted()
CASE ACCEPTED()
OF ?PEO:City
  IF PEO:City OR ?PEO:City{Prop:Req}
    CIT:City = PEO:City
    IF Access:Cities.Fetch(CIT:KeyCity)
      IF SELF.Run(2,SelectRecord) = RequestCompleted
        PEO:City = CIT:City
      ELSE !<--- these are SELECT(?PEO:City)
           !<--- not in the CYCLE
           !<--- when selected END !<--- code
      END
    END
    ThisWindow.Reset()
  END

More importantly, the expected program behavior is exactly the same for both, and the point at which the call is made is not a relevant difference.

What exactly do we expect a lookup to do?

When there is no value in the control or when the value that is in the control is not valid, we expect the lookup to present a list from which the user may (must) select a value. "Not valid?" Not in the validation file. Whenever a valid value is in the control, we expect the lookup to do nothing - nothing at all, period.

The Falling-Down-Easy Way

Right click the control, select Actions, complete the three prompts. Along the way you may have to add the lookup file to your file schema. Total keystrokes: six if the file is already in the file layout, eight if it is not. That's pretty easy and you don't even have to type anything in, just make selections.

One important caveat: the Fetch method clears the record buffer. So, if you have begun an entry in the field, the lookup will not start at the closest record. Instead, it will start at the top of the file. Jon Waterhouse posted a template modification on 5 May 1998 to the topspeed.products.c4 newsgroup that you might want to check out (article title, "Less than optimal lookup behavior in ABC templates?").

The completed worksheets look like:

LU1.GIF

LU2.GIF

Actually, it can get even easier. How about no keystrokes at all? If you selected the "Must Be in File" option in the dictionary and created the app or the procedure with a wizard, this code will be generated for you. In fact, if you selected "Must Be in File," this code will be generated into the When Control is Accepted block for you whenever you populate the field on a window. Now, that is easy. (On the other hand, if you ever change the relationship between the files - a relationship is required by the "Must Be in File" option - you will get mysterious compiler errors all over the place. There have been a number of undesirable behaviors laid at the foot of this option, so be warned.)

Pros: easy to implement, behaves as expected.

Cons: difficult to place embedded code precisely. It is especially difficult to place embedded code after the call since the first available embed is after the host window is refreshed (re-displayed).

In addition, if you do place embedded code before or after the lookup, it will appear disconnected in the embed tree:

LU3.GIF

This is not a major problem but, looking at the embed list, it can be easy to forget that you have a lookup on the field.

Recommended use: lookups and validations where little or no embedded code is required. The overwhelming majority of lookups are handled perfectly by this method.

The A-Tad-Harder-But-More-Customizable Way

From the control's embed tree, select the embed you want to use: before or after generated code (for the control). Select the CallProcedureAsLookup code template and select the lookup procedure.

This template provides its own "embeds" for before and after the lookup call and if-canceled. This allows you to do, for example, a Select(?) to force the user back to the empty field.

LU4.GIF

Pros: While not quite as easy as the previous method, this way of doing lookups is still extremely easy. You can determine what is to happen if the user fails to select a record. You can copy over other fields from the lookup file. In general, you can fine-tune the behavior of the lookup with substantial precision.

Cons: Requires typing. This is no joke; should you make a typing mistake, you cannot edit it from the Embeditor or the embed tree. Further, that tiny little window (shades of CPD!) is not easy to work in. But, when you need fine control, this is the way to go.

Recommended use: When you need embeds immediately surrounding the lookup and before the host window is refreshed or when you have modest pre- or post-processing code, use this method.

The Tear-Off-Your-Shirt-Pound-Your-Chest-and-Roll-Your-Own Way

When you need complete control (emphasis on "need")(emphasis on "complete"), nothing in the world beats doing it yourself (actually, there really is no other choice, is there?). Thankfully, the need to do so is rare.

Here is a situation I inherited, not once but twice, in the past few months. One file contains a FullName string and the file I want to use as a lookup contains LastName and FirstName separately.

Clearly, any standard lookup will fail when passing through the control. It will also fail during Non-Stop mode (i.e., when the Ok button is pressed and {Prop:AcceptAll} becomes true). If you do not turn off the Perform Lookup during Non-Stop Select option (on the Actions tab), you'll never get off the form.

Suppose we had a routine to parse the full name into LOC:FirstName and LOC:LastName. Then, we could place the following in either the control's Accepted or Selected embed (depending on the effect we want):

DO ParseName
CUS:LastName = LOC:LastName !Prime key fields
CUS:FirstName = LOC:FirstName
IF Access:Customer.Fetch(CUS:NameKey) !If no match
  GlobalRequest = SelectRecord !call lookup
  BrowseCustomers
  IF GlobalResponse = RequestCompleted !write field back
    FIL:Name = CLIP(CUS:FirstName) & ' ' & CUS:LastName
    FIL:EMail = CUS:EMail
    !any other assignments or computations
  ELSE !otherwise,
    SELECT(?) !re-select control
    CYCLE
  END
END
ThisWindow.Reset

(For you "old timers" out there, do you notice how very much like Clarion for DOS this code looks?)

Pros: You have total control over where, when and how your lookups occur. Typos can be fixed from the Embeditor or the embed tree. This code can also be placed in a global validation embed and, then, would only need to by typed once.

Cons: Lots of typing is required and you will not be able to use the FieldLookupButton template.

Recommended use: When nothing else will work, also when computations or a number of fields need to be copied, or even a conditional, second, lookup performed, this is it. When you want some non-standard behavior after the user completes or cancels the lookup, again, this is the way to go.

If you find yourself using this method frequently, you can make life much easier for yourself. Create a text file with the boilerplate code. That is, create a file with all of the code except the actual field and key names. When you need to do a lookup, import this file into the embed and just populate the field and key names (using the pop-up field list).

Customizing Lookups

There is nothing in the world I hate more than having to create a procedure that duplicates another in all but a few functionalities. If I absolutely have to, that's one thing, but if I can make a few runtime adjustments, so much the better (we'll all spend hours figuring out how to avoid a few minutes of typing). Re-using an existing browse as a lookup is, relatively speaking, a piece of cake.

There are only a few things I might want to do: change the caption, disable some of the buttons and, perhaps, center the window.

While there is a Browse Preparation, Request to Select Record embed, I am long in the habit of using After Opening the Window (a/k/a WindowManager, Executable Code Section ... Init ... (),Byte with Priority "Last"):

IF ThisWindow.Request = SelectRecord
  ?BrowseWindow{Prop:Text} = 'Select Customer or Else!'
  ?BrowseWindow{Prop:Center} = True
END
! And, inside the Accept loop (never got anything else to work properly):
IF ThisWindow.Request = SelectRecord
  DISABLE(?Insert,?Cancel) !Only "Select" is available
END

Summary

Well, there you have it. Three ways to do two kinds of lookups. If one of these techniques doesn't satisfy your needs, your needs are a lot more complex than anything I've seen.

Printer-friendly version

 
 

Search

 

Advanced Search
Topical Index

Related Articles

Subscribe to
ClarionMag

One year: $189

(includes all back issues since '99)

Renewals from $139

Two years: $289

Renewals from $239

More Info

Subscribe Now!

ClarionMag Blog

RSS Feeds

Updates via Email

Enter your Email


Powered by FeedBlitz

Quick Links