怎样编写DELPHI向导(一)
发信人: strayli (stray), 信区: Delphi
标 题: How To Write Delphi Wizards(1)
发信站: BBS 水木清华站 (Thu Nov 5 21:59:25 1998) WWW-POST
How To Write Delphi Wizards
------------------------------------------------------------------------------
--
Delphi and C++Builder are truly open development environments, in that they
have interfaces to enable us to integrate our own tools and experts within
their IDE. This article will focus on writing and integrating Wizards
(previously called Experts) with Delphi. The resulting (32-bits) Wizards will
be compatible with Delphi 2.0x, Delphi 3 and C++Builder.
Delphi has four kinds of Wizards: Project Experts, Form Experts, Standard
Experts and (32-bits only) AddIn Experts. The first two can be found in the
Repository, Standard Experts can be found under the Help menu (like the
Database Form Expert), while AddIn Experts have to provide their own
menu-interface with the Delphi IDE (typicaly anywhere in the menu except for
the Help Menu, which seems to be reserved for Standard Experts only).
Project and Form Experts can be activated whenever you create a new Project
or Form (just like Project and Form Templates). Standard and AddIn Experts
are the other kind of Wizards that generally do not create a new project or
form, but provide some kind of information, or only create a new file or
unit.
If you've ever tried an Wizard, you know what power and ease they can bring
to you. The Project Expert develops an entire project for you based on your
specific preferences (like for example the Application Wizard). The Form
Experts develop custom forms that are added to your current project. The
Database Form Expert, for example, generates a form that displays data from
an external database. These example Wizards are not just external tools that
can be started from Delphi, they actually communicate with Delphi and are an
integrated part of the development environment. While this is not so strange
for the existing Delphi Experts (after all, they were developed and added by
the same team that developed Delphi in the first place, and we all know
Delphi's IDE is written in Delphi), it sounds intriguing at least to know
that we, too, can write a Delphi Wizard that is able to communicate with
Delphi in the same way. Could we actually write an Wizard that also opens
files in the IDE, that can be used to start a new project from scratch? Yes, all this is possible, and more, as we will see
shortly!
1. TIExpert Interface
The major reason why everybody thinks writing custom Wizards is difficult, is
because they are not documented. Not in the manuals or on-line Help, that is
(they are documented in my book The Revolutionary Guide to Delphi 2 and in my
column in The Delphi Magazine). If you take a look at the documentation and
source code on your harddisk, you'll find some important files and even two
example Wizards that are installed automatically by Delphi itself. The
important example files can be found in the DOC, SOURCE\VCL or
SOURCE\TOOLSAPI subdirectories, and the main files are EXPTINTF.PAS,
TOOLINTF.PAS, VIRTINTF.PAS and SHAREMEM.PAS. The first one shows how to
derive and register our own Wizard, while the second one shows how to use the
tool-services of Delphi to make the integration with the IDE complete.
In order to start working on a custom wizard, we have to take a look at the
abstract base class definition TIExpert in EXPTINTF.PAS, which is as follows
for the 32-bits versions of Delphi:
Type
TExpertStyle = (esStandard, esForm, esProject, esAddIn);
TExpertState = set of (esEnabled, esChecked);
TIExpert = class(TInterface)
public
{ Expert UI strings }
function GetIDString: string; virtual; stdcall; abstract;
function GetName: string; virtual; stdcall; abstract;
function GetAuthor: string; virtual; stdcall; abstract;
function GetStyle: TExpertStyle; virtual; stdcall; abstract;
function GetMenuText: string; virtual; stdcall; abstract;
function GetState: TExpertState; virtual; stdcall; abstract;
function GetGlyph: HICON; virtual; stdcall; abstract;
function GetComment: string; virtual; stdcall; abstract;
function GetPage: string; virtual; stdcall; abstract;
{ Launch the Expert }
procedure Execute; virtual; stdcall; abstract;
end;
2. TGenericExpert: Hello, World!
If we want to derive our own Wizard, say TGenericExpert, we have to derive it
from the abstract base class TIExpert, which has seven or nine abstract
member functions (GetStyle, GetName, GetComment, GetGlyph, GetState,
GetIDString and GetMenuText, and for the 32-bits versions of Delphi also
GetAuthor and GetPage) and one member procedure Execute. Since TIExpert is an
abstract base class, we need to override every function we need for any
particular Wizard.
unit Generic;
interface
uses
Windows, ExptIntf;
Type
TGenericExpert = class(TIExpert)
public
{ Expert Style }
function GetStyle: TExpertStyle; override;
{ Expert Strings }
function GetIDString: string; override;
function GetName: string; override;
function GetAuthor: string; override;
function GetMenuText: string; override;
function GetState: TExpertState; override;
function GetGlyph: HICON; override;
function GetComment: string; override;
function GetPage: string; override;
{ Expert Action }
procedure Execute; override;
end;
procedure Register;
implementation
uses
Dialogs;
{ The implementation details of TGenericExpert will follow in the text }
procedure Register;
begin
RegisterLibraryExpert(TGenericExpert.Create)
end {Register};
end.
Let's have a closer look at our generic Wizard from this listing. Since
TIExpert is an abstract base class, we need to override every function we
need for our TGenericExpert. First of all, we need to specify the style of
the Wizard with the GetStyle method that can return one of three (or four)
possible values: esStandard to tell the IDE to treat the interface to this
Wizard as a menu item on the Help menu, esForm to tell the IDE to treat this
Wizard interface in a fashion similar to form templates, or esProject to tell
the IDE to treat this interface in a fashion similar to project templates.
For 32-bits Delphi Wizards only, we can also return esAddIn here, to indicate
that this is a special klind of Wizard that handles all its own interfaceing
to the IDE through the TIToolServices interface. For our TGenericExpert, a
Standard type Wizard that only shows a MessageDlg to say hello to the world,
we can use the esStandard style.
function TGenericExpert.GetStyle: TExpertStyle;
begin
Result := esStandard
end {GetStyle};
The GetIDString should be unique to all Wizards that could be installed. By
convention, the format of the string is: CompanyName.ExpertFunction, like
Borland.Expert or DrBob.GenericExpert.
function TGenericExpert.GetIDString: String;
begin
Result := 'DrBob.TGenericExpert'
end {GetIDString};
After we've set the style of the Wizard, all we need to do is fill the other
options accordingly. The GetName must return a unique descriptive name
identifying this Wizard, like 'Generic Wizard'.
function TGenericExpert.GetName: String;
begin
Result := 'Generic Wizard'
end {GetName};
If the style is esForm or esProject, then - for 32-bits versions of Delphi
only - we need to return a valid name for the Author. In this case, the style
is esStandard, so we can return an empty string instead. For an esForm or
esProject style Wizard the name would be displayed in the Object Repository
of the 32-bits versions of Delphi.
{$IFDEF WIN32}
function TGenericExpert.GetAuthor: String;
begin
Result := 'Bob Swart (aka Dr.Bob)' { although not needed for esStandard
}
end {GetAuthor};
{$ENDIF}
If style is esForm or esProject then GetGlyph should return a handle to a
bitmap (for Delphi 1) or icon (for Delphi 2.0x and 3) to be displayed in the
form or project list boxes or dialogs. This bitmap should have a size of
60x40 pixels in 16 colours. The icon should be 32x32 in 16 colours. Again,
since the style is just esStandard for our TGenericExpert, we can return 0
here. We can even combine the 16- and 32-bit version of GetGlyph here (0 is a
valid value to indicate that an icon or bitmap is empty). Note that if we
return a 0 when a bitmap or icon is needed, Delphi will use the default
image.
{$IFDEF WIN32}
function TGenericExpert.GetGlyph: HICON;
{$ELSE}
function TGenericExpert.GetGlyph: HBITMAP;
{$ENDIF}
begin
Result := 0 { not needed for esStandard }
end {GetGlyph};
If style is esForm or esProject then GetComment should return a 1 or 2 line
sentence describing the function of this Wizard. Since the style is
esStandard, we can return an empty string.
function TGenericExpert.GetComment: String;
begin
Result := '' { not needed for esStandard }
end {GetComment};
If style is esForm or esProject then - only for 32-bits versions of Delphi -
using GetPage we can specify the name of the page in the Object Repository
where to place our Wizard. If we don't specify a name here, then the Wizard
just gets added to the Default Form or Project page. Since we're writing an
esStandard Expert, we don't need to supply a page name, so we can return an
empty string again.
{$IFDEF WIN32}
function TGenericExpert.GetPage: String;
begin
Result := '' { not needed for esStandard }
end {GetPage};
{$ENDIF}
If style is esStandard then GetMenuText should return the actual text to
display for the menu item, like 'Generic Wizard'. Since this function is
called each time the parent menu is pulled-down, it is even possible to
provide context sensitive text.
function TGenericExpert.GetMenuText: String;
begin
Result := '&Generic Wizard...'
end {GetMenuText};
If the style is esStandard then GetState returning esChecked will cause the
menu to display a checkmark. This function is called each time the Wizard is
shown in a menu or listbox in order to determine how it should be displayed.
We just leave it esEnabled for now.
function TGenericExpert.GetState: TExpertState;
begin
Result := [esEnabled]
end {GetState};
Finally, the Execute method is called whenever this Wizard is invoked via the
menu, form gallery dialog, or project gallery dialog. Note that Execute is
never called for an esAddIn style Wizard (this kind of Wizard will handle all
its own interfacing to the IDE through the upcoming TIToolServices
interface). The style will determine how the Wizard was invoked. In this
case, we just call a MessageDlg in the Execute method to indicate that the
Wizard is actually alive.
procedure TGenericExpert.Execute;
begin
MessageDlg('Hello Nashville!', mtInformation, [mbOk], 0)
end {Execute};
To install our first Wizard, all we need to do is act like it's a new
component: For Delphi 1.0, pick Options | Install, for Delphi 2.0x and 3
select Component | Install, and add it to the list of installed components.
Delphi 1 and 2 simply add the Wizard the the Component Library, but Delphi 3
needs to add it to a package - the DCLUSR30 package by default: