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:
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.