Создание компонентов во время выполнения - Delphi

StackOverflow https://stackoverflow.com/questions/1005373

  •  05-07-2019
  •  | 
  •  

Вопрос

Как я могу создать компонент во время выполнения, а затем работать с ним (изменяя свойства и т.д.)?

Это было полезно?

Решение

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

Для невизуальных компонентов

var
  C: TMyComponent;
begin
  C := TMyComponent.Create(nil);
  try
    C.MyProperty := MyValue;
    //...
  finally
    C.Free;
  end;
end;

Для визуальных компонентов:

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

var
  C: TMyVisualComponent;
begin
  C := TMyVisualComponent.Create(Self);
  C.Left := 100;
  C.Top := 100;
  C.Width := 400;
  C.Height := 300;
  C.Visible := True;
  C.Parent := Self; //Any container: form, panel, ...

  C.MyProperty := MyValue,
  //...
end;

Несколько пояснений к приведенному выше коду:

  • При установке владельца компонента (параметра конструктора) компонент уничтожается при уничтожении формы-владельца.
  • Установка Parent свойство делает компонент видимым.Если вы забудете об этом, ваш компонент не будет отображаться.(Это легко пропустить :) )

Если ты хочешь множество компонентов вы можете сделать то же самое, что описано выше, но в цикле:

var
  B: TButton;
  i: Integer;
begin
  for i := 0 to 9 do
  begin
    B := TButton.Create(Self);
    B.Caption := Format('Button %d', [i]);
    B.Parent := Self;
    B.Height := 23;
    B.Width := 100;
    B.Left := 10;
    B.Top := 10 + i * 25;
  end;
end;

Это добавит 10 кнопок к левой границе формы.Если вы захотите изменить кнопки позже, вы можете сохранить их в виде списка.(Список компонентов это лучше всего подходит, но также взгляните на предложения из комментариев к этому ответу)

Как назначить обработчики событий:

Вы должны создать метод обработчика событий и присвоить его свойству event.

procedure TForm1.MyButtonClick(Sender: TObject);
var
  Button: TButton;
begin
  Button := Sender as TButton; 
  ShowMessage(Button.Caption + ' clicked');
end;

B := TButton.Create;
//...
B.OnClick := MyButtonClick;

Другие советы

Чтобы упростить процесс создания компонентов среды выполнения, вы можете использовать GExperts . <Ол>

  • Создайте компонент (или несколько компонентов) визуально и установите его свойства.
  • Выберите один или несколько компонентов и выполните GExperts, Компоненты для кода.
  • Вставьте сгенерированный код в ваше приложение.
  • Удалите компонент (ы) из визуального конструктора форм.
  • Пример (код создания TButton, сгенерированный таким образом):

    var
      btnTest: TButton;
    
    btnTest := TButton.Create(Self);
    with btnTest do
    begin
      Name := 'btnTest';
      Parent := Self;
      Left := 272;
      Top := 120;
      Width := 161;
      Height := 41;
      Caption := 'Component creation test';
      Default := True;
      ParentFont := False;
      TabOrder := 0;
    end;
    

    Я просто хотел бы добавить это при динамическом добавлении элементов управления ... было бы неплохо добавить их в список объектов (TObjectList), как предложено в < 1 > @Despatcher.

    procedure Tform1.AnyButtonClick(Sender: TObject);
    begin
      If Sender is TButton then
      begin
        Case Tbutton(Sender).Tag of 
        .
        .
        .
    // Or You can use the index in the list or some other property 
    // you have to decide what to do      
    // Or similar :)
      end;
    end;
    
    procedure TForm1.BtnAddComponent(Sender: TObJect)
    var
      AButton: TButton;
    begin
      AButton := TButton.Create(self);
      Abutton. Parent := [Self], [Panel1] [AnOther Visual Control];
      AButton.OnClick := AnyButtonClick;
    // Set Height and width and caption ect.
      .
      .
      . 
      AButton.Tag := MyList.Add(AButton);
    end;
    

    Вам необходимо добавить юнит 'Contnrs' в ваш список использования. I.e System.Contnrs.pas базовый блок контейнеров И вы можете иметь много списков объектов. Я предлагаю использовать TObjectList для каждого типа элемента управления, который вы используете например.

    Interface
     Uses Contnrs;
    Type
     TMyForm = class(TForm)
    private
       { Private declarations }
    public
       { Public declarations }
    end;
     Var
      MyForm: TMyForm;
      checkBoxCntrlsList: TObjectList; //a list for the checkBoxes I will createin a TPanel
      comboboxCntrlsList: TObjectList; //a list of comboBoxes that I will create in some Form Container
    

    это позволяет вам легко манипулировать / управлять каждым элементом управления, поскольку вы будете знать, к какому типу управления это относится, например.

    Var comboBox: TComboBox;
    I: Integer;
    
    begin
     For I = 0 to comboboxCntrlsList.Count -1 do // or however you like to identify the control you are accessing such as using the tag property as @Despatcher said
       Begin
        comboBox := comboboxCntrlsList.Items[I] as TComboBox;
        ...... your code here
       End;
    end;
    

    Это позволяет вам затем использовать методы и свойства этого элемента управления. Не забудьте создать списки TObjectLists, возможно, в форме события создания ...

    checkBoxCntrlsList := TObjectList.Create;
    comboboxCntrlsList := TObjectList.Create;
    
      

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

    Ответ был предложен - самый простой способ - это список объектов (компонентов). TObjectList является самым простым в использовании (в единицах подключения). Списки отличные!

      In Form1 Public
      MyList: TObjectList;
      procedure AnyButtonClick(Sender: TObject); 
    

    // Вы можете получить более сложные и объявить // TNotifyevents и назначить их, но давайте сделаем это просто :)   ,   ,   .

    procedure Tform1.AnyButtonClick(Sender: TObject);
    begin
      If Sender is TButton then
      begin
        Case Tbutton(Sender).Tag of 
        .
        .
        .
    // Or You can use the index in the list or some other property 
    // you have to decide what to do      
    // Or similar :)
      end;
    end;
    
    procedure TForm1.BtnAddComponent(Sender: TObJect)
    var
      AButton: TButton;
    begin
      AButton := TButton.Create(self);
      Abutton. Parent := [Self], [Panel1] [AnOther Visual Control];
      AButton.OnClick := AnyButtonClick;
    // Set Height and width and caption ect.
      .
      .
      . 
      AButton.Tag := MyList.Add(AButton);
    end;
    

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

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

    Во время исследования & создания формы delphi с использованием шаблона на основе xml " я нашел кое-что полезное, указывая RTTI и используя открытые инструменты api (я думаю, ToolsApi.pas). Посмотрите на интерфейсы в устройстве.

    Очень легко. Звоните Создать. Пример:

    procedure test
    var
      b : TButton;
    begin
      b:=TButton.Create(nil);
      b.visible:=false;
    end;
    

    Это создает компонент (TButton - это компонент) во время выполнения и устанавливает свойство видимым.

    <Ч>

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

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

    Если метод содержит код инициализации, ваше приложение может показывать непредвиденное поведение при создании во время выполнения. В этом случае проверьте, использовал ли средство записи компонента этот метод.

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

    Это пример того, как эмулировать тег кнопки в Evernote

    unit Unit7;
    
    interface
    
    uses
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,Vcl.Graphics,
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, CHButton, Vcl.ExtCtrls, RzPanel, CHPanel, RzCommon,RzBmpBtn, Vcl.StdCtrls;
    
    type
      // This is panel Button
      TButtonClose = class (TRzPanel)
       CloseButton : TRzBmpButton;
       procedure CloseButtonClick(Sender: TObject);
       procedure CloseButtonMouseEnter(Sender: TObject);
       procedure MouseDown(Sender: TObject; Button: TMouseButton;
                 Shift: TShiftState; X, Y: Integer);
       procedure MouseUp(Sender: TObject; Button: TMouseButton;
                 Shift: TShiftState; X, Y: Integer);
    public
       constructor Create(AOwner: TComponent); override;
       destructor Destroy; override;
    end;
    
    TForm7 = class(TForm)
       CHButton1: TCHButton;
       RzPanel1: TRzPanel;
       RzBmpButton1: TRzBmpButton;
       procedure CHButton1Click(Sender: TObject);
       procedure RzBmpButton1Click(Sender: TObject);
       procedure RzPanel1MouseDown(Sender: TObject; Button: TMouseButton;
         Shift: TShiftState; X, Y: Integer);
       procedure RzPanel1MouseUp(Sender: TObject; Button: TMouseButton;
         Shift: TShiftState; X, Y: Integer);
       procedure RzPanel1MouseEnter(Sender: TObject);
       procedure RzBmpButton1MouseEnter(Sender: TObject);
       procedure FormMouseEnter(Sender: TObject);
       procedure FormCreate(Sender: TObject);
    private
      { Private declarations }
    public
      { Public declarations }
    end;
    
    var
      Form7: TForm7;
      MyCloseButton : TButtonClose;
    
    implementation
    
    {$R *.dfm}
    
    // constructor for on the fly component created
    constructor TButtonClose.Create(AOwner: TComponent);
    begin
       inherited Create(AOwner);
    
       // Set Events for the component
       Self.OnMouseEnter := Self.CloseButtonMouseEnter;
       Self.OnMouseDown := Self.MouseDown;
       Self.OnMouseUp := Self.MouseUp;
       Self.Height := 25;
    
       // Close button on top panel Button
       // Inherited from Raize Bitmap Button
       CloseButton := TRzBmpButton.Create(self);
       // Set On Click Event for Close Button
       CloseButton.OnClick := Self.CloseButtonClick;
       // Place Close Button on Panel Button
       CloseButton.Parent := self;
       CloseButton.Left := 10;
       CloseButton.Top := 5;
       CloseButton.Visible := False;
       // Setting the image for the button
       CloseButton.Bitmaps.Up.LoadFromFile(ExtractFilePath(Application.ExeName)+'\close.bmp');
    end;
    
    procedure TButtonClose.CloseButtonClick(Sender: TObject);
    begin
       // Free the parent (Panel Button)
       TControl(Sender).Parent.Free;
    end;
    
    procedure TButtonClose.CloseButtonMouseEnter(Sender: TObject);
    begin
       // Show the Close button
       CloseButton.Visible := True;
    end;
    
    procedure TButtonClose.MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
       // Emulate Button down state, since it is panel
       TRzPanel(Sender).BorderOuter := fsLowered;
    end;
    
    procedure TButtonClose.MouseUp(Sender: TObject; Button: TMouseButton;
     Shift: TShiftState; X, Y: Integer);
    begin
       // Emulate Button up state, since it is panel
       TRzPanel(Sender).BorderOuter := fsRaised;
    end;
    
    destructor TButtonClose.Destroy;
    begin
       inherited Destroy;
    end;
    
    procedure TForm7.FormCreate(Sender: TObject);
    begin
       // Create Panel Button on the fly
       MyCloseButton := TButtonClose.Create(self);
       MyCloseButton.Caption := 'My Button';
       MyCloseButton.Left := 10;
       MyCloseButton.Top := 10;
       // Don't forget to place component on the form
       MyCloseButton.Parent := self;
    end;
    
    procedure TForm7.FormMouseEnter(Sender: TObject);
    begin
       if Assigned(RzBmpButton1) then
          RzBmpButton1.Visible := False;
    
       // Hide when mouse leave the button
       // Check first if myCloseButton Assigned or not before set visible property
       if Assigned(MyCloseButton.CloseButton) then
          MyCloseButton.CloseButton.Visible := False;
    end;
    
    procedure TForm7.RzBmpButton1Click(Sender: TObject);
    begin
       TControl(Sender).Parent.Free;
    end;
    
    procedure TForm7.RzBmpButton1MouseEnter(Sender: TObject);
    begin
       RzBmpButton1.Visible := True;
    end;
    
    procedure TForm7.RzPanel1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
      TRzPanel(Sender).BorderOuter := fsLowered;
    end;
    
    procedure TForm7.RzPanel1MouseEnter(Sender: TObject);
    begin
       RzBmpButton1.Visible := True;
    end;
    
    procedure TForm7.RzPanel1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
       TRzPanel(Sender).BorderOuter := fsRaised;
    end;
    
    procedure TForm7.CHButton1Click(Sender: TObject);
    begin
       FreeAndNil(Sender);
    end;
    
    end.
    
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top