怎样编写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: