Суббота, 18.05.2024
Королевство Delphi
Главное меню
Статьи
Наш опрос
Нашли свой исходник?
Всего ответов: 94
Статистика
Онлайн всего: 1
Гостей: 1
Пользователей: 0
Форма входа
Главная » Статьи » Система » Компоненты

Создание компонентов Delphi

Введение в создание компонентов Delphi

Пакеты компонентов

Шаблоны компонентов

Создание простейшего компонента

Создание сложного компонента

Добавление свойств и методов

Скрытие свойств из инспектора объектов

Использование Hook-процедур для создания компонентов

Редакторы свойств и редакторы компонентов

Редакторы свойств

Редакторы компонентов

Класс TFiler и сохранение данных в ресурсах

Заключение

Введение в создание компонентов Delphi

При разработке приложений с помощью Borland Delphi создавать компоненты удобно по следующим причинам:

  1. Простота использования. Компонент помещается на форму, и для него необходимо устанавливать значения свойств и писать код обработчиков событий. Поэтому если в проекте какое-либо сочетание элементов управления и  обработчиков связанных с ними  событий встречается в двух местах, то имеет смысл подумать о создании соответствующего компонента. Если же сочетание элементов управления и  обработчиков связанных с ними  событий встречается более двух раз, то создание компонента гарантированно сэкономит усилия при разработке приложения.
  2. Простая организация групповой разработки проекта. При групповой разработке отдельные части проекта можно определить как компоненты и поручить эту работу разным программистам. Компоненты можно отладить отдельно от приложения,  что сделать достаточно легко.
  3. Простой и эффективный способ обмена кодом с другими программистами. Имеется немало сайтов, например http://www.torry.net/, где можно найти свободно распространяемые компоненты или приобрести их за символическую плату.

Пакеты компонентов

В Delphi компоненты хранятся в пакетах (packages). Список используемых пакетов компонентов можно вызвать с помощью пункта меню Component/Install Packages (правда, этот диалог почему-то имеет заголовок Project Options).

При помощи этого диалога можно добавить новый пакет (Add), удалить имеющийся (Remove). Удаление означает не физическое удаление файла с диска, а удаление ссылки из среды разработки на данный пакет. При добавлении нового пакета компоненты, хранящиеся в нем, появляются на палитре, а при удалении – наоборот, исчезают. Пакет можно не удалять, а «спрятать» его содержимое на этапе разработки посредством снятия отметки напротив имени пакета в списке. Можно также просмотреть компоненты и их пиктограммы (Components). И наконец, можно отредактировать добавленные пользователем пакеты (Edit) – пакеты, поставляемые вместе с Delphi, редактировать нельзя (кнопка Edit недоступна).

В данном диалоге можно указать, каким образом создавать проект: с использованием runtime-пакетов или без них. Отсюда ясно, что пакеты компонентов бывают двух типов: runtime package (пакет, работающий во время выполнения) и design-time package (пакет, используемый во время разработки). Все они представляют собой DLL (динамически загружаемые библиотеки).

Runtime-пакеты (расширение *.bpl) поставляются конечному пользователю вместе с проектом, если проект был скомпилирован с включенной опцией Build with runtime packages. Само приложение (*.exe или *.dll) в этом случае получается небольшим, но вместе с ним надо передавать довольно объемные *.bpl-файлы. Согласно оценкам специалистов поставка проекта с runtime-пакетами дает преимущество в объеме поставляемых файлов, если только он включает пять или более модулей (*.exe или *.dll), написанных на Delphi. При совместной работе этих модулей достигается экономия ресурсов операционной системы, поскольку один загруженный в ОЗУ пакет обслуживает несколько модулей.

Design-time-пакеты (расширение *.dcp) используются только на этапе разработки. Во время разработки они поддерживают создание компонентов на форме. В скомпилированный проект Delphi включает код не из пакета компонентов, а из *.dcu-файлов. Хотя *.dcp-файл генерируется из *.dcu-файла, их содержимое может не совпадать, если в *.pas-файл были внесены изменения и пакет не был перекомпилирован. Компиляция возможна только для пакетов, созданных программистами. Это достигается нажатием кнопки Edit в вышеупомянутом диалоге. После этого появляется форма, которая позволяет производить манипуляции с пакетом.

