Создание компонентов и работа с ними в режиме RunTime в Delphi.

Здравствуйте уважаемые коллеги!

Создание визуальных (и не визуальных) компонентов в режиме RunTime и работа с ними может показаться не простой задачей для начинающих программистов. В данной статье, на примере своеобразного каклькулятора будет продемонстрирована работа с RunTime-созданными компонентами.
Описание будущей программы: по количеству, указанному пользователем, будет создавать N выпадающих списков, в которых можно выбрать математическое действие: сложение, вычитани, умножение, деление. И N полей ввода сумм. В зависимости от выбранного действия будет подсчитываться Итог.

Начнем с создания интерфейса.
Кидаем на форму панель TPanel на неё 2 кнопки TButton, 2 лейбла TLabel и 1 поле TEdit.
Выстраиваем их в следующей последовательности:
1) TLabelCaption: Создать;
2) TEditName: eCmpCount; Text: 0;
3) TLabelCaption: елементов;
4) TButtonName: btnCreate; Caption: Создать;
5) TButtonName: btnClear; Caption: Очистить.
В панели TPanel очистим Caption и свойство Align устанавливаем в alTop.
Добавляем еще 1 панель TPanel с 1 лейблом TLabel и 1 полем TEdit.
TPanel — очищаем Caption и Align устанавливаем в alBottom.
TLabelCaption: Результат;
TEditName: eResult; Text: 0; ReadOnly: True;
В середину кладем TScrollBox, называем sbElements и свойство Align устанавливаем в alClient.
На него кидаем 2 лейбла lblAction и lblSum с заголовками Действие и Сумма соответственно.
В итоге наша форма должна выглядеть примерно вот так:
MainDesign
В поле eCmpCount мы будем указывать кол-во компонент, которое необходимо создать. Поэтому нужно добавить обработку на событие OnKeyPress поля eCmpCount, которое не будет давать вводить ничего кроме цифр.

