6.6 Иерархия классов VCL Ранее в этой главе мы ознакомились с характеристиками четырех базисных типов компонент: стандартные, оригинальные, графические и невидимые. Теперь покажем место и назначение этих типов в иерархии объектов. Рис. 6.7 показывает ключевые классы в иерархической структуре, от которых произведены все компоненты VCL. Каждый объект представляет некоторый набор методов, событий и свойств и имеет специальное назначение. TObiect Рис. 6.7. Иерархия ключевых базовых классов VCL. Подобно тому как TObject является базовым классом для всех порождаемых классов, TComponent является базовым классом для всех порождаемых компонент. Невидимые компоненты произведены от класса TComponent. Графические компоненты, не ассоциированные с оконными элементами управления, произведены от класса TGraphicControl. Являясь оконными элементами, компоненты стандартного управления произведены непосредственно от класса TWinControl, а оригинальные компоненты — косвенно от класса TCustomControl, восходящего к TWinControl. Именно на уровне TWinControl и вводится "оконный дескриптор" (window handle). Рис. 6.8 продолжает иерархическую структурную схему компонентных классов VCL. Puc. 6.8. Дерево производных компонент от TCustomControl и TWinConlrol. 6.6.1 TObject TObject является базовым классом для всех прочих порождаемых классов. TObject инкапсулирует общее для всех объектов системы C++Builder функциональное поведение, обусловленное методами, которые обеспечивают: • Способность конструктора создавать, а деструктора разрушать объект-экземпляр класса в динамической памяти. Конструктор TObject возвращает указатель на создаваемый объект. • Информацию RTTI об имени, типе производного объекта и его свойствах, которые объявлены как _published. • Поддержку обработки сообщений. Большинство этих методов предназначены для внутреннего использования средой C++Builder, поэтому не следует прямо обращаться к ним из вашей программы. Часть методов TObject объявлены как статические (с ключевым словом static). Это означает, что вам не нужно создавать экземпляр данного класса для того, чтобы обратиться к его статическим методам. Все компоненты должны порождаться непосредственно от класса TComponent или от его потомков. TComponent, будучи в свою очередь потомком TObject, наследует его члены данных, методы и свойства. Используйте TObject для объявления простых объектов, которые не являются компонентами и не нуждаются в поточности и присваивании. Среди полезных не компонентных классов отметим TStringList, TIniFile и TPrinter. 6.6.2 TPersistent Класс TPersistent непосредственно произведен от TObject. Этот абстрактный класс не определяет никаких специальных свойств или событий, однако его производные приобретают особые способности присваивания и поточности. TPersistent определяет ряд поточных методов, используемых разработчиками компонент, которые могут быть перегружены производными компонентами: • Assign позволяет присваивать значения свойствам. • AssignTo позволяет присваивать содержимое одного объекта другому (например, как делает это производный от TPersistent класс TClipboard). • DefineProperties позволяет определить процедуру загрузки и сохранения в потоке особых дополнительных свойств. По умолчанию сохраняются только свойства, объявленные как _published. 6.6.3 TComponent Класс TComponent непосредственно произведен от TPersistent. Как уже было сказано, все компоненты являются производными от TComponent и могут находится в его владении. TComponent инкапсулирует общее для всех компонент функциональное поведение, обусловленное свойствами и методами, которые обеспечивают: • Перенос на форму из Палитры компонент и манипуляции в окне Редактора форм. • Способность владения и обслуживания других компонент. • Специальные характеристики поточности, с которыми может манипулировать Инспектор объектов на этапе проектирования. • Возможность манипулирования некоторыми невидимыми компонентами на стадии проектирования. Класс TComponent определяет ряд свойств, которые придают объекту особую функциональность:
Класс TComponent определяет ряд методов, которые придают объекту право владения другими компонентами и возможность доступа к ним посредством Инспектора объектов: • Destroying и DestroyComponents устанавливают атрибуты данной компоненты и компонент, которыми она владеет, в состояние, указывающее на то, что они подлежат уничтожению. • HasParent возвращает булево значение, указывающее на наличие родителя компоненты. Обращаться к этому методу следует до ссылок к родителю данной компоненты. Отметим, что наличие владельца компоненты не идентифицируется. • InsertComponent добавляет компоненту, передаваемую в качестве параметра, к перечню компонент, которыми владеет данная компонента, а RemoveComponent удаляет компоненту из этого перечня. • FindComponent возвращает указатель экземпляра компоненты, о которой известно только имя, но неизвестна ссылка на владельца. Допустим, что форма содержит экземпляр компоненты TEdit с именем Editl. Чтобы получить указатель на экземпляр Editl и адресовать его текст, используйте следующий код: void_fastcall TFormI::ButtonlClick(TObject *Sender) He создавайте экземпляров класса TComponent. Используйте TComponent в качестве базового класса при создании невидимых компонент. 6.6.4 TControl Класс TControl определяет общие для видимых компонент члены данных, методы и события. Поскольку элементы TControl обладают способностью отображать себя, некоторые его свойства оперируют с положением, размером и видом объекта (Top, Left, Width, Height и Cursor, Hint), а другие свойства относятся к параметрам области клиента (ClientRect, ClientWidth и ClientHeight). TControl также вводит свойства, устанавливающие видимость, доступность, цвет и шрифт элементов управления (Visible, Enabled, Color и Font). Свойства Text и Caption обеспечивают установку редактируемых текстов и названий. Наличие свойства Parent (Родитель), содержащего соответствующую ссылку, обусловлено возможностью класса TControl иметь родителя. Этот родитель может быть производным от TWinControl, поскольку родители обязаны быть оконными элементами управления. TControl содержит ряд событий, возникающих при манипуляциях мышью над, видимыми элементами управления (OnClick, OnDblClick, OnMouseDowit, OnMouseMove, OnMouseUp, OnDragOver, OnDragDrop и OnEndDrag). Поскольку TControl редко используется непосредственно, его события большинство свойств объявлены в секции protected. Разработчики производных компонент могут, таким образом, выбирать, какие свойства и события перенести в секцию public или _published, расширяя тем самым права доступа. Большинство компонент являются производными от TWinControl или TGraphicControl. Эти базовые классы рассматриваются в следующих параграфах. 6.6.5 TWinControl Класс TWinControl инкапсулирует оконные элементы управления с дескрипторами. Некоторые производные от TWinControl (компоненты TEdit, TListBox и TComboBox) инкапсулируют стандартные элементы управления Windows — поля редактирования, простые и комбинированные списки и т.д. Поэтому вам не придется манипулировать с ними посредством стандартных функций Windows API, a пользоваться свойствами и методами, предоставляемыми самими компонентами. Производные компоненты от TWinControl обладают тремя основными характеристиками: они имеют оконные дескрипторы, способны принимать фокус ввода и могут являться родителями других элементов управления. Поэтому многие свойства TWinControl предназначены для изменения фокуса, обслуживания событий клавиатуры и отображения потомков компоненты:
Методы TWinControl главным образом ориентированы на разработчиков компонент и предназначены для управления фокусом, получения статусной информации, диспетчеризации сообщений и позиционирования: • Broadcast используется для рассылки сообщений всем потомкам TWinControl. • CanFocus возвращает булево значение, которое определяет, может ли TWinControl принять фокус ввода. Например, компонента не сможет принять фокус, если ее свойство Visible имеет значение false. • ContainsControl определяет, содержится ли данный элемент управления внутри класса TWinControl. Этот метод не сообщает о том, является ли данный элемент потомком по отношению к TWinControl. Например, внешний класс TWinControl может быть родителем другого элемента, и эта родительская преемственность может продолжаться далее. Однако, все внутренние элементы содержатся во внешнем классе TWinControl. • ControlAtPos возвращает ссылку на потомка, если элемент управления заключен в заданных координатах области клиента родителя. Таким образом можно найти относительное положение потомка по отношению к родителю. • DisableAlign и EnableAlign используются для временного запрещения или разрешения выравнивания компонент внутри TWinControl. • Focused возвращает значение true, если TWinControl находится в фокусе ввода, т.е. является активным элементом формы, на которой он размещен. • HandleAllocated возвращает значение true, если элемент управления имеет оконный дескриптор. HandleNeeded создает новый дескриптор, если он еще не был создан. Аналогичное действие выполняется автоматически при прямом обращении к свойству Handle. • InsertControl добавляет элемент управления к свойству Controls (типа массив), делая TWinControl своим родителем. Лучший способ добавить потомка во время работы программы — просто присвоить ссылку на родителя свойству Parent. RemoveControl удаляет элемент управления из массщ Controls. • Invalidate и Repaint выполняют перерисовку компоненты. Мето; Repaint обрабатывает сообщение WM_PAINT, обращается к метол Update, который в свою очередь вызывает функцию Windows АPI UpdateWindow. PaintTo может использоваться для перерисовки содержимого TWinControl в область (device context) другого элемента управления. ReAlign вызывает повтор выравнивания компонент внутри TWinControl. ScaleBy используется для масштабирования TWinControl в заданном процентном отношении к исходному размеру. ScrollBy можно использовать, если вам не нравится логика прокрутки TWinControl, принятая по умолчанию. • SetBounds устанавливает свойства границ компоненты (Left, Top, Width, Height) для TWinControl. Прямое изменение каждого из указанных свойств менее эффективно, поскольку всякий раз сопряжено с перерисовкой SetFocus активизирует TWinControl. Другие методы, применяемые разработчиками компонент, предназначены для создания и уничтожения инкапсулированных TWinControl окон и их дескрипторов: . • CreateWnd создает оконный элемент управления, инкапсулированны TWinControl, посредством последовательного обращения к CreateParams и CreateWindowHandle. • CreateParams инициирует начальные значения всех оконных параметров Перегрузка этого метода позволяет менять оконные параметры, установлен ные по умолчанию. • CreateWindowHandle создает оконный дескриптор путем обращения функции Windows API Create WindowEx. • DestroyWnd уничтожает инкапсулированный оконный элемент управления путем обращения к методу DestroyWindowHandle, который в свою очередь обращается к функции Windows API Destroy Window. TWinControl имеет события, вызываемые взаимодействием с клавиатурой i изменением фокуса (OnKeyDown, OnKey Press, OnKeyUp, OnEnter и OnExif). Разрабатываемые компоненты редко происходят непосредственно от TWinControl. Лучше производить новые компоненты от общего класса TCustomControl, который предоставляет канву для рисования и обрабатывает сообщение WM_PAINT, или от некоторых более специализированных классов (TButtonControl, TCustomComboBox, TCustomEdit или TCustomListBox). 6.6.6 TGraphicControl Производные от абстрактного класса TGraphicControl, в отличие от TWinControl, не имеют оконного дескриптора, не могут принять фокус ввода и не могут являться родителями других элементов управления. Производные TGraphicControl используются в тех ситуациях, когда вы хотите изобразить на форме текст или графику, не обращаясь к функциональным возможностям обычных оконных элементов управления. Отметим следующие достоинства такого подхода. Во-первых, TGraphicControl не пользуется системными ресурсами Windows, так как не требует оконного дескриптора. Во-вторых, метод рисования TGraphicControl исполняются немного быстрее за счет того, что перерисовка компоненты не связана с диспетчеризацией сообщений Windows, a реализуется процессом рисования, заложенным в родителе данного элемента. Производные TGraphicControl имеют обработчики событий, вызываемые манипуляциями с мышью. TGraphicControl возлагает на пользователя операции перерисовки. Этот класс содержит свойство Canvas (Канва), которое обеспечивает доступ к отведенной для рисования поверхности, и виртуальный метод Paint, который вызывается в ответ на сообщение WM_PAINT, принимаемое родительским элементом управления. 6.6.7 TCustomControl Стандартные компоненты, как производные от TWinControl (например, TEdit и TListbox), уже имеют способности собственного отображения, предоставленные инкапсулированными элементами управления Windows. А как создать оконную компоненту, которая отображает себя в виде, соответствующем оригинальным требованиям пользователя? Решение именно этой задачи и обеспечивает TCustomControl. Будучи производным от класса TWinControl, TCustomControl является оконным элементом управления и, следовательно, может принять фокус ввода. Разработанные компоненты могут быть произведены от TCustomControl. Как и TGraphicControl, TCustomControl содержит свойство Canvas (Канва), которое предоставляет возможность произвольного рисования на выбранной прямоугольной области. По существу, производные компоненты от TCustomControl предоставляют в ваше распоряжение виртуальный метод Paint, перегрузка которого позволит рисовать компоненты так, как вы пожелаете. 6.7 Схема разработки компонент Процесс разработки собственной компоненты (мы будем называть ее TMyComponent) проходит через выполнение следующих этапов: 1. Создание модуля для новой компоненты. 2. Наследование производного класса от существующего базового компонентного класса. 3. Добавление нужных свойств, событий и методов. 4. Регистрация компоненты в C++Builder. 5. Отладка. 6. Инсталляция компоненты на Палитру. 7. Сохранение файлов компоненты. Далее будет показано, как некоторые из перечисленных действий, программируемых далее вручную. Мастер компонент способен выполнить автоматически (создание файлов модуля, наследование компонентного класса, объявление нового конструктора и регистрация компоненты). 6.7.1 Создание модуля компоненты Программный модуль состоит из двух файлов МуСоmр.срр и MyComp.h, которые компилируются в объектный файл с расширением MyComp.obj C++Builder использует модули в различных целях - каждая форма и большинств компонент (или их логических групп) имеют свой собственный модуль. При paзработке компоненты вы либо создаете новый модуль для компоненты, или до бавляете ее к существующему модулю. Чтобы создать модуль, выполните команду File | New и в открывшемся диалоre New Items выберите значок Unit. Чтобы добавить компоненту к существую щему модулю, выполните команду File | Open и в открывшемся диалоге отыщите ваш файл МуСоmр.срр. Имея открытый модуль в окне Редактора кода, вы можете приступить к разработке компоненты. Для начала перечислите в МуСоmр.h необходимые файлы фазы предкомпиляции и объявите производный класс ваше компоненты (Листинг 6.12). #include <vcl\sysutils.hpp> class TMyComponent : public < базовый компонентный класс > Листинг 6.12. Заготовка файла MyComp.h модуля компоненты. Пока мы создали компоненту, которая ничем не отличается от своего родителя. В следующем разделе описываются варианты наследования в зависимости от выбранного типа базового компонентного класса. 6.7.2 Наследование компоненты Любая компонента является производной от общего прародителя TComponent, от его более специализированных наследников (таких как TControl или TGraphicControl) или от существующего компонентного класса. Компонеитой может стать практически любой элемент вашей программы, поведением которого вы хотите управлять на стадии проектирования.
6.7.2.1 Модификация существующих компонент Простейший способ построить новую компоненту - это начать с существующей и изменить ее свойства. Вашей целью может являться добавление, исключение или замена значений по умолчанию некоторых свойств компоненты-образца. Вы можете использовать для этой цели любой подходящий абстрактный класс VCL, в название которого входит слово "Custom". Например, вы можете произвести новую компоненту списка со специальными свойствами, которых нет в стандартном классе TListBox. Поскольку нельзя прямо модифицировать TListBox, вы должны начать с ее ближайшего предшественника в иерархии классов. Для этой цели лучше всего подходит TCustomListBox, которая реализует все мыслимые свойства производных компонент списка, однако не выставляет всех их в секции __published. Наследуя вашу компоненту от одного из абстрактных типов (таких как TCustomListBox), вы всего лишь объявляете в секции __published те свойства, которые хотите включить в вашу компоненту, оставляя остальные в секции protected. 6.7.2.2 Создание оригинальных оконных компонент С оконным интерфейсным элементом, видимым во время работы программы, пользователь обычно может взаимодействовать. Все оконные компоненты являются производными от базового класса TWinControl. Стандартный элемент оконного управления характеризует так называемый "оконный дескриптор" (window handle), который заключен в свойстве Handle. Благодаря оконному дескриптору, Windows "узнает" данную компоненту, в частности, что она может принять фокус ввода и передавать оконный дескриптор функциям Windows API для идентификации рабочего окна. Хотя вы можете создать оригинальный интерфейсный элемент (который не имеет существующих аналогов и никак не связан с ними), используя TWinControl как отправную точку, C++Builder предоставляет компоненту TCustomControl как раз для этой цели. TCustomControl - это специализированный оконный элемент управления, который упрощает рисование сложных визуальных изображений. Если вашей компоненте не нужно принимать фокус ввода, вы можете наследовать ее от графического элемента управления, который дает экономию системных ресурсов. Все компоненты стандартного оконного управления: кнопки, списки, поля редактирования (за исключением TLabel, которая никогда не принимает фокус ввода) - являются косвенными производными TWinControl. 6.7.2.3 Создание графических компонент Оригинальные оконные и графические компоненты очень сходны. Однако в отличие от производных TCustomControl, графические компоненты лишены оконного дескриптора и не могут принять фокус ввода. Windows "не узнает" графические компоненты, поэтому их применение не приводит к дополнительным накладным расходам на передачу дескриптора. Графические компоненты обеспечивают отображение объектов без использования системных ресурсов. Вы должны производить новые графические компоненты от базового абстрактного класса TGraphicControl (который, в свою очередь, является потомком TControl). TGraphicControl предоставляет выбор канвы для рисования и обрабатывает сообщения WM_PAINT. Все, что вам следует сделать, это переопределить метод рисования Paint в соответствии заданными требованиями. 6.7.2.4 Создание невидимых компонент Мы знаем, что все без исключения компоненты имеют общего прародителя абстрактный класс TComponent. Однако, только невидимые компоненты можно наследовать непосредственно от TComponent. Любая производная от TComponent наследует все встроенные в нее свойства и методы. Невидимые компоненты встречаются довольно редко и используются, главным образом, в качестве интерфейсных элементов с другими компонентами (доступа к базам данных или как держатели диалоговых окон. 6.7.3 Добавление свойств, событий и методов Свойства представляют главную отличительную черту компонент, главным образом потому, что пользователи могут видеть и манипулировать ее свойствами во время проектирования, немедленно наблюдая реакцию времени выполнения программы. Мы уже знаем, что свойства, в отличие от члена данных, сами не хранят данные, однако методы чтения и записи организуют к ним доступ. Помните об этом, когда решите создать или изменить компонентные свойства. События играют чрезвычайно ответственную роль в поведении компонент, Событие - это связь между некоторым происшествием в системе (таким как воздействие пользователя на компоненту или изменение фокуса) и кодом-обработчиком события, который реагирует на это происшествие. Обработчик события почти всегда пишется прикладным программистом, ибо только он знает, какой должна быть реакция на данное событие. Используя события, прикладной программист может приспособить поведение компонент к своим требованиям, без необходимости изменения самих объектов. Предоставить прикладному программисту такую возможность в отношении новой компоненты и является задачей разработчика компоненты. События, возникающие в результате наиболее типичных действий пользователя (например, движений мышью) встроены во все стандартные компоненты VCL, однако вы также можете определить новые события. Мы уже знаем, что C++Builder реализует события как свойства. Помните об этом, когда решите создать или изменить компонентные события. Мы уже знаем, что компонентные методы ничем не отличаются от других объектных функций-членов. Хотя C++Builder не вводит никаких специальных ограничений на оформление компонентных методов, имеется ряд правил, которых стоит придерживаться. Помните об этом, когда решите создать или изменить компонентные методы.
|