9
Custom Component Editors in Delphi 7 - #1 When you start to work with custom components after a while you will realize what standard property editors and component editors is not enough. You will need to create your own editor. There is a lot of articles available how to implement Property editor or even Component Editor. There is some links: http://delphi.about.com/library/bluc/text/uc092501a.htm http://www.drbob42.com/delphi/property.htm They cover basis you will need to know when trying to create your own editor. Note. This article is not intended to give you a feature complete Component Editor, but demonstrates a technique of creating one. Editor created is a simple property editor for the component name. Even though it is not most useful functionality possible, but we hope it will give you an idea and shows a way how to create one of the best one for your own use. Note. If you see code where DsgnIntf file is in uses clause of the unit, you will need to remember what in Delphi 7 this file was split in two: DesignEditors, DesignIntf. Also you need to know where this files could be found. When you create your own editors you start to use functionality of Delphi IDE and in this case you have to include DesignIDE.DCP in Requires list for your DPK. You will soon find out what such file doesn't exist. Instead there is only designide70.bpl in "X:Program FilesBorlandDelphi7Bin" folder. Don't worry, file is actually in "X:Program FilesBorlandDelphi7Lib" folder. So, you will need to add it from this location. After this operation you will be able to use IDE design functionality such as editors and ToolAPI. Instead in this article I'm going to talk about Component Form Editor. Example of such editor is a Field List editor for TDataSet based components. Lets look at a source file DSDesign.pas in "X:Program FilesBorlandDelphi7SourceProperty Editors" folder. Most of implementation is here. If you will drill down to a code you will think what it is a lot of code to implement something like this. Don't worry it is much easier you could imagine. And in couple minutes you will agree with this. Step 1 - Create a component Oh, I don't think I need to spend a lot of time here. Open your Delphi Create new ProjectGroup - Delphi MenuFileNewOther... - select Project Group in "New" tab Add new package to your project group - Delphi MenuProjectAdd New Project - select Package - new package will be added into group Activate a project Add a new component into package - Delphi MenuComponentNew Component. You can inherit from any existing class, we will use TComponent as an Ancestor. Name it MyComponent and choose to install it. Delphi will generate all necessary code. Save it as a uMyComponent.pas file. Ok, now we are ready for next step.

component_editor.pdf

Embed Size (px)

Citation preview

Page 1: component_editor.pdf

Custom Component Editors in Delphi 7 - #1

When you start to work with custom components after a while you will realize what standard propertyeditors and component editors is not enough.You will need to create your own editor. There is a lot of articles available how to implement Propertyeditor or even Component Editor.There is some links:

• http://delphi.about.com/library/bluc/text/uc092501a.htm• http://www.drbob42.com/delphi/property.htm•

They cover basis you will need to know when trying to create your own editor.

Note. This article is not intended to give you a feature complete Component Editor, but demonstrates atechnique of creating one. Editor created is a simple property editor for the component name. Eventhough it is not most useful functionality possible, but we hope it will give you an idea and shows a wayhow to create one of the best one for your own use.

Note. If you see code where DsgnIntf file is in uses clause of the unit, you will need to remember what inDelphi 7 this file was split in two: DesignEditors, DesignIntf. Also you need to know where this files couldbe found. When you create your own editors you start to use functionality of Delphi IDE and in this caseyou have to include DesignIDE.DCP in Requires list for your DPK. You will soon find out what such filedoesn't exist. Instead there is only designide70.bpl in "X:Program FilesBorlandDelphi7Bin" folder. Don'tworry, file is actually in "X:Program FilesBorlandDelphi7Lib" folder. So, you will need to add it from thislocation. After this operation you will be able to use IDE design functionality such as editors and ToolAPI.

Instead in this article I'm going to talk about Component Form Editor. Example of such editor is a FieldList editor for TDataSet based components.Lets look at a source file DSDesign.pas in "X:Program FilesBorlandDelphi7SourceProperty Editors"folder. Most of implementation is here.If you will drill down to a code you will think what it is a lot of code to implement something like this. Don'tworry it is much easier you could imagine. And in couple minutes you will agree with this.

Step 1 - Create a component

Oh, I don't think I need to spend a lot of time here.

• Open your Delphi• Create new ProjectGroup - Delphi MenuFileNewOther... - select Project Group in "New" tab• Add new package to your project group - Delphi MenuProjectAdd New Project - select Package -

new package will be added into group• Activate a project• Add a new component into package - Delphi MenuComponentNew Component. You can inherit

from any existing class, we will use TComponent as an Ancestor. Name it MyComponent andchoose to install it. Delphi will generate all necessary code. Save it as a uMyComponent.pas file.

Ok, now we are ready for next step.

Page 2: component_editor.pdf

Step 2 - Create Component Editor

We need to add and register an editor for our component. In this article we will implement Dbl-Click editorfor our component.

• Add a new unit file into package and name it uMyEditor.pas• You will need to implement TComponentEditor descendant, there is a source code for this file

unit uMyEditor;

interfaceuses

DesignEditors, DesignIntf, Classes, uMyDesigner;

typeTMyComponentEditor = class(TComponentEditor)protected

function GetDesignerClass: TMyCmpDesignerClass; virtual;public

procedure Edit; override;procedure ExecuteVerb(Index: Integer); override;function GetVerb(Index: Integer): string; override;function GetVerbCount: Integer; override;

end;procedure Register;implementationuses

uMyComponent;

procedure Register;begin

RegisterComponentEditor(TComponent1, TMyComponentEditor);end;{ TMyComponentEditor }

procedure TMyComponentEditor.Edit;begin

ShowComponentEditor(Designer, Tcomponent1(Component), GetDesignerClass);end;procedure TMyComponentEditor.ExecuteVerb(Index: Integer);begin

Edit;end;function TMyComponentEditor.GetDesignerClass: TMyCmpDesignerClass;begin

Result := TMyCmpDesigner;end;

Page 3: component_editor.pdf

function TMyComponentEditor.GetVerb(Index: Integer): string;begin

Result := 'My Editor';end;function TMyComponentEditor.GetVerbCount: Integer;begin

Result := 1;end;end.

You will not be able to compile it, because we do use ShowComponentEditor which is not defined yet. Butlet looks at a code. Our TMyComponentEditor is a main class here. This class allow us specify a logic IDEwill invoke when we Dbl-click on component or use Context menu.

We define function GetDesignerClass: TMyCmpDesignerClass; virtual ; method here just in case if in afuture we will want to customize more our editor and introduce descendant from it.If you have just one action handled procedure TMyComponentEditor.ExecuteVerb(Index: Integer); willinclude just one line which invokes editor. You can extend Editor class to add more action but will notdiscuss it here. Please see examples from a links I mentioned before.

Important part of the code is where we invoke an Designer: ShowComponentEditor(Designer,Tcomponent1(Component), GetDesignerClass);This procedure will be defined on a next step. For now I only want describe a parameters we pass into it.

Designer - this is a link to current IDesigner interface which allows your code to communicate with IDE.Component - is a link to an instance of component you Dbl-click on a form and allows to obtain andchange information from our designer.GetDesignerClass - return information about a class for Designer which will be used to handle our logic.You can put just a class name here instead to call a function we defined.

Now we almost ready to create a designer itself.

Step 3 - Create Component communicator

This is important part. Major difference between implementation we will discuss here and one fromarticles you looked before is - we will create a Form based designer.When Designer of this type is invoked, IDE will create and show a form with some logic implemented in it.Form will be created for each instance of the component you work with. If you would like to share samedesigner and update it when change a focus, you will need to handle update of a state when you switchbetween a components.So, to implement a designer you will need to modify your component code like this (I did highlight it withRed color). And add stub for Designer class. This code is only used by component to communicate withdesigner. We will have real implementation defined later by inheriting from TMyComponentDesigner.

TMyComponentDesigner = class;Tcomponent1 = class(TComponent)private

FDesigner: TMyComponentDesigner;procedure SetDesigner(const Value: TMyComponentDesigner);

