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