Пакет содержит две секции. В секции Contains  приведен список модулей, формирующих компоненты данного пакета (*.pas- и *.dcu-файлы) и их пиктограммы (*.dcr-файлы). Секция Required содержит ссылки на другие пакеты, необходимые для работы этих компонентов. Добавление нового компонента к пакету выполняется кнопкой Add, удаление имеющегося – кнопкой Remove. До тех пор пока пакет не будет скомпилирован нажатием кнопки Compile, все изменения, вносимые в пакет, не будут появляться в среде разработки. И наконец, команда Install доступна в том случае, когда содержимое пакета удалено из среды разработки посредством снятия отметки напротив имени пакета в предыдущем диалоге.

Команда Option позволяет выбрать для компиляции пакета опции, аналогичные опциям проекта. В них можно определить тип данного пакета: работающий во время выполнения, работающий во время разработки, или тот и другой одновременно (тип пакета по умолчанию). В опциях определяются каталоги, в которых следует искать необходимые модули и сохранять результаты компиляции. В них также определяются действия, необходимые для отладки: проверять или нет диапазон допустимых значений, как осуществлять оптимизацию, как обрабатывать ошибки ввода-вывода. И наконец, в опции может быть включена информация о версии пакета. Это очень важно, если приложение распространяется вместе с runtime-пакетами: при работе программы установки информация о версии позволит корректно заменить устаревшие версии пакетов, и наоборот, при попытке инсталлировать пакет более ранней версии, чем уже имеющийся на данном компьютере, последний не будет перезаписан.

Шаблоны компонентов

Delphi позволяет создавать простейшие составные компоненты из нескольких обычных компонентов, выбранных на форме во время разработки. Соответствующий эксперт  вызывается с помощью пункта меню Components/Create Component Template. Этот пункт меню доступен, если на форме выделен хотя бы один компонент. После его выбора появляется диалоговая панель Component Template Information.

В этом диалоге следует указать имя класса и имя страницы на палитре компонентов, куда следует поместить новый компонент. Если страница с данным именем отсутствует на палитре компонентов, то она будет создана. Можно также изменить предложенную пиктограмму нового компонента, загрузив подходящий *.bmp-файл.

При создании шаблона запоминаются как свойства, измененные программистом в инспекторе объектов, так и обработчики событий, связанные с выделенными элементами управления. При этом обработчики событий запоминаются полностью, без фильтрации обращений к другим (не выделенным на форме) компонентам, глобальным переменным, методам и т.д. Соответственно, если в другом проекте такие компоненты (переменные, методы) отсутствуют, то при попытке скомпилировать такой проект будет получено диагностическое сообщение Unknown Identifier.

Когда следует пользоваться шаблонами? Прежде всего, в случаях, если необходимо изменить какие-либо свойства, которые имеются по умолчанию в базовом классе. Например, в каком-либо приложении используется элемент управления для редактирования строки текста желтого цвета. Можно поместить компонент TEdit на форму, изменить свойство Color на желтый, отметить данный компонент и сохранить как шаблон. После этого можно обращаться к данному шаблону, и помещенный на форму компонент будет иметь желтый цвет. Однако не стоит злоупотреблять данной возможностью, ведь для элемента управления с измененным цветом будет создан новый класс и в памяти будут размножены все виртуальные методы. Это отрицательно скажется на ресурсах операционной системы.

Использовать шаблоны компонентов удобно также, когда необходимо перенести ряд компонентов вместе с обработчиками событий с одной формы на другую. Для этого все они выделяются, создается шаблон компонентов, который и помещается на новую форму. При этом будут перенесены не только сами компоненты, но и обработчики событий, чего нельзя достичь при вызове команд Copy/Paste – в последнем случае обработчики событий будут утеряны.

Компоненты, создаваемые при помощи команды Create Component Template, существенно отличаются от обычных компонентов, создаваемых стандартным способом (описанным ниже). Визуально главное различие заключается в следующем: если шаблон включает в себя несколько элементов управления, то, после того как такой компонент помещен на форму, можно выделить отдельный элемент управления и удалить его – при этом остальные сохранятся на форме. Для стандартных компонентов, если они включают в себя несколько элементов управления, невозможно выделить один из них и удалить –компонент выделяется и удаляется целиком.

Создание простейшего компонента

При написании нового компонента необходимо ясно представлять, что компонент создается для программистов, а не для конечных пользователей. При этом желательно, чтобы программист не вникал в детали реализации компонента, а просто пользовался экспонируемыми им свойствами и событиями. Это достигается очень тщательным тестированием. Новый компонент необходимо тестировать даже в ситуациях, для работы в которых он явно не предназначен.

Поставим задачу следующим образом. Необходимо создать кнопку, которая будет издавать писк при нажатии, и реализовать ее в виде компонента так, чтобы программист мог поместить ее на форму и воспользоваться ею. Вообще, при рассмотрении компонентов мы довольно часто будем пользоваться простейшими внешними эффектами: писк, вывод сообщения и т.д. При этом подразумевается, что в тех местах, где используются внешние эффекты, может быть помещен любой, достаточно сложный код. Просто в данный момент он нас не интересует.

Создание компонента начинается с выбора пункта меню Component/New components. После этого сразу же появляется диалог New Component.

В этом диалоге необходимо определить класс-предок, имя вновь создаваемого класса, страницу на палитре, куда будет помещен новый компонент, имя модуля, содержащего реализацию нового компонента, и путь к нему. Если новый компонент использует другие модули, путь к которым не описан, то их необходимо определить в поле Search Path.

Итак, первая (и, пожалуй, главная) задача – выбор класса-предка. В выпадающем списке в качестве класса-предка предлагаются все компоненты, имеющиеся на палитре, в том числе и те, которые не входят в стандартную поставку Delphi. Необходимо в качестве класса-предка выбрать класс, который максимально приближен по свойствам к создаваемому классу. Для нашей задачи можно, например, выбрать в качестве предка TWinControl, но в этом случае нам потребуется реализовывать все визуальные эффекты нажатия кнопки и т.д. Поэтому мы выбираем в качестве предка TButton.

Имя вновь создаваемого класса должно отражать содержание компонента и ни в коем случае не совпадать с именем уже зарегистрированного компонента! На этапе заполнения данного диалога имена на совпадения не проверяются – приключения, связанные с такой ошибкой, начнутся позже…

При выборе страницы необходимо знать, что если задать имя несуществующей страницы, то будет создана новая.

И наконец, при нажатии как кнопки Install, так и кнопки OK, будет создана заготовка для реализации нового компонента. Однако при нажатии кнопки Install заготовка будет помещена на палитру компонентов, а при нажатии кнопки OK – просто создана. Рекомендуется пользоваться кнопкой Install. После того как компонент будет инсталлирован, его можно поместить на форму. Теперь все изменения, вносимые в код реализации компонента, будут компилироваться вместе с проектом, и программист сразу же будет получать сообщения об ошибках. Если компонент не инсталлировать, то для поиска ошибок его необходимо компилировать через редактор пакетов (см. выше) нажатием кнопки Compile, что менее удобно.

Итак, после нажатия кнопки Install появляется еще один диалог, который позволяет определить пакет, куда будет помещен данный компонент.

В этом диалоге имеются две страницы, на первой из них можно выбрать один из существующих пакетов, а на второй – создать новый. Весьма желательно давать краткое текстовое описание пакета, именно оно будет показываться в диалоге, вызываемом по команде Component/Install packages (см. выше). После выбора пакета и нажатия клавиши OK  вызывается редактор пакета, куда автоматически помещается вновь созданный модуль реализации нового компонента. Полезно не закрывать его, а сдвинуть в один из углов экрана, чтобы он мог быть активирован нажатием клавиши мыши.