protectedpublic

Page 4: component_editor.pdf

destructor Destroy; override;property Designer: TMyComponentDesigner read FDesigner write SetDesigner;

publishedend;TMyComponentDesigner = class(TObject)private

FComponent: Tcomponent1;protectedpublic

constructor Create(Component: Tcomponent1); virtual;destructor Destroy; override;procedure Modified; virtual;property Component: Tcomponent1 read FComponent;

end;constructor TMyComponentDesigner.Create(Component: Tcomponent1);begininherited Create;FComponent := Component;FComponent.Designer := Self;

end;destructor TMyComponentDesigner.Destroy;beginFComponent.Designer := nil;inherited;

end;procedure TMyComponentDesigner.Modified;begin// stubend;{ Tcomponent1 }

procedure Tcomponent1.SetDesigner(const Value: TMyComponentDesigner);begin

FDesigner := Value;end;destructor Tcomponent1.Destroy;begin

if Assigned(fDesigner) thenFreeAndNil(fDesigner);

inherited;end;

Step 3 - Create Component Designer

Now we are ready to implement a designer.

Page 5: component_editor.pdf

• Add new form to your package. Save a file as uMyDesigner.pas• Now there is a little trick. Designer Form is not just a regular form. It should be derived from

TDesignWindow to implement interfaces available for you to communicate with IDE. You willneed to replace class(TForm) on class(TDesignWindow). You can do it by modifying a sourcecode for this class.

• You will also implement 3 properties in this formproperty ComponentDesigner: TMyCmpDesigner read FComponentDesigner write SetComponentDesigner;property Component : Tcomponent1 read FComponent write SetComponent;property ComponentDesignerClass : TMyCmpDesignerClass read FCmpDesignerClass write SetCmpDesignerClass;

• And add implementation for ShowComponentEditor procedure we've saw before.

There is a source for this unit:unit uMyDesigner;

interfaceuses

DesignIntf, DesignWindows, // designer unitsuMyComponent, // component implementationForms, StdCtrls, Classes, Controls; // Form units

typeTMyCmpDesigner = class;TMyCmpDesignerClass = class of TMyCmpDesigner;

// Designer form declarationTfrmCmpDesigner = class(TDesignWindow)

Button1: TButton;Edit1: TEdit;procedure Button1Click(Sender: TObject);procedure FormClose(Sender: TObject; var Action: TCloseAction);

privateFCmpDesignerClass: TMyCmpDesignerClass;FComponent: Tcomponent1;

procedure SetComponentDesigner(const Value: TMyCmpDesigner);procedure SetCmpDesignerClass(const Value: TMyCmpDesignerClass);procedure SetComponent(const Value: Tcomponent1);

protectedFComponentDesigner: TMyCmpDesigner;

publicdestructor Destroy; override;property ComponentDesigner: TMyCmpDesigner read FComponentDesigner write SetComponentDesigner;property Component : Tcomponent1 read FComponent write SetComponent;property ComponentDesignerClass : TMyCmpDesignerClass read FCmpDesignerClass write SetCmpDesignerClass;

end;// designer implementationTMyCmpDesigner = class(TMyComponentDesigner)private

FComponentDesigner: TfrmCmpDesigner;

Page 6: component_editor.pdf

procedure SetComponentDesigner(const Value: TfrmCmpDesigner);public

destructor Destroy; override;procedure Modified; override;property ComponentDesigner: TfrmCmpDesigner read FComponentDesigner write SetComponentDesigner;

end;procedure ShowComponentEditor(aDesigner: IDesigner; aComponent: TComponent1; aDesignerClass: TMyCmpDesignerClass);function CreateComponentEditor(aDesigner: IDesigner; aComponent: TComponent1; aDesignerClass: TMyCmpDesignerClass; var aShared: Boolean): TfrmCmpDesigner;

implementation{$R *.dfm}

procedure ShowComponentEditor(aDesigner: IDesigner; aComponent: TComponent1; aDesignerClass: TMyCmpDesignerClass);var

