Версия для печати
Разработка эксперта IDE Delphi 2007 с визуальным наследованием форм и размещением их в репозитории
http://www.delphikingdom.com/asp/viewitem.asp?catalogID=1380Сергей Деев
дата публикации 22-09-2008 09:40Разработка эксперта IDE Delphi 2007 с визуальным наследованием форм и размещением их в репозитории Постановка задачи
При переходе с Delphi 7 на Delphi 2007 я столкнулся с проблемой разработки эксперта IDE с визуальным наследованием форм и размещением их в репозитории.
Все мои эксперты, написанные под Delphi 7, отказывались работать на Delphi 2007.
Я решил разобраться почему это происходит и написать данную статью.
Частичное решение данной проблемы описано в примере на странице Erik's Open Tools API FAQ and Resources.
Попробуем разобраться в данном примере и написать свой эксперт IDE с визуальным наследованием форм и размещением их в репозитории.
Пример эксперта
Заходим на страницу Erik's Open Tools API FAQ and Resources и скачиваем оттуда пример из раздела «How do I implement a module creator (IOTAModuleCreator/ IOTAFormWizard)?»
Получаем файл GXModuleCreator.
Рассмотрим более подробно данный файл.
Класс TGxModuleCreatorWizard
Как видим, этот класс является наследником от класса TNotifierObject, являющийся одним из основных классов с поддержкой интерфейсов. По сути это и есть наш класс-эксперт.
На первых трех интерфейсах IOTAWizard, IOTARepositoryWizard, IOTARepositoryWizard60 долго останавливаться не буду, т. к. они знакомы программистам по более ранним версиям Delphi.
А вот на интерфейсе IOTARepositoryWizard80 остановлюсь более подробно.
Интерфейс IOTARepositoryWizard80
В разделе «Known bugs in the Delphi 2007 Open Tools API (most also apply to earlier releases):» написано, что для создания и правильного функционирования эксперта репозитория необходимо, чтобы наш эксперт поддерживал этот интерфейс (QC 20898).
Остановимся на функциях данного интерфейса:
function GetPersonality: string;возвращает строку о персональной секции по умолчанию. Как видим из реализации данной функции, она всегда возвращает константу sDelphiPersonality.
function GetGalleryCategory: IOTAGalleryCategory;возвращает категорию репозотирия, в которую будет помещен наш эксперт. Как видно из реализации этой функции она возвращает категорию по имени, описанной в константе sCategoryDelphiNewFiles
Класс TGxModuleCreator
Как видим, этот класс является наследником от класса TInterfacedObject, и поддерживает хорошо знакомые по предыдущим версиям Delphi интерфейсы IOTACreator и IOTAModuleCreator TNotifierObject. По сути это класс, который будет отвечать за создание файла нашего модуля (pas) с помощью функции
function NewImplSource(const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile;и файла описания формы (dfm) с помощью функции
function NewFormFile(const FormIdent, AncestorIdent: string): IOTAFile;Процедура регистрации эксперта
Основным отличием процедуры регистрации эксперта является использование метода RegisterPackageWizard, а не RegisterCustomModule как в предыдущих версиях Delphi (раздел «Known bugs in the Delphi 2005 Open Tools API: »).
Создание собственного эксперта
Для простоты реализации примем, что мы будем с помощью этого эксперта создавать наследников одной и той же формы с двумя кнопками «ОК» и «Отмена».
Примем, что класс этой самой формы будет называться TBaseForm, а все наследники будут иметь префикс в названии класса TInhForm и порядковый номер.
Начнем работу с создания нового пакета. Зайдем в его опции и установим тип пакета как «DisignTime only» и сохраним данный пакет под именем dtInhFormWizard. Вставим в пакет пустой модуль и добавим к пакету в раздел «Requires» пакет designide. После чего приступим к разработке самого эксперта.
По условию задачи мы будем разрабатывать эксперта для наследования, имеет смысл предусмотреть в нашем модуле общих классов-предков нашего эксперта и создание самих модулей и форм.
Примем что, в дальнейшем мы не будем использовать этих предков в других пакетах, то есть расположим описание всех нужных нам классов после директивы implementation.
Получим следующий код:
unit UInhFormReg; interface procedure Register; implementation uses SysUtils, Classes, Windows, DesignIntf, ToolsApi; end;Приступим к нашим классам-предкам
Класс-предок всех экспертов TCustomFormWizard
Описание класса:
{TCustomFormWizard} TCustomFormWizard = class(TNotifierObject, IOTAWIzard, IOTARepositoryWizard, IOTARepositoryWizard60, IOTARepositoryWizard80, IOTAProjectWizard) protected function BaseFormName: String; virtual; abstract; function AncestorFormName: String; virtual; abstract; public { IOTANotifier } procedure AfterSave; procedure BeforeSave; procedure Destroyed; procedure Modified; { IOTAWizard } function GetIDString: string; function GetName: string; function GetState: TWizardState; procedure Execute; virtual; abstract; { IOTARepositoryWizard } function GetAuthor: string; function GetComment: string; function GetPage: string; function GetGlyph: Cardinal; {IOTARepositoryWizard60} function GetDesigner: string; {IOTARepositoryWizard80} function GetGalleryCategory: IOTAGalleryCategory; function GetPersonality: string; end;Реализация методов данного класса ничем не отличается от реализации процедур в примере за исключением методов:
function TCustomFormWizard.GetName: string; begin Result := BaseFormName; end; function TCustomFormWizard.GetIDString: string; begin Result := 'TCustomForm.' + BaseFormName + '.Wizard'; end; function TCustomFormWizard.GetComment: string; begin Result := 'Creates a new ' + BaseFormName; end;Как видим, они вызывают абстрактный метод
function BaseFormName: String; virtual; abstract;который мы будем переопределять в наследниках данного эксперта.
Кроме того следует обратить внимание, что метод
procedure Execute; virtual; abstract;тоже абстрактный, а значит, мы и его будем переопределять в наследниках этого эксперта.
Класс-предок всех экспертов TCustomFormModuleCreator
Описание класса:
{ TCustomFormModuleCreator } TCustomFormModuleCreator = class(TInterfacedObject, IOTACreator, IOTAModuleCreator) private FAncestorFormName: string; FBaseFormName : string; FClassName : string; FUnitIdent : string; FFileName : string; public constructor Create(AncestorFormName, BaseFormName: string); virtual; { IOTACreator } function GetCreatorType: string; function GetExisting: Boolean; function GetFileSystem: string; function GetOwner: IOTAModule; virtual; function GetUnnamed: Boolean; { IOTAModuleCreator } function GetAncestorName: string; function GetImplFileName: string; function GetIntfFileName: string; function GetFormName: string; function GetMainForm: Boolean; function GetShowForm: Boolean; function GetShowSource: Boolean; function NewFormFile(const FormIdent, AncestorIdent: string): IOTAFile; virtual; abstract; function NewImplSource(const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile; virtual; abstract; function NewIntfSource(const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile; procedure FormCreated(const FormEditor: IOTAFormEditor); end;Реализация методов данного класса ничем не отличается от реализации процедур в примере за исключением методов:
constructor TCustomFormModuleCreator.Create(AncestorFormName, BaseFormName: string); begin FAncestorFormName := AncestorFormName; FBaseFormName := BaseFormName; end; function TCustomFormModuleCreator.GetAncestorName: string; begin Result := FAncestorFormName; end; function TCustomFormModuleCreator.GetFormName: string; begin Result := FBaseFormName end;Следует отметить, что конструктор данного класса
constructor Create(AncestorFormName, BaseFormName: string);виртуальный, а методы
function NewImplSource(const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile;и
function NewImplSource(const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile;абстрактны, мы их будем переопределять в наследниках.
Класс TFormModuleSourceFile для поддержки интерфейса IOTAFile
Описание класса:
{ TFormModuleSourceFile } TFormModuleSourceFile = class(TInterfacedObject, IOTAFile) private FSource: string; public function GetSource: string; function GetAge: TDateTime; constructor Create(const Source: string); end;Реализация методов данного класса ничем не отличается от реализации процедур в примере.
Класс-наследник создания модуля основной формы TBaseFormModuleCreator
Описание класса:
{ TBaseFormModuleCreator } TBaseFormModuleCreator = class(TCustomFormModuleCreator) public constructor Create(AncestorFormName, BaseFormName: string); override; function NewFormFile(const FormIdent, AncestorIdent: string): IOTAFile; override; function NewImplSource(const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile; override; end;Реализация конструктора класса:
constructor TBaseFormModuleCreator.Create(AncestorFormName, BaseFormName: string); var AFileName: String; begin inherited Create(AncestorFormName, BaseFormName); (BorlandIDEServices as IOTAModuleServices).GetNewModuleAndClassName(BaseFormName, FUnitIdent, FClassName, FFileName); FUnitIdent := 'U' + BaseFormName; AFileName := ExtractFilePath(FFileName); FFileName := AFileName + FUnitIdent + '.pas'; end;В данном конструкторе принимаем меры, чтобы модуль с базовой формой назывался всегда UBaseForm, а сам файл модуля имел такое же имя.
В реализации методов NewFormFile и NewImplSource следует отметить наличие констант с описанием самого модуля и описания формы. Можно было бы записать эти константы в ресурс пакета и доставать их оттуда.
Класс-наследник эксперта TBaseFormModuleWizard
Описание класса:
{ TBaseFormModuleWizard } TBaseFormModuleWizard = class(TCustomFormWizard) protected function BaseFormName: String; override; function AncestorFormName: String; override; public procedure Execute; override; end;Реализация метода Execute:
procedure TBaseFormModuleWizard.Execute; var Module: IOTAModule; begin Module := (BorlandIDEServices as IOTAModuleServices).CreateModule(TBaseFormModuleCreator.Create(AncestorFormName, BaseFormName)); end;Как видим, в нем просто вызывается конструктор класса создания модуля TBaseFormModuleCreator с передачей в него наименований самой формы и ее предка.
Класс-наследник создания модуля форм-наследников TInhFormCreator
Описание класса:
{ TInhFormCreator } TInhFormCreator = class(TCustomFormModuleCreator) public constructor Create(AncestorFormName, BaseFormName: string); override; function NewFormFile(const FormIdent, AncestorIdent: string): IOTAFile; override; function NewImplSource(const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile; override; end;В конструкторе класса мы также принимаем меры, чтобы все модули наследуемых форм назывались UInhForm с порядковым номером, а сам файл модуля имел такое же наименование.
В методе NewFormFile в константе описания формы предусматриваем ее наследование
В методе NewImplSource в константе описания модуля предусматриваем ссылку на модуль базовой формы и наследование самой формы.
Класс-наследник эксперта TInhFormModuleWizard
Описание класса:
{ TInhFormModuleWizard } TInhFormModuleWizard = class(TCustomFormWizard) protected function BaseFormName: String; override; function AncestorFormName: String; override; public procedure Execute; override; end;Реализация метода Execute:
procedure TInhFormModuleWizard.Execute; var Module: IOTAModule; ActiveProject: IOTAProject; I: Integer; IsAncestorFormName: Boolean; begin ActiveProject := GetActiveProject; if ActiveProject <> nil then begin IsAncestorFormName := False; for I := 0 to ActiveProject.GetModuleCount - 1 do with ActiveProject.GetModule(I) do if FormName = AncestorFormName then begin IsAncestorFormName := True; Break; end; if not IsAncestorFormName then with TBaseFormModuleWizard.Create do try Execute; finally Free; end; Module := (BorlandIDEServices as IOTAModuleServices).CreateModule(TInhFormCreator.Create(AncestorFormName, BaseFormName)); end; end;Как видим, в методе создается объект типа текущий проект, затем если текущий проект создан в нем ищется базовая форма и если ее не находят, то вызывается конструктор ее эксперта, а затем и создание самого модуля формы-наследницы.
Процедура регистрации эксперта
procedure Register; begin RegisterPackageWizard(TInhFormModuleWizard.Create); end;Заключение
Полностью файл проекта можно посмотреть в приложенном файле.
Данный пример, после некоторых доработок, позволяет программисту создать эксперта IDE с визуальным наследованием форм и размещением их в репозитории сколь угодно сложности.
При написании статьи использовались материалы со страницы Erik's Open Tools API FAQ and Resources.
Прилагаемые файлы