Interacting with Forms in Delphi – Part 1

This is the first of several articles I expect to write on the topic of (programmatically) interacting with forms in Delphi. This is a look “under the hood” at some stuff that a lot of Delphi Developers take for granted. I’ll survey a variety of approaches I’ve seen in working with forms, some recommendations I’ve got, and hopefully lay the groundwork for a new way to approach dealing with forms. Along the way, we’ll also take a look at how LiveBindings can play a role in this effort as well.

For those who don’t know, Delphi is a programming environment released in 1995 (Valentine’s Day, if I recall correctly).  I’ve been using it ever since. It supports a language called Object Pascal, and it grew out of a very popular compiler called TurboPascal, the product that launched a company known as Borland, Inc., way back in the early days of home computers (back when they still ran MS-DOS).

(You can learn more about Delphi from the Embarcadero website.)

In the last article I posted, I made an effort to describe what the future of programming might be like if we’re to have any kind of quantum leap in the productivity of programmers. The software industry has revolutionized virtually every other field except our own. Today we employ the same edit/compile/debug form of iterative programming that has been used for over 50 years. That has got to change. We need something that’s more designed around dropping little logic blocks on a form and connecting them with lines that represent data flows. And it must be done in a way that allows the resulting diagrams to be executable.

I’ve seen some examples of efforts along this line, and they seem to rely on taking us back to the days of flowcharts. That’s not what I’m talking about. Flowcharts are nothing more than a more bulky graphical representation of imperative coding. Simply replacing programming statements (groups of words) with boxes, circles, and diamonds isn’t going to help when the language constructs keep getting increasingly abstract. You could never “execute” a flowchart any more than you can “execute” a dataflow diagram, although that isn’t stopping people from trying. We need executable diagrams that represent the things in our application at a relatively abstract level, not another charting tool with a bazillion shapes spread over an area the size of an 8′ by 20′ wall. (I’ve seen database design charts that large with notations in 10pt type! While they looked really cool, they seemed utterly useless to me.)

This is an overall topic that I find quite interesting for several reasons. First, and most generally, it highlights a property of all human languages where you have a notion of “technical accuracy” that contrasts (sometimes starkly) with “idiomatic usage”. That is to say, there are these things called “forms” in Delphi, and then there’s how you use them. Every developer seems to take a slightly different approach to them. Delphi is agnostic — it gives you the structure and hooks, and leaves it up to your own devices to decide what works best for you. It turns out that there is a more-or-less natural way of using forms. But, there’s an inherint limitation in the way Delphi’s forms are designed that makes them challenging to deal with consistently. If we examine this in the context of a larger data-flow model, we can begin to see a different, more optimal way of approaching their use.

Second, most software applications, or apps as they’re called today, typically employ a variety of forms to implement their needs. They can appear as multiple tabs or sub-forms (both common on mobile apps), separate pop-up forms, and fancy forms with many different focal points on them for doing related things.

Third, and most importantly, we need to be able to abstract out some general behaviors of these forms if we’re ever going to achieve any kind of diagram-based programming. Today, forms appear on design diagrams simply as a “box” with a comment that says (or implies) something like “show this form”. If you’re lucky, there’s some kind of annotation that shows a data structure used to pass data into the form, but I find this approach isn’t even very common in practice. In short, this is woefully insufficient when it comes to thinking of forms regarding data flow in and through an application.

So this is a series of articles that constitutes an inquiry into the roles that forms play in a typical application these days, and how we might want to transform how we interact with them programattically in order to begin approaching how they behave in a data-flow context.

To put it another way, we’re going to look at common idiomatic usages of forms (in Delphi), and then see how different idioms might give us more general (and generalizable) results.

TO OOP OR NOT TO OOP, THAT IS THE QUESTION

As a preface to where I’m headed with this discussion, I want to point out a very strange dichotomy in how the development community sees object-oriented programming (OOP) in general, and how we implement it when it comes to forms. Truth be told, I find this quite … weird. Maybe someone can explain it to me.

If you’re a developer being interviewed for a Deplhi or similar position and you’re asked to explain the basic principles behind OOP, you’ll say, “encapsulation, inheritance, and polymorphism”. Then you’d explain a bit about what each of those mean.

Then if you’re asked to explain the difference between a c struct or Pascal record vs. a class, you’d explain that a struct or record contains a bunch of data elements and you access them directly, whereas in a class you’d have both data elements and methods to access them — effectively encapsulating the data inside of the object and keeping the data elements from being accessed directly.

