Создание формы, компонентов на ней и управление обработчиками этих компонент в Run-Time
Создаю форму в ран-тайме и создаю на ней 3 Edit и 2 Button, подскажите как сделать обработчик этих компонентов.
Чтоб иметь доступ из этих кнопок с свойствам формы.
procedure cr_form;
var
F: TForm;
procedure cr_edit(x, y, order: integer);
var
E: TEdit;
begin
E := TEdit.Create(F);
try
E.Parent := F;
E.Name := 'Ed'+IntToStr(order);
E.Text := '';
E.TabOrder:=order;
E.Left := x;
E.Top := y;
E.Height := 25;
E.Width := 200;
finally
// E.Free;
end;
end;
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
16-01-2012 16:17
Полностью автоматическое формирование может понадобиться достаточно редко и действительно скорее в случаях вроде DatabaseDesktop. Однако полуавтоматический вариант может быть очень полезен. Например, в случае необходимости редактирования множества справочников без особых изысков. В этом случае вариант с одной универсальной формой, доводимой до ума под конкретный случай десятком строчек был бы горазд более предпочтителен, нежели пара десятков пар файлов pas+dfm.
В общем, вопрос показался мне интересным и я ради спортивного интереса наваял пример. Реализация не идеальна, это скорее набор идей, доведённый до минимально рабочего состояния, но пример полностью рабочий, готовый к немедленному применению и как минимум удовлетворяет критериям того, чего хотел автор вопроса.
Для удобства тестирования привожу все файлы проекта.
Project3.dpr
program Project3;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
RecEdit_Form in 'RecEdit_Form.pas' {RecEditForm},
Form_Utils in 'Form_Utils.pas';
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
procedure TForm1.Button1Click(Sender: TObject);
begin
with TRecEditForm.Create(DataSource1) do
try
Caption := 'Все поля горизонтально';
AddAllFields;
EditRec;
finally
Free;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
with TRecEditForm.Create(DataSource1) do
try
Caption := 'Все поля вертикально';
MakeVert;
VStep := VStep - 3;
AddAllFields;
EditRec;
finally
Free;
end;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
with TRecEditForm.Create(DataSource1) do
try
Caption := 'Комментарии автоматически по DisplayName';
AddField('FIRST_NAME');
AddField('LAST_NAME');
AddField('STATE');
AddField('CITY');
AddField('ADDRESS_1');
EditRec;
finally
Free;
end;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
with TRecEditForm.Create(DataSource1) do
try
Caption := 'Горизонтально, по умолчанию, свои комментарии';
AddField('FIRST_NAME', 'Имя');
AddField('LAST_NAME', 'Фамилия');
AddField('STATE', 'Штат');
AddField('CITY', 'Город');
AddField('ADDRESS_1', 'Адрес');
EditRec;
finally
Free;
end;
end;
procedure TForm1.Button5Click(Sender: TObject);
begin
with TRecEditForm.Create(DataSource1) do
try
Caption := 'Комментарий укорочен';
CaptionWidth := 50;
AddField('FIRST_NAME', 'Имя');
AddField('LAST_NAME', 'Фамилия');
AddField('STATE', 'Штат');
AddField('CITY', 'Город');
EditRec;
finally
Free;
end;
end;
procedure TForm1.Button6Click(Sender: TObject);
begin
with TRecEditForm.Create(DataSource1) do
try
Caption := 'Узкая форма, меньше шаг';
CaptionWidth := 50;
Width := 200;
VStep := VStep - 3;
AddField('FIRST_NAME', 'Имя');
AddField('LAST_NAME', 'Фамилия');
AddField('STATE', 'Штат');
AddField('CITY', 'Город');
EditRec;
finally
Free;
end;
end;
procedure TForm1.Button7Click(Sender: TObject);
begin
with TRecEditForm.Create(DataSource1) do
try
Caption := 'Вертикально, по умолчанию';
MakeVert;
AddField('FIRST_NAME', 'Имя');
AddField('LAST_NAME', 'Фамилия');
AddField('STATE', 'Штат');
AddField('CITY', 'Город');
EditRec;
finally
Free;
end;
end;
function TRecEditForm.EditRec: Boolean;
begin
FDataSet.Edit;
Result := (ShowModal = mrOk);
end;
procedure TRecEditForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if ModalResult = mrOk then
try
FDataSet.Post;
if FDataSet is TBDEDataSet then
TBDEDataSet(FDataSet).FlushBuffers;
except
ShowMessage('Ошибка при сохранении');
Action := caNone;
end
else
FDataSet.Cancel;
end;
16-01-2012 15:57 | Комментарий к предыдущим ответам
в общем случае с программой работает не программист, а неквалифицированный пользователь
В данном случае речь идет о заполнении справочников. Это уровень квалифицированного пользователя :)
16-01-2012 12:10 | Комментарий к предыдущим ответам
Однако мы пользуемся ObjectInspector'ом который умеет отображать структуру произвольного компонента.
Почему бы не работать подобным образом с записью произвольной базы? Там, ведь, тоже можно получить информацию о всех полях и их типах.
Потому что в общем случае с программой работает не программист, а неквалифицированный пользователь, страждущий дружелюбного интерфейса. И кстати, ObjectInspector позволяет настроить взаимоисключающие свойства, что тоже вряд ли понравится пользователю.
16-01-2012 10:46 | Комментарий к предыдущим ответам
Однако мы пользуемся ObjectInspector'ом который умеет отображать структуру произвольного компонента.
Почему бы не работать подобным образом с записью произвольной базы? Там, ведь, тоже можно получить информацию о всех полях и их типах.
В параметрической CAD-системе TFlex реализованы оба варианта для редактирования параметров фрагментов: стандартным образом генерируется табличка: имя параметра, тип, комментарий, значение, а, при необходимости, можно нарисовать уникальный диалог со всевозможными пояснениями, рисунками, комбобоксами и т.д.
16-01-2012 02:59 | Комментарий к предыдущим ответам
Я могу представить лишь один случай, когда автоматическая генерация формы по датасету удобна - программа типа Database Desktop, которой пользуются разработчики. Если программой пользуются обычные юзеры - лучше их все-таки пожалеть :)
16-01-2012 01:30 | Комментарий к предыдущим ответам
>>> Разве что для программы, работающей с произвольными данными
Для работы с произвольными данными — это пущая мелкомягкие делают: получается откровенная гадость, но все верят, что это круто. Я же говорил про ситуацию, когда "разнообразие датасетов не очень велико (при большом количестве самих форм)".
16-01-2012 00:44 | Комментарий к предыдущим ответам
Ну ты докопался!
Я докопался? Борланд раньше меня докопался, когда создал среду визуального программирования :)
Помню, как я на Клиппере создавал диалоги путем ручного описания координат каждого элемента. Бр-р-р... :)
реализовать правила автоматической генерации форм — вполне разумное решение.
Разве что для программы, работающей с произвольными данными, у которой не будет осмысленного с точки зрения обычного пользователя интерфейса (ну типа того же Акцесса).
16-01-2012 00:15 | Комментарий к предыдущим ответам
>>> То есть, удобнее 10-20 форм создавать через cr_edit, cr_button, cr_combobox, cr_dateedit, cr_label и т.п. с указанием координат каждого компонента, без возможности проверки корректности ввода?
Ну ты докопался! Знаешь такую вещь как MS Access? :D Вот там есть команда автоматической генерации формы по датасету. Иногда это даже удобно, хотя в общем случае результат, зачастую, получается весьма корявым и требует доработки напильником. Но если у тебя разнообразие датасетов не очень велико (при большом количестве самих форм), то реализовать правила автоматической генерации форм — вполне разумное решение.
если одна две формы то проще так, но если надо заполнять 10-20 разный справочников... то ни разу не удобно
То есть, удобнее 10-20 форм создавать через cr_edit, cr_button, cr_combobox, cr_dateedit, cr_label и т.п. с указанием координат каждого компонента, без возможности проверки корректности ввода? Ну, на вкус, на цвет товарища нет...
Дизайнтайм: Берем обычный грид. Устанавливаем два столбца.
Рантайм: Указываем число строк равное числу полей в базе. Первый столбец заполняем названиями полей.
Гораздо проще, чем в рантайм плодить эдиты.
В Дельфи7 есть и готовый компонент на два столбца, но названия я не помню, так как сам пользуюсь гридом в три столбца: во второй вывожу тип поля (в зависимости от него и подключается нужный обработчик).
интересная идея, сам чего не додумался не пойму...
спасибо, попробую реализовать
Дизайнтайм: Берем обычный грид. Устанавливаем два столбца.
Рантайм: Указываем число строк равное числу полей в базе. Первый столбец заполняем названиями полей.
Гораздо проще, чем в рантайм плодить эдиты.
В Дельфи7 есть и готовый компонент на два столбца, но названия я не помню, так как сам пользуюсь гридом в три столбца: во второй вывожу тип поля (в зависимости от него и подключается нужный обработчик).
я думал Сергей Перовский говорит о каком то компоненте, который позволяет редактировать записи предлагая изменять их отдельно от грида... но как я понимаю чудес не бывает ) буду писать как умею... если идея получится, то сделаю отдельный юнит с функциями и выложу сюда, людям на радость)
Сергей Перовский имел ввиду, что DBGrid может сам вывести все поля таблицы, даже если их количество заранее неизвестно. Но вряд ли это решение можно назвать универсальным. Иногда гораздо удобнее редактировать запись в отдельной форме. Которую, естественно, гораздо удобнее создать прямо в Дельфи, а не мучаться с рантайм.
Для начала посмотрите примеры работы с DBGrid.
Если явным образом не указано иное, выводятся все столбцы указанной таблицы.
Если нужно отображать отдельную запись, то существует компонент, аналогичный ObjectInspector'у.
На память названия не скажу.
Там придется прописывать имена параметров, но это проще, чем создавать кучу эдитов.
Наконец, можно сделать самому на основе StringGrid'а.
13-01-2012 05:51Вы не первый, кому нужно работать с таблицами, заранее неизвестной структуры.
Вовсе не обязательно создавать Edit-ы для этой цели.
Для этих целей существуют специальные компоненты.
не знал что такие компоненты существуют, подскажите пожалуйста какие?
Вы не первый, кому нужно работать с таблицами, заранее неизвестной структуры.
Вовсе не обязательно создавать Edit-ы для этой цели.
Для этих целей существуют специальные компоненты.
ну так создайте еще одну форму с эдитами и кнопками...
перед ее показом заполняйте (если необходимо) поля "по умолчанию", а после "Ок" вносите данные с формы в БД
with form2 do
begin
edit1.text:='Иванов';
if ShowModal = mrOk then <Обработка введенных данных>
end;
хочу заполнять через форму родителя справочники, а динамическая форма будет иметь Едиты чтоб данные вводить а при нажатии Ок введенные данные передаются в Родителя и там проверяются и заносятся в базу...
Не очень удачная идея. Вид динамически созданной формы непредсказуем, например, при смене разрешения у винды. Да и программировать такие формы весьма непросто. Не для того ли придумали визуальную среду программирования, чтобы "рисовать" формы на экране, а не мучительно прописывать их в коде? Кроме того, проверку введенных данных и их занесение в базу правильно организовывать не в родителе, а в самой форме ввода данных. А то получится, что юзер ввел 100 полей, нажал "ОК", а форма выведа сообщение "Не указан номер телефона" и удалилась вместе со всеми введенными данными :)
хочу заполнять через форму родителя справочники, а динамическая форма будет иметь Едиты чтоб данные вводить а при нажатии Ок введенные данные передаются в Родителя и там проверяются и заносятся в базу...
вот...
Я тоже не понял, зачем создавать форму в ран-тайм. Но если уж так надо - вовсе незачем делать обработчики для кнопок, достаточно назначить этим кнопкам соответствующий ModalResult.
Не пойдеть. Для создаваемой в рантайм формы сделайте класс-наследник от TForm, в него и поместите обработчик нажатия кнопки. Глобальную переменную выкиньте. А в самом обработчике обходитесь без переменной вообще, поскольку форма будет доступна, естественно, как Self. Такие вещи как явное обращение к Form3 в методах TForm3 (т.е. использование глобальной переменной вместо Self по умолчанию) - моветон.
>>>я переменную F объявил локально, а надо было глобально...
А вот тут поподробнее... Глобальные переменные вообще вещь ограниченного пользования, а как она к Вашей проблеме относится, если все в одном месте делается?
>>> подскажите как сделать обработчик этих компонентов
Я так понял, что речь идет об обработчиках событий, так? Назначать обработчики так же как и задавать любое другое значение процедурного типа. Через оператор присваивания.
Свойство TButton.OnClick (а именно это свойство содержит обработчик данного события) имеет тип TNotifyEvent. Если Вы заглянете в хелп или исходнки VCL, то можете найти, что этот тип определяется следующим образом
type
TNotifyEvent = procedure (Sender: TObject) of object;
То есть обработчиком можно назначить любой метод класса procedure с одним единственным параметром типа TObject.
Допустим, в Вашей форме TForm определен метод, который должен быть обработчиком события нажатия на кнопку
type
Form1 = class(TForm)
// ...
public
procedure MyOnClickEvent(Sender: TObject);
// ...
end;
Тогда Вы можете написать, например, вот так:
procedure cr_but(x, y, order: integer; Cap: string);
var
B: TButton;
begin
B := TButton.Create(F);
try
B.Parent := F;
B.OnClick := F.MyOnClickEvent;
// ...
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.