Delphi MDI + компоненты в режиме Run-Time

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by Sams, 20 May 2010.

  1. Sams

    Sams Member

    Joined:
    18 Apr 2009
    Messages:
    247
    Likes Received:
    70
    Reputations:
    17
    Раньше никогда не было потребности, но вот сейчас понадобился такой прекрасный "режим" как Run-Time, тобишь создание компонентов/окон в уже запущенной программе.

    1. Начнём с компонентов:
    Создаю я например компонент TEdit вот таким способом

    Code:
    [COLOR=YellowGreen]var[/COLOR]
    [COLOR=DarkOrange]x: TEdit;[/COLOR]
    
    [COLOR=YellowGreen]procedure TForm1.Button1Click(Sender: TObject);
    begin[/COLOR]
    [COLOR=DarkOrange]x := TEdit.Create(self);
    x.Parent := Form1;
    x.Left := 10;
    x.Top := 10;[/COLOR]
    [COLOR=YellowGreen]end;[/COLOR]
    Если много раз клацать на кнопку, то создаётся много текстовых полей (правда в данной примере они налаживаются друг на друга, то как координаты всегда будут (10; 10), но я проверял, заменив координаты переменными, которые при каждом нажатии увеличивал). И собственно от сюда вытекает вопрос:

    Какие имена присваиваются второму, третьему и т.д. полям, ведь я указал имя "x" ( var x: TEdit; ) то есть это имя первого поля, а остальные-то не могут быть названы так же :confused:
    Я думал, может x[номер поля], но ничего подобного.


    ---------------------------------------------------------------------------------------------------------

    С этим вопросом разобрался, вот ответ:
    Как положено - разобрался сам, помоги другим. Потому чуток пораскинув мозгами и почитав дополнительную литературу, пришел к выводу, что работать с компонентами не обязательно через массив.
    Можно сделать по такой схеме:
    1. Создаём компонент.
    2. Заносим в объект TList указатель на этот компонент.
    3. Для обращения к любому из компонентов вытягиваем указатель из TList.

    Таким образом мы не ограничиваемся размером массива.

    Покажу на примере (кинем на форму не одну кнопку, а две. Одна будет создавать поля, а другая - поменяет текст в определенном поле):

    Code:
    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure FormCreate(Sender: TObject);
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
      a, y: integer;
    [COLOR=YellowGreen]  ListComp: TList;[/COLOR]
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
    [COLOR=YellowGreen]ListComp := TList.Create;[/COLOR]
    a := 10;
    y := 10;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
    x: TEdit;
    
    begin
    x := TEdit.Create(self);
    x.Parent := Form1;
    x.Top := y;
    x.Left := a;
    [COLOR=YellowGreen]ListComp.Add(x);[/COLOR]
    a := a + 20;
    y := y + 20;
    end;
    
    [COLOR=YellowGreen]procedure TForm1.Button2Click(Sender: TObject);
    begin
    TEdit(CompList.Items[2]).Text := 'lol'; //Эта строка меняет текст в третьем текстовом поле. Номер текстового поля указываем в CompList.Items[номер полня] (счет ведется с нуля).
    end;[/COLOR]
    
    end.
    Создаём первой кнопкой несколько полей (больше, чем 3) и клацаем вторую кнопку - меняется содержимое третьего поля.


    2. Проблема с дочерними MDI окнами:

    1. Создаю основную форму ( Form.Style := fsMDIForm ).
    2. Создаю MDI форму ( From.Style := fsMDIChild ).
    3. В Project - Options перекидываю MDIchild форму в раздел Available forms.
    4. По идее на основной форме нужно создать кнопку с кодом:

    Code:
    [COLOR=DarkOrange]<имя дочерней формы> := TChildForm.Create(Owner);[/COLOR]
    Но надо как-то связать основную форму с MIDChild. Пробовал прописать в разделе uses модуль с MIDChild, но нихрена не дало :mad:

    ---------------------------------------------------------------------------------------------------------

    С этим вопросом тоже разобрался, вот ответ:

    Такс, с MDI окнами тоже разобрался.
    Создаём две формы - Form1, Form2.
    Form1.FormStyle := fsMDIForm;
    Form2.FormStyle := fsMDIChild;

    В Unit1, в разделе uses дописываем Unit2:
    uses
    ......, Unit2;

    На Form1 кидаем две кнопки (первая создаёт окна, вторая меняет заголовок определенного ока):

    Code:
    procedure TForm1.Button1Click(Sender: TObject);
    begin
    Form2 := TForm2.Create(Owner);
    end;
    
    procedure TForm1.Button2Click(Sender: TObject);
    begin
    Form1.MDIChildren[2].Caption := 'Woohoo'; [COLOR=YellowGreen]// Номер MDI окна указываем тут MDIChildren[/COLOR][номер окна]
    end;
    
     
    #1 Sams, 20 May 2010
    Last edited: 20 May 2010
  2. GhostOnline

    GhostOnline Active Member

    Joined:
    20 Dec 2008
    Messages:
    723
    Likes Received:
    110
    Reputations:
    22
    Насчет первого вопроса - x это всего лишь название переменной а не компонента. Название компонента можно установить/прочитать так: x.Name
    Насчет второго не скажу, с MDI никогда не работал, ибо не нужно было
    Но убери создание, т.к. эта форма у тебя уже в avalable forms значит по идее создается автоматически. Просто "имя дочерней формы".Show или как там для mdi окон

    Сорри, ступил.
    Значит так, следующий код показал дочернее mdi окно у меня, при том что окно этой формы в avalaible forms:
    Form2 := TForm2.Create(self);
    Form2.Show;
    где TForm2 - класс вторичной формы и свойства выставлены как у тебя
     
    #2 GhostOnline, 20 May 2010
    Last edited: 20 May 2010
  3. Sams

    Sams Member

    Joined:
    18 Apr 2009
    Messages:
    247
    Likes Received:
    70
    Reputations:
    17
    Это немного не то. Например я таким образом создал 5 полей, как мне изменить свойство Text в 4-ом поле?! Ведь имя я указывал только одно - "х".

    Это показать лишь одно, а если мне надо создать штук 10 динамично на основе одного класса с одним именем, и получать к ним доступ через свойство MDIChildren[номер]?

    ----------------------------------------

    Добавлено:
    В общем насколько я понял, в моём примере, runtime заключается в создании неограниченного количества однотипных объектов имеющих одно общее имя, обращение к которым происходит при помощи определенного свойства имеющего структуру массива.
     
    #3 Sams, 20 May 2010
    Last edited: 20 May 2010
  4. GhostOnline

    GhostOnline Active Member

    Joined:
    20 Dec 2008
    Messages:
    723
    Likes Received:
    110
    Reputations:
    22
    Создавая поле, присваивай ему уникальное имя Name и обращайся к нему по имени :D
    procedure TForm1.Button2Click(Sender: TObject);
    var x : TEdit;
    begin
    x := TEdit.Create(self);
    x.Name := 'Edit1';
    x.Parent := self;
    end;

    procedure TForm1.Button3Click(Sender: TObject);
    begin
    TEdit(FindComponent('Edit1')).Text := 'I''ve found you!';
    end;

    Еще раз повторяю: х - название переменной, а не компонента
     
  5. Sams

    Sams Member

    Joined:
    18 Apr 2009
    Messages:
    247
    Likes Received:
    70
    Reputations:
    17
    Но ведь ты же обращаешься к компоненту именно через имя этой переменной. Я не полный баран в делфи, да и программирую не первый год, просто никогда не работал с runtime.
     
  6. GhostOnline

    GhostOnline Active Member

    Joined:
    20 Dec 2008
    Messages:
    723
    Likes Received:
    110
    Reputations:
    22
    Если так, то почему не заюзаешь обычный массив?
    Edits : array of TEdit :confused:
    Я то подумал у тебя нестандартная задача, и предложил нестандартное решение

    ПС я программирую меньше года
     
  7. Sams

    Sams Member

    Joined:
    18 Apr 2009
    Messages:
    247
    Likes Received:
    70
    Reputations:
    17
    Как написано в книге (которую именно сейчас я держу в руках и листаю), создается неограниченное количество МДИ окон с одним именем, а потом обращение к ним происходит через свойство MDIChildren[номер окна]
     
  8. Sams

    Sams Member

    Joined:
    18 Apr 2009
    Messages:
    247
    Likes Received:
    70
    Reputations:
    17
    В случае с компонентами так и делают - создают массив, но мне интересно, делфи шпарит до посинения при нажатии на кнопку текстовые поля, какие тогда у них имена, как к ним обращаться? С массивом всегда успею сделать :)
     
  9. GhostOnline

    GhostOnline Active Member

    Joined:
    20 Dec 2008
    Messages:
    723
    Likes Received:
    110
    Reputations:
    22
    Все это делается через обычные динамические массивы

    Блин..
    есть переменная например edit
    делая так edit := TEdit.Create(self); ты создаешь компонент и эта переменная указыват на этот компонент.
    Если ты повторишь эту процедуру - создашь новый компонент и эта переменная будет указывать уже на новый, а старый "потеряется". Откуда дельфе знать, может тебе надоело это поле и ты решил его выкинуть? А вот Name - уникальное, дельфи не даст тебе создать 2 компонента с одним именем. Так что если не хочешь использовать массивы - делай через Name как я тебе написал. Все, вопросов после этого остаться не должно я думаю.
     
    #9 GhostOnline, 20 May 2010
    Last edited: 20 May 2010
  10. Sams

    Sams Member

    Joined:
    18 Apr 2009
    Messages:
    247
    Likes Received:
    70
    Reputations:
    17
    Ну тут без создания массива. Создается лишь одна переменная типа TChildForm;
     
  11. Sams

    Sams Member

    Joined:
    18 Apr 2009
    Messages:
    247
    Likes Received:
    70
    Reputations:
    17
    Вот тут-то ты и не прав.

    Кинь на форму один button:

    Code:
    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure FormCreate(Sender: TObject);
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
      a, y: integer;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
    a := 10;
    y := 10;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
    x: TEdit;
    
    begin
    x := TEdit.Create(self);
    x.Parent := Form1;
    x.Top := y;
    x.Left := a;
    a := a + 20;
    y := y + 20;
    end;
    
    end.
    И клацни раз 5 на кнопку...И ты увидишь 5 текстовых полей, а не 1, которое удаляет предыдущее и пересоздается.
     
  12. GhostOnline

    GhostOnline Active Member

    Joined:
    20 Dec 2008
    Messages:
    723
    Likes Received:
    110
    Reputations:
    22
    Ты тролль?
    Где я написал что старый компонент удалится?
    Потеряется ссылка на него!
    А конкретнее х будет указывать уже на новый компонент, а к старому ты никак не сможешь обратится, только по имени
    Почитай про модель объектных ссылок в дельфи
     
    1 person likes this.
  13. Sams

    Sams Member

    Joined:
    18 Apr 2009
    Messages:
    247
    Likes Received:
    70
    Reputations:
    17
    Нет, эльф 80-го уровня :D
    Чуть тупонул. Всё, с этим вопросом я разобрался. Спасибо, понял.
     
  14. Sams

    Sams Member

    Joined:
    18 Apr 2009
    Messages:
    247
    Likes Received:
    70
    Reputations:
    17
    Как положено - разобрался сам, помоги другим. Потому чуток пораскинув мозгами и почитав дополнительную литературу, пришел к выводу, что работать с компонентами не обязательно через массив.
    Можно сделать по такой схеме:
    1. Создаём компонент.
    2. Заносим в объект TList указатель на этот компонент.
    3. Для обращения к любому из компонентов вытягиваем указатель из TList.

    Таким образом мы не ограничиваемся размером массива.

    Покажу на примере чуть выше (кинем на форму не одну кнопку, а две. Одна будет создавать поля, а другая - поменяет текст в определенном поле):

    Code:
    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure FormCreate(Sender: TObject);
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
      a, y: integer;
    [COLOR=YellowGreen]  ListComp: TList;[/COLOR]
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
    [COLOR=YellowGreen]ListComp := TList.Create;[/COLOR]
    a := 10;
    y := 10;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
    x: TEdit;
    
    begin
    x := TEdit.Create(self);
    x.Parent := Form1;
    x.Top := y;
    x.Left := a;
    [COLOR=YellowGreen]ListComp.Add(x);[/COLOR]
    a := a + 20;
    y := y + 20;
    end;
    
    [COLOR=YellowGreen]procedure TForm1.Button2Click(Sender: TObject);
    begin
    TEdit(CompList.Items[2]).Text := 'lol'; //Эта строка меняет текст в третьем текстовом поле. Номер текстового поля указываем в CompList.Items[номер полня] (счет ведется с нуля).
    end;[/COLOR]
    
    end.
    Создаём первой кнопкой несколько полей (больше, чем 3) и клацаем вторую кнопку - меняется содержимое третьего поля.
     
    #14 Sams, 20 May 2010
    Last edited: 20 May 2010
  15. Sams

    Sams Member

    Joined:
    18 Apr 2009
    Messages:
    247
    Likes Received:
    70
    Reputations:
    17
    Такс, с MDI окнами тоже разобрался.
    Создаём две формы - Form1, Form2.
    Form1.FormStyle := fsMDIForm;
    Form2.FormStyle := fsMDIChild;

    В Unit1, в разделе uses дописываем Unit2:
    uses
    ......, Unit2;

    На Form1 кидаем две кнопки (первая создаёт окна, вторая меняет заголовок определенного ока):

    Code:
    procedure TForm1.Button1Click(Sender: TObject);
    begin
    Form2 := TForm2.Create(Owner);
    end;
    
    procedure TForm1.Button2Click(Sender: TObject);
    begin
    Form1.MDIChildren[2].Caption := 'Woohoo'; [COLOR=YellowGreen]// Номер MDI окна указываем тут MDIChildren[/COLOR][номер окна]
    end;
    
    Обновил первый пост.
    Тему можно закрывать.