Interacting with Forms in Delphi – Part 2: Selection Forms

This is the second part of a series I’m doing on the topic of interacting with forms in Delphi. This time we’re going to focus on a particular kind of form that I’ll call the “Selection Form”.

BACKGROUND

There are tons of simple selection forms that we encounter on most programs today: font selectors, brush selectors, date selectors, directory and file selectors, etc. The most common ones have standardized representations that become familiar to us fairly quickly, either because they’re a standard part of the operating system, or because the app developer came up with his/her own versions. These aren’t the kinds of selection forms we’re interested in.

The selection forms I’m talking about are used by an application when the programmer needs to find some kind of reference key for locating data for the user, often through a database lookup operation. Databases normally use integers as keys, called “IDs”, and their exact values are irrelevant. All you know is that any given ID lets you find one specific record.

So as a programmer, when you’re presented with a situation where you need to find a database record by ID and you don’t know what it is, you … ask the user. This is typically done with a selection form. They’re also called “lookup forms”. They’re usually displayed as pop-up forms displayed modally, which means they hold the focus until you close them.

You might be looking for a Client record, a Vendor record, or a Transaction record; regardless, there may be several different ways someone could look-up that bit of data. A Client named “Jane Doe” might be referred to by name — but is it “Jane Doe” or “Doe, Jane” or “J. Doe” or something else?

By the way, it’s not a Good Idea to use the actual name (or whatever data field value) as the key because the data can change — Jane could get married to John Smith, then change her name to Jane Smith. That would throw everything out of kilter. It’s only OK if you know that, over the lifetime of the database you’re creating, that the names you use are unique and you know that won’t change.

A common approach for looking up somebody by name, for example, would be a small popup form that lists people by name, either <last_name><first_name> or <first_name><last_name>, starting with names beginning with ‘A’. You could have a button that lets the user change the sorting order, so the list could start with ‘Z’ instead. Another trick is to make an edit field that watches what is being typed, and each time a letter is typed it refreshes the display to show only names that begin with the letters entered thus far.

A couple of more complicated examples of selection forms are: looking up medical billing codes using standard medical terms; and looking up part numbers from part names (which might be anything but standardized).

WHAT’S ON A SELECTION FORM?

The controls used on a selection form can vary: edit boxes, list boxes, combo-boxes, radio buttons, and/or any combination thereof. The point is, you need to provide a way for the user to narrow down a selection quickly using easily-recognized or relevant cues or keywords.

Additional challenges come into play when you discover that you can have duplicates to select from and no way for the user to discriminate between them. For example, try searching for someone named “Jim Smith” on Facebook. Good luck! You probably need to know not just their name, but where they live and their birthdate or current address or phone#.  I’ve tried searching for people I know are on Facebook with zero luck using FB’s search mechanisms. I had to look for some material I knew they posted and find them that way. Sometimes, selection mechanisms can be really tricky to design to work both effectively and efficiently!

I give these examples to illustrate the breadth of scope that selection forms typically address. As one can imagine, they can range from fairly simple to incredibly complicated. But in the end, they’re all the same: you present a small form to the user with some selection criteria that allows the user to sort through and select something of interest quickly and easily.

That said, I’m going to focus now on the mechanisms that are involved in displaying selection forms to users. This is where we’ll begin to highlight the wide variety of ways to interact with forms in Delphi.

OUR EXAMPLE: NFL TEAMS AND TEAM ROSTERS

For this example, I’m going to focus on one of America’s most popular pasttimes: football. This domain struck me as a good example to use because it’s neither “too complex” nor “too simple”. It seems “just right” for our purposes.

The National Football League (NFL) is broken into eight “leagues”, with four “teams” in each league, for a total of 32 teams. Each team has 60-80 “players” that are listed in what’s called a “team roster”.

We’ll create a selection form to select a team, then load a roster for that team and display it.

While this sounds like quite a simple problem statement, it will give us ample opportunity to examine a wide variety of issues that are encountered while interacting with just a simple selection form.

Each league has a name, each team has a name, and each player has a name. These names are, in fact, how most people relate to them. The league and team names are known to be unique; I don’t know if all the player names are unique, but I’ll assume they’re not. (It’s always bad to assume people’s names are unique!)

I chose this domain as an example because it’s so familiar to most people (here in America, anyway) and I figured there’s lots of easily accessible data (eg., on the internet) that provides us with a rich variety of sources and options. It’s too much data to load by hand, so we’re going to look for ways to automate the capture of this data. In fact, it took me far less time to write an import routine to capture team roster data from some websites than it would take me to enter the same data by hand. As programmers, we’re always looking for ways like this to simplify our work. 🙂

Here’s how my selection form looks at design time in the Delphi IDE:

2014-08-13_23-25-37Now, please don’t get too hung-up with the forms I use here: their visual aspects are not what we’re interested in. I decided to use two TRadioGroup widgets in this case. They’re designed to let you first select a league, then select a team in that league. I could have just as easily used a single listbox with all of the team names in it, but that seemed too simple.

In this case, the league names are statically given in the top RadioGroup (assigned within the IDE through the Object Inspector), while the names of the team in each league are generated dynamically (ie., at run-time) when you click on one of the league names above. This represents a fairly typical way of presenting Master/Detail data lists, although I could use any number of ways to show the data on the form itself. (And I didn’t need to assign the league names statically, either.) Again, the visual aspects of the form are not what we’re interested in here — it’s the general behavior of the form, and how we interact with the form under the hood that I’ll be focusing on.

In fact, from this point on, I’ll mainly be describing things from the inside — how we as programmers view things. This is also where we start to dive into the meat of this article, where we look at interacting with forms in Delphi.

STEP 1 — CREATE THE FORM

The first step is, obviously enough, creating the form. Simple, eh? Well … not necessarily. Since we’re using Delphi, we first need to decide if we want the form created automatically when the program starts up, or if we want to create the form on-the-fly (dynamically) whenever we need it, and then destroy it. No decision defaults to automatic creation at program startup.

A sort of “middle ground” exists in that we might want to create the form only once, the first time we use it, then leave it instantiated. As the app’s architect, you’ll need to decide which approach makes the most sense in each case.

For small apps and smallish forms, it’s fine to use the default mechanism that creates the forms automatically at startup; but for larger apps (ie., those with dozens or hundreds of forms) and apps with large complex forms, it’s usually better to create them dynamically on an as-needed basis, then free them when we’re done using them.

This is because the memory resources they consume is usually relatively high (and often grow over time), while the time it takes to create them on-the-fly becomes less negligiible as computers continue to get faster. So it’s often better to create and free them as needed to conserve memory usage, although that’s even becoming less important as computers contain increasing amounts of memory as well. Again, as the app’s architect, you need to make these sorts of calls.

As a general rule, selection forms tend to be fairly simple and what I’d call light-weight. They usually only have a few controls on them, and those controls will typically be populated when the form is created or used.

Here’s a bit of code that illustrates how we’d create a form ourselves in two different ways: one uses the Application.CreateForm method that Delphi generates when creating a form at startup; the other uses the standard <form>.Create constructor call.

procedure form1.DoSomething;
var
  theForm : TNFLTeamSelection_form;
begin
  // we can create the form dynamically this way, which uses the form
  // variable that Delphi generates automatically in the form unit
  Application.CreateForm(TNFLTeamSelection_form, NFLTeamSelection_form);
  try
    {do stuff with the form}
  finally
    NFLTeamSelection_form.Free;
  end;

  // or we can create the form using the form's normal constructor
  // as well as a local variable 'theForm' to hold the form's reference
  theForm := TNFLTeamSelection_form.Create(self);
  try
    {do stuff with the form}
  finally
    theForm.Free;
  end;


CHOICE OF FORM VARIABLE TO USE

Every form the Delphi IDE creates contains a global variable at the bottom of the interface section that’s used to hold a reference to the form at run-time. In this case, it’s declared this way:

var
  NFLTeamSelection_form : TNFLTeamSelection_form

It’s fine to use this as your form variable, as long as you only ever have one instance of the form in existence at any given time.  If you create a second instance of the form the same way and you overwrite the existing reference in this global variable, then you just lost any way to access the earlier form’s instance. Oops! This is called “memory leakage”, and you should do your best to avoid it altogether.

So I like to comment out the global instance variable in the forms and use local variables instead. The only thing is, if the form is going to persist for more than the life of the method that creates it, then you need to save its reference in a list or somewhere else that can be accessed by other methods, preferably only methods within the same class (usually another form).

STEP 2 — INITIALIZING THE FORM

This is where we need to inject data into the form that’s relevant to the user at this moment. The league names are defined statically in the upper Radio Group box, so we need a way to inject the team names into the form. Of course, in this particular example, we could have just as easily defined them within the form’s unit itself, but that’s not realistic.

Imagine this is for an auto dealer franchise, where the upper Radio Group consists of eight pre-defined geographical regions, and the lower one lists car dealers in each region with the highest sales of a particular car model for the month so far.

In other words, it’s fairly common for the upper selector (or Master data) to be statically defined while the lower selector (the Detail view) is loaded dynamically whenever a selection in the Master is made. And the Detail data is loaded into the form once when it’s created.

The question is this: how do you initialize the form so it contains the detail data when it’s created?

More specifically, how do we load the 32 team names (organized by league) into the form when we create it as part of its initialization process?

Now … here’s where things start to get interesting. We just created the form, right? So in the OOP (Object Oriented Programming) world, it’s ready to go. It’s fully initialized already, no?

But the team names are not there yet, so it can’t be fully initialized!

Uhhh … watcha talk’n ’bout, Willis?

Remember, a form in delphi is simply an object derived from a base class called TForm thusly:

type
  TNFLTeamSelection_form = class(TForm)

If you look at the empty form, you may notice something interesting: there’s no constructor defined!

Instead, we’re using the default constructor that’s defined in TCustomForm:

    constructor Create(AOwner: TComponent); override;

We’re taught that, in the OOP world, you’re supposed to initialize objects by passing parameters to the object through its constructor.  The constructor is already defined — in a base class no less! So what are we supposed to do? Hmmm….

Right here … this is the first place people start to go a bit crazy by improvising stuff in Delphi.

Think back to the first time you encountered this in Delphi … what did you do? How did you resolve this dilemma?

ASSIGNMENT

I’m going to end this part of the discussion right here and leave you with an assignment before we continue:

Jot down some notes on all the ways you can think of to address this issue: How do you initialize a form in Delphi?

In this case, we need to inject a list of team names organized by league into the form:

*:AFC-NORTH
Baltimore Ravens:BAL
Cincinatti Bengals:CIN
Cleveland Browns:CLE
Pittsburgh Steelers:PIT
*:NFC-NORTH
Chicago Bears:CHI
Detroit Lions:DET
Green Bay Packers:GB
Minnesota Vikings:MIN
*:AFC-SOUTH
Houston Texans:HOU
Indianapolis Colts:IND
Jacksonville Jaguars:JAX
Tennessee Titans:TEN
*:NFC-SOUTH
Atlanta Falcons:ATL
Carolina Panthers:CAR
New Orleans Saints:NO
Tampa Bay Buccaneers:TB
*:AFC-EAST
Buffalo Bills:BUF
Miami Dolphins:MIA
New England Patriots:NE
New York Jets:NYJ
*:NFC-EAST
Dallas Cowboys:DAL
New York Giants:NYG
Philadelphia Eagles:PHI
Washington Redskins:WAS
*:AFC-WEST
Denver Broncos:DEN
Kansas City Chiefs:KC
Oakland Raiders:OAK
San Diego Chargers:SD
*:NFC-WEST
Arizona Cardinals:ARZ
San Francisco 49ers:SF
Seattle Seahawks:SEA
St. Louis Rams:STL

In the next installment, I’ll go over every way I can think of, and explain the pros and cons of each one.

Remember, this discussion is about how to interact with forms in Delphi. If you can’t figure out how to initialize a simple selection form, how in the world are you going to deal with a far more complex form that’s got dozens of properties, listboxes, and other controls on it?

Finally, keep in mind … this isn’t about forms, per se. These are issues that affect classes over the entire design spectrum. I’m focusing on forms for two reasons: (1) in most respects, they’re just like any other object you’d work with in Delphi; and (2) they’re a standard object that Delphi generates and everybody can relate to.

2 thoughts on “Interacting with Forms in Delphi – Part 2: Selection Forms

  1. Jon Grewer

    Great article!

    NFLTeamSelection_form : NFLTeamSelection_form

    should be

    NFLTeamSelection_form : TNFLTeamSelection_form

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *