Здравствуйте уважаемые коллеги!
Создание визуальных (и не визуальных) компонентов в режиме RunTime и работа с ними может показаться не простой задачей для начинающих программистов. В данной статье, на примере своеобразного каклькулятора будет продемонстрирована работа с RunTime-созданными компонентами.
Описание будущей программы: по количеству, указанному пользователем, будет создавать N выпадающих списков, в которых можно выбрать математическое действие: сложение, вычитани, умножение, деление. И N полей ввода сумм. В зависимости от выбранного действия будет подсчитываться Итог.
Начнем с создания интерфейса.
Кидаем на форму панель TPanel на неё 2 кнопки TButton, 2 лейбла TLabel и 1 поле TEdit.
Выстраиваем их в следующей последовательности:
1) TLabel — Caption: Создать;
2) TEdit — Name: eCmpCount; Text: 0;
3) TLabel — Caption: елементов;
4) TButton — Name: btnCreate; Caption: Создать;
5) TButton — Name: btnClear; Caption: Очистить.
В панели TPanel очистим Caption и свойство Align устанавливаем в alTop.
Добавляем еще 1 панель TPanel с 1 лейблом TLabel и 1 полем TEdit.
TPanel — очищаем Caption и Align устанавливаем в alBottom.
TLabel — Caption: Результат;
TEdit — Name: eResult; Text: 0; ReadOnly: True;
В середину кладем TScrollBox, называем sbElements и свойство Align устанавливаем в alClient.
На него кидаем 2 лейбла lblAction и lblSum с заголовками Действие и Сумма соответственно.
В итоге наша форма должна выглядеть примерно вот так:
В поле 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’; — Обнулим результат.
Запускаем — проверяем:
Сумма считается, все ОК!
Пример очень простой и расчитан на начинающего разработчика и если у вас есть вопросы по данной теме, можете писать их в комментариях или на почту: info@asd-soft.ru
Исходники примера можно скачать по ссылке: RunTimeComponents