Динамическое создание визуальных компонентов (Часть 2)

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

Дата: 15.08.2009 г.

Рейтинг: 0

Метки:

Пошаговая инструкция:
Задача:
Создать n компонентов с возможностью редактирования некоторой информации.

Решение:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Button1: TButton;
    Button2: TButton;
    ScrollBox1: TScrollBox;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    procedure my_procedure(Sender:TObject);
    { Private declarations }
  public
    procedure my_write_caption(p_text:String;p_num:Integer);
    { Public declarations }
  end;

var
  Form1: TForm1;
  mylabels:array of TLabel;
  mybuttons:array of TButton;
  mypanels:array of TPanel;
implementation

uses Unit2;

{$R *.dfm}

procedure TForm1.my_write_caption(p_text:String;p_num:Integer);
begin
  mylabels[p_num].Caption := p_text;
  mybuttons[p_num].Left := 20 +  mylabels[p_num].Width;
end;

procedure TForm1.my_procedure(Sender:TObject);
begin
  if Sender is TButton then
    with (Sender as TButton) do
      begin
        Form2.Edit1.Text := mylabels[StrToInt(Hint)].Caption;
        Form2.num_of_element := StrToInt(Hint);
      end;
  Form2.ShowModal;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ListBox1.Items.Add('Запись'+IntToStr(ListBox1.Items.Count));
end;

procedure TForm1.Button2Click(Sender: TObject);
var i:Integer;
begin
  if listbox1.Items.Count > 0  then
     begin
      SetLength(mylabels,listbox1.Items.Count);
      SetLength(mybuttons,listbox1.Items.Count);
      SetLength(mypanels, listbox1.Items.Count)
     end;
  for i := 0 to listbox1.Items.Count - 1 do
    begin
      mylabels[i] := TLabel.Create(self);
      mybuttons[i] := TButton.Create(self);
      mypanels[i] := TPanel.Create(self);
      with mypanels[i] do
        begin
          Parent  := ScrollBox1;
          Caption := Listbox1.Items.Strings[i];
          Height  := 26;
          Width   := 120;
          Top     := 6 + (i * 26);
          Left    := 10;
          Caption := '';
          Visible := True
        end;
      with mylabels[i] do
        begin
          Parent  := mypanels[i];
          Caption := Listbox1.Items.Strings[i];
          Top     := 2;
          Left    := 2;
          Visible := True
        end;
      with mybuttons[i] do
        begin
          Parent  := mypanels[i];
          Height  := 24;
          Width   := 42;
          Top     := 2;
          Left    := 20 + mylabels[i].Width;
          Hint    := IntToStr(i);
          onclick := my_procedure;
          Caption := 'Edit';
          Visible := True
        end;
    end;

end;

end.


unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm2 = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    Button2: TButton;
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    num_of_element:Integer;
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

uses Unit1;

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
begin
  Form1.my_write_caption(Edit1.Text, num_of_element);
  Form2.Close;
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
  Form2.Close;
end;

end.


Внешний вид:

Динамическое создание визуальных компонентов (Часть 2)



Описание решения:
Для отображения информации используем шаблон включающий в себя три компонента Label,Button и Panel. На последнем и будут размещены два предыдущих. О создании компонентов в Run-Time я уже писал в первой части, поэтому некоторые моменты я описывать не буду. В данном случае до обращения программы к списку мы не можем знать сколько нам нужно будет компонентов, поэтому мы используем динамические массивы, для каждого из компонентов. Начнем с того, что мы объявим эти массивы в классе TForm1.
var
  Form1: TForm1;
  mylabels:array of TLabel;
  mybuttons:array of TButton;
  mypanels:array of TPanel;


Объявляем мы их в классе формы для того, чтобы массивы компонентов были доступны для любой дочерней процедуры или функции класса TForm1.
Как видите на форме изначально есть две кнопки для добавления записи в список и для построения, в соответствии со списком, визуальных компонентов. Для добавления записи в список используем процедуру:
procedure TForm1.Button1Click(Sender: TObject);
begin
  ListBox1.Items.Add('Запись'+IntToStr(ListBox1.Items.Count));
end;


Процедура Button2Click требует более подробного рассмотрения =) Для работы с массивами нам нужно указать их длину:
  if listbox1.Items.Count > 0  then
     begin
      SetLength(mylabels,listbox1.Items.Count);
      SetLength(mybuttons,listbox1.Items.Count);
      SetLength(mypanels, listbox1.Items.Count)
     end;

После задания длинны массивов начинаем с ними работать. С помощью цикла:
for i := 0 to listbox1.Items.Count - 1 do
    begin
      mylabels[i] := TLabel.Create(self);
      mybuttons[i] := TButton.Create(self);
      mypanels[i] := TPanel.Create(self);
      with mypanels[i] do
        begin
          Parent  := ScrollBox1;
          Caption := Listbox1.Items.Strings[i];
          Height  := 26;
          Width   := 120;
          Top     := 6 + (i * 26);
          Left    := 10;
          Caption := '';
          Visible := True
        end;
      with mylabels[i] do
        begin
          Parent  := mypanels[i];
          Caption := Listbox1.Items.Strings[i];
          Top     := 2;
          Left    := 2;
          Visible := True
        end;
      with mybuttons[i] do
        begin
          Parent  := mypanels[i];
          Height  := 24;
          Width   := 42;
          Top     := 2;
          Left    := 20 + mylabels[i].Width;
          Hint    := IntToStr(i);
          onclick := my_procedure;
          Caption := 'Edit';
          Visible := True
        end;


Работу с одной позицией можно разбить на четыре основных действия:
1. Фактическое создание компонентов
      mylabels[i] := TLabel.Create(self);
      mybuttons[i] := TButton.Create(self);
      mypanels[i] := TPanel.Create(self);



2. Описание свойств панели
      with mypanels[i] do
        begin
          Parent  := ScrollBox1;
          Caption := Listbox1.Items.Strings[i];
          Height  := 26;
          Width   := 120;
          Top     := 6 + (i * 26);
          Left    := 10;
          Caption := '';
          Visible := True
        end;



3.Описание свойств лейблы
      with mylabels[i] do
        begin
          Parent  := mypanels[i];
          Caption := Listbox1.Items.Strings[i];
          Top     := 2;
          Left    := 2;
          Visible := True
        end;



4.Описание свойств кнопки
      with mybuttons[i] do
        begin
          Parent  := mypanels[i];
          Height  := 24;
          Width   := 42;
          Top     := 2;
          Left    := 20 + mylabels[i].Width;
          Hint    := IntToStr(i);
          onclick := my_procedure;
          Caption := 'Edit';
          Visible := True
        end;


Теперь нам нужно подумать, как заставить кнопку выполняющею вызов формы для редактирования лейблы, передавать параметры лейблы соответствующей нажатой кнопке. Но my_procedure корректно компилируется только с параметром Sender типа TObject. Поетому я решил сделать это так. Надеюсь вы обратили внимание, что в описании каждого элемента из массива кнопок в параметр Hint программа вписывала его порядковый номер (это и есть нам так необходимый параметр). Теперь осталось вытащить его и обработать в нашей my_procedure.
procedure TForm1.my_procedure(Sender:TObject);
begin
  if Sender is TButton then
    with (Sender as TButton) do
      begin
        Form2.Edit1.Text := mylabels[StrToInt(Hint)].Caption;
        Form2.num_of_element := StrToInt(Hint);
      end;
  Form2.ShowModal;
end;



Процедура сопровождается всего одним параметром и для динамики мы будем его использовать. По сути Sender указывает на того, кто привел нас к этой процедуре, именно это нам и нужно. Проверяем пришли мы к my_procedure через кнопку и если да, то мы работаем с Sender, как с кнопкой
if Sender is TButton then
    with (Sender as TButton) do


От сендера, как от кнопки, нам нужен только параметр Hint и мы с помощью хинта передаем на вторую форму данные для редактирования и возвращения измененного значения записи.
Form2.Edit1.Text := mylabels[StrToInt(Hint)].Caption;
Form2.num_of_element := StrToInt(Hint);


Ну а теперь ко второй форме:
Form2.ShowModal;


После редактирования значения записи вызывается процедура записи с первой формы.
Form1.my_write_caption(Edit1.Text, num_of_element);


Ну и собственно сама процедура записи и смещения кнопки, здесь я думаю ничего сложного нет.
procedure TForm1.my_write_caption(p_text:String;p_num:Integer);
begin
  mylabels[p_num].Caption := p_text;
  mybuttons[p_num].Left := 20 +  mylabels[p_num].Width;
end;



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

Удачных проб!

С уважением, Сургай Владимир.



Похожие статьи: