PDA

Просмотр полной версии : Delphi MDI + компоненты в режиме Run-Time


Sams
20.05.2010, 15:26
Раньше никогда не было потребности, но вот сейчас понадобился такой прекрасный "режим" как Run-Time, тобишь создание компонентов/окон в уже запущенной программе.

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

var
x: TEdit;

procedure TForm1.Button1Click(Sender: TObject);
begin
x := TEdit.Create(self);
x.Parent := Form1;
x.Left := 10;
x.Top := 10;
end;

Если много раз клацать на кнопку, то создаётся много текстовых полей (правда в данной примере они налаживаются друг на друга, то как координаты всегда будут (10; 10), но я проверял, заменив координаты переменными, которые при каждом нажатии увеличивал). И собственно от сюда вытекает вопрос:

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

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

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

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

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

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;
ListComp: TList;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
ListComp := TList.Create;
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;
ListComp.Add(x);
a := a + 20;
y := y + 20;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
TEdit(CompList.Items[2]).Text := 'lol'; //Эта строка меняет текст в третьем текстовом поле. Номер текстового поля указываем в CompList.Items[номер полня] (счет ведется с нуля).
end;

end.

Создаём первой кнопкой несколько полей (больше, чем 3) и клацаем вторую кнопку - меняется содержимое третьего поля.


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

<имя дочерней формы> := TChildForm.Create(Owner);

Но надо как-то связать основную форму с MIDChild. Пробовал прописать в разделе uses модуль с MIDChild, но нихрена не дало :mad:

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

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

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

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

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

procedure TForm1.Button1Click(Sender: TObject);
begin
Form2 := TForm2.Create(Owner);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
Form1.MDIChildren[2].Caption := 'Woohoo'; // Номер MDI окна указываем тут MDIChildren[номер окна]
end;

GhostOnline
20.05.2010, 15:58
Насчет первого вопроса - x это всего лишь название переменной а не компонента. Название компонента можно установить/прочитать так: x.Name
Насчет второго не скажу, с MDI никогда не работал, ибо не нужно было
Но убери создание, т.к. эта форма у тебя уже в avalable forms значит по идее создается автоматически. Просто "имя дочерней формы".Show или как там для mdi окон

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

Sams
20.05.2010, 16:11
Насчет первого вопроса - x это всего лишь название переменной а не компонента. Название компонента можно установить/прочитать так: x.Name
Это немного не то. Например я таким образом создал 5 полей, как мне изменить свойство Text в 4-ом поле?! Ведь имя я указывал только одно - "х".

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

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

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

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

GhostOnline
20.05.2010, 16:17
Это немного не то. Например я таким образом создал 5 полей, как мне изменить свойство Text в 4-ом поле?! Ведь имя я указывал только одно - "х".
Создавая поле, присваивай ему уникальное имя 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;

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

Sams
20.05.2010, 16:21
Еще раз повторяю: х - название переменной, а не компонента
Но ведь ты же обращаешься к компоненту именно через имя этой переменной. Я не полный баран в делфи, да и программирую не первый год, просто никогда не работал с runtime.

GhostOnline
20.05.2010, 16:26
Я не полный баран в делфи, да и программирую не первый год, просто никогда не работал с runtime.
Если так, то почему не заюзаешь обычный массив?
Edits : array of TEdit :confused:
Я то подумал у тебя нестандартная задача, и предложил нестандартное решение

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

Sams
20.05.2010, 16:28
Создавая поле, присваивай ему уникальное имя Name и обращайся к нему по имени
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;

Как написано в книге (которую именно сейчас я держу в руках и листаю), создается неограниченное количество МДИ окон с одним именем, а потом обращение к ним происходит через свойство MDIChildren[номер окна]

Sams
20.05.2010, 16:31
В случае с компонентами так и делают - создают массив, но мне интересно, делфи шпарит до посинения при нажатии на кнопку текстовые поля, какие тогда у них имена, как к ним обращаться? С массивом всегда успею сделать :)

GhostOnline
20.05.2010, 16:32
Как написано в книге (которую именно сейчас я держу в руках и листаю), создается неограниченное количество МДИ окон с одним именем, а потом обращение к ним происходит через свойство MDIChildren[номер окна]
Все это делается через обычные динамические массивы

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

Sams
20.05.2010, 16:34
Ну тут без создания массива. Создается лишь одна переменная типа TChildForm;

Sams
20.05.2010, 16:49
Если ты повторишь эту процедуру - создашь новый компонент и эта переменная будет указывать уже на новый, а старый "потеряется"
Вот тут-то ты и не прав.

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

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, которое удаляет предыдущее и пересоздается.

GhostOnline
20.05.2010, 16:53
Ты тролль?
Где я написал что старый компонент удалится?
Потеряется ссылка на него!
А конкретнее х будет указывать уже на новый компонент, а к старому ты никак не сможешь обратится, только по имени
Почитай про модель объектных ссылок в дельфи

Sams
20.05.2010, 16:54
Ты тролль?
Нет, эльф 80-го уровня :D
Чуть тупонул. Всё, с этим вопросом я разобрался. Спасибо, понял.

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

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

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

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;
ListComp: TList;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
ListComp := TList.Create;
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;
ListComp.Add(x);
a := a + 20;
y := y + 20;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
TEdit(CompList.Items[2]).Text := 'lol'; //Эта строка меняет текст в третьем текстовом поле. Номер текстового поля указываем в CompList.Items[номер полня] (счет ведется с нуля).
end;

end.

Создаём первой кнопкой несколько полей (больше, чем 3) и клацаем вторую кнопку - меняется содержимое третьего поля.

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

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

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

procedure TForm1.Button1Click(Sender: TObject);
begin
Form2 := TForm2.Create(Owner);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
Form1.MDIChildren[2].Caption := 'Woohoo'; // Номер MDI окна указываем тут MDIChildren[номер окна]
end;


Обновил первый пост.
Тему можно закрывать.