procedure TfmMain.eCmpCountKeyPress(Sender: TObject; var Key: Char);
begin
  if not (Key in ['0'..'9', #8]) then
    Key := #0
end;

Если вводимый символ Key не входит в диапозон от 0 до 9 и не равняется #8 (Клавиша Backspace), то обнуляем вводимый символ.

Аналогично полю eCmpCount нам необходимо ограничивать ввод для создаваемых в RunTime полей ввода сумм, однако мы хотим вводить не только целые, но и дробные числа.
Код события будет выглядеть следующим образом:

private
  // Описываем процедуру в разделе Private
  procedure onSumEditKeyPress(Sender: TObject; var Key: Char); 
public
  { Public declarations }
end;
***
// и реализацию в разделе implementation
procedure TfmMain.onSumEditKeyPress(Sender: TObject; var Key: Char);
var
  fs: TFormatSettings;
begin
  fs := TFormatSettings.Create;
  if not (Key in ['0'..'9', #8, fs.DecimalSeparator]) then
    Key := #0
end;

В зависимости от настроек системы дробные числа можно писать через точку или через запятую. Что бы оградить от ошибок, получим эти настройки из специальной структуры:
TFormatSettings — Текущие настройки форматов системы.
fs.DecimalSeparator — разделитель целой и дробной части указанный в системе.

Перейдем непосредственно к созданию компонентов.
Для начала добавим глобальную переменную FTop: integer; для определения верхней границы создаваемого элемента.
На событие OnCreate формы задаим начальное значение FTop:

private
  FTop: integer;
  procedure onSumEditKeyPress(Sender: TObject; var Key: Char);
public
  { Public declarations }
end;

procedure TfmMain.FormCreate(Sender: TObject);
begin
  FTop := lblAction.Top + lblAction.Height + 10;
end;

Которое будет равняться верхней границе метки lblAction плюс её высота плюс отступ в 10 пикселей.

Теперь напишем код для кнопки «Создать».

procedure TfmMain.btnCreateClick(Sender: TObject);
var
  i,x: integer;
  cb: TComboBox;
  edt: TEdit;
begin
  x := StrToInt(eCmpCount.Text);
  if x > 0 then
    for i := 0 to x - 1 do begin
      cb := TComboBox.Create(sbElements);
      cb.Parent := sbElements;
      cb.Top := FTop;
      cb.Left := 8;
      cb.Width := 72;
      cb.Style := csDropDownList;
      cb.Items.Add('Выбрать');
      cb.Items.Add('+');
      cb.Items.Add('-');
      cb.Items.Add('*');
      cb.Items.Add('/');
      cb.ItemIndex := 0;
      cb.OnChange := calcTotal;

      edt := TEdit.Create(sbElements);
      edt.Parent := sbElements;
      edt.Top := FTop;
      edt.Left := cb.Left + cb.Width + 8;
      edt.OnKeyPress := onSumEditKeyPress;
      edt.OnChange := calcTotal;

      cb.Tag := Integer(Pointer(edt));

      Inc(FTop, cb.Height + 10);
    end;
end;

Разберем код по подробнее:
x := StrToInt(eCmpCount.Text); — Получаем кол-во елементов, которое нужно создать.
cb := TComboBox.Create(sbElements); — Создаем выпадающий список.
cb.Parent := sbElements; — В качестве родительского элемента указываем ScrollBox.
cb.Top := FTop; — Задаем верхнюю координату.
cb.Left := 8; — Левая граница.
cb.Width := 72; — Ширина.
cb.Style := csDropDownList; — Тип выпадающейго списка, который позволяет только выбрать из существующих элементов и не позволяет вводить значение вручную.
cb.Items.Add — Добавляем типы математических действий.
cb.ItemIndex := 0; — По умолчанию будет выбран пункт «Выбрать».
cb.OnChange := calcTotal; — На изменение значения будет вызываться событие calcTotal, код которого я напишу немного ниже.

edt := TEdit.Create(sbElements); — Создаем поле ввода сумм.
edt.Parent := sbElements; — В качестве родительского элемента указываем ScrollBox.
edt.Top := FTop; — Задаем верхнюю координату.
edt.Left := cb.Left + cb.Width + 8; — Левая граница высчитывается относительно выпадающего списка, как его левая граница плюс ширина плюс 8 пикселей отступа.
edt.OnKeyPress := onSumEditKeyPress; — Проверяем кооректный ввод данных.
edt.OnChange := calcTotal; — При изменении пересчитываем итоговую сумму.
cb.Tag := Integer(Pointer(edt)); — Сохраняем указатель на поле суммы в свойство Tag выпадающейго списка, тем самым как бы связывая их в пару.
Inc(FTop, cb.Height + 10); — Увеличиваем переменную верхней границы на высоту созданного выпадающего списка плюс отступ 10 пикселей.

Напишем код процедуры calcTotal, которая будет подсчитывать итог:

private
  FTop: integer;
  procedure onSumEditKeyPress(Sender: TObject; var Key: Char);
  procedure calcTotal(Sender: TObject);
public
***
procedure TfmMain.calcTotal(Sender: TObject);
var
  i: integer;
  res: currency;
begin
  res := 0;
  for i := 0 to sbElements.ComponentCount - 1 do
    if (sbElements.Components[i] is TComboBox)
      and (TComboBox(sbElements.Components[i]).ItemIndex > 0) then
      case TComboBox(sbElements.Components[i]).ItemIndex of
        1: res := res + StrToCurr(TEdit(Pointer(TComboBox(sbElements.Components[i]).Tag)).Text);
        2: res := res - StrToCurr(TEdit(Pointer(TComboBox(sbElements.Components[i]).Tag)).Text);
        3: res := res * StrToCurr(TEdit(Pointer(TComboBox(sbElements.Components[i]).Tag)).Text);
        4: res := res / StrToCurr(TEdit(Pointer(TComboBox(sbElements.Components[i]).Tag)).Text);
      end;
  eResult.Text := CurrToStr(res);
end;

res := 0; — Обнулим переменную результата.
for i := 0 to sbElements.ComponentCount — 1 do — Цикл для всех комонентов содержащихся в sbElements.
if (sbElements.Components[i] is TComboBox) — Если i-ый компонент пренадлежит к классу TComboBox
and (TComboBox(sbElements.Components[i]).ItemIndex > 0) then — и выбранный в нем элемент больше 0 (так как нулевой элемент в списке у нас «Выбрать»).
case TComboBox(sbElements.Components[i]).ItemIndex of — В зависимости от выбранного элемента в списке выполним действие:
Поле суммы получаем через указатель, который мы сохранили в свойстве Tag выпадающего списка при создании компонентов.
1: res := res + StrToCurr(TEdit(Pointer(TComboBox(sbElements.Components[i]).Tag)).Text);
2: res := res — StrToCurr(TEdit(Pointer(TComboBox(sbElements.Components[i]).Tag)).Text);
3: res := res * StrToCurr(TEdit(Pointer(TComboBox(sbElements.Components[i]).Tag)).Text);
4: res := res / StrToCurr(TEdit(Pointer(TComboBox(sbElements.Components[i]).Tag)).Text);
eResult.Text := CurrToStr(res); — Результат запишем в соответствующее поле. Функция CurrToStr переводит число типа currency в строку.

И последнее, что нам осталось это написать код для кнопки «Очистить»:

procedure TfmMain.btnClearClick(Sender: TObject);
var
  i: integer;
begin
  for i := sbElements.ComponentCount - 1 downto 0 do
    if (sbElements.Components[i] <> lblAction) and (sbElements.Components[i] <> lblSum) then
      sbElements.Components[i].Free;
  FTop := lblAction.Top + lblAction.Height + 10;
  eResult.Text := '0';
end;

for i := sbElements.ComponentCount — 1 downto 0 do — Выполняем цикл для всех компонентов содержащихся в sbElements в обратном порядке.
if (sbElements.Components[i] <> lblAction) and (sbElements.Components[i] <> lblSum) then — Если это не лейблы «Действие» и «Сумма»
sbElements.Components[i].Free; — Уничтожим этот компонент.
FTop := lblAction.Top + lblAction.Height + 10; — Отсчет верхней границы вернем в исходное состояние.
eResult.Text := ‘0’; — Обнулим результат.

Запускаем — проверяем:
MainRunning
Сумма считается, все ОК!
Пример очень простой и расчитан на начинающего разработчика и если у вас есть вопросы по данной теме, можете писать их в комментариях или на почту: info@asd-soft.ru

Исходники примера можно скачать по ссылке: RunTimeComponents

Статья добавлена в Delphi. Добавить ссылку в закладки.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *