Чтобы полностью понять и почувствовать все преимущества Delphi, Вам нужно
хорошо изучить язык Object Pascal. И хотя возможности визуальной части
Delphi чрезвычайно богаты, хорошим программистом может стать только тот,
кто хорошо разбирается в технике ручного написания кода.
По мере обсуждения темы данного раздела мы рассмотрим несколько простых
примеров, которые, тем не менее, демонстрируют технику использования важных
управляющих элементов Windows.
Создание методов с помощью визуальных средств
В предыдущем уроке Вы видели, что синтаксический “скелет” метода может
быть сгенерирован с помощью визуальных средств. Для этого, напомним, нужно
в Инспекторе Объектов дважды щелкнуть мышкой на пустой строчке напротив
названия интересующего Вас события в требуемом компоненте. Заметим, если
эта строчка не пуста, то двойной щелчок на ней просто переместит Вас в
окне Редактора Кода в то место, где находится данный метод.
Для более глубокого понимания дальнейшего изложения кратко остановимся
на концепции объектно-ориентированного программирования. Для начала определим
базовое понятие объектно-ориентированного программирования - класс. Класс
- это категория объектов, обладающих одинаковыми свойствами и поведением.
При этом объект представляет собой просто экземпляр какого-либо класса.
Например, в Delphi тип “форма” (окно) является классом, а переменная этого
типа - объектом. Метод - это процедура, которая определена как часть класса
и инкапсулирована (содержится) в нем. Методы манипулируют полями и свойствами
классов (хотя могут работать и с любыми переменными) и имеют автоматический
доступ к любым полям и методам своего класса. Доступ к полям и методам
других классов зависит от уровня “защищенности” этих полей и методов.
Пока же для нас важно то, что методы можно создавать как визуальными средствами,
так и путем написания кода вручную.
Давайте рассмотрим процесс создания программы CONTROL1, которая поможет
нам изучить технику написания методов в Delphi.
Для создания программы CONTROL1 поместите с помощью мышки компонент Edit
(находится на страничке “Standard” Палитры Компонентов) на форму. После
этого ваша форма будет иметь вид, показанный на 8.
Теперь перейдите в Object Inspector, выберите страничку “Events” и дважды
щелкните в пустой строчке напротив события OnDblClick, как показано на
8. После этого в активизировавшемся окне Редактора Вы увидите сгенерированный
“скелет” метода Edit1DblClick, являющегося реакцией на событие OnDblClick:
procedure TForm1.Edit1DblClick(Sender: TObject);
begin
end;
После генерации процедуры Вы можете оставить ее имя таким, каким “установил”
Delphi, или изменить его на любое другое (для этого просто введите новое
имя в указанной выше строке Инспектора Объектов справа от требуемого события
и нажмите Enter).
Теперь в окне Редактора Кода введите смысловую часть метода:
procedure TForm1.Edit1DblClick(Sender: TObject);
begin
Edit1.Text:= 'Вы дважды щелкнули в строке редактирования';
end;
Сохраните программу. Во время выполнения дважды щелкните на строке редактирования.
Текст в этой строке изменится в соответствии с тем, что мы написали в
методе Edit1DblClick: см. 8.
Рис. 8: Содержимое управляющего элемента TEdit изменяется после двойного
щелчка по нему
8 и 8 предоставляют полный код программы CONTROL1.
Листинг -A: Программа CONTROL1 демонстрирует, как создавать и использовать
методы в Delphi.
program Control1;
uses
Forms,Main in 'MAIN.PAS' {Form1};
begin
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Листинг -B: Головной модуль программы CONTROL1.
unit Main;
interface
uses
WinTypes, WinProcs,Classes, Graphics, Controls,Printers, Menus,
Forms, StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
procedure Edit1DblClick(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Edit1DblClick(Sender: TObject);
begin
Edit1.Text := 'Вы дважды щелкнули в строке редактирования';
end;
end.
После того, как Ваша программа загрузится в память, выполняются две строчки
кода в CONTROL1.DPR, автоматически сгенерированные компилятором:
Application.CreateForm(TForm1, Form1);
Application.Run;
Первая строка запрашивает память у операционной системы и создает там
объект Form1, являющийся экземпляром класса TForm1. Вторая строка указывает
объекту Application, “по умолчанию” декларированному в Delphi, чтобы он
запустил на выполнение главную форму приложения. В данном месте мы не
будем подробно останавливаться на классе TApplication и на автоматически
создаваемом его экземпляре - Application. Важно понять, что главное его
предназначение - быть неким ядром, управляющим выполнением Вашей программы.
Как правило, у большинства примеров, которыми мы будем оперировать в
наших уроках, файлы проектов .DPR практически одинаковы. Поэтому в дальнейшем
там, где они не отличаются кардинально друг от друга, мы не будем приводить
их текст. Более того, в файл .DPR, автоматически генерируемый Delphi,
в большинстве случаев нет необходимости заглядывать, поскольку все действия,
производимые им, являются стандартными.
Итак, мы видели, что большинство кода Delphi генерирует автоматически.
В большинстве приложений все, что Вам остается сделать - это вставить
одну или несколько строк кода, как в методе Edit1DblClick:
Edit1.Text := 'Вы дважды щелкнули в строке редактирования';
Хотя внешний интерфейс программы CONTROL1 достаточно прост, она (программа)
имеет строгую внутреннюю структуру. Каждая программа в Delphi состоит
из файла проекта, имеющего расширение .DPR и одного или нескольких модулей,
имеющих расширение .PAS. Модуль, в котором содержится главная форма проекта,
называется головным. Указанием компилятору о связях между модулями является
предложение Uses, которое определяет зависимость модулей.
Нет никакого функционального различия между модулями, созданными Вам
в Редакторе, и модулями, сгенерированными Delphi автоматически. В любом
случае модуль подразделяется на три секции:
· Заголовок
· Секция Interface
· Секция Implementation
Таким образом, “скелет” модуля выглядит следующим образом:
unit Main; {Заголовок модуля}
interface {Секция Interface}
implementation {Секция Implementation}
end.
В интерфейсной секции (interface) описывается все то, что должно быть
видимо для других модулей (типы, переменные, классы, константы, процедуры,
функции). В секции implementation помещается код, реализующий классы,
процедуры или функции.
Передача параметров
В Delphi процедурам и функциям (а, следовательно, и методам классов)
могут передаваться параметры для того, чтобы обеспечить их необходимой
для работы информацией. Программа PARAMS демонстрирует, как использовать
передачу параметров в методы Delphi. Кроме того, мы узнаем, как:
создавать свои собственные процедуры добавлять процедуру в класс, формируя
метод класса вызывать одну процедуру из другой.
Программа PARAMS позволяет Вам вводить фразы в строки редактирования.
После нажатия кнопки “Вызов процедуры WriteAll” строка из управляющего
элемента EditSource скопируется в шесть управляющих элементов - строк
редактирования, как показано на 8.
Далее мы не будем подробно останавливаться на том, как размещать компоненты
на форме - считаем, что это Вы уже умеете. После того как Вы разместили
на форме семь компонентов Edit, переименуйте с помощью Инспектора Объектов
седьмой компонент (Edit7) в EditSource. Положите на форму компонент Button,
и в Object Inspector измените его заголовок (свойство Caption) на “Вызов
процедуры WriteAll” (естественно, Вы можете заменить его шрифт, цвет и
т.д.).
После завершения проектирования формы класс TForm1 будет выглядеть следующим
образом:
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
Edit6: TEdit;
EditSource: TEdit;
Button1: TButton;
end;
Следующий шаг состоит в добавлении метода, вызываемого по нажатию пользователем
кнопки Button1. Это, напомним, можно сделать двумя способами:
-
Перейти в Инспекторе Объектов на страничку “Events” (предварительно
выбрав компонент Button1 на форме), выбрать слово OnClick и дважды
щелкнуть мышкой на пустой строчке справа от него
-
Просто дважды щелкнуть на компоненте Button1 на форме.
Delphi сгенерирует следующую “заготовку”:
procedure TForm1.Button1Click(Sender: TObject);
begin
end;
Цель программы PARAMS - научить Вас писать процедуры и передавать в них
параметры. В частности, программа PARAMS реагирует на нажатие кнопки Button1
путем вызова процедуры WriteAll и передачи ей в качестве параметра содержимого
строки редактирования EditSource (EditSource.Text).
procedure TForm1.Button1Click(Sender: TObject);
begin
WriteAll(EditSource.Text);
end;
Важно понять, что объект EditSource является экземпляром класса TEdit
и, следовательно, имеет свойство Text, содержащее набранный в строке редактирования
текст. Как Вы уже, наверное, успели заметить, по умолчанию свойство Text
содержит значение, совпадающее со значением имени компонента (Name) -
в данном случае “EditSource”. Свойство Text Вы, естественно, можете редактировать
как в режиме проектирования, так и во время выполнения.
Текст, который должен быть отображен в шести строках редактирования,
передается процедуре WriteAll как параметр. Чтобы передать параметр процедуре,
просто напишите имя этой процедуры и заключите передаваемый параметр (параметры)
в скобки - вот так:
WriteAll(EditSource.Text);
Заголовок этой процедуры выглядит следующим образом:
procedure TForm1.WriteAll(NewString: String);
где указано, что передаваемый процедуре параметр NewString должен иметь
тип String.
Вспомним, что задача процедуры WriteAll состоит в копировании содержимого
строки редактирования EditSource в шесть других строк редактирования Edit1-Edit6.
Поэтому процедура должна выглядеть следующим образом:
procedure TForm1.WriteAll(NewString: String);
begin
Edit1.Text := NewString;
Edit2.Text := NewString;
Edit3.Text := NewString;
Edit4.Text := NewString;
Edit5.Text := NewString;
Edit6.Text := NewString;
end;
Поскольку процедура WriteAll не является откликом на какое-либо событие
в Delphi, то ее нужно полностью написать “вручную”. Простейший способ
сделать это - скопировать заголовок какой-либо уже имеющейся процедуры,
исправить его, а затем дописать необходимый код.
Возвратимся еще раз к заголовку процедуры. Заголовок состоит из пяти
частей:
procedure TForm1.WriteAll(NewString: String);
Первая часть - зарезервированное слово “procedure”; пятая часть -
концевая точка с запятой “;”. Обе эти части служат определенным синтаксическим
целям, а именно: первая информирует компилятор о том, что определен
синтаксический блок “процедура”, а вторая указывает на окончание заголовка
(собственно говоря, все операторы в Delphi должны заканчиваться точкой
с запятой).
Вторая часть заголовка - слово “TForm1”, которое квалифицирует то
обстоятельство, что данная процедура является методом класса TForm1.
Третья часть заголовка - имя процедуры. Вы можете выбрать его любым,
по вашему усмотрению. В данном случае мы назвали процедуру “WriteAll”.
Четвертая часть заголовка - параметр. Параметр декларируется внутри
скобок и, в свою очередь, состоит из двух частей. Первая часть - имя
параметра, вторая - его тип. Эти части разделены двоеточием. Если
Вы описываете в процедуре более чем один параметр, нужно разделить
их точкой с запятой, например:
procedure Example(Param1: String; Param2: String);
После того как Вы создали “вручную” заголовок процедуры, являющейся методом
класса, Вы должны включить его в декларацию класса, например, путем копирования
(еще раз напомним, что для методов, являющихся откликами на дельфийские
события, данное включение производится автоматически):
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
Edit6: TEdit;
EditSource: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure WriteAll(NewString: String);
end;
В данном месте нет необходимости оставлять в заголовке метода слово “TForm1”,
так как оно уже присутствует в описании класса.
8 показывает полный текст головного модуля программы PARAMS. Мы не включили
сюда файл проекта, поскольку, как уже упоминалось, он практически одинаков
для всех программ.
Листинг -C: Исходный код головного модуля программы PARAMS показывает,
как использовать строки редактирования и как передавать параметры.
Unit Main;
interface
uses
WinTypes, WinProcs, Classes,Graphics, Controls,Printers, Forms,
StdCtrls;
tye
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
Edit6: TEdit;
EditSource: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure WriteAll(NewString: String);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.WriteAll(NewString: String);
begin
Edit1.Text := NewString;
Edit2.Text := NewString;
Edit3.Text := NewString;
Edit4.Text := NewString;
Edit5.Text := NewString;
Edit6.Text := NewString;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
WriteAll(EditSource.Text);
end;
end.
При экспериментах с программой PARAMS Вы можете попробовать изменить
имена процедур и параметров. Однако, следует помнить, что ряд слов в Delphi
являются зарезервированными, и употреблять их в идентификаторах (именах
процедур, функций, переменных, типов, констант) не разрешается - компилятор
сразу же обнаружит ошибку. К ним относятся такие слова, как “procedure”,
“string”, “begin”, “end” и т.п.; полный же список их приведен в on-line
справочнике Delphi.
Не старайтесь запомнить сразу все зарезервированные слова - компилятор
“напомнит” Вам о неправильном их использовании выдачей сообщения типа
“Identifier expected.” (Ожидался идентификатор, а обнаружено зарезервированное
слово).
Более сложные методы и управляющие элементы
Теперь, когда Вы освоили базовые понятия в системе программирования Delphi,
можно продолжить изучение компонент и способов создания их методов.
В программе CONTROL1, рассмотренной в начале урока, был сгенерирован
метод, являющийся откликом на событие OnClick строки редактирования Edit1.
Аналогично, можно сгенерировать метод, являющийся реакцией на событие
OnDblClick. В программе CONTROL2, имеющейся на диске, расширен список
находящихся на форме компонентов и для многих из них определены события
OnClick и OnDblClick. Для исследования Вы можете просто скопировать файлы
проекта CONTROL1 в новую директорию CONTROL2, изменить имя проекта на
CONTROL2.DPR (в этом файле после ключевого слова “program” также должно
стоять название “CONTROL2”) и добавить компоненты Label, GroupBox, CheckBox,
RadioButton, Button на форму (эти компоненты находятся на страничке “Standard”
Палитры Компонентов). Ваша форма будет иметь примерно следующий вид -
8.
Заметим, что Вы должны
“положить” компонент GroupBox на форму до того, как Вы добавите компоненты
CheckBox и RadioButton, которые, в нашем примере, должны быть “внутри”
группового элемента. Иначе, объекты CheckBox1, CheckBox2, RadioButton1
и RadioButton2 будут “думать”, что их родителем является форма Form1 и
при перемещении GroupBox1 по форме не будут перемещаться вместе с ней.
Таким образом, во избежание проблем, компонент, который должен быть “родителем”
других компонент (Panel, GroupBox, Notebook, StringGrid, ScrollBox и т.д.),
нужно помещать на форму до помещения на нее его “детей”. Если Вы все же
забыли об этом и поместили “родителя” (например, GroupBox) на форму после
размещения на ней его “потомков” (например, CheckBox и RadioButton) -
не отчаивайтесь! Отметьте все необходимые объекты и скопируйте (с удалением)
их в буфер обмена с помощью команд меню Edit|Cut. После этого отметьте
на форме нужный Вам объект (GroupBox1) и выполните команду меню Edit|Paste.
После этого все выделенные Вами ранее объекты будут помещены на форму,
и их “родителем” будет GroupBox1. Описанный механизм является стандартным
и может быть использован для всех видимых компонент.
Выберите объект Label1. Создайте для него метод, являющийся откликом
на событие OnDblClick. Введите в метод одну строчку, например:
procedure TForm1.Label1DblClick(Sender: TObject);
begin
Edit1.Text := 'Двойной щелчок на Label1';
end;
Запустите программу на выполнение и дважды щелкните мышкой на метке Label1.
Вы увидите, что строка редактирования изменится, и в ней появится текст
“Двойной щелчок на Label1”.
Теперь закройте приложение и возвратитесь в режим проектирования. Добавьте
обработчики событий OnClick и OnDblClick для каждого объекта, имеющегося
на форме. Текст вашего головного модуля будет выглядеть следующим образом:
Листинг -D: Головной модуль программы CONTROL2.
Unit Main;
interface
uses WinTypes, WinProcs, Classes,Graphics, Controls,
StdCtrls,Printers, Menus, Forms;
type
TForm1 = class(TForm)
Label1: TLabel;
Edit1: TEdit;
Button1: TButton;
GroupBox1: TGroupBox;
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
RadioButton1: TRadioButton;
RadioButton2: TRadioButton;
procedure Edit1DblClick(Sender: TObject);
procedure Label1DblClick(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
procedure CheckBox2Click(Sender: TObject);
procedure RadioButton1Click(Sender: TObject);
procedure RadioButton2Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Edit1DblClick(Sender: TObject);
begin
Edit1.Text := 'Двойной щелчок на Edit1';
end;
procedure TForm1.Label1DblClick(Sender: TObject);
begin
Edit1.Text := 'Двойной щелчок на Label1';
end ;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
Edit1.Text := 'Щелчок на CheckBox1';
end;
procedure TForm1.CheckBox2Click(Sender: TObject);
begin
Edit1.Text := 'Щелчок на CheckBox2';
end;
procedure TForm1.RadioButton1Click(Sender: TObject);
begin
Edit1.Text := 'Щелчок на RadioButton1';
end;
procedure TForm1.RadioButton2Click(Sender: TObject);
begin
Edit1.Text := 'Щелчок на Radiobutton2';
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text := 'Щелчок на Button1';
end;
end.
Эта программа служит двум целям:
-
Она показывает, как создавать процедуры (методы) и как “наполнять”
их содержательной “начинкой”
-
Она демонстрирует технику работы с управляющими элементами Windows.
Информация периода выполнения. Программа CONTROL3
Как Вы, наверное, заметили, методы программы CONTROL2, являющиеся откликами
на события OnClick и OnDblClick, во многом похожи друг на друга.
Открытость среды Delphi позволяет получать и оперировать информацией
особого рода, называемой информацией периода выполнения (RTTI - run-time
type information). Эта информация организована в виде нескольких уровней.
Верхний уровень RTTI представлен как средство проверки и приведения типов
с использованием ключевых слов is и as.
Ключевое слово is дает программисту возможность определить, имеет ли
данный объект требуемый тип или является одним из наследников данного
типа, например, таким образом:
if MyObject is TSomeObj then ...
Имеется возможность использовать RTTI и для процесса приведения объектного
типа, используя ключевое слово as:
if MyObject is TSomeObj then
(MyObject as TSomeObj).MyField:=...
что эквивалентно:
TSomeObj(MyObject).MyField:=...
Средний уровень RTTI использует методы объектов и классов для подмены
операций as и is на этапе компиляции. В основном, все эти методы заложены
в базовом классе TObject, от которого наследуются все классы библиотеки
компонент VCL. Для любого потомка TObject доступны, в числе прочих, следующие
информационные методы:
-
ClassName - возвращает имя класса, экземпляром которого является
объект
-
ClassInfo - возвращает указатель на таблицу с RTTI, содержащей информацию
о типе объекта, типе его родителя, а также о всех его публикуемых
свойствах, методах и событиях
-
ClassParent - возвращает тип родителя объекта
-
ClassType - возвращает тип самого объекта
-
InheritsFrom - возвращает логическое значение, определяющее, является
ли объект потомком указанного класса
-
InstanceSize - возвращает размер объекта в байтах.
Эти методы могут использоваться в Вашем коде напрямую.
Нижний уровень RTTI определяется в дельфийском модуле TypInfo и представляет
особый интерес для разработчиков компонент. Через него можно получить
доступ к внутренним структурам Delphi, в том числе, к ресурсам форм, инспектору
объектов и т.п.
Итак, доступ к информации периода выполнения в Delphi позволяет динамически
получать как имя объекта, находящегося на форме, так и название класса,
которому он принадлежит (и еще много другой полезной информации; но об
этом - в дальнейших уроках). Для этого используется свойство Name, имеющееся
у любого класса-наследника TComponent (а таковыми являются все компоненты,
входящие в дельфийскую библиотеку VCL), и метод ClassName, доступный для
любого потомка класса базового TObject. А, поскольку класс TComponent,
в свою очередь, является наследником класса TObject, то он доступен для
всех компонент из библиотеки VCL.
Вернувшись к нашим примерам, мы можем заменить целую “кучу” методов двумя,
реализующими события OnClick и OnDblClick для всех объектов сразу. Для
этого можно скопировать все файлы из CONTROL2 в новый директорий CONTROL3
или использовать для работы уже имеющуюся на диске программу. Создадим
стандартным образом методы ControlDblClick и ControlClick для какого-либо
объекта (например, для Label1). Введем в них следующие строки:
procedure TForm1.ControlDblClick(Sender: TObject);
begin
Edit1.Text := 'Двойной щелчок на ' +(Sender as TComponent).Name +
' (класс ' + Sender.ClassName + ')';
end;
procedure TForm1.ControlClick(Sender: TObject);
begin
Edit1.Text := 'Щелчок на ' +(Sender as TComponent).Name +
' (класс ' + Sender.ClassName + ')';
end;
Теперь назначим данные методы всем событиям OnClick и OnDblClick, имеющимся
у расположенных на форме объектов. Мы видим, что размер программы существенно
сократился, а функциональность ее значительно выросла. В режиме выполнения
после, например, щелчка на объекте CheckBox1 приложение будет иметь вид,
изображенный на 8.
Итак, мы видим, что используя информацию периода выполнения, можно сделать
программу очень гибкой и универсальной.
Заключение
В этом уроке мы рассмотрели, как управлять методами компонент во время
выполнения программы. Кроме того, мы изучили, как что такое информация
периода выполнения и научились использовать ее в целях создания гибких
и универсальных приложений.
[ Предыдущий урок | Содержание
| Следующий урок ]
|