Академический Документы
Профессиональный Документы
Культура Документы
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.
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. But let looks at a code. Our TMyComponentEditor is a main class here. This class allow us specify a logic IDE will invoke when we Dbl-click on component or use Context menu. We define function GetDesignerClass: TMyCmpDesignerClass; virtual ; method here just in case if in a future 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); will include just one line which invokes editor. You can extend Editor class to add more action but will not discuss 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 and change 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.
destructor Destroy; override; property Designer: TMyComponentDesigner read FDesigner write SetDesigner; published end; TMyComponentDesigner = class(TObject) private FComponent: Tcomponent1; protected public constructor Create(Component: Tcomponent1); virtual; destructor Destroy; override; procedure Modified; virtual; property Component: Tcomponent1 read FComponent; end; constructor TMyComponentDesigner.Create(Component: Tcomponent1); begin inherited Create; FComponent := Component; FComponent.Designer := Self; end; destructor TMyComponentDesigner.Destroy; begin FComponent.Designer := nil; inherited; end; procedure TMyComponentDesigner.Modified; begin // stub end; { Tcomponent1 } procedure Tcomponent1.SetDesigner(const Value: TMyComponentDesigner); begin FDesigner := Value; end; destructor Tcomponent1.Destroy; begin if Assigned(fDesigner) then FreeAndNil(fDesigner); inherited; end;
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 will need to replace class(TForm) on class(TDesignWindow). You can do it by modifying a source code for this class. You will also implement 3 properties in this form property ComponentDesigner: TMyCmpDesigner read FComponentDesigner write SetCompon property Component : Tcomponent1 read FComponent write SetComponent; property ComponentDesignerClass : TMyCmpDesignerClass read FCmpDesignerClass write And add implementation for ShowComponentEditor procedure we've saw before. There is a source for this unit: unit uMyDesigner; interface uses DesignIntf, DesignWindows, // designer units uMyComponent, // component implementation Forms, StdCtrls, Classes, Controls; // Form units type TMyCmpDesigner = class; TMyCmpDesignerClass = class of TMyCmpDesigner; // Designer form declaration TfrmCmpDesigner = class(TDesignWindow) Button1: TButton; Edit1: TEdit; procedure Button1Click(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private FCmpDesignerClass: TMyCmpDesignerClass; FComponent: Tcomponent1; procedure SetComponentDesigner(const Value: TMyCmpDesigner); procedure SetCmpDesignerClass(const Value: TMyCmpDesignerClass); procedure SetComponent(const Value: Tcomponent1); protected FComponentDesigner: TMyCmpDesigner; public destructor Destroy; override;
property ComponentDesigner: TMyCmpDesigner read FComponentDesigner write SetComponentD property Component : Tcomponent1 read FComponent write SetComponent; property ComponentDesignerClass : TMyCmpDesignerClass read FCmpDesignerClass write Set end; // designer implementation TMyCmpDesigner = class(TMyComponentDesigner) private FComponentDesigner: TfrmCmpDesigner;
procedure SetComponentDesigner(const Value: TfrmCmpDesigner); public destructor Destroy; override; procedure Modified; override;
procedure ShowComponentEditor(aDesigner: IDesigner; aComponent: TComponent1; aDesignerClas function CreateComponentEditor(aDesigner: IDesigner; aComponent: TComponent1; aDesignerCla implementation {$R *.dfm}
procedure ShowComponentEditor(aDesigner: IDesigner; aComponent: TComponent1; aDesignerClas var lCmpDesigner: TfrmCmpDesigner; lShared: Boolean; begin lCmpDesigner := CreateComponentEditor(aDesigner, aComponent, aDesignerClass, lShared); if lCmpDesigner <> nil then lCmpDesigner.Show; end;
function CreateComponentEditor(aDesigner: IDesigner; aComponent: Tcomponent1; aDesignerCla begin aShared := True; if aComponent.Designer <> nil then Result := (aComponent.Designer as TMyCmpDesigner).ComponentDesigner else begin 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 then begin FComponentDesigner.ComponentDesigner := nil; FComponentDesigner.Release; end; inherited; end; procedure TMyCmpDesigner.Modified;
begin inherited; 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 then begin 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 then begin if FComponent <> nil then begin FComponentDesigner.Free; FComponentDesigner := nil; end; FComponent := Value; if FComponent <> nil then begin FComponentDesigner := FCmpDesignerClass.Create(Value); FComponentDesigner.FComponentDesigner := Self; Caption := FComponent.Name; Edit1.Text := FComponent.Name; end else begin Release; end; end; end;
procedure TfrmCmpDesigner.SetComponentDesigner(const Value: TMyCmpDesigner); begin FComponentDesigner := Value; end; procedure TfrmCmpDesigner.Button1Click(Sender: TObject); begin if Assigned(FComponent) then begin 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 will give 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
Copyright 2008 Serge Dosyukov ALL RIGHTS RESERVED. NO PART OF THIS DOCUMENT CAN BE COPIED IN ANY FORM WITHOUT THE EXPRESS, WRITTEN CONSENT OF THE AUTHOR