Designing Dialog Components

(in Delphi 4)

Richard Maley

 

Form: Design your Dialog as a form.

 

Polish: Test and Refine the functionality of this form. Polish every aspect of the form's functionality.

 

Plan: Visualize every way a developer will want to use your dialog component.

 

Properties: Identify properties and events that you believe developers may want to control the component's functionality. For each of these stub in a property statement in the public or published section of the form's type definition. Although a Form does not normally have a published section add one in preparation for making this into a component. Properties and events placed in the published section get displayed in the Object Inspector.

                  example:

                  public
                    { Public declarations }
                    procedure Loaded;             override;
                    Property  PasswordAttempt     : Integer;
                    Property  DBPassword          : String;
                    Property  ComputerName        : String;
                    Property  NetLoginName        : String;
                    Property  ApplyChanges        : Boolean;
                  published
                    { Published declarations }
                    Property  SplashMode          : Boolean;
                    Property  TitleSub1           : String;
                    Property  TitleSub2           : String;
                    Property  CaptionLogin        : String;
                    Property  TitleMain           : String;
                    Property  MaxPasswordAttempts : Integer;
                    Property  UserName            : String;
                    Property  Connected           : Boolean;
                    Property  OnFontChange        : TNotifyEvent;
                  end;

 

Shift-Ctrl C: Once you think you have all the properties identified place the cursor at the end of the first property you have created and press Shift-Ctrl C. The private variables, the Read and Write definitions and the set procedures will be generated automatically.

 

                  example:

                  private
                  { Private declarations }
                    FMaxPasswordAttempts: Integer;
                    FPasswordAttempt: Integer;
                    FUserName: String;
                    FNetLoginName: String;
                    FDBPassword: String;
                    FConnected: Boolean;
                    FTitle: String;
                    FCaptionLogin: String;
                    FSplashMode: Boolean;
                    FTitleSub1: String;
                    FTitleSub2: String;
                    FComputerName: String;
                    FApplyChanges: Boolean;
                    sgPara1          : String;
                    sgPara2          : String;
                    procedure SetMaxPasswordAttempts(const Value: Integer);
                    procedure SetPasswordAttempt(const Value: Integer);
                    procedure SetUserName(const Value: String);
                    procedure SetNetLoginName(const Value: String);
                    procedure SetDBPassword(const Value: String);
                    procedure SetConnected(const Value: Boolean);
                    procedure SetTitle(const Value: String);
                    procedure SetCaptionLogin(const Value: String);
                    procedure SetTitleSub1(const Value: String);
                    procedure SetTitleSub2(const Value: String);
                    procedure SetComputerName(const Value: String);
                    procedure SetApplyChanges(const Value: Boolean);
                  public
                    { Public declarations }
                    procedure Loaded;             override;
                    Property  PasswordAttempt     : Integer
                      read FPasswordAttempt
                      write SetPasswordAttempt;
                    Property  DBPassword          : String
                      read FDBPassword
                      write SetDBPassword;
                    Property  ComputerName        : String
                      read FComputerName
                      write SetComputerName;
                    Property  NetLoginName        : String
                      read FNetLoginName
                      write SetNetLoginName;
                    Property  ApplyChanges        : Boolean
                      read FApplyChanges
                      write SetApplyChanges;
                  published
                    { Published declarations }
                    Property  SplashMode          : Boolean
                      read FSplashMode
                      write FSplashMode;
                    Property  TitleSub1           : String
                      read FTitleSub1
                      write SetTitleSub1;
                    Property  TitleSub2           : String
                      read FTitleSub2
                      write SetTitleSub2;
                    Property  CaptionLogin        : String
                      read FCaptionLogin
                      write SetCaptionLogin;
                    Property  TitleMain           : String
                      read FTitle
                      write SetTitle;
                    Property  MaxPasswordAttempts : Integer
                      read FMaxPasswordAttempts
                      write SetMaxPasswordAttempts;
                    Property  UserName            : String
                      read FUserName
                      write SetUserName;
                    Property  Connected           : Boolean
                      read FConnected
                      write SetConnected;
                    Property  OnFontChange        : TNotifyEvent
                      read FOnFontChange
                      write SetOnFontChange;
                  end;

                  Implementation

                  procedure TMyComponent.SetMaxPasswordAttempts(
                    const Value: Integer);
                  begin
                    FMaxPasswordAttempts := Value;
                  end;

                  ......

We are now done working with the Form.

 

New Unit: Open a new Unit. This will contain the base unit for the component and as many child components as you desire.

 

In the new Unit create the type definition for your base class. It is Delphi tradition and a good policy to create a base class for your new component that publishes nothing. As an example TPanel is a descendant of TCustomPanel. None of the unique Panel Properties are published in TCustomPanel.

 

Copy the entire type definition from the form to this new unit.

 

Modify the class line as follows:

                  Old: TMyForm = class(TForm)

                  New: TCustomMyComponent = class(TComponent)

 

Delete everything between the class line and the line above the private section.

                  Old:
                  TCustomMyComponent = class(TComponent)
                   pnlBase: TPanel;
                   pnlBase2: TPanel;
                   pnlBase3: TPanel;
                   Database: TDatabase;
                   pnllogin: TPanel;
                   pnlWarning: TPanel;
                   pnlwarningBot: TPanel;
                   pnlWarningButtons: TPanel;
                   btnAgree: TButton;
                   ...
                   ...
                  private

                  Should look like this.
                  New:
                  TCustomMyComponent = class(TComponent)
                  private

Set Procs:Copy the property set procedures from to the form and correct the class designation associated with each of them.

 

Register Interface: After the type definition in the interface section of the unit put the following:

                  procedure Register;

Register Implementation: Either at the top or the bottom of the implementation section put a modification of the following:

                  procedure Register;
                  begin
                    RegisterComponents('My Components', [TMyComponent]);
                  end;

Execute: An execute function needs to be written. Executes normally return a Boolean to indicate success or failure. Your component should do the same thing. The execute function creates the Form, sets all properties, shows the Form modally, extracts all required values and frees the form. Since it is possible that through this component developers might want to display the component both modally and nonmodally an "InitForm" procedure should be developed that both processes can use.

                  function TCustomMyComponent.Execute : Boolean;
                  begin
                    Form   := TMyForm.Create(Self);
                    Try
                      Result := ExecuteUnique;
                    Finally
                      Form.Destroy;
                    End;
                  end;

                  function TCustomMyComponent.ExecuteUnique: Boolean;
                  begin
                    InitForm(Form);
                    Form.ShowModal;
                    Result := Form.ApplyChanges;
                    If Result Then
                    Begin
                      Try Connected           := Form.Connected           Except End;
                      Try UserName            := Form.UserName            Except End;
                      Try Height              := Form.Height              Except End;
                      Try NetLoginName        := Form.NetLoginName        Except End;
                      Try ComputerName        := Form.ComputerName        Except End;
                      Try PasswordAttempt     := Form.PasswordAttempt     Except End;
                      Try Width               := Form.Width               Except End;
                    End;
                  End;

                  //All property values should be assigned to the form
                  Procedure TCustomMyComponent.InitForm(Form: TMyForm);
                  begin
                    Try Form.SplashMode          := False               Except End;
                    Try Form.ApplyChanges        := ApplyChanges        Except End;
                    Try Form.pnlBase.Color       := Border1Color        Except End;
                    Try Form.pnlBase2.Color      := Border2Color        Except End;
                    Try Form.pnlBase3.Color      := Border3Color        Except End;
                    Try Form.pnllogin.Color      := Border4Color        Except End;
                    Try Form.pnlWarning.Color    := Border4Color        Except End;
                    Try Form.BorderIcons         := BorderIcons         Except End;
                    Try Form.BorderStyle         := BorderStyle         Except End;
                    Try Form.Caption             := Caption             Except End;
                    Try Form.Connected           := Connected           Except End;
                    Try Form.Database            := Database            Except End;
                    Try Form.Database2           := Database2           Except End;
                    Try Form.Database3           := Database3           Except End;
                    Try Form.Database4           := Database4           Except End;
                    Try Form.Database5           := Database5           Except End;
                    Try Form.UserName            := UserName            Except End;
                    Try Form.DBPassword          := DBPassword          Except End;
                    Try Form.Font                := Font                Except End;
                    Try Form.lblMainTitle.Font   := FontTitleMain       Except End;
                    Try Form.lblTitleSub1.Font   := FontTitleSub1       Except End;
                    Try Form.lblTitleSub2.Font   := FontTitleSub2       Except End;
                    ...
                    ...
                    ...
                    ...
                    ...
                    ...
                    ...
                    Try Form.Height              := Height              Except End;
                    Try Form.ImgSplash.Picture.Assign(Picture);         Except End;
                    Try Form.MaxPasswordAttempts := MaxPasswordAttempts Except End;
                    Try Form.NetLoginName        := NetLoginName        Except End;
                    Try Form.ComputerName        := ComputerName        Except End;
                    Try Form.PasswordAttempt     := PasswordAttempt     Except End;
                    Try Form.TitleMain           := TitleMain           Except End;
                    Try Form.lblMainTitle.Top    := TitleMainTop        Except End;
                    Try Form.TitleSub1           := TitleSub1           Except End;
                    Try Form.lblTitleSub1.Top    := TitleSub1Top        Except End;
                    Try Form.TitleSub2           := TitleSub2           Except End;
                    Try Form.lblTitleSub2.Top    := TitleSub2Top        Except End;
                  End;

Constructor: A Create procedure needs to be developed for the component. All property values are set to their defaults in the constructor.

                  constructor TCustomMyComponent.Create(AOwner: TComponent);
                  begin
                    inherited Create (AOwner); //Should be first
                    ApplyChanges        := True;
                    Border1Color        := clRed;
                    Border2Color        := clWhite;
                    Border3Color        := clBlue;
                    Border4Color        := clRed;
                    BorderIcons         := [];
                    BorderStyle         := bsDialog;
                    Connected           := False;
                    Database            := nil;
                    Database2           := nil;
                    Database3           := nil;
                    Database4           := nil;
                    Database5           := nil;
                    UserName            := '';
                    DBPassword          := '';
                    FFont               := TFont.Create;
                    FFontTitleMain      := TFont.Create;
                    FFontTitleSub1      := TFont.Create;
                    FFontTitleSub2      := TFont.Create;
                    FFontWarningText    := TFont.Create;
                    Form                := nil;
                    FPicture            := TPicture.Create();
                    FWarningText        := TStringList.Create();
                    FWarningText.Clear;
                    Height              := 430;
                    MaxPasswordAttempts := 3;
                    NetLoginName        := '';
                    ComputerName        := '';
                    PasswordAttempt     := 0;
                    SetFontCharacteristics(FFontTitleMain,0,clYellow,-64,'Arial Black',fpDefault,48);
                    SetFontCharacteristics(FFontTitleSub1,0,clYellow,-16,'Arial Black',fpDefault,12);
                    SetFontCharacteristics(FFontTitleSub2,0,clYellow,-16,'Arial Black',fpDefault,12);
                    SetFontCharacteristics(FFontWarningText,0,clRed,-16,'Arial',fpDefault,12);
                    ShowWarning         := True;
                    TitleMain           := '';
                    TitleMainTop        := 69;
                    TitleSub1           := '';
                    TitleSub1Top        := 13;
                    TitleSub2           := '';
                    TitleSub2Top        := 40;
                    WarningTextColor    := clRed;
                    Width               := 600;
                  end;

Destructor:

                  destructor TCustomMycomponent.Destroy;
                  begin
                    FPicture          .Free;
                    FPicture          := nil;
                    inherited         Destroy; //Should be last
                  end;

Interface: Make sure Constructor, Destructor, Execute, ExecuteUnique, InitForm are all listed in the public section of the type definition.

 

NonModal Functionality: Create 2 procedures to control nonmodal showing of the dialog.

                   public
                     Procedure   Show; Virtual;
                     Procedure   Close; Virtual;

                   implementation

                   Procedure TCustomMyComponent.Show;
                   Begin
                     If not Assigned(MyForm) Then
                       MyForm := TMyForm.Create(Application);
                     InitForm(MyForm);
                     MyForm.IsNonModal := True;
                     MyForm.Show;
                   End;

                   Procedure TCustomMyComponent.Close;
                   Begin
                     If Assigned(MyForm) Then MyForm.Close;
                     MyForm.IsNonModal := False;
                   End;

Non Custom Component: Create TMyComponent and publish the properties you want to appear in the object Inspector;

                   TMyComponent = class(CustomMyComponent)
                   published
                     Property  SplashMode;
                     Property  TitleSub1;
                     Property  TitleSub2;
                     Property  CaptionLogin;
                     Property  TitleMain;
                     Property  MaxPasswordAttempts;
                     Property  UserName;
                     Property  Connected;
                     Property  OnFontChange;
                   End;

Component Bitmap: Use the Image Editor that comes with Delphi to create a bitmap for your new component. Create a new *.DCR file in the same name as the unit we just created (where the register procedure is). I recommend 24x24 16 bit color. Name the bitmap the same name as your component's class name including the "T" and name it in all caps, i.e., UPPERCASE.

 

Testing: Try to install the component. Step through the errors until Delphi is happy and your component is installed. ALWAYS INSTALL YOUR NEW COMPONENTS INTO A NEW PACKAGE. NEVER REUSE PRIOR ONES. DON'T MIX TEST COMPONENTS WITH PRODUCTION ONES.