Объектно-ориентированное программирование. Создание нового компонента
Объектно-ориентированное программирование, его основные принципы, классы и объекты. Описание и создание нового класса, организация его подключения. Инструменты для работы с компонентами. Регистрация нового компонента. Создание редактора свойств.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | методичка |
Язык | русский |
Дата добавления | 02.10.2012 |
Размер файла | 177,5 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Размещено на http://www.allbest.ru/
Факультет прикладной математики и механики
Методические указания к курсу
Визуальное программирование в Delphi
Объектно-ориентированное программирование. Создание нового компонента
для студентов 3 курса всех форм обучения
Составители:
доц. каф. МО ЭВМ
Н.А. Тюкачев
доц. каф. ТК и АР
В.Г. Рудалев
асс.каф. МО ЭВМ
М.В. Бакланов
В методических указаниях рассматриваются основные понятия объектно-ориентированного программирования (классы, поля, методы, свойства) и основные принципы (инкапсуляция, наследование и полиморфизм). Приведен пример создания нового класса и компонента.
Методические указания рассчитаны на студентов дневного и вечерних отделений, аспирантов, на научных и инженерно-технических работников, занимающихся разработкой программного обеспечения ЭВМ.
Рецензент: д.т.н., зав. к-рой ПМ ВГТА Матвеев М.Г.
Печатается по решению редакционно-издательского совета
факультета прикладной математики и механики
Воронежского государственного университета
Коллектив авторов, 1999
Оформление.
Воронежский государственный университет, 1999
Оглавление
- 1. Объектно-ориентированное программирование
- 1.1 Классы и объекты
- 1.2 Принципы ООП
- 1.2.1 Области видимости
- 1.2.2 Инкапсуляция
- 1.2.3 Наследование
- 1.2.4 Полиморфизм
- 1.3 События
- 2. Создание нового класса
- 2.1 Описание нового класса
- 2.2 Подключение нового класса
- 3. Инструменты для работы с компонентами
- 3.1 Пункт New Component
- 3.2 Пункт Install Component
- 3.2.1 Существующий пакет (Into existing package)
- 3.2.2 Новый пакет (Into new package)
- 3.3 Пункт Import ActiveX Control
- 3.4 Пункт Сreate Сomponent Тemplate
- 3.5 Пункт Install Packages
- 3.6 Пункт Configure Palette
- 4. Регистрация нового компонента
- 5. Создание редактора свойств
- 6. Задания
- Литература
1. Объектно-ориентированное программирование
1.1 Классы и объекты
программирование класс редактор подключение
Класс является развитием понятия записи (record) и включает в себя поля, методы и свойства. В Delphi все классы образуют дерево и имеют общего предка TОbject. При описании нового класса в скобках указывается его непосредственный предок:
type
TMyEdit = class(TEdit)
FField: MyType; // поле
procedure MyProc; // метод
end;
Замечание. Если класс происходит от TОbject, то предка можно не указывать.
В разделе описания переменных необходимо описать переменную этого типа и эта переменная называется экземпляром класса или объектом. Например: var MyEdit: TMyEdit.
Экземпляр класса является динамической переменной - указателем, содержащим адрес объекта (но для экземпляров классов не надо использовать оператор разадресации ^). Поэтому у любого объекта должен быть метод, распределяющий память для объекта (constructor) и метод, освобождающий память (destructor). Конструктор принято именовать Create, деструктор - Destroy, например,
type
TMyEdit = class(TEdit)
FField: MyType; // поле
сonstructor Create;
destructor Destroy;
procedure MyProc; // метод
end;
Разновидностью деструктора является метод Free, который перед освобождением памяти делает проверку: if Self<>nil then Destroy. Специальная ссылка Self всегда указывает на текущий объект.
Отметим важные особенности конструктора. Конструктор работает как функция, возвращающая адрес объекта: MyEdit:=TMyEdit.Create. Для классов TComponent при вызове конструктора указывается параметр Owner типа TComponent - объект-владелец компонента, например,
Button1:=TButton.Create (Form1);
(Owner - это указатель на компонент, вызывающий при своем создании/уничтожении конструкторы/деструкторы подчиненных компонентов. В данном случае им является форма.)
Отметим, что при вызове конструктора указывается не имя объекта, а имя класса.
Поля класса - это уникальные для каждого экземпляра данные, обладающие своим типом.
Методы класса - это процедуры и функции, предназначенные для оперирования полями. Внутри методов доступен указатель на вызвавший их объект Self.
Свойства класса - это совокупность полей и методов чтения/обновления этих полей.
1.2 Принципы ООП
Объектно-ориентированное программирование основано на трех принципах: инкапсуляция, наследование и полиморфизм.
1.2.1 Области видимости
В Delphi определены четыре директивы, позволяющие разграничить доступ к полям и методам класса:
Таблица 1. Уровни доступа к полям и методам класса
Private |
Личные |
Невидимы вне модуля или программы, в которой класс объявлен |
|
Protected |
Защищенные |
Видим везде в модуле, в котором класс объявляется, и из любого класса потомка, независимо от модуля, где класс потомка появляется, т.е. для разработчиков других компонентов. |
|
Public |
Общие |
Объекты данного уровня видимы везде, где возможна ссылка на класс. |
|
Published |
Опубликованные |
Нет никаких ограничений по доступу. Свойства попадают в Инспектор Объектов во время проектирования. Класс должен происходить от Tсomponent Могут быть опубликованы свойства типа: порядковый, строка, класс, интерфейс, указатель на метод, любой вещественный тип, за исключением Real48. |
|
Automated |
Автоматизированные |
Видимость как у public полей и методов. Различие заключается в том, что информация типа Automated (необходимая для серверов Automation) генерируется для автоматизированных полей и методов, которые появляются только в классах, происходящих от классы TАutoObject модуля OleAuto. Этот модуль и само зарезервированное слово Automated, поддерживаются для обратной совместимости. |
Порожденный класс может переместить свойства в раздел с меньшими ограничениями (protected - в public или в published, public - в published), но не может переместить свойство в раздел с большими ограничениями в доступе.
1.2.2 Инкапсуляция
Поля класса не должны быть доступны напрямую. Чтение и обновление полей реализуется вызовом соответствующих методов. В методы чтения/обновления могут, в частности, входить прорисовка компонентов и проверка корректности передаваемых величин.
Принцип инкапсуляции гласит, что все поля должны быть описаны в приватной части класса и доступ к ним может осуществляться только через доступные методы и свойства.
В простейшем случае свойство определяется следующим образом:
property <имя свойства>: <тип свойства> [read <метод чтения свойства>] [write <метод записи свойства>]
Пример. Доступ к полю FField типа integer реализуется через свойство PField типа string. Преобразование типов происходит при вызове методов чтения/записи.
interface
type TMyClass=class(TParentClass)
private
FField: integer; // поле
function GetField: string; // метод
procedure SetField(AValue: string);// метод
published
property PField: string // свойство
read GetField write SetField;
end;
var MyObj: TMyClass; // экземпляр
implementation
function TMyClass.GetField: string;
begin
Result:=IntToStr(FField);
end;
procedure TMyClass.SetField(AValue: string);
var x,Code: integer;
begin
Val(AValue,x,Code);
// проверка корректности нового значения
if (x<>FField) and (Code=0) then
begin
FField:=x;
//…
// Дополнительные действия, если они необходимы
end;
end;
end.
В данном примере доступ для чтения поля реализован функцией GetField. Если специальный метод чтения/обновления не требуется, то вместо имени метода можно указать имя поля (но только в случае, если тип свойства совпадает с типом поля). Например: property PField: string read FField write SetField. Если свойство доступно только для чтения или для записи, то соответствующий метод можно опустить.
Подчеркнем, что при обращении к свойству методы чтения/обновления не указываются. Для пользователя обращение к свойству такое же, как к полю, например, MyObj.PField:='100'; …; s:=MyObj.PField. Вызовы необходимых методов встраиваются компилятором автоматически.
Свойство может быть векторным, например,
property Items[Index: integer]: real read GetItems write SetItems; при этом методы чтения и обновления имеют вид
function GetItems (Index: integer): real;
procedure SetItems (Index: integer; NewValue: real);
Для программиста такое свойство выглядит как массив, к элементам которого можно обращаться обычным образом (например, Items[i]:=1.5), хотя соответствующее ему поле в действительности может массивом и не являться. Одному из векторных свойств можно приписать атрибут default, тогда при обращении к нему имя свойства можно не указывать, например, MyObject.Items[i]:=1.5 будет эквивалентно MyObject[i]:=1.5.
1.2.3 Наследование
Класс-потомок наследует все поля и методы класса-предка.
Таблица 2. Поведение методов при наследовании
Тип метода |
Перекрытие |
Адрес метода |
Особенности |
|
Static |
Формируется на стадии компиляции |
Метод вызывается путём прямого обращения к нему. |
||
Virtual |
Override |
Атрибут virtual сообщает компилятору о необходимости сохранить адрес метода в таблице VMT класса. Компоновка произойдет во время выполнения. |
Таблица VMT создается для всех виртуальных методов класса: унаследованных и перекрытых. Занимает достаточно много места, вызов метода медленнее, чем у методов типа static |
|
Dynamic |
Override |
Атрибут dynamic сообщает компилятору о необходимости сохранить адрес в таблице DMT. Компоновка произойдет во время выполнения. |
Таблица DMT создается только для методов, созданных в этом классе. Занимает совсем немного места, но вызов методов медленнее, чем у методов типа virtual |
|
Message |
При перекрытии не используется директива override |
Сообщает компилятору о необходимости сохранить адрес в DMT. Необходимо указать конкретную константу, характеризующую сообщение. Компоновка произойдет во время выполнения. |
Директива используется для создания методов обработки сообщений Windows |
|
Abstract |
Override |
Нет адреса |
Метод не может быть вызван, но может быть перекрыт. Является основой механизма полиморфизма. |
Замечание. Для перекрытия всех методов, кроме static и message, необходимо использовать только директиву override.
В качестве примера рассмотрим класс TGraphicControl, предком которого является класс TСontrol. Этот класс описан в VCL:
TGraphicControl = class(TControl)
private
FCanvas: TCanvas;
protected
procedure Paint; virtual;
property Canvas: TCanvas read FCanvas;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
В классе TGraphicControl добавляется поле FCanvas, свойство Canvas и перекрываются конструктор Create и деструктор Destroy.
В конструкторе Create с помощью служебного слова inherited вызывается метод предка Create. Затем инициализируется поле FCanvas.
constructor TGraphicControl.Create(AOwner:
TComponent);
begin
inherited Create(AOwner);
FCanvas := TControlCanvas.Create; //
TControlCanvas(FCanvas).Control := Self;
end;
В деструкторе порядок действия обратный: сначала освобождается память от поля FCanvas, а затем с помощью служебного слова inherited вызывается метод предка Destroy:
destructor TGraphicControl.Destroy;
begin
if CaptureControl = Self then
SetCaptureControl(nil);
FCanvas.Free;
inherited Destroy;
end;
Виртуальный метод Paint представляет собой пустую процедуру:
procedure TGraphicControl.Paint;
begin
end;
Теперь рассмотрим класс простых геометрических фигур TShape, родителем которого является класс TGraphicControl. В этот класс добавлены новые поля FPen, FBrush, FShape, методы обновления этих полей SetBrush, SetPen, SetShape и свойства Pen, Brush, Shape:
TShapeType = (stRectangle, stSquare, stRoundRect,
stRoundSquare, stEllipse, stCircle);
TShape = class(TGraphicControl)
private
FPen: TPen;
FBrush: TBrush;
FShape: TShapeType;
procedure SetBrush(Value: TBrush);
procedure SetPen(Value: TPen);
procedure SetShape(Value: TShapeType);
protected
procedure Paint; override;
public
constructor Create(AOwner: TComponent);override;
destructor Destroy; override;
published
procedure StyleChanged(Sender: TObject);
property Align;
property Anchors;
property Brush: TBrush read FBrush write SetBrush;
property DragCursor;
property DragKind;
property DragMode;
property Enabled;
property Constraints;
property ParentShowHint;
property Pen: TPen read FPen write SetPen;
property Shape: TShapeType read FShape
write SetShape default stRectangle;
property ShowHint;
property Visible;
property OnDragDrop;
property OnDragOver;
property OnEndDock;
property OnEndDrag;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDock;
property OnStartDrag;
end;
Свойства Align, Anchors, DragCursor, DragKind, DragMode, Enabled, Constraints, ParentShowHint, ShowHint, Visible, OnDragDrop, OnDragOver, OnEndDock, OnEndDrag, OnMouseDown, OnMouseMove, OnMouseUp, OnStartDock, OnStartDrag описаны в разделе protected родительского (для компонента TGraphicControl) класса TControl. В дочернем классе TShape они перенесены в раздел published, что приводит к появлению этих свойств в Инспекторе Объектов.
В классе TShape перекрываются методы Create, Destroy и Paint. При перекрытии метода Create назначаются размеры компонента и инициализируются поля инструментов:
constructor TShape.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := ControlStyle + [csReplicatable];
Width := 65; Height := 65;
FPen := TPen.Create;
FPen.OnChange := StyleChanged;
FBrush := TBrush.Create;
FBrush.OnChange := StyleChanged;
end;
destructor TShape.Destroy;
begin
FPen.Free; FBrush.Free;
inherited Destroy;
end;
Более содержательным является перекрытие метода рисования Paint:
procedure TShape.Paint;
var X,Y,W,H,S: Integer;
begin
with Canvas do
begin
Pen := FPen; Brush := FBrush;
X := Pen.Width div 2; Y := X;
W := Width - Pen.Width + 1;
H := Height - Pen.Width + 1;
if Pen.Width = 0 then
begin
Dec(W); Dec(H);
end;
if W < H then S := W else S := H;
if FShape in[stSquare,stRoundSquare,stCircle] then
begin
Inc(X, (W - S) div 2); Inc(Y, (H - S) div 2);
W := S; H := S;
end;
case FShape of
stRectangle, stSquare:
Rectangle(X,Y,X+W,Y+H);
stRoundRect, stRoundSquare:
RoundRect(X,Y,X+W,Y+H,S div 4,S div 4);
stCircle, stEllipse:
Ellipse(X,Y,X+W,Y+H);
end;
end;
end;
Методы обновления полей используют метод Assign и достаточно просты:
procedure TShape.SetBrush(Value: TBrush);
begin
FBrush.Assign(Value);
end;
procedure TShape.SetPen(Value: TPen);
begin
FPen.Assign(Value);
end;
procedure TShape.SetShape(Value: TShapeType);
begin
if FShape <> Value then
begin
FShape := Value;
Invalidate; // перерисовка без мерцания
end;
end;
1.2.4 Полиморфизм
Прежде чем формулировать принцип полиморфизма, сформулируем правило контроля соответствия типов: указателю на экземпляр класса-предка может быть присвоен адрес экземпляра любого дочернего класса.
Полиморфизм - способность объекта видоизменять свои свойства и методы в течение жизни после своего создания во время выполнения программы.
Полиморфизм может проявляться либо в результате присваивания объекту-предку объекта любого дочернего класса (при этом вместо методов предка будут вызваны методы фактически присвоенного объекта), либо когда различные объекты в иерархии вызывают один и тот же унаследованный метод, модифицируя его «на лету» своими специфическими способами. Например, прикомпоновывая к нему свои собственные методы.
В обоих случаях вызываются методы, принадлежащие фактическому классу вызывающего их объекта, и этот класс неизвестен во время компиляции. Поэтому компоновка объектов и методов здесь должна происходить только во время выполнения, такая компоновка называется динамической (или поздним связыванием), в отличие от статической компоновки (раннего связывания). Методы, компонуемые во время выполнения, называются виртуальными, разновидности их (virtual, dynamic, message, abstract) приведены в табл. 2. Использование виртуальных методов - основа и необходимое условие полиморфизма.
Полиморфизм экономит код, заставляя всю иерархию объектов функционировать в едином стиле. Детали реализации скрываются в виртуальных методах. Рекомендуется все методы, которые могут в дальнейшем перекрываться, объявлять виртуальными, так как статическое перекрытие не обеспечит полиморфизма. Чтобы принцип полиморфизма выполнялся и для свойств, объявляйте методы чтения/обновления свойства виртуальными.
В качестве простого примера рассмотрим класс TField, порождающий два класса: TExtField и TIntField:
type TField=class
function GetData: string; virtual; abstract;
end;
TIntField=class(TField)
FData: integer;
function GetData: string; override;
end;
TExtField=class(TField)
FData: extended;
function GetData: string; override;
end;
implementation
function TExtField.GetData: string;
begin
GetData:=FloatToStrF(Fdata, ffFixed, 6, 3);
end;
function TIntField.GetData: string;
begin
GetData:=IntToStr(FData);
end;
procedure ShowData(A: TField);
begin
Label1.Caption:=A.GetData;
end;
В родительском классе представлен абстрактный виртуальный метод GetData, являющийся корнем для перекрывающих методов в дочерних классах TIntField и TextField (см. рис. 1). Абстрактный метод не содержит никаких действий и обязательно должен перекрываться в дочерних классах. Абстрактными могут быть только виртуальные или динамические методы. Перекрывающие методы преобразовывают целое или вещественное поле объекта к строке символов, естественно, для каждого класса по-разному. Внешняя процедура ShowData отображает эту строку на визуальном компоненте. Если процедура ShowData использует в качестве формального параметра экземпляр A типа TField, то при вызове метода в качестве фактического параметра может быть использован объект TIntField и TextField (см. рис. 1). Очевидно, компилятору «не дано знать», какой из объектов окажется во время выполнения программы на месте объекта A в методе ShowData (в кого «перевоплотится» объект A), и, соответственно, чей метод здесь необходимо вызвать. Попытка статически прикомпоновать метод GetData объекта A без объявления его виртуальным методом не приведет к успеху, так как этот метод для A абстрактный. Проблема может быть решена поздним связыванием с использованием виртуальных методов. Тогда при выполнении программы будет вызван метод для фактического типа объекта A. Заметим, что без использования виртуальных методов пришлось бы в процедуре ShowData «вручную» анализировать тип объекта, и для каждого типа вызывать свой метод.
Рис. 1
Каждый объект содержит поля, адреса статических методов и указатель на класс - указатель на специальную служебную структуру (см. рис. 2). Класс как структура состоит из двух частей. Начиная с адреса, на который ссылается указатель на класс, находится таблица виртуальных методов (VMT), содержащая адреса всех виртуальных методов класса (в порядке их объявления), в том числе унаследованных от предков. Длина одной записи VMT - 4 байта. Перед таблицей VMT (с отрицательным смещением) находится информация о классе: имя класса, размер экземпляра, указатель на класс-предок, адреса процедур создания и уничтожения экземпляра класса и др. Смещения описаны в модуле System.pas (каталог ..\Delphi\Source\Rtl\Sys). По одному из этих адресов (смещение -48) располагается адрес таблицы динамических методов класса (DMT).
В начале таблицы DMT записывается количество элементов таблицы, затем - индексы динамических методов. Каждому динамическому методу присваивается свой уникальный индекс. Нумерация индексов начинается с -1 и идет по убыванию. После индексов в DMT располагаются адреса динамических методов. Отметим, что в таблице VMT содержатся адреса всех виртуальных методов класса, независимо от того, унаследованы они или перекрыты, а в таблице DMT - индексы и адреса только тех методов, которые описаны в данном классе. В случае, если индекс вызываемого динамического метода не найден в таблице DMT данного класса, он отыскивается в таблицах DMT предков. При использовании динамических методов экономится память, но тратится дополнительное время.
Таким образом, вся информация, необходимая для позднего связывания, доступна во время работы программы. Более подробно техническая реализация полиморфизма в Object Pascal описана в книге [1].
Пример. Для объекта MyObject1 типа
type TClass1 = class(TClass2)
F1: integer;
F2: string;
procedure StatMetod1;
procedure VirtMetod1;
procedure VirtMetod2;
procedure DynMetod1;
procedure DynMetod2;
end;
упомянутые структуры можно изобразить следующим образом (рис.2):
Рис.2
Дополнительные возможности контроля за типами объектов, получившимися в результате полиморфного присваивания, предоставляют операции is и as. Операция is анализирует информацию о типе времени выполнения. Выражение <объект> is <класс> принимает значение True, если объект совместим по присваиванию с классом, т.е. является объектом данного или порожденного класса. Например, внутри обработчика события фактический тип параметра Sender заранее неизвестен, и можно записать, к примеру,
If Sender is TLabel then TLabel(Sender).Caption := 'Заголовок';
Конструкция <объект> as <класс> сообщает компилятору, что данный объект следует рассматривать как принадлежащий указанному классу. От стандартной операции приведения типов <Класс> (<Объект>) использование as отличается наличием проверки на совместимость типов во время выполнения (как при использовании is). Попытка приведения к несовместимому типу приведет к возникновению исключительной ситуации, которую можно обработать программно.
Пример. Предположим, что обработчик события щелчка мыши, совместно используемый компонентами Panel1 и Label1, должен изменять цвет выбранного компонента. Очевидно, строка
Sender.Color:=clRed;
вызовет ошибку компиляции, т.к. у объекта Sender класса TObject нет свойства Color. Оператор
(Sender as TLabel).Color:= clRed;
не вызовет у компилятора возражений. Но если в качестве Sender окажется Panel1, то во время выполнения будет сгенерирована исключительная ситуация.
Правильно, казалось бы, писать
(Sender as TControl).Color:=ClRed,
но компилятор эту запись не пропустит: свойство Color у TControl находится в секции Protected, и, следовательно, доступно лишь через потомков TControl,например, TLabel. Конструкция будет правильной, если свойство является Public или Published. Поэтому остается только
if Sender is TPanel then Panel1.color:=clRed;
if Sender is TLabel then Label1.color:=clRed;
1.3 События
Событие компонента - это его свойство процедурного типа, предназначенное для создания реакции компонента на те или иные внешние воздействия со стороны пользователя, аппаратуры или других программ. Такому свойству необходимо присвоить адрес метода, который будет вызываться в момент наступления события с данным компонентом. События имеют различное число параметров. В простейшем случае параметр один - Sender типа TObject, указывающий на объект-источник события. Соответствующие события, например, события OnClick и OnDblClick, имеют тип
type TNotifyEvent=procedure(Sender: TObject) of object;
В описание процедурного типа для метода необходимо добавлять зарезервированные слова of object. Обычный процедурный тип и процедурный тип для метода несовместимы, так как в метод, кроме описанных параметров, неявно передается параметр Self - указатель на вызвавший его объект. Тип TNotifyEvent имеют, в частности, события щелчка мышью на компоненте. В других событиях могут присутствовать дополнительные параметры, например, для события перемещения указателя мыши OnMouseMove описание типа события имеет вид
Type TMouseMoveEvent = procedure(Sender: TObject; Shift: TShiftState; X, Y: Integer) of object;
где параметр Shift позволяет узнать, были ли нажаты при перемещении указателя управляющие клавиши или кнопки мыши;
параметры X и Y содержат новые координаты указателя мыши в клиентской области объекта Sender.
Событие нажатия/отпускания кнопки мыши имеет тип
TMouseEvent=procedure(Sender: TObject;
Button: TMouseButton; Shift: TShiftState;
X, Y: Integer) of object,
где Button: TMouseButton - идентификатор кнопки, TMouseButton = (mbLeft, mbRight, mbMiddle).
Нажатие и отпускание клавиш клавиатуры OnKeyDown и OnKeyPressed:
TKeyEvent = procedure(Sender: TObject; var Key: Word;
Shift: TShiftState) of object,
где Key - виртуальный код Windows нажатой клавиши.
Cобытие OnKeyPress типа
TKeyPressEvent = procedure(Sender: TObject; var Key: Char) of object
возникает при вводе с клавиатуры символа ASCII. Здесь Key - ASCII-код нажатой клавиши.
Заметим, что сообщения от клавиатуры поступают тому элементу, который в данный момент имел фокус ввода. Если у формы, владеющей элементом управления, установить значение свойства KeyPreview в True, то события будут сначала обработаны формой.
Перечень типов стандартных событий см. в файле controls.pas в каталоге ..\Delphi\Source\VCL.
Совместимые события можно присваивать во время выполнения программы, например,
Button1.OnMouseMove:=Button2.OnMouseMove;
Button1.OnClick:=Form1.OnPaint;
Edit1.OnChange:=Button1Click;
В результате один объект будет использовать обработчики совместимого события другого объекта. Эта возможность называется делегированием.
2. Создание нового класса
2.1 Описание нового класса
В модулеUnitGr будем создавать класс TGr, предназначенный для рисования функции y=A*x*x в окне (x1,y1,x2,y2). Класс TGr наследует свойства класса TGraphicControl.
TGr = class(TGraphicControl)
private
FPen: TPen;
FBrush: TBrush;
Fx1,Fy1,Fx2,Fy2: real;
Fa: real;
function II(x: real): integer;
function JJ(y: real): integer;
function F(x: real): real;
procedure SetX1(A: real);
procedure SetX2(A: real);
procedure SetY1(A: real);
procedure SetY2(A: real);
procedure SetA(A: real);
procedure SetBrush(Value: TBrush);
procedure SetPen(Value: TPen);
protected
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Paint; override;
published
procedure StyleChanged(Sender: TObject);
property Align;
property Anchors;
property Brush: TBrush read FBrush write SetBrush;
property DragCursor;
property DragKind;
property DragMode;
property Enabled;
property Constraints;
property ParentShowHint;
property Pen: TPen read FPen write SetPen;
property ShowHint;
property Visible;
property x1: real read fx1 write SetX1;
property x2: real read fx2 write SetX2;
property y1: real read fy1 write SetY1;
property y2: real read fy2 write SetY2;
property A: real read fa write SetA;
property OnDragDrop;
property OnDragOver;
property OnEndDock;
property OnEndDrag;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnStartDock;
property OnStartDrag;
end;
Замечание. Экземпляр класса Graph2 описывать в этом модуле не надо.
Для описания "бумажного" окна введем поля Fx1, Fy1, Fx2, Fy2. Доступ к этим полям реализован через свойства x1, y1, x2 и y2. Для обновления полей предназначены методы SetX1, SetX2, SetY1 и SetY2, в которых не только назначается новое значение поля, но и методом Paint перерисовывается компонент:
procedure TGr.SetX1(A: real);
begin
if fx1<>A then
begin
fx1:=A; Paint;
end;
end;
Конструктор Create перекрывают соответствующий метод предка:
constructor TGraph2.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := ControlStyle + [csReplicatable];
fx1:=-1; fx2:=10; fy1:=-1; fy2:=10; fa:=1;
Left:=10; Top:=10; Width := 300; Height := 200;
FPen := TPen.Create;
FPen.OnChange := StyleChanged;
FBrush := TBrush.Create;
FBrush.OnChange := StyleChanged;
end;
Замечание. В конструкторе нельзя использовать свойства fx1, fx2, fy1, fy2, fa. Это связано с тем, что в методах обновления свойств типа SetX1 вызывается метод рисования Paint, а экземпляр класса TGraph2 еще не создан.
Деструктор Destroy также перекрывает метод предка:
destructor TGraph2.Destroy;
begin
FPen.Free;
FBrush.Free;
inherited Destroy;
end;
В приватный раздел описания класса добавлены методы масштабирования:
function TGraph2.II(x: real): integer;
begin
Result:=Round((x-fx1)*Width/(fx2-fx1));
end;
function TGraph2.JJ(y: real): integer;
begin
Result:=Height-Round((y-fy1)*Height/(fy2-fy1));
end;
и функция F(x):
function TGraph2.F(x: real): real;
begin
Result:=fa*x*x;
end;
Основным методом класса является метод рисования Paint, описанный в разделе public класса:
procedure TGraph2.Paint;
const n=60;
var i: integer;
h,x: real;
begin
h:=(x2-x1)/n;
with Canvas do
begin
Rectangle(0, 0, Width, Height);
MoveTo(II(x1),JJ(0)); LineTo(II(x2),JJ(0));
MoveTo(II(0),JJ(y1)); LineTo(II(0),JJ(y2));
x:=x1; MoveTo(II(x),JJ(F(x)));
for i:=1 to n do
begin
x:=x+h; LineTo(II(x),JJ(F(x)));
end;
end;
end;
2.2 Подключение нового класса
Для проверки работы компонента создадим новый проект, в разделе описания переменных введем экземпляр Gr: TGraph2, и на основную форму проекта Form1 выставим кнопку SpeedButton1. Для события onClick этой кнопки создадим обработчик:
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
Gr:=TGr.Create(Self); Gr.Parent:=Self;
end;
При нажатии кнопки метод TGr.Create выделяет память для компонента TGr и указывает, что владельцем этого компонента является форма Form1 (Gr.Parent:=Self).
Тестирующий проект готов для работы.
3. Инструменты для работы с компонентами
В меню Delphi пункт Component содержит подпункты: New Component, Instal Component, Import ActiveX Control, Install Packages, Configure Palette. Эти пункты предназначены для облегчения создания новых компонентов, подключения созданных компонентов, настройки свойств компонентов палитры, создания новых линеек палитры и определения состава компонентов на этих линейках.
Прежде чем включить компонент в палитру, его необходимо включить в один из пакетов (package) компонентов. Пакет - это разновидность динамически загружаемой библиотеки (DLL), соответственно, главное его преимущество перед обычным модулем (*.dcu) - возможность совместного использования несколькими программами, при этом средний размер исполняемого файла программы уменьшается, например, с 500 до 25 Кбайт. Содержимое пакетов отображается на палитре компонентов, файлы пакетов находятся в каталоге Windows\System. Информация о составе пакета содержится в файле *.dpk. Основные компоненты, поставляемые с Delphi, входят в пакет vcl40.bpl, дополнительные (факультативные) находятся в отдельных файлах. Для работы с пакетами предназначен редактор пакетов, в котором можно добавлять, удалять и компилировать пакеты.
3.1 Пункт New Component
При выборе этого пункта появляется диалог New Component (рис. 3), используемый для создания модуля, в котором будет описан новый компонент.
Рис. 3
Таблица 3. Описание элементов диалога New Component
Ancestor Type |
Используйте выпадающий список для выбора родительского класса или введите имя родительского класса для нового компонента. Новый компонент унаследует все свойства, методы и события от своего предка, если они не будут перекрыты в декларации компонента. После выбора базового класса измените Class Name и Unit File Name. Можно принять или отредактировать эти данные. |
|
Class Name |
Имя нового класса. Это имя должно начинаться с символа T. |
|
Palette Page |
Используйте выпадающий список для выбора линейки палитры или введите имя линейки, на которой должен появиться новый компонент. |
|
Unit File Name |
Имя модуля, которое будет содержать новый компонент. Если каталог модуля не содержится в Search Path, он будет туда добавлен. |
|
Search Path |
Маршрут будет использоваться для поиска файла. |
|
Install |
Устанавливает компонент в новом или существующем пакете. После установки пакета будет выведено окно диалога Package Editor. Используйте эту кнопку только для установки уже созданного компонента. |
|
Ok |
Отображает код компонента в Code Editor. Создает пустую заготовку модуля, в котором должен содержаться компонент. Альтернативный вариант - использование пункта меню File | New Unit. |
3.2 Пункт Install Component
Выбрав пункт Component|Install Component, можно установить компонент в существующий или новый пакет (рис. 4).
3.2.1 Существующий пакет (Into existing package)
В этом случае можно включить код компонента, идентифицируемый модулем, в существующий пакетный файл.
Рис.4
Таблица 4. Описание элементов страницы Into existing package
Unit file name |
Укажите имя модуля, который Вы хотите установить. Если путь к модулю указан в маршруте поиска, полное имя модуля не требуется. Если каталог модуля не указан в маршруте поиска, то он будет добавлен к концу. |
|
Search path |
Маршрут используется Delphi для того, чтобы найти файлы. |
|
Package file name |
Используйте выпадающий список для того, чтобы выбрать имя включенного пакета или ввести имя другого существующего пакета. |
|
Package description |
Краткое описание выбранного пакета. |
Нажав кнопку Ok, вызываем диалог редактора пакета (рис. 5), позволяющего откомпилировать (кнопка Compile) текущий пакет (секция
Contains) и установить его в среду программирования как пакет времени проектирования (кнопка Install). Кнопка Add добавляет модуль в пакет, а Remove удаляет выделенный модуль из пакета. Кнопка Option отображает стандартное окно опций проектов и пакетов.
Рис. 5
3.2.2 Новый пакет (Into new package)
В этом случае создается новый пакет, куда подключается модуль, содержащий код компонента. Элементы страницы Into new package такие же, как и у страницы Into existing package.
Таблица 5. Описание элементов страницы Into new package
Unit file name |
Укажите имя модуля, который Вы хотите установить. Если путь к модулю указан в маршруте поиска, полное имя модуля не требуется. Если каталог модуля не указан в маршруте поиска, то он будет добавлен к концу. |
|
Search path |
Маршрут используется Delphi для того, чтобы найти файлы. |
|
Package file name |
Введите имя создаваемого пакета. Можно включить маршрут каталога с именем; в противном случае, пакет находится в текущем каталоге. Если Вы набираете имя файла непосредственно в диалоге Into new package, расширение .DPK будет добавлено автоматически. Имена пакетов должны быть уникальными в пределах проекта. Если Вы называете пакет "MyComp", то редактор пакета генерирует исходный файл с именем "MyComp.DPK"; компилятор генерирует выполняемый и бинарный файлы с именами "MyComp.BPL" и "MyComp.DCP", соответственно. Имя "MyComp" используется для того, чтобы ссылаться на пакет в статье requires другого пакета или в приложении. |
|
Package description |
Краткое описание пакета. |
После ввода необходимой информации на странице Into new package, нажмите OK. Появится диалог с меню редактора пакетов (рис. 4).
3.3 Пункт Import ActiveX Control
Используется для подключения компонентов ActiveX (файлы *.ocx, *.dll), зарегистрированных в реестре Windows, и отображения их в указанной странице палитры компонентов.
3.4 Пункт Сreate Сomponent Тemplate
Для того чтобы создать измененный компонент:
Выберите существующий компонент и установите его на форме.
Для выбранного компонента в Инспекторе Объектов установите свойства и события для вашего шаблона.
Выберите пункт Component|Create Component Template для того, чтобы дать шаблону новое имя, положение в палитре и изображение (рис. 6).
Рис. 6
Таблица 6. Описание элементов диалога Сreate Сomponent Тemplate
Component Name |
Это поле показывает имя, выбранного компонента, к которому добавлен суффикс "template". |
|
Palette Page |
Это поле используется для выбора линейки (страницы) палитры компонентов или создания новой линейки. |
|
Palette Icon |
Поле показывает изображение, используемое для компонента. Для изменения этого изображения необходимо нажать кнопку Change.Для создания нового шаблона компонента нажмите OK. |
3.5 Пункт Install Packages
Существуют два вида пакетов - пакеты времени выполнения (run-time packages), хранящиеся в файлах *.bpl и загружаемые во время выполнения программы, и соответствующие им пакеты времени разработки (design time packages), хранящиеся в файлах *.dcp и предназначенные для установки компонентов в среду разработчика. Пакет *.dcp создается компилятором в процессе подготовки пакета *.bpl.
Диалоговое окно (рис.7) позволяет инсталлировать в IDE пакеты времени разработки и указать пакеты времени выполнения, используемые в данном проекте.
Рис. 7
Окно Design Packages предоставляет список пакетов, включенных в IDE. Для работы со списком используются следующие кнопки (табл.7).
Табл.7
Add |
Инсталлирует пакет времени проектирования. Пакет будет доступен во всех проектах. Delphi автоматически добавляет пакеты *.bpl, которые требуются при инсталляции DPL-пакетов. |
|
Remove |
Удаляет из IDE выделенный пакет. Для удаления можно убрать соответствующий флажок. Все компоненты, зарегистрированные в пакете, будут недоступны. |
|
Edit |
Открывает выбранный пакет в Редакторе пакетов, если доступен его исходный текст. |
|
Components |
Показывает компоненты, включенные в данный пакет. |
Окно Runtime Packages. Флажок Build With Runtime Packages указывает на необходимость использования BPL-пакетов во время создания EXE-файла. Имя требуемого пакета дописывается в строку редактирования или выбирается нажатием кнопки Add.
Если пакет используется в проекте, Delphi в процессе компиляции находит DCP файл. Delphi не сможет найти этот файл, если путь к DCP-файлам не указан в списке путей поиска библиотек. При необходимости отредактируйте этот список, выбрав пункт меню Tools|Environment Options (страница Library).
Чтобы использовать текущую конфигурацию пакетов в качестве конфигурации по умолчанию для всех проектов, установите флажок Default.
3.6 Пункт Configure Palette
Выбор пункта вызывает диалоговое окно, позволяющее изменить внешний вид палитры компонентов: переименовать, добавить, изменить порядок страниц палитры и отдельных значков компонентов внутри страницы (рис.8). На содержимое пакетов эти изменения влияния не оказывают.
Размещено на http://www.allbest.ru/
Рис. 8
4. Регистрация нового компонента
Для подготовки модуля, в котором содержится наш новый компонент TGraph2, к регистрации компонента в палитре компонентов необходимо добавить в модуль глобальную процедуру регистрации:
procedure Register;
begin
RegisterComponents('MyPalette', [TGraph2]);
end;
В этой процедуре необходимо указать имя линейки палитры компонентов MyPalette и имя регистрируемого класса TGraph2. Если для создания шаблона компонента использовался пункт меню Component | New Component, то вызов этой процедуры будет включен в модуль автоматически.
Выбрав пункт меню Component|Install Component, установить компонент в существующий или новый пакет (рис. 4).
Вызвать этот пакет в редактор пакетов (рис. 5) и откомпилировать его.
5. Создание редактора свойств
В Delphi имеется возможность изменять не только библиотеку визуальных компонентов, но и саму среду программирования. В частности, можно модифицировать имеющиеся и создавать новые редакторы свойств. Редакторы свойств определяют функционирование правой части страницы Property в инспекторе объектов. Необходимость в написании редакторов свойств чаще всего возникает при разработке новых компонентов. Однако созданные редакторы можно связать и с уже существующими названиями или типами свойств.
Стандартные редакторы свойств выполнены в виде классов. Описания классов находятся в модуле Dsgnintf.pas (см. каталог ..\Delphi\Source\ToolsApi). Изучение этого модуля - необходимый этап при написании собственных редакторов свойств.
Базовым является класс TPropertyEditor. Его потомками являются типизированные классы TIntegerProperty, TColorProperty, TCharProperty, TSetProperty, TFloatProperty, TStringProperty, TFontNameProperty, TCaptionProperty, TFontProperty и др. Назначения классов соответствуют названию. Класс TFloatProperty связан со свойствами, которые имеют тип Float, класс TFontProperty связан со свойствами, которые имеют тип TFont и т.п. Для создания нового редактора свойств необходимо выбрать в качестве предка либо один из типизированных классов, либо, если подходящего класса нет, выбрать непосредственно TPropertyEditor, и переопределить некоторые его методы. Чаще всего переопределяются методы Edit и GetAttributes. Метод procedure Edit; virtual; вызывается при нажатии кнопки `…' или по двойному щелчку мыши на свойстве, при этом появляется диалоговое окно редактирования свойства (пример - свойство Font). В теле метода Edit часто присутствует вызов метода SetValue, устанавливающего значение отредактированного свойства:
procedure SetValue(const Value: string);virtual;
Параметром метода SetValue всегда является переменная строкового типа, содержащая отредактированное значение свойства в строковом представлении. Для класса TPropertyEditor этот метод не содержит действий, поэтому для использования он должен перекрываться в типизированных потомках. Программист должен позаботиться о соответствующем преобразовании в методе SetValue строки Value к типу редактируемого свойства. Для некоторых свойств бывает трудно подобрать подходящий типизированный класс TXXXProperty, поэтому приходится полностью перепрограммировать метод Edit, игнорируя метод SetValue.
Метод function GetAttributes: TPropertyAttributes; virtual; позволяет задать способ отображения свойства в инспекторе объектов. Внутри этого метода следует определить множество (Set) возвращае-мых значений типа TPropertyAttributes (см. табл.8).
Таблица 8.
PaValueList |
Редактор свойств может возвращать список значений для этого свойства. Если это свойство установлено, то нужно вызывать метод GetValues. Если для одного и того же компонента установить два флага PaValueList и PaDialog, то в редакторе свойств отобразится только кнопка выпадающего списка, диалоговое окно будет раскрыто при двойном щелчке по полю редактирования свойства. |
|
PaSortList. |
Инспектор объектов будет сортировать список, полученный методом GetValues. |
|
PaSubProperties |
Свойство имеет подсвойства, которые будут показываться ниже в виде иерархии (outline) |
|
PaDialog |
Показывает, что метод Edit будет вызывать диалог. Если данный атрибут установлен, то появится кнопка “Многоточие” справа от свойства в Инспекторе Объектов. |
|
PaMultiSelect |
Позволяет свойству оставаться в Инспекторе Объектов, когда на форме выбрано сразу несколько объектов |
|
PaAutoUpdate |
Если этот атрибут установлен, то метод SetValue будет вызываться при каждом изменении, произведенном в редакторе, а не после завершения редактирования (пример - свойство Caption). |
|
PaReadOnly |
В инспекторе объектов свойство не редактируется. |
- Пример. Предположим, что разработанный Вами компонент содержит свойство Text типа string. Создадим для этого свойства простейший редактор, состоящий из обычного диалогового окна редактирования строк. Откроем новый модуль Unit1 и запишем в него
unit Unit1;
interface
uses … , Dsgnintf;
type TTextProperty=class(TStringProperty)
public
function GetAttributes: TPropertyAttributes; override;
procedure edit; override;
end;
procedure Register;
implementation
procedure TTextProperty.Edit;
var
s: String;
begin
s:='';
InputQuery('Text', 'Enter Text', S);
SetValue(S);
end;
function TTextProperty.GetAttributes;
begin
Result:=inherited GetAttributes+[paDialog, paReadOnly];
end;
procedure Register;
begin
RegisterPropertyEditor(TypeInfo(String), TControl,
'text', TTextProperty);
end;
end.
Компиляция и установка редактора свойств происходит при помощи пункта меню Component|Install Component. Отметим, что редактор свойств создается аналогично компоненту, соответствующий пакет включается в IDE и отображается в списке установленных пакетов, но сам компонент - редактор - отсутствует на палитре компонентов.
Объявление процедуры регистрации редактора свойств имеет вид
RegisterPropertyEditor(PropertyType: PTypeInfo; ComponentClass: TClass; const PropertyName: string; EditorClass: TPropertyEditorClass);
Первый параметр указывает тип свойства, обрабатываемого редактором.
Второй параметр определяет, какие компоненты будут использовать данный редактор. Если указать здесь Nil или TСomponent, редактор будет использоваться всеми компонентами, имеющими свойства заданного типа.
Третий параметр определяет имя свойства, обрабатываемого редактором. Если имя свойства - пустая строка, то редактор используется для всех свойств, удовлетворяющих первым двум параметрам.
В четвертом параметре указывается объектовый тип редактора, связываемого со свойствами, определяемыми первыми тремя параметрами.
6. Задания
Компонент "Системные часы".
Компонент "Системная дата".
Компонент "Атрибуты файла".
Компонент "Зависимые ползунки".
Компонент "Непрямоугольная кнопка".
Компонент "Многогранник".
Компонент "Многоугольник".
Компонент "График функции".
Компонент "Непрямоугольное изображение".
Компонент "MaskEdit".
Компонент "StringGrid+ComboBox".
Компонент "Метод наименьших квадратов".
Компонент "Сплайны".
Компонент "Визуальное бинарное дерево".
Компонент "Edit+Label".
Компонент "Столбиковая диаграмма".
Компонент "Круговая диаграмма".
Компонент "Плотности распределения вероятностей ".
Компонент "Визуальное редактирование графа".
Компонент "Моделирование временного ряда".
Компонент "Идентификация временного ряда".
Компонент “Корреляционные функции”
Компонент "Спектры случайных процессов".
Компонент "Частотные характеристики".
Компонент "Годограф".
Компонент "Инерционное звено второго порядка".
Редактор многострочной контекстной подсказки.
Редактор свойства TPen.
Литература
Дарахвелидзе П., Марков Е. Delphi - среда визуального программирования. - С.-П.: “BHV - Санкт-Петербург”, 1996. - 352 с.
Дж. Дантеманн, Дж. Мишел, Д. Тейлор. Программирование в среде Delphi. - Киев: “DiaSoft Ltd.”, 1995. - 607 с.
Калверт Ч. Delphi 2. Энциклопедия пользователя: Пер. с англ./ - Чарльз Калверт. - Киев: “DiaSoft Ltd.”, 1996. - 736 с.
Орлик С. Секреты Delphi на примерах. М.: “BINOM”, 1996. - 351 с.
Рэй Лишнер. Секреты Delphi 2. - Киев: “DiaSoft Ltd.”, 1996. - 779 с.
Фаронов В.В. Delphi 4. Учебный курс. - М.: "Нолидж", 1998. - 448 с.
Эндрю Возневич. Освой самостоятельно Delphi. - М.: “Бином”, 1996. - 729 с.
Размещено на Allbest.ru
Подобные документы
Объектно-ориентированное программирование как методология программирования, опирающаяся на инкапсуляции, полиморфизме и наследовании. Общая форма класса. Наследование как процесс, посредством которого один объект получает свойства другого объекта.
презентация [214,9 K], добавлен 26.10.2013Создание программного обеспечения - системы имитационного моделирования на тему "Производственная линия с пунктами технического контроля". Описание входных и выходных данных. Объектно-ориентированное программирование. Диаграммы модулей и процессов.
курсовая работа [1,2 M], добавлен 09.01.2014Свойства объектно-ориентированного языка программирования. Понятия инкапсуляции и наследования. Виртуальные функции и полиморфизм. Инициализация экземпляра объекта с помощью конструктора. Динамическое создание объектов. Совместимость объектных типов.
реферат [17,0 K], добавлен 15.04.2015Анализ объектно-ориентированного программирования, имитирующего способы выполнения предметов. Основные принципы объектно-ориентированного программирования: инкапсуляция, наследование, полиморфизм. Понятие классов, полей, методов, сообщений, событий.
контрольная работа [51,7 K], добавлен 22.01.2013Понятие алгоритма и его характеристики как основного элемента программирования. Формы представления алгоритмов, основные алгоритмические структуры. Структурное и событийно-ориентированное программирование. Объектно-ориентированное программирование.
реферат [86,0 K], добавлен 17.07.2008Разработка программы с использованием принципов объектно-ориентированного программирования на языке высокого уровня С средствами Microsoft Visual Studio 2010. Построение алгоритма реализации. Класс программы, инструкция по использованию программы.
курсовая работа [1,0 M], добавлен 26.12.2013Изучение принципов объектно-ориентированного программирования, в котором основными концепциями являются понятия классов и объектов. Свойства этого вида программирования: инкапсуляция, полиморфизм, наследование. Описание класса. Конструкторы и деструкторы.
презентация [74,8 K], добавлен 14.10.2013Объектно-ориентированное программирование. Особенности использования формата CHM, его преимущества. Создание электронного учебника на тему "Язык программирования C++" с помощью компиляции html-страниц. Правильное сочетание тегов, структура документа.
курсовая работа [1,0 M], добавлен 27.10.2012Приемы и правила объектно-ориентированного программирования с использованием языка С++. Общие принципы разработки объектно-ориентированных программ. Основные конструкции языка С++. Разработка различных программ для Windows с использованием WIN32 API.
учебное пособие [1,6 M], добавлен 28.12.2013Технологии программирования. Сущность объектно-ориентированного подхода к программированию. Назначение Си, исторические сведения. Алфавит, базовые типы и описание данных. Структуры и объединения. Операторы Си++. Функции. Библиотека времени выполнения.
курс лекций [51,9 K], добавлен 03.10.2008