|
Creating a new TForm descendant that will show new properties in the
Object Inspector is not as easy as creating a component with new
properties. Since the IDE is intimately involved with a Form at
design time there is a bit of overhead that must be done before the
IDE can interact with a new TForm descendant.
First define the new Form as you would any other class,
deriving from TCustomForm (or TForm if you want all properties of
TForm exposed in the new Form).
|
type
TCustomNewDecendantForm = class(TCustomForm)
private
FNewProperty: Integer;
protected
property NewProperty: Integer read
FNewProperty write FNewProperty;
end;
TNewDescendantForm = class(TCustomNewDescendantForm)
published
property Caption;
property NewProperty;
property PixelsPerInch; //
PixelsPerInch is necessary at Runtime.
//... publish any other
TCustomForm property that is appropriate here
end; |
Next the IDE needs to have the new object class
registered so it can interact with it. This relies on a mysterious
undocumented procedure
|
procedure
RegisterCustomModule(ComponentBaseClass: TComponentClass;
CustomModuleClass: TCustomModuleClass); |
Now all we need to do is register our new class, telling the
IDE that it is a Custom Module
|
procedure Register
begin
RegisterCustomModule(TNewDescendantForm, TCustomModule);
end; |
There is one catch here
if needing to support older compilers. Starting with D6 TCustomModule
is defined in DesignEdtiors.pas. The easiest way to handle compiler
issues is to use the Compilers.inc
file included with most of the demos.
|
uses
{$IFDEF COMPILER_6_UP}
DesignIntf, // DsgnIntf
renamed to DesignIntf in D6
DesignEditors, //
TCustomModule moved to DesignEditors in D6
{$ELSE}
DsgnIntf,
{$ENDIF}
ToolsApi; |
Ok, all looks good so far. There is a catch though, do
you see it? Actually there are two problems.
To create a new Form you go to the Object
Repository (or use a short cut to it) and select New Form. Are
you seeing where this is going? Yes, we need to make a Wizard that
registers the new Form Module and creates a new item in the Object
Repository so the user can create an instance of it.
If you have not seen the pattern yet you will
be happy to know that a lot of the Module Creator and File interface
implementation is repetitive so why don't we create some classes that
can handle the details and only needs one or two methods overridden
to use. You may look through the CreatorUtilities.pas file in
the the OTADescendantForm
demo for the details.
The Wizard is the standard template used in the Repository
Wizard. Since a most of the methods in this implementation
usually need to be overridden keeping a copy of a template is worth
the effort. What is notable in Repository Wizard in the OTADescendantForm
demo is the implementation of the Execute method:
|
procedure TDescendantFormWizard.Execute;
begin
(BorlandIDEServices as IOTAModuleServices).CreateModule(TNewDescendantCreator.Create);
end; |
This is just simply using a Form
Creator when the user selects our Repository object, but what is
a TNewDescendantCreator object? TNewDescendantCreator
is defined in the demo by:
|
uses
CreatorUtilites;
interface
//
// A New
TModuleCreatorFile descendant to make add our DescendantForm unit
// to the uses clause of
the file
//
TNewDescendantFile = class(TModuleCreatorFile)
public
function GetSource: string; override;
end;
//
// A New
TFormCreatorModule descendant to make our new Form class the ancestor
// Form, and to use our
new TModuleCreatorFile class
//
TNewDescendantCreator = class(TFormCreatorModule)
public
function GetAncestorName: string; override;
function GetImplFile: TModuleCreatorFileClass; override;
end;
implementation
{ TNewDescendantCreator }
function TNewDescendantCreator.GetAncestorName: string;
begin
Result := 'NewDescendantForm'
end;
function TNewDescendantCreator.GetImplFile: TModuleCreatorFileClass;
begin
//
Tell the
Creator to use our new DescendantFile object
Result := TNewDescendantFile
end;
{ TNewDescendantFile }
function TNewDescendantFile.GetSource: string;
begin
Result := inherited GetSource;
// Add the DescendantForm
unit to the uses clause
// NOTE: This is
dependant on Graphics not being the last uint in the uses clause
Result := StringReplace(Result, 'Graphics,',
'Graphics, DescendantForm,', [rfIgnoreCase]);
end; |
All we had to do was to override the GetAncestorName to have the
Creator use our new Form class as the ancestor. The inherited
TFormCreatorModule does the rest. Now create a package and add the
wizard files to it and intall the package into the IDE.
The resulting code in the IDE looks like this:
|
unit Unit1;
// This code was generated by
the Mustangpeak OTA Wizard
// Demo.
// www.Mustangpeak.net
//
// Don't forget if a Custom Form
with an ancestor that
// is not TForm is created the
uses clause must include
// the unit that contains the
Custom Form source code
// and the path to that source
code must be on the IDE's
// path.
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, DescendantForm,
Controls, Forms;
type
TNewDescendantForm1 = class(TNewDescendantForm)
private
{ Private declarations }
public { Public
declarations }
end;
var
NewDescendantForm1: TNewDescendantForm1;
implementation
{$R *.dfm}
end. |
And if we look in the Object Inspector we see our new Property!

Creating Forms that can have new properties in
the Object Inspector takes a bit if work but with a few reusable
classes it can be accomplished in only a few minutes.
Back to OpenTools API page.
|