<

Go Premium for a chance to win a PS4. Enter to Win

x

Delphi blackbox technique

Published on
12,899 Points
5,699 Views
7 Endorsements
Last Modified:
Geert Gruwez
6 months until my next "Did i really beat the cancer ?" check
This article explains how to create forms/units independent of other forms/units object names in a delphi project.

Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi project. In Delphi this is easy, just use the "Add file to project (Shift+F11)" to add the unit from the other project,
add name of the unit to your other unit uses implementation and the form is available in this project. But then ... you hit compile ... the compiler starts complaining with errors like
Form2 not found

Open in new window


Let's consider the following problem: The application needs a name and description for a new article from the user.

Most Delphi programmers would solve this problem with a form like this:
Create a new Form, give it a new name (frmNewArticle), put a TEdit (editArticleName),  a TMemo (memoArticleDescr) and a button (btnOk, modalresult = mrOk) on it, and then call that new form from the main form like this:  
procedure TfrmMain.btnGetNewArticleClick(Sender: TObject);
var 
  frm: TfrmNewArticle;
begin
  frm := TfrmNewArticle.Create(Self);
  try
    if frm.ShowModal = mrOk then 
    begin
      labelArticleName.Caption := frm.editArticleName.Text;
      memoArticleDescr.Text := frm.memoArticleDescr.Text;
    end;
  finally
    FreeAndNil(frm);
  end;
end;     

Open in new window

the unit code for the frmNewArticle form could look like this:
unit uNewArticle;
 
interface
    
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Buttons, StdCtrls, ExtCtrls;
  
type
  TfrmNewArticle = class(TForm)
    editArticleName: TEdit;
    labelArticleName: TLabel;
    memoArticleDescr: TMemo;
    labelArticleDescr: TLabel;
    btnOk: TBitBtn;
    btnCancel: TBitBtn;
  end;
 
var 
  frmNewArticle: TfrmNewArticle;
   
implementation 
 
{$R *.dfm } 
   
end.

Open in new window


This works, no problem... But consider what would happen when a other developer decides to:
  Change the names of the edit and memo
    - editArticleName to edName
    - memoArticleDescr to edDescr

  Change the name of the form (and type)
    - frmNewArticle to frmBasicInput

This would force you into rewriting parts or your project too. And off course, nobody likes that. But in which way can we program so this doesn't happen? Well, you guessed it, with the blackbox technique.

The first thing you need to do is change the way the input form is addressed by using a function. Place that function in the user input form
unit uNewArticle;
 
interface
    
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Buttons, StdCtrls, ExtCtrls;
  
type
  TfrmNewArticle = class(TForm)
    editArticleName: TEdit;
    labelArticleName: TLabel;
    memoArticleDescr: TMemo;
    labelArticleDescr: TLabel;
    btnOk: TBitBtn;
    btnCancel: TBitBtn;
  end;
 
var 
  frmNewArticle: TfrmNewArticle;
   
// new function inserted
function NewArticle_NameAndDescr(var aNewName: string; var aNewDescr: string): boolean;
   
implementation 
 
{$R *.dfm }  
  
// new function implementation inserted
function NewArticle_NameAndDescr(var aNewName: string; var aNewDescr: string): boolean;
var frm: TfrmNewArticle;
begin
  Result := False;
  frm := TfrmNewArticle.Create(nil);
  try
    frm.editArticleName.Text := aNewName;
    frm.memoArticleDescr.Text := aNewDescr;
    if frm.ShowModal = mrOk then 
    begin
      Result := True;
      aNewName := frm.editArticleName.Text;
      aNewDescr := frm.memoArticleDescr.Text;
    end;
  finally
    FreeAndNil(frm);
  end;
end; 
  
end.

Open in new window


Next is to use this new function inside the mainform
 
procedure TfrmMain.btnGetNewArticleClick(Sender: TObject);
var aName, aDescr: string;
begin
  aName := '';
  aDescr := '';
  if NewArticle_NameAndDescr(aName, aDescr) then 
  begin
    labelArticleName.Caption := aName;
    memoArticleDescr.Text := aDescr;
  end;
end;     

Open in new window


Now what would happen if somebody were to change the names of the edit and memo? The compiler would complain too, but it would be in the unit where the names changed, and not in your unit! A good programmer would solve the compiler errors in the function you inserted, and thus solving the problem for you as well.

Now let's extend on this. If we were to change the form and add a edit for a barcode number, and if we wanted this barcode in our main form, we would have to change our function header too. A solution could be :
  
function NewArticle_NameAndDescr(var aNewName: string; var aNewDescr: string; 
  var aNewBarcode: string): boolean;
  

Open in new window


This is leading to a different problem: the parameters in the header of the function/procedure.

A more adaptable approach for the parameters would be using a TStrings to pass/retreive info. Let's change the function so it has only a TStrings as parameter (you want to change the contents of the TStrings, not the parameter pointer, so no var )
 
function NewArticle_NameAndDescr(Params: TStrings): boolean;
var frm: TfrmNewArticle;
begin
  Result := False;
  frm := TfrmNewArticle.Create(nil);
  try
    frm.editArticleName.Text := Params.Values['NAME'];
    frm.memoArticleDescr.Text := Params.Values['DESCR'];
    if frm.ShowModal = mrOk then 
    begin
      Result := True;
      Params.Values['NAME'] := frm.editArticleName.Text;
      Params.Values['DESCR'] := frm.memoArticleDescr.Text;
    end;
  finally
    FreeAndNil(frm);
  end;
end; 

Open in new window

Using this new function would be like this:
 
procedure TfrmMain.btnGetNewArticleClick(Sender: TObject);
var Params: TStringList;
begin
  Params := TStringList.Create;
  try
    // no need for init of Name and Descr
    // Params.Text is empty !!!
    if NewArticle_NameAndDescr(Params) then 
    begin
      labelArticleName.Caption := Params.Values['NAME'];
      memoArticleDescr.Text := Params.Values['DESCR'];
    end;
  finally
    FreeAndNil(Params);
  end;
end;     

Open in new window


And with the new barcode:
 
function NewArticle_NameAndDescr(Params: TStrings): boolean;
var frm: TfrmNewArticle;
begin
  Result := False;
  frm := TfrmNewArticle.Create(nil);
  try
    frm.editArticleName.Text := Params.Values['NAME'];
    frm.memoArticleDescr.Text := Params.Values['DESCR'];
    frm.editArticleBarcode.Text := Params.Values['BARCODE'];
    if frm.ShowModal = mrOk then 
    begin
      Result := True;
      Params.Values['NAME'] := frm.editArticleName.Text;
      Params.Values['DESCR'] := frm.memoArticleDescr.Text;
      Params.Values['BARCODE'] := frm.editBarcode.Text;
    end;
  finally
    FreeAndNil(frm);
  end;
end; 

Open in new window


This is a simple demo of a blackbox technique using a function with parameter TStrings. The basic idea is to put the function/procedure in the interface section of the same unit.

If anybody changes names of the form or it's components then the developer will get warnings at compile time about any extra code changes needed. With this you can make units using this form independent of the implementation of the name of the form or its component names.
7
Comment
2 Comments
 
LVL 3

Expert Comment

by:KarlisB
Man, where where you 2 months ago.
wait, where was I that i didn't saw this article???

well, I did same in similar maner :)
0
 
LVL 7

Expert Comment

by:samenglish
Thanks for sharing your expertise.
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Join & Write a Comment

Loops Section Overview
When cloud platforms entered the scene, users and companies jumped on board to take advantage of the many benefits, like the ability to work and connect with company information from various locations. What many didn't foresee was the increased riskā€¦

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month