Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Книга о KOL.doc
Скачиваний:
29
Добавлен:
30.04.2019
Размер:
1.77 Mб
Скачать

7.3.2. Разработка визуальных расширений (контролов)

Гораздо более серьезная тема – это разработка собственного визуального объекта. Сразу следует отметить, что в KOL не принято создавать такие визуальные расширения просто потому, что захотелось чуть-чуть поправить настройку начальных свойств какого-либо существующего визуального компонента.

Я помню первые годы триумфального шествия Delphi. Каждый начинающий программист рад был воспользоваться невероятной простотой нового механизма создания собственных компонентов: появлялись сотни компонентов типа «круглая кнопка» или «лампочка». Реально полезных компонентов, действительно расширяющих возможности стандартной библиотеки, чаще всего, оказывалось гораздо меньше.

Первоначально многие разработчики визуальных компонентов пошли по пути «внедрения» визуального объекта TControl внутрь своего наследника от TObj. Но этот способ чреват тем, что образованный таким способом «компонент» не сможет предоставить все необходимые рычаги управления объектом, упрятав свой визуализатор внутрь. Либо вам придется написать неимоверно много кода, который обеспечит доступ ко всем необходимым свойствам нового визуального объекта. Либо, предоставить объект TControl наружу через свойство или открытое всем ветрам поле, что тоже не очень-то красиво (И обращение к какому-либо свойству будет теперь выглядеть MyObj.Control.Width, например).

Еще одна проблема, которая возникает при использовании метода «внедрения» объекта TControl внутрь своего наследника от TObj, заключается в проблеме корректного удаления отработавших объектов. Можно добавить методом Add2AutoFree свой объект к родителю, но тогда нельзя будет напрямую уничтожить его методом Free или процедурой Free_And_Nil: при разрушении родителя еще раз будет выполняться деструктор, и на выходе программа, скорее всего, сломается. Правильнее в этом случае добавить объект-контейнер к списку объектов автоматического разрушения самого управляемого визуального контрола, включенного в него. И тогда можно будет разрушить его, вызвав либо специальный метод включающего объекта, который обратиться к методу Free для контрола, либо предоставить в своем наследнике TObj прямой доступ к этому контролу, чтобы можно было для разрушения оконного объекта использовать именно его метод Free.

К сожалению, даже успели было появиться «туториалы» и первые визуальные расширения, использующие такую технику встраивания внутрь TObj до того, как я предпринял необходимые усилия, и направил компоненто-творчество в правильное русло.

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

Но здесь есть одно «НО». В отличие от VCL, в KOL не используются виртуальные конструкторы. Здесь принято для конструирования объектов определять функции NewXXXX( параметры ). Казалось бы, ну и что: создаем новый тип объекта, пишем для него функцию NewMyControl( … ): PMyControl и …

И вот здесь начинается проблема: что написать в коде этой функции, если создавать объект-предок TControl следует как минимум функцией _NewControl, а она сможет создать только объект типа TControl, но никак не его наследник.

На самом деле, проблема возникает только в случае, если к объекту-наследнику добавляются новые поля (не говоря уже о новых виртуальных методах, практика показала, что без таковых вообще можно прекрасно обойтись). В случае же, если добавляются только не виртуальные методы, то проблема решается очень просто: достаточно внутри своего конструктора вызвать _NewControl, и вернуть на выходе именно этот полученный результат, приведя его к требуемому типу (PMyControl). К сожалению, создание действительно нового объекта практически никогда не обходится без добавления новых полей, так что проблема никуда не делась.

В итоге, было принято следующее соглашение: для объекта, наследуемого от TControl, новые поля добавляются через дополнительную структуру или объект, для чего задействуются свойства CustomData и CustomObj.

Задействовать можно либо свойство CustomData (если достаточно простой структуры для хранения новых полей), либо CustomObj (если желательно получить какие-то бенефиции от объекта в качестве хранителя новых полей), либо оба вместе (но обычно достаточно одного из них). При разрушении объекта типа TControl его поля CustomData и CustomObj разрушаются автоматически, причем для удаления структуры, на которую указывает CustomData, используется функция FreeMem, а для разрушения объекта CustomObj – метод Free.

// Примечание: Польза от применения именно объекта CustomObj может заключаться, как минимум, в возможности определить для него собственный деструктор, в котором будут освобождены еще какие-либо выделенные во время работы ресурсы //.

В результате, количество полей объекта TControl в наследнике не изменяется. Это дает возможность поступить так же, как и в предыдущем случае: в своем «конструкторе» NewMyControl вызвать функцию _NewControl для конструирования визуального объекта, а на выходе привести полученный объект к типу PMyControl.

Дополнительный плюс такого подхода еще и в том, что ваше расширение продолжает использовать все ту же единую таблицу виртуальных методов своего наследника TControl (мы уже договорились о том, что новых виртуальных методов добавлять вы не будете, иначе вышеописанный трюк невозможен). А это значит, что увеличение кода произойдет только на размер кода использованных в приложении новых методов.

И, как я уже говорил, имеется некоторое количество материала для чтения, специально предназначенного вниманию тех, кто желает создавать собственные расширения, визуальные и невизуальные. Вы можете найти их на сайтах, посвященных KOL и MCK, так же как и многочисленные примеры таких расширений. Так что я позволю себе сократить объем этого документа и избежать ненужного дублирования информации.