Здравствуйте, уважаемые рыцари!
На форме ScrollBox, на нем панели с размещенными на них некоторым (разным) количеством
объектов следующих классов, это будут: TSpeedButton, TPanel, TEdit, TStringGrid, TScrollBox...
В run-time, по нажатию на один из SpeedButton'ов на любой из панелей должна быть
создана точная копия (только, H будет +dH) этой самой панели и всех ее объектов
(размеры, цвета и т.д.) вместе с их функциональностью. Данные копировать не надо,
они будут другие.
Сейчас я делаю так (попробовал сделать - получилось):
Cначала создаю саму "несущую панель" панель, там где нужно (копирую все параметры
с исходной).
Затем в цикле для каждого Control'а на исходной панели проверяю его тип и создаю
соответсвующий Control на новой панели. Каждое свойство "старого" объекта по
одному копирую на новый объект:
if Control is TStringGrid
then
begin
OldStrG:=TStringGrid(Control);
NewStrG:=TStringGrid.Create(PanelNew);
// NewStrG.Assign(OldStrG);
NewStrG.Parent:=PanelNew;
NewStrG.Top:=OldStrG.Top;
... строк 15, наверно, будет ...
end
else
if Control is T... then begin ... end
else if ...
...
Задумался, что это не есть хорошо и вот я здесь... Есть ли красивый или может
быть правильный, способ клонирования? Чтоб после этого, новый экземпляр жил
своей маленькой жизнью и всех радовал?
Попробовал, Assign, получил: Debugger Exception Notification
---------------------------
Project MejPlan.exe raised exception class EConvertError with message
'Cannot assign a TStringGrid to a TStringGrid'. Process stopped.
Use Step or Run to continue.
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
14-07-2009 15:22 | Сообщение от автора вопроса
Фреймы оказались очень кстати, сейчас осваиваюсь. Вопросы есть - решаю как могу, может быть не самыми оптимальными способами, но пока ощущение комфортное. Тупиковых ситуаций еще не было :).
Минус небольшой чувствую в том, что везде теперь эти фреймы надо прописывать явно, т.е. так:
var
aFrame1: TFrame1;
aFrame2: TFrame2;
aFrame3: TFrame3;
aFrame4: TFrame4; // они все разные и
aFrame5: TFrame5; // приходится делать кучу условий в каждом обработчике,
а раньше было так:
aPanel: TPanel; // любая панель!
Существует много разных приёмов, о которых вы можете не догадываться, но которые могут сильно облегчить жизнь. Если вы опишите, что именно вам нужно от этих фреймов, как вы их хотите использовать и какие трудности встречаете на своём пути, то, возможно, вам смогут подсказать более оптимальное решение. Например, у меня есть большие сомнения по поводу необходимости использования переменных для обращения к динамически создаваемым фреймам...
Не стал тянуть - переделал на фреймы :). Насоздавал в design-time каких нужно фреймов (шаблонов), все визуальное оптимизировал - получилось всего 5 "шаблонов". Теперь все экземпляры фреймов создаю только в run-time, когда и там, где это необходимо. Все поля пустые, у StringGrid'ов при Create фрейма заполняются только заголовки. Owner - у всех форма.
Минус небольшой чувствую в том, что везде теперь эти фреймы надо прописывать явно, т.е. так:
var
aFrame1: TFrame1;
aFrame2: TFrame2;
aFrame3: TFrame3;
aFrame4: TFrame4; // они все разные и
aFrame5: TFrame5; // приходится делать кучу условий в каждом обработчике,
а раньше было так:
aPanel: TPanel; // любая панель! С тем, что на ней работал через FindControl, а для этого придумал способ именования объектов, используя, так сказать, корни, например, Edit, суффиксы (все одинаковые для объектов одного клона, например 1r1) и окончания (_1p1). Придумать было нелегко:), но, достаточно легко использовать (не напрягаясь, собирал циклами имя любого объекта, например: 'Edit' или 'Label' + '2p3_2r1_2'). Но чтобы что-то изменить в desine - тяжело, "фиг", извиняюсь за французский, запомнишь все это.
Сейчас все очень наглядно: aFrame2.EditDate.Text ... :)))
Клонировать и правда можно гораздо проще, чем присваиванием полей, но я так понял, Вам значения потом придется стирать если они должны быть пусты. На всякий случай, привел пример с использованием потоков (TMemoryStream), любой TControl формы клонируется в OnClick. Зато размеры всех клеточек грида сохраняются и т.д. А если копия должна выглядеть так, как разработано в design time и данные не должны сохраняться, лучше TFrame правда не найти.
type
THackControl = class(TControl)
end;
procedure TForm1.ControlClick(Sender: TObject);
var
MemoryStream: TMemoryStream;
NewControlName, TempName: string;
OldControl, NewControl: TControl;
ControlClass: TControlClass;
begin
if not (Sender is TControl) then
Exit;
OldControl := Sender as TControl;
ControlClass := TControlClass(OldControl.ClassType);
MemoryStream := TMemoryStream.Create;
try
MemoryStream.WriteComponent(OldControl);
MemoryStream.Position := 0;
NewControl := ControlClass.Create(Self);
TempName := OldControl.Name;
try
OldControl.Name := '';
MemoryStream.ReadComponent(NewControl);
with NewControl do
begin
Left := OldControl.Left + 5;
Top := OldControl.Top + 5;
Parent := Self;
Name := Format('Panel%d_%d', [Left, Top]);
THackControl(NewControl).OnClick := ControlClick;
end;
finally
OldControl.Name := TempName;
end;
finally
MemoryStream.Free;
end;
end;
Скажу я, вам, Frame'ы рулез!!! Жаль, что раньше не разобрался с ними. В одной из книжек я встретил такую формулировку (не цитата): "Используйте фрйэймы всегда, когда это необходимо, НЕ ДАРОМ они расположены в стандартных компонентах ПЕРВЫМИ."!!!
Удивительная память человека: "посмотрел" как первокласник на букварь на палитру и вспомнил, как это было 9 лет назад, фразу вспомнил почти дословно, потому-что удивился тогда такой категоричности. Необходимости в них не было, все эти годы занимался программами внешне скромными. Тогда попробовал - не получилось... и все, "осадочек" остался... И знаете что - вот, сейчас жалею себя - столько времени потратил зря :)!!! Теперь сам всем буду говорить: "Пользуйтесь фреймами"! В следующей версии будут фреймы :))). А эту всю свою "шелуху" вычищу начисто! Вот уж действительно, спасибо!
Да, пожалуй, есть хороший смысл owner'ом делать форму! Сейчас я не уверен, надо ли переделывать, но когда буду писать выгрузку данных станет понятно... На сколько часто панели будут создаваться и уничтожаться, тут вопрос к заказчикам, мне так кажется, что или никогда - все данные сразу грузятся... или два раза в месяц - только одна дополнительная панель, но клиент всегда прав! И пусть ему будет хорошо, и это искренне! И пусть панелей будет столько, сколько надо для его работы. Спасибо за помощь!
>>> Бел Амор, Вы не, ту строчку скопировали, создаю объект: NewStrG:=TStringGrid.Create(PanelNew),
Хм... Действительно... Поторопился... :)
>>> На сколько я понимаю, Owner уничтожит своих подопечных, когда ему самому придет время?... У меня уже сомнение появилось...
Owner - тот, кто будет уничтожать компоненты, находящиеся в его списке Components при своём уничтожении. Все копоненты, которые помещаются на форму в дизайнере, автоматически имеют Owner'ом форму. В список Components формы попадают как визуальные компоненты (TPanel, TEdit и др.), так и невизуальные (TTable, TDataSource и т.д.). Parent - тот, на ком визуально "лежит" элемент управления. В его списке Controls содержатся уже только наследники TControl, т.е. именно визуальные компоненты вроде TPanel, TEdit и др., находящиеся именно на нём. Невизуальные компоненты вроде TDataSource в этот список входить не могут. К невизуальным также относятся компоненты, которые иногда называют "псевдовизуальными". Они вроде бы иногда становятся видимыми, но они не лежат на каком-либо визуальном компоненте и не имеют координат на нём. Они являются компонентами (наследниками TComponent), но не являются наследниками TControl. К таким "псевдовизуальным" компонентам относятся, например, TPopupMenu, TOpenDialog.
>>> Я рассудил так: если владельцем будет форма - придется все динамическое хранить в списке, и следить за "чистотой его рядов". Если панель не убирает свое хозяйство, когда сама Destroy'ится, буду добавлять и удалять объекты "вручную"
Здесь есть такой момент: своих "подопечных" уничтожает не только Owner, но и Parent. Так что, если вы поместите на панель элемент, для которого Owner - форма, а Parent - соответственно, панель, то при ручном уничтожении панели она уничтожит и этот элемент, поскольку, она для него хоть и не Owner, но всё-таки Parent.
Рекомендация всегда назначать динамически создаваемым элементам Owner'ом форму связана с тем, что в этом случае мы получаем следующее:
1. Однообразие: мы всегда знаем, кто является владельцем любого элемента, находящегося на форме.
2. Расширение возможностей: в лице Components формы мы всегда имеем под рукой список всех компонентов, находящихся на форме, независимо от их визуального расположения. А элементы, расположенные, например, на конкретной панели, мы всегда можем найти через Controls этой панели.
Эта рекомендация не является категоричной, при наличии необходимости можно от неё отходить, но в общем случае лучше придерживаться. В случае, когда панели интенсивно создаются и уничтожаются, наверное, логичнее назначать владельцем именно панель, чтобы лишний раз не дёргать форму, заставляя её каждый раз перестраивать достаточно длинный список Components.
Копирование свойств необходимо, что-бы, внутреннее размещение объектов на новой панели воспроизвести в точности как на "клонируемой", и цвета и шрифты, кстати, теперь это происходит совершенно замечательно!
С фреймами я, как-то, сходу, не разобрался, к сожалению. А панельки разные, "клонируются" все этой одной процедурой - совершенно любая из имеющихся, меньше 200 строк кода получилось.
Я не настаиваю на фреймах, тем более, если у вас уже есть работающий вариант. Но при случае, с фреймами лучше познакомиться - пригодится. Так, пару слов:
1. Создаётся и разрабатывается так-же как и форма:
File-New-Other-Delphi Projects-Delphi Files-Frame-Ok
2. Используется:
а) В дизайнере: Палитра-Standard-Frames-клик на форме, выбор из списка.
б) В рантайм: создаётся и вставляется как обычный элемент, но появляется весь фрейм со всеми находящимися на нём элементами.
При работе с фреймами есть ряд особенностей, со временем разберётесь. А в качестве примера могу предложить »вопрос КС №66089«
P.S. Не совсем по теме, но, возможно, вам будет ещё интересен »вопрос КС №71458«
Бел Амор, Вы не, ту строчку скопировали, создаю объект: NewStrG:=TStringGrid.Create(PanelNew),
а этой: OldStrG:=TStringGrid(Control) - привожу Control к нужному классу, в данном случае это TStringGrid, чтоб его свойства присвоить новому StringGrid'у.
procedure TFormMain.BtAddClick(Sender: TObject);
var
Control: TControl;
OldStrG, NewStrG: TStringGrid;
OldEdit, NewEdit: TEdit;
OldPanel, NewPanel: TPanel;
SpBt: TSpeedButton;
...
begin
SpBt:=Sender as TSpeedButton; // где-то нажата кнопка '+'...
OldPanel:=(SpBt.Parent as TPanel); // на этой панели!
NewPanel:=TPanel.Create(OldPanel.Parent as TPanel);
// на самом деле все немного по другому, но думаю, так тоже будет работать.
// Текст этот набираю здесь (не копирую), чтоб имена были как в вопросе.
...
// Выставляю свойства у новой панели - это не интересно.
...
for i:=0 to OldPanel.ControlCount-1 do
begin
Control:=OldPanel.Controls[i];
if Control is TEdit
then
begin
OldEdit:=TEdit(Control);
NewEdit:=TEdit.Create(NewPanel);
NewEdit.Parent:=NewPanel;
NewEdit.Font:=OldEdit.Font;
...// копирую свойства Edit'а
end
else
if Control is TStringGrid
then
begin
...// фсе повторяется опять...
end
else
if Control is TSpeedButton
then
begin
...// вот, такая лестница из классов...
end
...
Old'ы - никогда не будут самостоятельными объектами - только указывать на существующие,
а создаваемые New'сы - будут храниться, у Owner'а.
На сколько я понимаю, Owner уничтожит своих подопечных, когда ему самому придет время?... У меня уже сомнение появилось... Здесь я надеюсь на комментарии, пока не поздно :))). До этого ничего визуального в Run-Time не Create'ил.
Я рассудил так: если владельцем будет форма - придется все динамическое хранить в списке, и следить за "чистотой его рядов". Если панель не убирает свое хозяйство, когда сама Destroy'ится, буду добавлять и удалять объекты "вручную" :) - как иногда здесь пишут. Чуть раньше такой список был - потом, вот, "озарило". Если что все верну - легко!
Копирование свойств необходимо, что-бы, внутреннее размещение объектов на новой панели воспроизвести в точности как на "клонируемой", и цвета и шрифты, кстати, теперь это происходит совершенно замечательно!
С фреймами я, как-то, сходу, не разобрался, к сожалению. А панельки разные, "клонируются" все этой одной процедурой - совершенно любая из имеющихся, меньше 200 строк кода получилось. Думаю, сюда её не надо - адаптировать много придется.
Сейчас работаю в Delphi 6.
Хотя мне сложно представить необходимость такого клонирования, как вы описали в вопросе, хотел бы всё-таки обратить внимание на несколько моментов в вашем коде.
>>> OldStrG:=TStringGrid(Control);
Владельцем лучше всегда назначать форму, если нет веских оснований поступать иначе:
OldStrG:=TStringGrid(Self);
if Control is TStringGrid
then
begin
OldStrG:=TStringGrid(Control);
NewStrG:=TStringGrid.Create(PanelNew);
// NewStrG.Assign(OldStrG);
NewStrG.Parent:=PanelNew; NewStrG.Top:=OldStrG.Top;
... строк 15, наверно, будет ...
end
Многие свойства, в частности, координаты и размеры, объявляются в TControl, соответственно, нет совершенно никакой необходимости дублировать присваивание Left, Top, Width, Height для каждого конкретного типа.
на любой из панелей должна быть
создана точная копия (только, H будет +dH)
Если вы используете Delphi выше 7 версии, возможно, вам будет небезынтересна TFlowPanel: »вопрос КС №70414«
В run-time, по нажатию на один из SpeedButton'ов на любой из панелей должна быть
создана точная копия (только, H будет +dH) этой самой панели и всех ее объектов
(размеры, цвета и т.д.) вместе с их функциональностью.
Вы же собираетесь клонировать не произвольную панель с заранее неизвестной структурой, а какую-то конкретную?
Тогда логичнее один раз разработать фрейм и вставлять его где надо, хоть в дизайн-тайме, хоть в рантайме.
Assign точно не сработает - для визуальных компонентов он не реализуется. Разве что вы напишете наследников, реализуете там Assign сами и будете использовать только их.
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.