суббота, 19 января 2013 г.

Создание базовой формы

Данная заметка является продолжением предыдущей.

Вступительное слово

Я решил не писать отдельную статью. Вместо этого я буду проделывать задуманную работу по маленьким этапам, каждый из которых выкладывать в репозиторий на 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:. Должно получиться что-то типа такого:

image

После этого, нужно создать новый проект File \ New \ VCL Forms Application - Delphi, либо открыть существующий.

В итоге, открыв пункт меню File \ New \ Other… в категории Delphi Files Вы увидите такую картинку:

image

Что с этим делать?

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 коммент.:

Сергей комментирует...

Николай, надеюсь моя статья подвигла двигаться немного дальше.

Aleksey Timohin комментирует...

А я у себя в проекте не стал заморачиваться с созданием визарда. Поэтому, просто создаю обычную форму TForm, а потом в редакторе кода заменяю предка у новой формы на своего TMyBaseForm. Этот способ работает, если TMyBaseForm - это просто класс, без DFM-ки. В случае с DFM-кой, наверное лучше формы создавать с помощью визарда (или вручную править ещё и DFM-ку, заменяя object на inherited).

Анонимный комментирует...

Велосипедостроители :)
File -> New -> Other -> Inheritable Items

IL комментирует...

Разве первым словом в произведенной визардом форме/фрейме не должно быть inherited вместо object?
Интересно получается, когда делаешь наследование от своих форм (с dfm), то не указание inherited в dfm наследника приводит к ошибкам. Что обычно случается, когда при переделки формы под наследование от базовой формы забывают переделать dfm. http://wiert.me/2009/07/22/delphi-frames-as-visual-components-changing-your-inheritance/
А здесь как-будто и нет той самой dfm, от которой происходит наследование. В общем я запутался :) помогите понять.

IL комментирует...

Оказывается есть перевод 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 к сожалению не может информировать об обновленных ранее постах. Ну и в конце все это можно объеденить в одну большую статью.

Отправить комментарий

.

.