Лекция № 3
Тема: «Программное создание и удаление компонентов на форме»

 
План

    1. Динамическое создание компонентов

    2. Динамическое удаление компонентов

    3. Пример

1. Динамическое создание компонентов

  Динамически создаваемые компоненты - это компоненты, место в памяти под которые выделяется по мере необходимости в процессе работы приложения. Этим они и отличаются от компонентов, которые помещаются на Форму при проектировании приложения. Возможность создавать компоненты динамически это очень большое удобство для программиста. Например, можно создавать в цикле сразу много однотипных компонентов, формируя из них массив, которым в дальнейшем очень просто управлять.

   Все компоненты, как объекты, имеют множество свойств, определяющих их работу. При установке компонента на Форму из палитры большинство этих свойств определяются системой Delphi автоматически. При создании динамического компонента программист должен описать и настроить их вручную. Посмотрим, как это делается.

   Прежде всего, для появления динамически создаваемого компонента нужно выделить под него место в памяти. Выделением места в памяти компьютера под любой компонент занимается конструктор типа объекта этого компонента - метод Create. Для этого сначала нужно описать переменную нужного типа, а затем для выделения памяти воспользоваться методом Create. Метод Create имеет параметр Owner, определяющий так называемого "владельца" для создаваемого компонента.

   Хотя на самом деле владелец нужен не для создания, а для уничтожения компонента. То есть, при уничтожении компонента-владельца происходит автоматическое уничтожение всех компонентов, у которых он указан в качестве владельца.   При обычной установке компонента из палитры система делает владельцем этого компонента Форму. Проще всего поступать так же. Однако можно указать в качестве владельца сам этот компонент, воспользовавшись в качестве параметра ключевым словом Self.

   Когда компонент создан, то есть место в памяти под него выделено, можно задавать значения параметрам этого объекта. Прежде всего, это ещё один компонент, так называемый "родитель". Компонент-родитель будет отвечать за отрисовку нашего динамически создаваемого компонента. Это значит, что новый компонент появится в границах компонента-родителя. Естественно, компонент не может быть родителем для самого себя. Имя компонента-родителя просто присваивается свойству Parent создаваемого динамически компонента.    

Вот общая схема "конструирования" динамически создаваемого компонента:

 

var Component: TComponent;//Описать переменную для компонента

begin      

    Component:=TComponent.Create(Owner);//Задать владельца      

    Component.Parent:=Parent;//Задать родителя    

end;

 

   На этом создание компонента можно считать законченным, и он успешно появляется (или "не появляется", если он не визуальный!) в приложении. Остальные свойства будут присвоены ему по умолчанию самой системой Delphi.

   Естественно, для визуальных компонентов значения таких свойств по умолчанию как положение на Форме, ширина, высота мало кого устроят. Нужные размеры и положение компонента также придётся задать программисту.

   Например, создадим динамически поле Memo, зададим ему размеры, координаты расположения и текст по умолчанию. Пусть он появляется на Форме по нажатию кнопки:   

 

procedure TForm1.Button1Click(Sender: TObject);

//описываем переменную класса TMemo

var memo: TMemo;

begin   

    //в переменной создаем экземпляр компонента (владелец - форма)   

    memo:=TMemo.Create(Form1);   

    //для компонента родитель - форма       

    memo.Parent:=Form1;   

    //положение компонента на форме   

    memo.Left:=50;   

    memo.Top:=50;   

    //размеры компонента   

    memo.Width:=250;   

    memo.Height:=100;

    //текст по умолчанию   

    memo.Text:='Доброе утро!';

end; 

 

Результат работы представлен на рисунке.

 

Каждый компонент также имеет свойство Name. По умолчанию Delphi присвоит ему типовое имя с присвоением очередного порядкового номера: Memo1. Программист при создании компонента также может присвоить свойству Name нужное значение, например:

 

  Memo.Name:='myMemo';

 

  К данному компоненту можно обращаться как по этому имени, так и с указанием переменной, с помощью которой он был создан (в нашем случае -  Memo). Естественно, такая переменная должна быть глобальной.

 

Подключение модулей для работы с компонентами

Для работы с любым компонентом необходимо в коде формы подключать соответствующий модуль.  Если форма проектируется вручную, то при добавлении с помощью мыши компонентов на форму, автоматически происходит подключение нужных модулей. Если компоненты на форме появляются динамически, то может возникнуть ситуация, когда компилятор не сможет распознать тип компонента (TEdit, TMemo, TButton и др.) Это произойдет потому, что Вы не добавляли этот компонент на форму вручную и, соответственно, модуль для этого компонента не подключен.

Для решения проблемы разместите на форме хотя бы один из компонентов, которые планируется создавать динамически. В результате в коде будет подключен нужный модуль. Теперь удалите созданный компонент. При этом имя модуля в коде удалено не будет и вы сможете создавать компоненты динамически.

 

Присваивание обработчиков событий.

Выше мы рассмотрели как динамически создавать компоненты и задавать их свойства. Помимо свойств компоненты также имеют события. Если вам нужно создать обработчик события для динамически созданного компонента, то необходимо выполнять следующие действия:

- вручную нанесите на форму нужный компонент и создайте для него нужный обработчик;

- удалите компонент с формы. При этом текст обработчика останется в коде формы;

- для динамически созданного компонента выполните присваивание созданного обработчика нужному событию с помощью команды:

 

Component.Событие:=имя_обработчика;

 

Например, пусть имеется обработчик события под именем Button1Click. Динамически создадим кнопку и присвоим ей этот обработчик:

 

//описываем переменную класса TButton

var but:TButton;

begin

    //создаем экземпляр кнопки

    but:=TButton.Create(Form1);

    //задаем родителя кнопки

    but.Parent:=Form1;

    //задаем положение на форме

    but.Left:=10;

    but.Top:=10;

    //задаем размер

    but.Width:=40;

    but.Height:=20;

    //задаем надпись на кнопке

    but.Caption:='Кнопка 1';

    //задаем обработчик для события OnClick

    but.OnClick:=Button1Click;

end;

 2. Динамическое удаление компонентов

Компоненты, создаваемые динамически, обычно должны удаляться с формы, как только в них отпадает надобность. Для этого каждый компонент имеет стандартный метод:

 

Component.Free;

 

Если на форме создано большое число компонентов, то удалить их все можно с помощью цикла по массиву Controls.

 

Controls[i].Free;

 

3. Пример

Пусть на форме имеется кнопка, которая создает 10 текстовых полей и вторую кнопку. Поля разместим по горизонтали в строку с интервалом 10 точек и отступом от верхнего края в 10 точек. Кнопку разместим под первым текстовым полем с интервалом по вертикали в 10 точек . Все динамически созданные компоненты нужно отделить от остальных. Для этого зададим им свойство Tag=1.

 

procedure TForm1.Button1Click(Sender: TObject);

var

//переменная для работы с текстовыми полями

edit: TEdit;

//переменная для работы с кнопкой

button:TButton;

//счетчик цикла

i: integer;

//положение очередной кнопки по горизонтали

x: integer;

begin

    //первое поле отступает от левого края на 10 точек

    x:=10;

    //запускаем цикл для 10 полей

    for I := 1 to 10 do

    begin

        //создаем экземпляр текстового поля

        edit:=Tedit.Create(Form1);

        //задаем родителя для поля

        edit.Parent:=Form1;

        //задаем положение поля по горизонтали

        edit.Left:=x;

        //по вертикали поле отступает на 10 точек

        edit.Top:=10;

        //задаем произвольный размер поля

        edit.Width:=30;

        edit.Height:=20;

        //маркируем динамический компонент

        edit.Tag:=1;

        //смещаем координату по горизонтали

        //(+ширина поля + следующий отступ на 10 точек)

        x:=x+edit.width+10;

    end;

 

    //создаем экземпляр кнопки

    button:=TButton.Create(Form1);

    //задаем родителя кнопки

    button.Parent:=Form1;

    //по горизонтали кнопка отступает на 10 точек (отступ первого поля)

    button.Left:=10;

    //по вертикали кнопка отступает от первого поля на 10 точек

    //(отступ поля (10 точек) + высота поля (20 точек) + отступ кнопки (10 точек)

    button.Top:=10+20+10;

    //задаем размер кнопки

    button.Width:=50;

    button.Height:=20;

    //задаем надпись на кнопке

    button.Caption:='Очистить';

    //маркируем динамический компонент

    button.Tag:=1;

    //событию OnClick присваиваем имя обработчика события

    button.OnClick:=Clear;

end;

 

Для динамически созданной кнопки нужно создать обработчик события, который удалит с формы все созданные компоненты. Напоминаю, что различить их мы можем по свойству Tag=1.

Для создания обработчика события OnClick для кнопки в разделе Interface кода модуля формы, внутри класса формы найдите заголовок обработчика такого же события для button1. Скопируйте его и поменяйте имя на Clear. Должно получиться так:

 

 procedure Clear(Sender: TObject);

 

Далее, в разделе Implementation найдите заголовок процедуры обработчика события OnClick для кнопки Button1. Скопируйте его и измените имя на Clear. Должно получиться так:

 

procedure TForm1.Clear(Sender: TObject);

begin

end;

 

Внутри созданного обработчика напишем код, который удаляет с формы все компоненты, у которых свойство Tag=1.

 

procedure TForm1.Clear(Sender: TObject);

var

//счетчик цикла

i:integer;

//номер текущего компонента на форме

pos:integer;

begin

    //начинаем с первого компонента (нумерация с 0)

    pos:=0;

    //запускаем цикл по всем компонентам формы

    for i:=0 to ControlCount - 1 do

    begin

        //если очередной компонент имеет свойство Tag=1

        if Controls[pos].Tag=1 then

        begin

            //удаляем компонент с формы

            Controls[pos].free;

            //корректируем позицию компонентов в массиве с учетом удаления

            pos:=pos-1;

        end;

        //переходим к следующему компоненту

        pos:=pos+1;

    end;

end;

 

Вопросы для самоконтроля

1. Как динамически создать компонент на форме и задать для него свойства?

2. Как подключить нужный модуль при динамическом создании компонентов на форме?

3. Как создать обработчик события для динамического компонента и присвоить его нужному компоненту?

4. Как удалить компонент с формы? Как удалить несколько компонентов с формы?