怎样编写DELPHI向导(二)

发信人: strayli (stray), 信区: Delphi 
标  题: How to write Delphi wizard(2) 
发信站: BBS 水木清华站 (Thu Nov  5 22:01:06 1998) WWW-POST 
 
After we click onthe OK-button to add the generic unit with our GenericExpert  
to the DCLUSR30 package, we need to confirm that Delphi needs to rebuild the  
package:   
 After the package is rebuilt and installed into the Delphi 3 IDE again, we  
can inspect the Package Editor and see that the generic unit is now part of  
it. This simple example already illustrates that packages are not limited to  
components, but can contain Wizards as well.      
When Delphi is done with compiling and linking COMPLIB.DCL or DCLUSR30.DPL,  
you can find our first new Wizard in the Help menu:      
Just select the "Generic Wizard" and it will show the world that it's alive:  
 As we can see, only the Execute method contains any significant code, and  
this will remain so for all Wizards to come. In order to avoid that we have  
to print a long listing in this paper for an Wizard where only one method is  
relevant, I'll propose the following technique: let's use a table to define  
the nine methods, and only specify the Execute method in detail. Our  
TGenericExpert would then become the following:    
TGenericExpert   
GetStyle: esStandard   
GetIDString: DrBob.TGenericExpert   
GetName: Generic Wizard   
GetAuthor (win32): Bob Swart (aka Dr.Bob)   
GetMenuText: &Generic Wizard...   
GetState: [esEnabled]   
GetGlyph: 0   
GetPage (win32):     
GetComment:      
With only the Execute method outlined in detail (see previous listing). We  
will use this notation in the rest of this session.   

 
3. TSysInfoExpert  
Instead of just popping up a MessageDlg, we can show any form we'd like. In  
fact, this is just were the fun starts. Generally, we can consider our Wizard  
to consist of two parts: the Wizard engine and the form interface. We've just  
seen how to write the Wizard engine, and we all know how to write form  
interfaces, so let's put these two together and write our first  
something-more-than-trivial information Wizard. The information that I want  
the Wizard to present can be found in the SysUtils unit, and consists of the  
country specific informatin regarding currency and date/time formatting  
constants. In the on-line help we can find which constants are defined in  
SysUtils, but we can't see their value. This is unfortunate, since most of  
the time Delphi is of course up-and-running while we're developing, so  
SysUtils is active as well (remember: Delphi is written in Delphi!) and  
should know about these values.   
 
So, using the Dialog Expert, we can create a Multipage dialog, using a  
TabbedNotebook, and give the three pages the names "Currency", "Date" and  
"Time". Next, we must drop a label on each of the pages, set autosize for  
each label to false, and make them about as big as the entire notebook (so  
multiple lines can be viewed). The source code for the form merely consists  
of putting the right values on the right places when the form is created (in  
the OnCreate handler), so nothing complex at all for the interface side of  
the SysInfo Wizard. The engine of TSysInfoExpert is as follows:   
 
 
TSysInfoExpert   
GetStyle: esStandard   
GetIDString: DrBob.TSysInfoExpert   
GetName: SysInfo Wizard   
GetAuthor (win32): Bob Swart (aka Dr.Bob)   
GetMenuText: &SysInfo Wizard...   
GetState: [esEnabled]   
GetGlyph: 0   
GetPage (win32):     
GetComment:     
 
The Execute method of the SysInfo Wizard is almost as easy, since all we need  
to do is to create, show and free the form with the desired information.  
That's it. The source code for the Execute procedure is as follows:   
 
 
  procedure TSysInfoExpert.Execute;  
  begin  
    with TSysInfoForm.Create(nil) do  
    begin  
      ShowModal;  
      Free  
    end  
  end {Execute};  
 
And presto! Our first "useful" Wizard, showing information at design time  
that isn't available any other way:   
 
   
This is only the first of many examples where we will see an Wizard engine  
that will show an interface form to show (or get) information to the user.  
One source of information to provide (or actions to apply) can be obtained  
from the so-called toolservices interface Delphi offers us in the  
TIToolServices class.   
 
4. ToolServices  
We've seen some generic but in fact pretty much useless Wizard so far. In  
order to write truly more useful Wizards, we need to do something special  
inside the Execute method, like show a (more) interesting form in which a lot  
of things can happen, a bit like we introduced with the TSysInfoExpert.  
Did you ever feel the need to load some project other than a .DPR file in the  
IDE? No? Never written any DLLs in Delphi? Well, I often have the need to  
open a .PAS or any file with an extension other than .DPR inside the IDE as  
my project. In fact, my need is so big, I want to write an Wizard to help me  
in browsing over my disk and directories in search for a certain file to open  
as a new project.  
But is this possible? To answer this question, we need to take a look at  
TOOLINTF.PAS from Delphi 1.0, the file that contains the definition of  
TIToolServices (the "I" stands for Interface again), which is as follows:   
 
 
unit ToolIntf;  
interface  
uses  
  WinTypes, VirtIntf;  
 
Type  
  TIToolServices = class(TInterface)  
  public  
    { Action interfaces }  
    function CloseProject: Boolean; virtual; export; abstract;  
    function OpenProject(const ProjName: string): Boolean; virtual; export;  
abstract;  
    function OpenProjectInfo(const ProjName: string): Boolean; virtual;  
export; abstract;  
    function SaveProject: Boolean; virtual; export; abstract;  
    function CloseFile(const FileName: string): Boolean; virtual; export;  
abstract;  
    function SaveFile(const FileName: string): Boolean; virtual; export;  
abstract;  
    function OpenFile(const FileName: string): Boolean; virtual; export;  
abstract;  
    function ReloadFile(const FileName: string): Boolean; virtual; export;  
abstract;  
    function ModalDialogBox(Instance: THandle; TemplateName: PChar;   
WndParent: HWnd;  
      DialogFunc: TFarProc; InitParam: LongInt): Integer; virtual; export;  
abstract;  
    function CreateModule(const ModuleName: string; Source, Form: TIStream;  
      CreateFlags: TCreateModuleFlags): Boolean; virtual; export; abstract;  
 
    { Project/UI information }  
    function GetParentHandle: HWND; virtual; export; abstract;  
    function GetProjectName: string; virtual; export; abstract;  
    function GetUnitCount: Integer; virtual; export; abstract;  
    function GetUnitName(Index: Integer): string; virtual; export; abstract;  
    function GetFormCount: Integer; virtual; export; abstract;  
    function GetFormName(Index: Integer): string; virtual; export; abstract;  
    function GetCurrentFile: string; virtual; export; abstract;  
    function IsFileOpen(const FileName: string): Boolean; virtual; export;  
abstract;  
    function GetNewModuleName(var UnitIdent, FileName: string): Boolean;  
             virtual; export; abstract;  
 
    { Component Library interface }  
    function GetModuleCount: Integer; virtual; export; abstract;  
    function GetModuleName(Index: Integer): string; virtual; export;  
abstract;  
    function GetComponentCount(ModIndex: Integer): Integer; virtual; export;  
abstract;  
    function GetComponentName(ModIndex,CompIndex: Integer): string; virtual;  
export; abstract;  
 
   {function InstallModule(const ModuleName: string): Boolean; virtual;  
export; abstract;  
    function CompileLibrary: Boolean; virtual; export; abstract;  
   }  
 
    { Error handling }  
    procedure RaiseException(const Message: string); virtual; export;  
abstract;  
  end;  
 
implementation  
 
The Tool services object is created on the application (Delphi/C++Builder)  
side, and is passed to the VCS/Expert Manager DLL during initialization. Note  
that the application (Delphi/C++Builder) is responsible for creating and  
freeing the interface object, and the client should never free the interface.  
 
The following ToolServices functions are available to the client (for Delphi  
1.0 as well as 2.0x and 3):   
 
 
Actions   
CloseProject  
returns True if no project open, or if the currently open project can be  
closed.   
OpenProject  
returns True if the named project can be opened. You have to pass an empty  
string to create a new project and main form.   
OpenProjectInfo  
returns True if the named project file can be opened. This routine bypasses  
all the normal project load features (such as loading a desktop file, showing  
the source code, etc), and simply opens the .DPR and .OPT files.   
SaveProject  
returns True if the project is unmodified, if there is no project open, or if  
the open project can be saved.   
CloseFile  
returns True if the specified file is not currently open, or if it can be  
closed.   
OpenFile  
returns True if the specified file is already open or can be opened.   
ReloadFile  
returns True if the file is already open and was reloaded from disk. (NOTE:  
This will not perform any checking of the current editor state).   
RefreshBuffers  
causes the IDE to check the time/date stamp of each open file to determine if  
the file has changed on disk. If so, the file is re-read.   
ModalDialogBox  
used by non-VCL DLL's to present a dialog box which is modal. Note that DLLs  
written using VCL can simply call a form's ShowModal function.   
CreateModule  
Will create new module from memory images of the source and, optionally, the  
form file.   
The TCreateModuleFlags are:   
cmAddToProject: Add the new module to the currently open project.   
cmShowSource: Show the source file in the top-most editor window.   
cmShowForm: If a form is created, show it above the source.   
cmUnNamed: Will mark the module as unnamed which will cause the SaveAs dialog  
to show the first time the user attempts to save the file.   
cmNewUnit: Creates a new unit and adds it to the current project. NOTE: all  
other parameters are ignored.   
cmNewForm: Creates a new form and adds it to the current project. NOTE: all  
other parameters are ignored.   
cmMainForm: If the module includes a form, make it the main form of the  
currently open project. Only valid with the cmAddToProject option.   
cmMarkModified: Will insure that the new module is marked as modified.   
Informational   
 
GetParentHandle  
returns a HWND which should be used as the parent for windows created by the  
client.   
GetProjectName  
returns a fully qualified path name of the currently open project file, or an  
empty string if no project is open.   
GetUnitCount  
returns the current number of units belonging to the project.   
GetUnitName  
returns a fully qualified name of the specified unit.   
GetFormCount  
returns the current number of forms belonging to the project.   
GetFormName  
returns a fully qualified name of the specified form file.   
GetCurrentFile  
returns a fully qualified name of the current file, which could either be a  
form or unit (.PAS). Returns a blank string if no file is currently selected.  
 
IsFileOpen