Лекция № 8
Тема: «Приемы обработки однотипных компонентов»
План
1. Использование подпрограмм в обработчике событий
2. Обращение к активному компоненту не по имени
3. Обработка группы компонентов
4. Обработка компонентов как массива
5. Создание пользовательских модулей
1. Использование подпрограмм в обработчике событий
Приложения можно разрабатывать в любой последовательности. В вашем расположении огромное число компонентов, они поддерживают большое число событий. Так что стоит ли мудрить: переноси компоненты на форму, пиши обработчики их событий и получай награду за прекрасно сделанное приложение.
Честно говоря, создаваемые нами ранее программы так и строились. Это были тестовые приложения, единственной задачей которых было показать возможности различных компонентов. Но настоящее приложение так проектировать нельзя. Бессистемно созданное приложение, даже очень хорошее, через некоторое время становится непонятным самому разработчику, не говоря уже о проблемах сопровождения такого приложения кем-то из коллег. Подобное приложение, если возникает необходимость его доработки, проще создать заново, чем разбираться в его хитросплетениях.
Решением проблемы является создание универсальных подпрограмм для выполнения тех или иных действий в системе. Имея готовую подпрограмму в обработчике события нужного компонента достаточно будет просто вызывать эту подпрограмму по ее имени. При этом, если какое-либо действие вызывается несколькими компонентами (кнопкой, командой меню и т.п.), то для всех этих компонентов можно создать один обработчик с вызовом подпрограммы.
Таким образом код не содержит дублирующих фрагментов, а в случае внесения изменений достаточно изменить код подпрограммы один раз.
2. Обращение к активному компоненту не по имени
Для создания универсальных подпрограмм-обработчиков событий одним из условий является обращение к компонентам не по имени. Например: пусть имеется форма регистрации с четырьмя полями и кнопкой.

Нужно написать обработчик, который при вводе пароля переводит курсор на следующее поле, если в текущем поле введено 5 символов.
Для первого поля в событии OnChange напишем код:
procedure TForm1.Edit1Change (Sender:Tobject)
begin
//если в первое поле введено 5 символов
if length(Edit1.Text) = 5 then
//то переходим во второе поле
Edit2.SetFocus;
end;
Для второго поля в событии OnChange напишем код:
procedure TForm1.Edit2Change (Sender:Tobject)
begin
//если во второе поле введено 5 символов
if length(Edit2.Text) = 5 then
//то переходим в третье поле
Edit3.SetFocus;
end;
Для третьего поля в событии OnChange напишем код:
procedure TForm1.Edit3Change (Sender: Tobject)
begin
//если в третьем поле введено 5 символов
if length(Edit3.Text) = 5 then
//то переходим в четвертое поле
Edit4.SetFocus;
end;
Для четвертого поля в событии OnChange напишем код:
procedure TForm1.Edit4Change (Sender: Tobject)
begin
//если в четвертом поле введено 5 символов
if length(Edit4.Text) = 5 then
//то переходим на кнопку
Button1.SetFocus;
end;
Как видно из примера, код обработчиков похож, но он не является универсальным для всех полей, так как в нем используются различные имена компонентов. Чтобы этот код был универсальным, нужно обращаться к компонентам не по имени, а используя дополнительные возможности Delphi. Рассмотрим эти возможности.
Обращение к текущему компоненту
Для обращения к текущему компоненту можно использовать запись:
Tтип_компонента(ActiveControl)
где тип компонента - это название компонента на панели компонентов. Если вы не помните название компонента, наведите указатель мыши на его кнопку на панели и в подсказке прочитайте его название.
К примеру:
TEdit(ActiveControl) - обращение к текущему компоненту как текстовому полю;
TButton(ActiveControl) - обращение к текущему компоненту как к кнопке;
TListbox(ActiveControl) - обращение к текущему компоненту как к списоку;
Переход между компонентами
Для перехода на нужный компонент существует стандартный метод:
имя_компонента.SetFocus; //не рекомендуется
Но этот метод требует указания имени компонента, что не рекомендуется делать в универсальных процедурах. В Delphi существует метод для перехода на следующий компонент относительно текущего:
SelectNext (ActiveControl, true, true);
Создание универсального кода для формы регистрации
Вернемся к нашей формы регистрации и попробуем написать универсальный код для перехода между компонентами при вводе.
Для первого поля в события OnChange введите код:
procedure TForm1.Edit1Click (Sender: TObject);
begin
//если длина текста текущего поля = 5
if length(TEdit(ActiveControl).Text) = 5 then
//перейти на следующий компонент
SelectNext(ActiveControl, true, true);
end;
Для того, чтобы для других полей на форме выполнялся такой же код, выделите эти поля и в события OnChange выберите из списка событие Edit1Change.
3. Обработка группы компонентов
Обращение к произвольному компоненту по его номеру
Каждый компонент на форме имеет порядковый номер (нумерация начинается с 0). Для обращения к компоненту по его номеру используют свойство:
Tтип_компонента(Controls [i])
где i - номер компонента. К примеру:
TEdit(Controls[0]) - обращение к первому компоненту, который является текстовым полем;
TButton(Controls[3]) - обращение к четвертому компоненту, который является кнопкой;
TListbox(Controls[4]) - обращение к пятому компоненту, который является списком.
Распознавания типа компонента
Обязательно следите за тем, чтобы тип компонента с конкретным номером был верным, иначе будет выдаваться сообщение об ошибке. Например, вы решили очистить первый компонент на форме с помощью команды:
TEdit(Controls[0]).Text := '';
Если первым компонентом окажется не текстовое поле, а, например, кнопка Button, то выдается ошибка. Это происходит потому, что кнопка не имеет свойства Text. Для решения этой проблемы необходимо перед обращунием к компоненту, проверить его тип с помощью команды:
if Controls[i].ClassName = 'TТип_компонента' then
Обратите внимание, что верхний регистр при указании типа компонента обязателен.
В нашем примере для обращения к полю можно ввести код:
if Controls[0].ClassName = 'TEdit' hen
TEdit(Controls[0]).Text := '';
Если первый компонент на форме окажется текстовым полем, то он будет очищен. Если же первый компонент на форме окажется не текстовым полем, то код не сработает, и ошибка не возникнет.
Обработка набора полей в цикле
При написании кода часто приходится обрабатывать несколько компонентов на форме. В этом случае можно использовать цикл:
for i: = 0 to ControlCount-1 do
. . .Controls [i]. . .
В цикле используется свойство формы ControlCount, которое хранит количество всех компонентов на форме. Так как компоненты на форме нумеруются с 0, то цикл выполняется до ControlCount-1.
Приведенный цикл пройдет по всем компонентам на форме, но среди компонентов может быть много разнотипных: надписи, текстовые поля, флажки и т.д. Поэтому, если надо пройти по всем компонентам, но обработать только компоненты нужного класса, то в цикле используют оператор if.
for i: = 0 to ControlCount-1 do
if Controls [i].ClassName = 'ТТип_компонента' then
. . .Controls [i]. . .
Пример. Пусть имеется форма вида:

Для кнопки "Очистить" напишем код, который очищает все текстовые поля на форме.
procedure TForm1.Button1Click (Sender: TObject);
var i: integer;
begin
for i: = 0 to ControlCount-1 do
if Controls[i].ClassName = 'TEdit' then
TEdit(Controls[i]).Text := '';
end;
Пометка компонентов
Иногда при написании универсального кода надо среди однотипных компонентов отделить некоторые из них. Например, пусть на предыдущей форме поле с результатом очищать не нужно. Для этого это поле надо как-то пометить. Для пометки можно использовать свойство Tag, которую имеет каждый компонент. Это свойство может хранить произвольное целое значение и по умолчанию всегда равен 0. Если в это свойство ввести значение, отличное от 0, и затем проверять значение свойства в коде, то соответствующий компонент можно исключать из обработки.
Для обращения к этому свойству в коде используют запись:
Controls[i].Tag
В нашем случае можно для поля с результатом в свойства Tag отметить значение 1. Тогда код для кнопки "Удалить" будет иметь вид:
procedure TForm1.Button1Click (Sender: TObject);
var i: integer;
begin
for i: = 0 to ControlCount-1 do
if (Controls[i].ClassName = 'TEdit')
and (Controls[i].Tag <> 1) then
TEdit(Controls[i]).Text := '';
end;
4. Обработка компонентов как массива
Группу однотипных компонентов можно обрабатывать как массив.
Например, пусть имеется форма вида:

Для кнопки напишите код, который подсчитывает среднее значение только в заполненных полях и отображает результат в поле слева от кнопки. Для отделения поля с результатом от других полей, зададим в свойстве Tag этого поля значение 1.
Для кнопки напишем код:
procedure TForm1.Button1Click (Sender: TObject);
var i, kol: integer; s: real;
begin
//сначала количество и сумма уровне 0
kol: = 0;
s: = 0;
// в цикле проходим по всем компонентам
for i: = 0 to ControlCount-1 do
// если текущий компонент текстовое поле
if (Controls[i].ClassName = 'TEdit')
// если это не поле результата и поле не пустое
and (Controls[i].Tag <> 1) and (TEdit(Controls[i]).Text <> '') then
begin
// добавляем его к сумме
s := s + StrToFloat(TEdit(Controls[i]).Text);
// увеличиваем количество полей на 1
kol := kol + 1;
end;
// для выдачи результата надо найти поле со свойством Tag = 1
// в цикле проходим по всем компонентам
for i: = 0 to ControlCount-1 do
// если текущий компонент текстовое поле
if (Controls[i].ClassName = 'TEdit')
// если это поле результата (Tag = 1)
and (Controls[i].Tag = 1) then
begin
// если количество не 0 (поля в массиве заполнялись)
if kol > 0 then
// в поле результата отражаем среднее
// и округляем до 2 знаков
TEdit(Controls[i]).Text := FloatToStr(RoundTo(s / kol, -2))
// если количество равно 0 (поля в массиве НЕ заполнялись)
else
// поле результата очищается
TEdit(Controls[i]).Text := '';
//завершаем работу цикла
break;
end;
end;
Для работы этого кода подключите модуль Math (для функции RoundTo()).
5. Создание пользовательских модулей
Когда мы писали приложение для расчета выражения, то для использования некоторых алгебраических функций мы подключали модуль math. То есть существует возможность оформить нужные процедуры или функции в отдельный модуль, а затем вызывать их в программах после подключения модуля.
Такой же подход можно использовать и для своих подпрограмм. Для создания модуля необходимо:
- выполните команду File - New - Unit. В первой строке после ключевого слова Unit введите имя модуля. Обязательно сохраните модуль в файл под именем, совпадающим с заданным именем модуля;
- после ключевого слова interface создайте раздел подключения основных модулей. Для этого перейдите в код формы и скопируйте оператор uses в код вашего модуля;
- ниже в разделе interface введите заголовок процедуры или функции, которые нужно включить в модуль. При этом, если в коде планируется обращаться к компонентам и их свойствам, то в качестве входного параметра обязательно укажите имя формы. Например:
//опишем заголовок процедуры для очистки текстовых полей
//в качестве входного параметра указывается имя формы
procedure clear (f:TForm);
- в разделе implementation введите код для реализации процедур или функций модуля. При этом имена компонентов указываются вместе с именем формы, задаваемой в качетве входного параметра. Например, опишем реализацию процедуры очистки всех полей на форме:
procedure clear (f:TForm);
var i: integer;
begin
for i: = 0 to f.ControlCount-1 do
if f.Controls[i].ClassName = 'TEdit' then
TEdit(f.Controls[i]).Text := '';
end;
- после отладки скопируйте созданный модуль в папку Lib с установленной средой Delphi (в этой папке, по умолчанию, размещаются все модули Delphi);
Пример. Пусть создан модуль с именем MyUnit.pas с процедурой clear (f:TForm). В новом проекте нанесите на форму любое число текстовых полей. Создайте кнопку для очистки полей.
В коде формы в разделе uses подключите модуль MyUnit.
Для кнопки напишите код:
//вызываем процедуру очистки полей на форме form1
clear(form1);
Если поля находятся на форме form2, то для их очистки процедура вызывается так:
//вызываем процедуру очистки полей на форме form2
clear(form2);
Вопросы для самоконтроля
1. Для чего создают универсальные подпрограммы в обработчиках событий?
2. Как обратиться к текущему компоненту на форме не по имени? Пример.
3. Какой метод используют для перехода на следующий компонент?
4. Как обратиться к произвольному компоненту на форме не по имени? Пример.
5. Как определить тип компонента на форме? Пример.
6. Как определить общее количество компонентов на форме?
7. Как обработать набор компонентов на форме?
8. Каким образом можно в группе однотипных компонентов отделить нужные?