lCmpDesigner: TfrmCmpDesigner;lShared: Boolean;

beginlCmpDesigner := CreateComponentEditor(aDesigner, aComponent, aDesignerClass, lShared);if lCmpDesigner <> nil then

lCmpDesigner.Show;end;function CreateComponentEditor(aDesigner: IDesigner; aComponent: Tcomponent1; aDesignerClass: TMyCmpDesignerClass; var aShared: Boolean): TfrmCmpDesigner;begin

aShared := True;if aComponent.Designer <> nil then

Result := (aComponent.Designer as TMyCmpDesigner).ComponentDesignerelsebegin

Result := TfrmCmpDesigner.Create(Application);Result.ComponentDesignerClass := aDesignerClass;Result.Designer := aDesigner;Result.Component := aComponent;aShared := False;

end;end;{ TMyCmpDesigner }

destructor TMyCmpDesigner.Destroy;begin

if FComponentDesigner <> nil thenbegin

FComponentDesigner.ComponentDesigner := nil;FComponentDesigner.Release;

end;inherited;

end;procedure TMyCmpDesigner.Modified;

Page 7: component_editor.pdf

begininherited;if Assigned(FComponentDesigner) and Assigned(FComponentDesigner.Designer) then

FComponentDesigner.Designer.Modified;end;procedure TMyCmpDesigner.SetComponentDesigner(const Value: TfrmCmpDesigner);begin

FComponentDesigner := Value;end;{ TfrmCmpDesigner }

destructor TfrmCmpDesigner.Destroy;begin

if FComponentDesigner <> nil thenbegin

FComponentDesigner.ComponentDesigner := nil;FComponentDesigner.Free;

end;inherited;

end;procedure TfrmCmpDesigner.SetCmpDesignerClass(const Value: TMyCmpDesignerClass);begin

FCmpDesignerClass := Value;end;procedure TfrmCmpDesigner.SetComponent(const Value: Tcomponent1);begin

if FComponent <> Value thenbegin

if FComponent <> nil thenbegin

FComponentDesigner.Free;FComponentDesigner := nil;

end;FComponent := Value;

if FComponent <> nil thenbegin

FComponentDesigner := FCmpDesignerClass.Create(Value);FComponentDesigner.FComponentDesigner := Self;Caption := FComponent.Name;Edit1.Text := FComponent.Name;

endelsebegin

Release;end;

end;end;

Page 8: component_editor.pdf

procedure TfrmCmpDesigner.SetComponentDesigner(const Value: TMyCmpDesigner);begin

FComponentDesigner := Value;end;procedure TfrmCmpDesigner.Button1Click(Sender: TObject);begin

if Assigned(FComponent) thenbegin

FComponent.Name := Edit1.Text;Designer.Modified;

end;Close;

end;procedure TfrmCmpDesigner.FormClose(Sender: TObject;

var Action: TCloseAction);begin

Action := caFree;end;end.

Installation

... We have a package ready for compile.Link to full source code for this article.After package is installed in IDE and component is ready for use.Our Designer will allow to change a Name property of the component via form. It is just example but it willgive you an idea where to go.

Notes.1. Remember to call Designer.Modified; to let associated component Object Inspector know what

you did modify some of the properties and Inspector should repopulate values.2. Our Designer form is created as float form and will stay opened even you did change property or

component focus. Any modification you will make in it will be reflected by associated component

7. Links

• This article on the web• This article on the BDN• Sample project

About the Author

Serge Dosyukov is a founder of Dragon Software, a consulting company which provides a consulting andtraining services for Delphi, MSSQL Server, Wise Installation System and Wise for Windows Installersince 1996.For information about on-site training and consulting you can contact author via email Serge or visit hisWeb site at www.dragonsoft.us.

Page 9: component_editor.pdf

Copyright © 2008 Serge DosyukovALL RIGHTS RESERVED. NO PART OF THIS DOCUMENT CAN BE COPIED IN ANY FORM WITHOUTTHE EXPRESS, WRITTEN CONSENT OF THE AUTHOR