For example, here’s a short record definition of a 3D point with a name, and how we’d define two such points and add them together:

type
  TNamed3DPointRec = record
    x, y, z : double
    name : string;
  end;

var
  pt1, pt2, rslt : TNamed3DPointRec;
begin
  // define the first poine
  pt1.x := 2.0;
  pt1.y := 4.0;
  pt1.z := 1.0;
  pt1.name := 'First point';

  // define the second point
  pt2.x := 6.0;
  pt2.y := 3.0;
  pt2.z := 2.0;
  pt2.name := 'Second point';

  // add the two together  ((1))
  rslt.x := pt1.x + pt2.x;
  rslt.y := pt1.y + pt2.y;
  rslt.z := pt1.z + pt2.z;
  rslt.name := pt1.name + '+' + pt2.name;

  // display the result
  PrintLn( Format( 'Result: x=%f  y=%f  z=%f  Name=%s',
                   [rslt.x, rslt.y, rslt.z, rslt.name] ) );
end;

Here’s how that might look if we use a class instead of a record. For the uninitated, you’ll notice that it’s a bit longer. But in the bigger scheme of things, such an object-oriented design approach does, in fact, end up reducing the total number of lines of code in your programs when you do it right. (And I’ve even skipped showing another 50 or so lines of code to implement the class outlined here!)

type
  TNamed3DPointCl = class
  private
    Fx, Fy, Fz : double
    Fname    : string;
  protected
    function  GetX : double;            // GetX simply does: Result := Fx
    procedure SetX( aValue : double );  // SetX simply does: Fx := aValue
    function  GetY : double;
    procedure SetY( aValue : double );
    function  GetZ : double;
    procedure SetZ( aValue : double );
    function  GetName : double;
    procedure SetName( aValue : double );

  public
    // The constructor initializes Fx, Fy, etc., from the values passed
    constructor Create( aX, aY, aZ : double; aName : string );
    destructor  Destroy;

    // The Add function essentially does what's at ((1)) above
    function Add( aOtherPt : TNamed3DPointCl ) : TNamed3DPointCl; 

    // Properties let you read and write the values indirectly.
    // That is, you don't need to know the internal variable names or how
    // they're actually implemented.
    property X    : double read GetX write SetX;
    property Y    : double read GetY write SetY;
    property Z    : double read GetZ write SetZ;
    property Name : double read GetName write SetName;
  end;

var
  pt1, pt2, rslt : TNamed3DPointCl;
begin
  // define the first point
  pt1 := TNamed3DPointCl.Create( 2, 4, 1, 'First point' );

  // define the second point
  pt2 := TNamed3DPointCl.Create( 6, 3, 2, 'Second point' );

  // add pt2 to pt1
  rslt := pt1.Add( pt2 );

  // finally, display the result
  PrintLn( Format( 'Result: x=%f  y=%f  z=%f  Name=%s',
                    [rslt.x, rslt.y, rslt.z, rslt.name] ) );
end;

Now I’m sure this is very obvious to anybody who’s programmed using OOP in any language. There’s nothing fancy going on here at all (although I hinted at the implementations of the functions rather than showing them in order to save space).

So now you’re in that interview and you’re asked, Why do you use properties in the class, rather than just making the data values public and accessing them directly? The correct answer lies in the “encapsulation” principle — you can insulate the implementation (instance) from the interface (property) and thereby decouple the way clients of the class use it from how the class is actually implemented. This is one of the hallmarks of OOP.

Now you’re asked to sketch out what a form’s code might look like that lets you add or edit the members of a TNamed3DClass instance defined above. So you whip out something like this — a simple form with four edit fields and two buttons (OK, and Cancel):

unit unit1;

interface

uses ...

TForm1 : class( TForm )
  edtX : TEdit;
  edtY : TEdit;
  edtZ : TEdit;
  edtName : TEdit;
  btnOK : TButton;
  btnCancel : TButton;
private
  { private members }
public
  { public members }
end;

var
  Form1 : TForm1;

implementation

end.

Your unit that uses it does something like this … and this is where it gets weird. See if you catch where I’m going with this…

// assume we're using auto-instatiated forms

// assign values to the fields using the earlier example  ((2))
form1.edtX := FloatToStr( pt1.x );
form1.edtY := FloatToStr( pt1.y );
form1.edtZ := FloatToStr( pt1.z );
form1.edtName := pt1.Name;

// Now show the form and wait for the user to click a button.
if (form1.ShowModal = mrOK) then
begin
  // Update the values in pt1 from the form.
  pt1.x := form1.edtX.ToFloat;
  pt1.Y := form1.edtY.ToFloat;
  pt1.z := form1.edtZ.ToFloat;
  pt1.name := form1.edtName;
end;

Now here’s the $64,000 question your interviewer asks next: Why did you access the form variables at ((2)) directly rather than defining properties in the form class and use THEM to access the values, like you did in the earlier class? After all, your form is simply a class, right?

uhhh … well … because that’s how we’ve always dealt with getting data into and out of forms!

BINGO!

Wati! What happened to that “encapsulation” principle we just talked about?

Why do we SAY “this is how we deal with objects in the OOP world” and then turn around and deal with certain objects called FORMS as if OOP didn’t exist?

This is what I refer to here as “plumbing” — the mechanisms (ie., code) used to get data into and out of forms.

DESIGN PATTERNS vs. FORMS

Three months before Delphi was released, a book was published that has been responsible for altering the landscape of computer science and software development more than anything else I can think of since I wrote my first program in 1972. That book is called, “Design Patterns“, and it was the seminal work published by four gentlemen who, curiously enough. are better known within the software field as the “Gang of Four” (or “GoF”) than by name.

Design patterns have done more to alter how software is designed, discussed, and implemented than anything before or since.

Yet, the way we interact with forms in Delphi — as defined in Delphi 1 way back in February 1995 — is exactly the same today in 2014, nearly 20 years later.  One big reason is this: while the way the Delphi IDE (Integrated Development Environment) works has improved by leaps and bounds over the years, the “template” it uses to generate code for the forms it helps you create has not changed one bit.

Meanwhile, various software patterns have been defined, and continue to evolve, that show us how to decouple user interface (UI) code from logic, and how to reduce the coupling between the UI parts (ie., the forms) and the logic parts as much as possible. They have acronyms like MFC, MVVM, and so forth. Yet the Delphi IDE continues to generate the same vanilla forms it has done from Day 1, as if design patterns and frameworks never existed.

(Someone told me the “Architect” version of Delphi has some stuff built-in for this, but I wouldn’t know. I’ve never worked anywhere that has licensed anything other than their “Professional” edition, and I cannot afford to spend what the “Architect” edition costs for my own personal needs. Especially since nobody has ever asked me about these additional features or how they’re used or could be beneficial for a project.)

Not surprisingly, neither the plumbing nor the idiomatic code expressions that we employ when interacting with forms have changed at all either.

Which gets us back to the last interview question: why do we routinely define properties for accessing data inside of classes as a matter of course, yet flatly dismiss their use within forms for the exact same purposes? (Every time I’ve defined properties in a form I’m working on, my collegues have universally looked at it and ask, “What the hell did you define properties for?”)

I’d like to see us break this silly habit, but not simply by advocating for defining properties in every form. Rather, I want to examine the role of the plumbing, and how we can abstract it out in ways that ultimately make it easier to interact with our forms, while making them more testable and as decoupled from our code as possible.

We’ll start by taking a quick look at form-based app design in Delphi, then diving into things in more detail in the next installment.

FORM-BASED APPLICATION DESIGN

Most software today seems to be a collection of forms. Delphi, along with similar development tools like Microsoft’s Visual Studio (for C# and .NET), Eclipse (for Java), and other such platforms, make the development of form-based applications very easy. Historically speaking, Delphi was the first true WYSIWYG, component-based drag-and-drop form design and coding tool on the market. It has set the pace in many respects for everything that has come along since.

Today we have a situation where most of these programming tools (so-called “Integrated Development Environments”) are all structured pretty much the same. You have a “Code pane” and a “Design pane”. You edit code in the former, and work with the visual layout of the form in the latter.

Here is what my Delphi XE5 IDE looks like in the Code pane (tab) view:

2014-08-10_22-45-14

Here’s what the Design pane (tab) view is like showing one of the forms:

2014-08-10_22-44-55

The box on the left side is called the “Object Inspector”. It allows you to alter the properties of selected objects on the form in the Design pane simply by changing their values. That relieves you of having to write lines of code to do the same thing. It’s very cool.

The box at the top-right side is the Project Manager pane, and the box below it is the Tool Palette. The Project Manager shows all of the forms and units in your app (or project). The Tool Palette gives you a list of all the various widgets you can drop on a form in the Design pane, organized into categories. They use a “what you see is what you get” (WYSIWYG) approach, so you know roughly how the form will look when it’s running (what we call “run-time” vs. “design-time”).

WHAT’S MISSING?

I’m showing you these things to highlight what’s missing in todays design tools.

Both of these panes, or tabs, show two aspects of one single view — that of a single form (or unit, which is a code file with no visual form).

What’s missing is an “app pane” that shows what the intended data flows are between forms at run-time, starting with the “main form”.

The Project Manager pane lists all of the forms and units, but they’re organized in a way that reflects how the application is built — not how it works. Any correlation between the order in which forms are called or used is purely coincidental.

Now, at this point, if you’re a software developer, you’re probably thinking, “oh, you’re talking about the design specs“.  Well, sort of.

But … let me ask you this … remember when form layouts were also considered part of the “design specs” and you had no way to actually see them inside of your IDE? (If you didn’t start working in the field before the early-to-mid 90’s then you probably don’t. Although, they still seem to be common in web-based development, so who knows…)

Actually, we didn’t have IDEs back then; all we had was a text editor and compiler tools that we launched from the command line, and our code files — witihin which we had to describe, in code (ie., using a technical language), how the form was supposed to look. And we spent onehelluvalot of time getting that description to generate a visual form that came close to what the design spec showed. It was tedious, time-consuming, and people still do it today for some domains (web forms come to mind).

Today, using something like Delphi, I can spend 15-30 minutes dropping widgets on a form, filling them with some random data, and viola! We have a design spec for a form!

And, in fact, that would pretty much be the end of it … IF … we didn’t have to write a TON of code that serves as plumbing to get data into and out of that silly form. There’s often the need to implement various things called “business rules” that constrain values that can be present or not in some fields on the form.

Actually, IF you’re using Delphi, and IF you have a live database somewhere with data in it that you can access, then you can drop specialized widgets called data-aware controls onto the form, hook them up to a TTable and TDataSource, set a few other properties in the Object Inspector, set an Active property or two to True, and you’re able to see live data appear right in your form, with not one single line of code needed!

Those are a few BIG “IFs”. Some applications lend themselves to that kind of design. If so, that’s great. Building new forms for such needs is a walk in the park.

In my experience, because of the kind of applications I work on mostly, it’s not the case at all. There’s no back-end database we can connect to directly. Most of the time, there’s no database at all. You have a variety of files, different types of media, stuff on the internet, text and images generated from within the app, and logic that acts like a magic wand, all of which allow you to create amazingly powerful apps that jump through hoops and make people go, “WOW!”

In fact, today an increasing number of apps are connecting to web-based services to pull down various data and integrate them into the app. A highly technical term called “mash-ups” is used to describe these kinds of apps. Protocols commonly used for accessing remote data include, “SOAP”, “JSON”, “XML/XSL”, “REST”, and others.

Just when IDEs mastered the art of connecting to homogeneous databases, the industry started to go bonkers on us and said, “Sorry, now you need to deal with a dozen different protocols and a bazillion different types of data structures scattered across dozens of servers on the internet!”

So we’re back to being plumbers again, spending most of our time writing plumbing code. 🙁

FOUR KINDS OF FORMS NEED FOUR KINDS OF PLUMBING

In software today, there tend to be four common ways forms are employed (in order of increasing complexity):

  • Prompts and alerts
  • Selection boxes
  • Data entry/edit (CRUD)
  • Data/Domain management

The thing we typically call our “main form” is what I’m referring to as the last item, “Data/Domain Management”. The “main form” isn’t of particular interest to us in this discussion; rather, we’ll focus on the other three; however, that doesn’t mean highly complex forms that address managing data for domain-specific needs aren’t important. We just won’t focus on them right away. (In the end, what we’ll discuss for the middle two will apply equally to more complex forms.)

Actually, the first, “Prompts and alerts” are very simple: if it can be done with a MessageDlg or ShowMessage call, then there’s not much to address in this discussion. If you need more than that, it probably will be covered by one of the middle two types of forms.

Here’s what my “main form” looks like presently, and a simple alert generated with a call to ShowMessage:

2014-08-11_01-00-56

I won’t have much more to say about these two directly. The next series of posts will focus on Selection Boxes and CRUD-type Data Forms. Stay tuned!

 

 

One thought on “Interacting with Forms in Delphi – Part 1

  1. William Meyer

    Actually, TP was introduced back when we still ran CP/M, and MS-DOS was new and ugly. And my Z80 was faster than a PC-XT.

    Reply

Leave a Reply

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