<

Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x

Delphi blackbox technique

Published on
12,774 Points
5,574 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
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

On Demand Webinar - Networking for the Cloud Era

This webinar discusses:
-Common barriers companies experience when moving to the cloud
-How SD-WAN changes the way we look at networks
-Best practices customers should employ moving forward with cloud migration
-What happens behind the scenes of SteelConnect’s one-click button

Join & Write a Comment

This tutorial will teach you the special effect of super speed similar to the fictional character Wally West aka "The Flash" After Shake : http://www.videocopilot.net/presets/after_shake/ All lightning effects with instructions : http://www.mediaf…
In this video, Percona Solutions Engineer Barrett Chambers discusses some of the basic syntax differences between MySQL and MongoDB. To learn more check out our webinar on MongoDB administration for MySQL DBA: https://www.percona.com/resources/we…
Suggested Courses

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month