Данная заметка является продолжением предыдущей.
Вступительное слово
Я решил не писать отдельную статью. Вместо этого я буду проделывать задуманную работу по маленьким этапам, каждый из которых выкладывать в репозиторий на github.
Ещё я подумал, что если Вы всё ещё используете старые версии Delphi, то скорее всего, Вы их используете для поддержки существующих больших проектов. А потому, Вы врядли будете использовать мои исходники как есть. К тому же, у меня нет сейчас возможности дополнительно тестировать исходники на совместимость, поэтому я убрал директивы условной компиляции {$IFDEF} для разных версий Delphi.
Все исходники написаны в Delphi 2010, доступны для свободного скачивания и использования. Последняя версия доступна на этой странице, ссылка для быстрого скачивания.
Далее я буду ссылаться на эти исходники.
И ещё я долго не мог выбрать подходящего имени для классов базовых формы и фреймы. Остановился на TBaseForm / TBaseFrame без всяких префиксов, типа TdnForm (dn – от DelphiNotes). Я исхожу из предположения – если Вы в своём проекте уже используете базовую форму/фрейму, то Вы врядли будете устанавливать ещё одну базовую, скорее всего Вы просто подсмотрите мой код и не более. А если ещё нет – то разумно для базовой формы иметь нормальное имя класса, чтобы не спотыкаться на префиксы при наборе кода. Имя же модуля, в котором находятся базовые форма и фрейма – BaseForms.pas.
Цель
На данном этапе я хочу интегрировать форму/фрейму в IDE Delphi. Это необходимо, чтобы дизайнер мог корректно работать со свойствами и событиями, которые в будущем будут появляться. А для этого необходим визард. Поэтому пока я создам пустую базовую форму и фрейму (просто наследники от TForm/TFrame), и сосредоточусь именно на визарде.
Работа
Визард – это отдельный Design-Time Package – обычный пакет, который создаётся при создании компонент. У любой компоненты есть иконка, поэтому сначала рисуем иконки. См. файлы BaseForm.ico и BaseFrame.ico. Затем помещаем их в ресурсный файл BaseFormsDesign.dcr. Для этого используется BaseFormsDesign.rc и make_dcr.bat (достаточно запустить bat-файл).
Теперь создаём пакет, и включаем в него необходимые файлы. Код визарда находится в BaseFormsDesign.pas. Я не буду подробно останавливаться на коде, отмечу только функцию GetGlyph, которая загружает подготовленные иконки.
Собираем пакет и устанавливаем.
Результат
Вам достаточно скачать исходники, открыть .dpk-файл, и в окне Project Manager, щёлкнув правой кнопкой по названию пакета, выбрать пункт Install. Затем закрыть пакет не сохраняя, и прописать путь к файлу BaseFroms.pas через меню Tools \ Options… => Environment Options \ Delphi Options \ Library - Win32 => Library path:. Должно получиться что-то типа такого:
После этого, нужно создать новый проект File \ New \ VCL Forms Application - Delphi, либо открыть существующий.
В итоге, открыв пункт меню File \ New \ Other… в категории Delphi Files Вы увидите такую картинку:
Что с этим делать?
DN Base Form и DN Base Frame создают базовую форму и базовую фрейму по аналогии с обычной Form и Frame. Пока базовые форма и фрейма не содержат полезного кода. Далее я буду наполнять BaseForms.pas полезным кодом, иногда после этого придётся переустанавливать пакет в IDE.
Если у вас уже есть проект (либо Вы создали пустой проект), и Вы хотите у какой-нибудь формы/фрейме (автоматически созданной форме Form1) подменить дефолтового родителя на базового, это делается очень просто:
- открываем исходный код модуля;
- в uses добавляем BaseForms;
- родителя TForm/TFrame заменяем на TBaseForm/TBaseFrame.
Теперь, чтобы дизайнер IDE узнал, что перед ним новая форма, делаем так:
- нажимаем F12 (переход в визуальный редактор формы/фреймы);
- нажимаем Alt+F12 (переход в текстовый редактор dfm-файла формы/фреймы);
- нажимаем Alt+F12 ещё раз, и дизайнер загружает форму/фрейму заново.
Это можно проделать без сохранения модуля и сразу увидеть результат. Но иногда Delphi не даёт переключиться в редактор dfm-файла (это справедливо для фрейм, если есть другие открытые модули, в которые встроена текущая). Тогда так: сохранить, закрыть и открыть файл заново.
Ссылки:
Основу визарда я в своё время подсмотрел у Tnt Unicode Controls, которые были бесплатными. Теперь проект в исходном виде уже не существует. Для адаптации визарда для Delphi 2010 мне помогли:
UPD: несколько замечаний
Замечание 1
Хочу сделать акцент на следующую вещь. IDE Delphi – это обычное приложение… написанное на Delphi. Такое же как и ваше. Внутри оно работает по тем же законам, как и ваше. И наоборот.
Когда в редакторе Delphi открывается pas-файл, IDE с ним не делает ничего хитрого, просто открывает текст в редакторе. Тоже самое происходит, если вы файл откроете в блокноте. Подсветка синтаксиса, сворачивание кода и т.п. – это рюшечки. И компилятор работает только лишь с исходными текстами, поэтому для компиляции достаточно только pas-файлов. dfm как бы и не нужны.
Гораздо интереснее ситуация, когда dfm-файл открывается в визуальном редакторе. Вот тут IDE делает примерно тоже, что и ваше приложение при создании экземпляра формы/фреймы. А именно: по dfm-описанию воссоздаётся экземпляр класса формы/фреймы со всеми дочерними контролами. И этот экземпляр создаётся не в воздухе, а как часть IDE. Отличие заключается лишь в том, что у формы/фреймы и всех её компонентов выставлено csDesigning в ComponentState, поэтому взаимодействие с формой отличается от Вашего приложения. А дальше в дизайнере есть Object Inspector – он работает со свойствами созданного экземпляра через RTTI. А откуда берётся RTTI? Ведь не из исходников же, загруженных параллельно в редакторе? Иначе бы пришлось их компилировать на лету.
RTTI берётся из bpl-файла, который на самом деле является обычной dll-кой (скомпилированным бинарником) и “цепляется” к IDE динамически. bpl – это пакет, который внутри себя может содержать что угодно. Принято туда помещать компоненты и визарды для того, чтобы визуальный дизайнер IDE мог корректно отображать компоненты и их свойства. Это делается для удобства программиста.
Ещё раз: файла BaseForms.pas достаточно для сборки Вашего приложения. BaseForms не завязан на код визарда. Визард же нужен для дизайнера, поэтому код визарда находится в отдельном pas-файле и компилируется в отдельный bpl-пакет, о котором ваше приложение может и не знать и прекрасно работать.
Замечание 2
На момент написания этой заметки код BaseForms.pas не содержит полезного кода. Поэтому никаких “плюшек” от использования визарда (ну кроме автоматической прописки BaseForms в uses) на данном этапе мы не получим. Визард нам пригодится, когда мы в базовые форму и фрейму будем добавлять published-свойства и события.
7 коммент.:
Николай, надеюсь моя статья подвигла двигаться немного дальше.
А я у себя в проекте не стал заморачиваться с созданием визарда. Поэтому, просто создаю обычную форму TForm, а потом в редакторе кода заменяю предка у новой формы на своего TMyBaseForm. Этот способ работает, если TMyBaseForm - это просто класс, без DFM-ки. В случае с DFM-кой, наверное лучше формы создавать с помощью визарда (или вручную править ещё и DFM-ку, заменяя object на inherited).
Велосипедостроители :)
File -> New -> Other -> Inheritable Items
Разве первым словом в произведенной визардом форме/фрейме не должно быть inherited вместо object?
Интересно получается, когда делаешь наследование от своих форм (с dfm), то не указание inherited в dfm наследника приводит к ошибкам. Что обычно случается, когда при переделки формы под наследование от базовой формы забывают переделать dfm. http://wiert.me/2009/07/22/delphi-frames-as-visual-components-changing-your-inheritance/
А здесь как-будто и нет той самой dfm, от которой происходит наследование. В общем я запутался :) помогите понять.
Оказывается есть перевод http://delphi2010.ru/?p=71
Спасибо за комментарии.
Сергей, на статью я вышел поиском и, пробежав беглым взглядом, сразу понял, каких интерфейсов не хватает. За это и спасибо. И за вторую ссылку - по ней я пытался запустить свой пакет из-под отладчика (но с наскоку не получилось, под отладчиком Delphi выпала в бесконечный AV, видимо из-за каких-то других установленных экспертов).
Aleksey, да, так можно. Визард необходим лишь для того, чтобы зарегистрировать в IDE published-свойства и обработчики событий базовой формы/фреймы. Чтобы дизайнер знал об этих новых свойствах и мог их писать в dfm.
Анонимный, Inheritable Items - это немного другое. Я этим тоже пользуюсь, планирую позже об этом подробнее написать.
IL,
> Разве первым словом в произведенной визардом форме/фрейме не должно быть inherited вместо object?
Нет. Если Вы заметили, в моих базовых формах/фреймах нет dfm-файла. Это просто наследники от TForm/TFrame, причём оба они находятся в одном pas-файле. inherited - это слово для обработчика dfm-файла, которое говорит, что необходимо сначала загрузить часть dfm-файла родительского класса, а потом уже грузить текущий файл. В нашем случае родительские классы не имеют dfm, поэтому произведённые визардом модули подобны стандартным Form и Frame.
> помогите понять.
Чуть попозже будет UPDATE этой заметки, потом будут следующие. Я планирую показать, когда будет object, а когда inherited. На самом деле там всё просто...
Маленькая просьба. Тема безусловно интересная, однако хочется чтоб новый материал появлялся не ввиде updat-ов а ввиде новых постов. очень неудобно отслеживать изменения в материалах. Delphifeeds к сожалению не может информировать об обновленных ранее постах. Ну и в конце все это можно объеденить в одну большую статью.
Отправить комментарий