Одновременно в редакторе кода будет создана «заготовка» для описания нового компонента:

unit ButtonBeep; 
 
interface 
uses 
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
 StdCtrls; 
 
type 
 TButtonBeep = class(TButton) 
 private 
 { Private declarations } 
 protected 
 { Protected declarations } 
 public 
 { Public declarations } 
 published 
 { Published declarations } 
 end; 
 
procedure Register; 
 
implementation 
procedure Register; 
begin 
 RegisterComponents('Samples', [TButtonBeep]); 
end; 
 
end. 

В самом новом классе объявлены четыре секции, значение которых детально описано в разделе «Область видимости переменных и методов» предыдущей статьи данного цикла (КомпьютерПресс № 1'2001). Кроме того, в новом классе определена процедура Register, которая вызывается средой разработки Delphi при инсталляции данного модуля как компонента. Она содержит имя страницы на палитре, куда помещается данный компонент, и в квадратных скобках – имя класса. Вообще, в качестве параметра метод Register принимает массив типов классов, ведь в одном модуле может быть реализовано несколько компонентов. Поэтому они отделяются друг от друга запятой, например:

procedure Register; 
begin 
 RegisterComponents('Samples', [TButtonBeep,TButtonColor,TMyEdit]); 
end; 

Продолжим решение поставленной задачи – создание кнопки, которая издает писк. Поступим сначала тривиально (но как выяснится потом, неверно) – назначим обработчик события OnClick в конструкторе кнопки. Для этого в секции private определим заголовок нового метода BtClick(Sender:TObject) и реализуем его в секции реализации:

procedure TButtonBeep.BtClick(Sender:TObject); 
begin 
 Beep; 
end; 

Далее перепишем конструктор кнопки. Для этого определим в секции public заголовок конструктора:

constructor Create(AOwner:TComponent); override; 

с обязательной директивой override! Реализуем его в секции реализации:

constructor TButtonBeep.Create(AOwner:TComponent); 
begin 
 inherited Create(AOwner); 
 OnClick:=BtClick; 
end; 

После этого скомпилируем компонент. Поставим со страницы Samples кнопку на форму и запустим проект на выполнение. Можно убедиться, что кнопка при нажатии пищит!

Теперь вновь перейдем в среду разработки и назначим обработчик события OnClick в инспекторе объектов. В обработчике события выведем текст в заголовок формы:

procedure TForm1.ButtonBeep1Click(Sender:TObject); 
begin 
 Caption:='Test'; 
end; 

Запустим проект на выполнение и попробуем нажать на кнопку. Заголовок формы меняется,  но кнопка пищать перестала! Ошибка заключается в том, что на одно событие кнопки OnClick мы попытались определить два обработчика: один внутри компонента BtClick, а другой назначили с помощью инспектора объектов. После отработки конструктора TButtonBeep у нас была ссылка на первый обработчик BtClick. Затем происходит загрузка ресурсов, обработчику события OnClick назначается метод ButtonBeep1Click. При этом ссылка на первый обработчик — BtClick — безвозвратно теряется.

Таким образом, при написании новых компонентов всегда следует учитывать возможность изменения свойств и обработчиков событий с помощью инспектора объектов. Если какое-либо свойство (событие) не должно меняться, его не следует отображать в инспекторе объектов. А если оно уже отображается, его следует скрыть (об этом мы поговорим позже). Программист имеет полное право изменить любые свойства в инспекторе объектов, и если после этого компонент перестает работать, в этом виноват разработчик компонента, но ни в коем случае не программист, его использующий.

Как же все-таки корректно решить данную задачу? Один из способов создания компонентов — переписывание уже имеющихся методов. При рассмотрении файла StdCtrls.pas, где реализованы исходные коды для компонента TButton, можно отметить в нем наличие динамического метода Click, который можно переписать. Поэтому вновь возвращаемся к исходному коду, созданному экспертом Delphi при создании компонента (убираем конструктор и метод BtClick). Затем в секции public определяем заголовок метода:

procedure Click; override; 

и приводим реализацию метода:

procedure TButtonBeep.Click; 
begin 
 inherited Click; 
 beep; 
end; 

Можно убедиться, что кнопка при нажатии издает писк. Кроме того, при назначении обработчика событий в инспекторе объектов этот обработчик выполняется и писк не исчезает! Компонент реализован корректно.

На данном примере полезно проанализировать возможные ошибки при написании кода:

  1. Забытая директива override при определении заголовка метода Click. Кнопка перестает пищать, следовательно, метод Click не вызывается.
  2. Забытый вызов метода-предка (inherited Click) в реализации процедуры Click. Кнопка продолжает пищать при нажатии, но код в назначенном в инспекторе объектов обработчике событий не выполняется. Следовательно, метод Click класса TButton вызывает событие OnClick.

Теперь поменяем пиктограмму компонента TButtonBeep на палитре. По умолчанию для нового компонента используется  пиктограмма компонента-предка. Для этого вызовем редактор Image Editor командой Tools/Image Editor. В редакторе вызовем команду File/New/Component Resource File (*.dcr). После команды Resource/New/Bitmap появится диалог, в котором предлагается размер пиктограммы 32х32. Эти размеры по умолчанию следует изменить на 24х24 – такой размер обязаны иметь пиктограммы компонентов! После нажатия кнопки OK следует нарисовать какое-либо изображение при помощи стандартных инструментов, похожих на инструменты редактора Paint. Помните, что цвет левого нижнего пиксела является цветом маски – данный цвет будет «прозрачным».

После этого необходимо переопределить имя ресурса с пиктограммой, по умолчанию его имя ‑ Bitmap1. Новое имя ресурса обязано совпадать с именем класса – в нашем случае TButtonBeep.

Теперь необходимо сохранить файл с пиктограммой в том же самом каталоге, где находится модуль, содержащий процедуру Register для данного компонента, и с тем же самым именем, что и имя модуля. Только вот расширение у файла будет не *.pas, а *.dcr. Файл с пиктограммой компонента готов. Однако если мы посмотрим на палитру компонентов, то увидим, что там по-прежнему сохраняется старая пиктограмма. Если перезагрузить Delphi или даже операционную систему, старая пиктограмма по-прежнему останется на палитре. Для того чтобы поменять пиктограмму, необходима повторная регистрация компонента. Для этого необходимо:

  • активизировать окно с пакетом, содержащим данный компонент (в нашем случае – TestComp), если он не был закрыт при выполнении упражнений. Если был закрыт ‑ то вызвать его с помощью пункта меню Component/Install Packages, затем выбрать пакет (в нашем случае – Test Component Creation) и нажать кнопку Edit;
  • нажать кнопку Remove. В возникающем диалоге отметить модуль, который необходимо удалить, в нашем случае – ButtonBeep.pas  После этого следует нажать кнопку OK;
  • перекомпилировать пакет компонентов. Если все было выполнено правильно, то возникнет сообщение об обновлении палитры компонентов.
  • нажмите кнопку Add и в диалоге инсталляции нового компонента определите путь к модулю ButtonBeep. После нажатия кнопки OK в окне‑редакторе пакета появятся как модуль *.pas, так и ссылка на файл *.dcr.
  • нажмите кнопку Compile. После компиляции пакета появится сообщение, что компонент TButtonBeep был инсталлирован в палитре компонентов. Теперь пиктограмма компонента соответствует нарисованной.

Данный пример следует рассматривать как тестовое упражнение. Перед написанием нового компонента необходимо посмотреть, существуют ли аналогичные среди свободно распространяемых компонентов. Имеются практически любые кнопки: прозрачные, убегающие, круглые, цветные и т.д. Примерно так же обстоит дело с другими компонентами – потомками одного класса. Поэтому чаще всего приходится реализовывать компоненты, состоящие из нескольких элементов управления.

Таким образом, в данном примере мы изучили применение переписывания методов для создания новых компонентов.

Создание сложного компонента

Предположим, в приложении необходимо ввести список фамилий клиентов. В том же самом приложении потребуется и ввод списка телефонов. Ввод списка ‑ довольно распространенная операция, поэтому следует подумать о реализации его в виде компонента.

Для ввода нового элемента в список потребуется редактор –  компонент TEdit. Далее пользователь должен иметь возможность просмотреть список – понадобится компонент TListBox. Кроме того, потребуются команды для занесения текущего значения из TEdit в список, редактирование выбранного элемента списка и его удаление. Проще всего эти команды реализовать с помощью кнопок. Для упрощения задачи поместим на форму одну кнопку, при нажатии которой будем добавлять содержимое компонента TEdit в список.

Итак, мы должны создать новый компонент, который включал бы в себя TEdit, TListBox и TButton. Как всегда, начнем его создание с команды Component/New Component. После этого появляется диалог, в котором следует определить класс-предок, имя класса, имя модуля. С именем класса и именем модуля никаких сложностей не возникает, а вот имя класса-предка неясно. У нас имеются три элемента управления. Общим классом-предком для них является TWinControl. Но если в качестве класса-предка выбрать его, нас ожидает очень длительная и утомительная реализация кода TButton, TEdit и TListBox. В таких случаях необходимо в качестве класса-предка выбирать компонент, способный быть «папой» по отношению к другим компонентам. Среди стандартных компонентов, распространяемых вместе с Delphi, таких три: TPanel, TGroupBox, TScrollBox. Выберем в качестве класса-предка панель, но не сам компонент TPanel, а класс TCustomPanel. Преимущества выбора TCustomPanel перед TPanel мы обсудим ниже.

Назовем новый класс именем TListAdd и нажмем кнопку Install. После выбора пакета компонент будет установлен в палитру, откуда его можно поместить на форму вновь созданного приложения. Это удобно, поскольку при компиляции проекта модуль компонента также будет компилироваться и при наличии ошибок компилятор выдаст сообщение.

Было бы удобно поместить наши элементы управления на какую-либо форму и затем создать из них компонент. В стандартной поставке Delphi такой эксперт отсутствует. Поэтому необходимо будет создавать компоненты самим и размещать их на панели. Создание элементов управления – TButton, TEdit и TListBox ‑ разумно выполнить в конструкторе TCustomPanel, для чего, очевидно, необходимо его переписать. Разместим пока элементы управления в квадрате 100х100. Координаты их также необходимо определять в конструкторе. При этом следует иметь в виду, что после отработки конструктора любого элемента управления он еще не имеет родителя, то есть не знает, относительно какого окна ему надо отсчитывать координаты левого верхнего угла. Попытка изменить координаты дочернего окна, у которого отсутствует родитель, немедленно приведет к генерации исключения. Поэтому первым оператором после вызова конструктора элемента управления будет назначение ему родителя, в качестве которого выберем TCustomPanel. Ее же сделаем и их владельцем, в этом случае не понадобится переписывать деструктор.

Итак, в секции uses добавляем модуль StdCtrls, где находятся описания классов TEdit, TButton и TListBox, а в секции private определяем три переменные:

private 
 FEdit:TEdit; 
 FListBox:TListBox; 
 FButton:TButton; 

В секции public объявляем заголовок конструктора с обязательной директивой override:

constructor Create(AOwner:TComponent); override; 

Реализуем конструктор в секции реализации:

constructor TListAdd.Create(AOwner:TComponent); 
begin 
 inherited Create(AOwner); 
 FButton:=TButton.Create(Self); 
 FButton.Parent:=Self; 
 FButton.Left:=5; 
 FButton.Top:=5; 
 FButton.Width:=40; 
 FButton.Height:=25; 
 
 FEdit:=TEdit.Create(Self); 
 FEdit.Parent:=Self; 
 FEdit.Left:=50; 
 FEdit.Top:=5; 
 FEdit.Width:=45; 
 FEdit.Height:=25; 
 
 FListBox:=TListBox.Create(Self); 
 FListBox.Parent:=Self; 
 FListBox.Left:=5; 
 FListBox.Top:=35; 
 FListBox.Width:=90; 
 FListBox.Height:=60; 
end; 

Еще раз следует подчеркнуть, что деструктор в данном случае переписывать не надо: панель является владельцем всех элементов управления,  и при вызове ее деструктора деструкторы элементов управления будут вызваны автоматически.

После перекомпиляции компонента при помощи редактора пакетов изменения в компоненте уже можно увидеть визуально, на этапе разработки.

Первый недостаток, который бросается в глаза, – неадекватное поведение элементов управления при масштабировании компонента. При изменении его размеров размеры и положение элементов не меняются. Кроме того, компонент можно сделать маленьким, так что три элемента управления не уместятся на нем. И наконец, при установке компонента на форму с палитры компонентов простым щелчком мыши его размеры также оставляют желать лучшего.

Для начала исправим размеры компонентов по умолчанию, то есть те, которые ему присваиваются автоматически при щелчке мышью на палитре компонентов с последующим щелчком на форме. Для этого в конструкторе просто следует указать новые размеры панели:

Width:=100; 
Height:=100; 

Затем требуется улучшить поведение компонента при масштабировании. Для этого необходимо получить сообщение о том, что размеры изменились. При изменении размера какого-либо элемента управления система посылает ему сообщение WM_SIZE. Это сообщение необходимо перехватить. Для этого в секции private опишем заголовок перехватчика сообщения:

 procedure WMSize(var Message:Tmessage); message WM_SIZE; 

и в секции реализации реализуем его обработчик:

procedure TListAdd.WMSize(var Message:TMessage); 
begin 
 inherited; 
 if Width<100 then Width:=100; 
 if Height<100 then Height:=100; 
 FEdit.Width:=Width-55; 
 FListBox.Width:=Width-10; 
 FListBox.Height:=Height-40; 
end; 

Первый оператор – вызов обработчика WM_SIZE по умолчанию (inherited). После его вызова в свойствах Width и Height будут находиться новая ширина и высота панели. После этого определяются минимальные размеры компонента, в данном случае ‑ 100х100. Если размер по горизонтали или вертикали меньше минимального, то ему присваивается минимальное значение. Затем происходит масштабирование элементов управления так, чтобы они заполняли всю панель с небольшими отступами. Скомпилировав компонент через редактор пакетов, можно уже на этапе разработки отметить корректное поведение элементов управления на панели при масштабировании, а также то, что размер компонента нельзя сделать менее чем 100х100.

Теперь полезно будет запустить весь проект на выполнение, попробовать вводить данные в однострочный редактор текста и нажимать кнопку. При этом ничего в список не добавляется. И не удивительно, что нигде в нашем компоненте не указано, что надо делать при нажатии кнопки. Для того чтобы сделать обработчик события, связанного с нажатием кнопки, можно поступить, как при написании компонента TbuttonBeep, то есть определить новый класс ‑ потомок TButton и переписать метод Click. Однако определение нового класса требует системных ресурсов (размножаются виртуальные методы). Если мы отметим компонент на форме и посмотрим на инспектор объектов, то обнаружим, что компонент TlistAdd экспонирует немного свойств и ни одного события, в том числе ни одного обработчика события кнопки OnClick. Поэтому то, что в прошлой главе мы отвергли как неправильный метод,– переопределение обработчика кнопки OnClick в данном случае применимо, поскольку программист не может в инспекторе объектов назначить новый обработчик. Итак, в секции private описываем заголовок нового метода:

 procedure BtClick(Sender:TObject); 

В реализации конструктора TListAdd присваиваем этот обработчик обработчику событий FButton.OnClick:

 FButton.OnClick:=BtClick; 

И наконец, реализуем метод BtClick:

procedure TListAdd.BtClick(Sender:TObject); 
begin 
 if length(FEdit.Text)>0 then begin 
 FListBox.Items.Add(FEdit.Text); 
 FEdit.Text:=''; 
 FEdit.SetFocus; 
 end; 
end; 

Сначала проверим, не пуст ли однострочный редактор: мы не будем добавлять в список пустые строки. Затем переносим содержимое редактора в список (FListBox.Items.Add(FEdit.Text);) и подготавливаем редактор к вводу следующего значения – а именно, очищаем его от текста (который уже перенесен в список) и переносим на него фокус ввода. Теперь после компиляции и запуска приложения можно убедиться, что оно работает корректно – при нажатии кнопки содержимое редактора переносится в список.

Добавление свойств и методов

Если рядом с компонентом TListAdd поместить компонент TPanel и сравнить показываемое в инспекторе объектов, то можно отметить, что для панели экспонируется достаточно большое количество свойств и событий, а для TListAdd – только несколько свойств. Между тем класс TCustomPanel является предком обоих компонентов. Для того чтобы понять причину, откроем модуль ExtCtrls.pas и рассмотрим разницу между классами TCustomPanel и TPanel. Можно отметить, что все методы и переменные, которые обеспечивают функциональность панели, определены на уровне класса TCustomPanel. В нем же определены и свойства, которые затем отображаются в инспекторе объектов для TPanel, только эти свойства определены в секции Protected. Реализация же класса TPanel чрезвычайно проста: в качестве предка определяется TCustomPanel, и свойства этого класса редекларируются, но уже в секции published. Становится понятно, что необходимо сделать в классе TListAdd для появления в инспекторе объектов свойств и методов класса TcustomPanel, а именно редекларировать свойства. В секции published класса TListAdd запишем:

 property Align; 
 property OnMouseDown; 

При редекларации свойства не требуется указывать его тип и ссылаться на переменные или методы чтения или записи свойства. После компиляции компонента через редактор пакетов в инспекторе объектов можно наблюдать появление свойства Align и события OnMouseDown. Таким образом, для потомков TCustom…-классов программист имеет возможность выбирать, какие свойства и события следует отображать в инспекторе объектов, а какие нет. Именно по этой причине TCustom…-классы рекомендуется использовать в качестве предков для создания компонентов.

Теперь рассмотрим, как можно ввести новое свойство (то, что мы делали выше –редекларация уже имеющихся свойств). В качестве подходящего свойства для отображения в инспекторе объектов можно использовать текст на кнопке: пусть программист, пользующийся компонентом TListAdd, самостоятельно меняет текст на этапе разработки. Попытка ввести новое свойство (назовем его BtCaption) с помощью объявления:

property BtCaption:string read FButton.Caption write FButton.Caption; 

приводит к ошибке при попытке компиляции компонента. Поэтому определим заголовки двух методов в секции private:

function GetBtCaption:string; 
procedure SetBtCaption(const Value:string); 

В секции published объявим свойство BtCaption:

property BtCaption:string read GetBtCaption write SetBtCaption; 

И наконец, реализуем два объявленных метода в секции реализации:

function TListAdd.GetBtCaption:string; 
begin 
 Result:=FButton.Caption; 
end; 
 
procedure TListAdd.SetBtCaption(const Value:string); 
begin 
 FButton.Caption:=Value; 
end; 

После компиляции компонента с помощью редактора пакетов в инспекторе объектов появляется новое свойство. Изменение значения этого свойства отражается прямо на этапе разработки.

Продолжение...

Получить ссылку на материал

Категория: Компоненты | Добавил: Барон (14.12.2011)
Просмотров: 6397 | Теги: компонент, delphi | Рейтинг: 0.0/0
[ Пожертвования для сайта ] [ Пожаловаться на материал ]

Если вам помог материал сайта кликните по оплаченной рекламе размещенной в центре

Поиск
Категории раздела
ActiveX [10]
CORBA и COM [16]
Kol и MCK [23]
WinAPI [28]
Компоненты [27]
Работа с Bluetooth [4]
Железо [8]
Текст [18]
Разное [98]
Королевство Delphi © 2010-2024
Яндекс цитирования