Board index » cppbuilder » How to replace a component with a new one (at runtime)

How to replace a component with a new one (at runtime)


2005-03-23 08:33:32 PM
cppbuilder9
hi all!
Can anyone tell me how i can replace a component instance
with a new one?
For example, i have a TImage called Img1 on my form.
then i dynamically create a new TImage called Img2.
I want to replace the Img1 with Img2.
I thought it would be as simple as adding Img2 to the form
(parent, etc) and then deleting Img1. but the z-order of
controls are affected, since Img2, will always be the topmost.
I am using the the stream's ReadComponent() and passing
Img1 to it, and it does update the properties of Img2.
So, it's almost like replacing the old controls... almost.
there is a thing called "default" for properties
(which doesnt get stored - somthing i unfortunately, learned
too late). Not all properties are updated or reset.
Some properties from the old component still remains.
Basically, if there really is no way to do a "replacecontrol()"
what i want to find out then is a way to "re-initialize" my
controls/components' properties.
thanks for any help! :)
 
 

Re:How to replace a component with a new one (at runtime)

Have you tried assigning Img2 to Img1 'Img1->Assign()'?
mickey wrote:
Quote
hi all!

Can anyone tell me how i can replace a component instance
with a new one?
For example, i have a TImage called Img1 on my form.
then i dynamically create a new TImage called Img2.
I want to replace the Img1 with Img2.

I thought it would be as simple as adding Img2 to the form
(parent, etc) and then deleting Img1. but the z-order of
controls are affected, since Img2, will always be the topmost.

I am using the the stream's ReadComponent() and passing
Img1 to it, and it does update the properties of Img2.
So, it's almost like replacing the old controls... almost.

there is a thing called "default" for properties
(which doesnt get stored - somthing i unfortunately, learned
too late). Not all properties are updated or reset.
Some properties from the old component still remains.

Basically, if there really is no way to do a "replacecontrol()"
what i want to find out then is a way to "re-initialize" my
controls/components' properties.

thanks for any help! :)
 

Re:How to replace a component with a new one (at runtime)

hello again. :)
Quote
Have you tried assigning Img2 to Img1 'Img1->Assign()'?
i actually did (long ago) but i dropped it because, im also
using some other controls (standard and custom) on my form.
There are a lot of properties that Assign() simply cannot,
well, assign.
i've actually found a workaround...
a. put "Controls" in a list - the Controls array of the form
-to get the current z-order
b. create Img2 (or any other control of controls) and add it
to my form.
c. find Img1 in my control's list, and replace it with
Img2 (actually, just the pointers).
d. call BringToFront() for each controls in my list.
As it turns out, the z-order of controls is simply,
the order of the Controls in the form's Controls array!
-thankfully!
anyhow, if there is a better way to do this,
please tell me so.
Thank you!
Happy Programming! :)
Brian Plotkin < XXXX@XXXXX.COM >wrote:
Quote
Have you tried assigning Img2 to Img1 'Img1->Assign()'?

mickey wrote:
>hi all!
>
>Can anyone tell me how i can replace a component instance
>with a new one?
>For example, i have a TImage called Img1 on my form.
>then i dynamically create a new TImage called Img2.
>I want to replace the Img1 with Img2.
>
>I thought it would be as simple as adding Img2 to the form
>(parent, etc) and then deleting Img1. but the z-order of
>controls are affected, since Img2, will always be the topmost.
>
>I am using the the stream's ReadComponent() and passing
>Img1 to it, and it does update the properties of Img2.
>So, it's almost like replacing the old controls... almost.
>
>there is a thing called "default" for properties
>(which doesnt get stored - somthing i unfortunately, learned
>too late). Not all properties are updated or reset.
>Some properties from the old component still remains.
>
>Basically, if there really is no way to do a "replacecontrol()"
>what i want to find out then is a way to "re-initialize" my
>controls/components' properties.
>
>thanks for any help! :)
 

{smallsort}

Re:How to replace a component with a new one (at runtime)

"Brian Plotkin" < XXXX@XXXXX.COM >wrote in message
Quote
Have you tried assigning Img2 to Img1 'Img1->Assign()'?
That will not work. Most components do not implement the Assign() method at
all. TImage does not..
Gambit
 

Re:How to replace a component with a new one (at runtime)

"mickey" < XXXX@XXXXX.COM >wrote in message
Quote
For example, i have a TImage called Img1 on my
form. then i dynamically create a new TImage called
Img2. I want to replace the Img1 with Img2.
Why not just load the new graphic into Img1 directly and not use Img2 at
all? What EXACTLY are you trying to accomplish by having Img2 present?
Gambit
 

Re:How to replace a component with a new one (at runtime)

Quote
What EXACTLY are you trying to accomplish by having Img2 present?
okay, im actually trying <yup, trying>to make undo/redo
support for a form where users can drag-drop different
controls during runtime. basically similar to IDE's form editor.
and the users can save this unto a file, then reload later.
to allow for undo/redo, previously, i saved the whole
form file into a stream. everytime the user changes a property
of a control (i.e. left, top) i save the whole form.
then when undo/redo, i just reload the file from that stream.
that works fine and is really easy to code, but
it simply eats too much memory (i assumed). The stream
would eat a lot of memory if the file contained tons of
images (with bmp data).
so in my new implementation, i just save the control
that was edited. then for undo/redo, i just reload the
control from my stream. that would keep me from saving
all the other controls in the stream when they are not
needed.
with that, i came to this problem at hand. before,
i was simply use the stream's ReadComponent(MyInstance) to
reload the properties. but since default properties are not
saved, the MyInstance retains those 'unsaved' properties.
i wanted to reset the properties of the controls,
but would not want to re-set each property as they are varied.
i have found a work-around here though. because my
current problem is retaining the z-order. i simply
restore the order through a series of BringToFront()'s.
Thank you very much. :)
"Remy Lebeau \(TeamB\)" < XXXX@XXXXX.COM >wrote:
Quote

"mickey" < XXXX@XXXXX.COM >wrote in message
news:4241621c$ XXXX@XXXXX.COM ...

>For example, i have a TImage called Img1 on my
>form. then i dynamically create a new TImage called
>Img2. I want to replace the Img1 with Img2.

Why not just load the new graphic into Img1 directly and not use Img2 at
all? What EXACTLY are you trying to accomplish by having Img2 present?


Gambit


 

Re:How to replace a component with a new one (at runtime)

"mickey" < XXXX@XXXXX.COM >wrote in message
Quote
to allow for undo/redo, previously, i saved the whole
form file into a stream. everytime the user changes a
property of a control (i.e. left, top) i save the whole form.
That is a bit of wasted memory for individual changes. A better solution
might be to keep track of the individual changes, and then revert them back
one at a time as needed. One way to do that is to write a base class with
an abstract Undo() method, and then derive specialized classes that override
that method. For each change that is performed, instantiate the appropriate
decendant class, fill it with whatever information it needs to revert the
change, and then add the class to a list. To undo, simply run through the
list one at a time calling the Undo() method for each operation. This will
also allow you to support multiple undo/redo operations as well.
For example (untested, error checking omitted for simplicity):
class TChangeOperation
{
private:
TList *FList;
public:
TUndo(TList *AList)
: FList(AList)
{
FList->Add(this);
}
virtual ~TUndo() {}
virtual void Redo() = 0;
virtual void Undo() = 0;
};
template< typename T>
class TAddComponent : public TChangeOperation
{
protected:
T *FComp;
TComponent *FOwner;
TMemoryStream *FSave;
public:
TAddComponent(TList *AList, TComponent *AOwner)
: TChangeOperation(AList), FComp(NULL), FOwner(AOwner),
FSave(NULL)
{
FComp = new T(AOwner);
}
~TAddComponent()
{
if( FSave )
delete FSave;
}
virtual void Redo()
{
if( FSave )
{
if( FComp )
delete FComp;
FSave->Position = 0;
FComp = static_cast<T*>(FSave->ReadComponent(NULL));
FOwner->InsertComponent(FComp);
}
}
virtual void Undo()
{
if( FSave )
FSave->Clear();
else
FSave = new TMemoryStream;
FSave->WriteComponent(FComp);
delete FComp;
FComp = NULL;
}
__property T* Component = {read=FComp};
};
template< typename T>
class TAddControl : public TAddComponent<T>
{
private:
TWinControl *FParent;
public:
TAddControl(TList *AList, TComponent *AOwner, TWinControl *AParent)
: TAddComponent<T>(AList, AOwner), FParent(AParent)
{
FComp->Parent = AParent;
}
virtual void Redo()
{
TAddComponent<T>::Redo();
FComp->Parent = FParent;
}
};
class TSetControlBounds : public TChangeOperation
{
private:
TControl *FControl;
TRect FNew, FOld;
public:
TSetControlBounds(TList *AList, TControl *AControl, const TRect
&ABounds)
: TChangeOperation(AList), FControl(AControl), FNew(ABounds)
{
FOld = AControl->BoundsRect;
AControl->BoundsRect = ABounds;
}
virtual void Redo()
{
FControl->BoundsRect = FNew;
}
virtual void Undo()
{
FControl->BoundsRect = FOld;
}
};
class TSetControlCaption : public TChangeOperation
{
private:
TControl *FControl;
AnsiString FNew, FOld;
public:
TSetControlCaption(TList *AList, TControl *AControl, const
AnsiString &ACaption)
: TChangeOperation(AList), FControl(AControl), FNew(ACaption)
{
FOld = AControl->Caption;
AControl->Caption = ACaption;
}
virtual void Redo()
{
FControl->Caption = FNew;
}
virtual void Undo()
{
FControl->Caption = FOld;
}
};
// etc ...
class TMyForm : public TForm
{
private:
TList *UndoList
TList *RedoList
void __fastcall AddButton();
void __fastcall Undo(int NumItems = -1);
void __fastcall Redo(int NumItems = -1);
public:
__fastcall TMyForm(TComponent *Owner);
__fastcall ~TMyForm();
};
__fastcall TMyForm::TMyForm(TComponent *Owner)
: TForm(Owner)
{
UndoList = new TList;
RedoList = new TList;
}
__fastcall TMyForm::~TMyForm()
{
for(int x = 0; x < UndoList->Count; ++x)
delete static_cast<TChangeOperation*>(UndoList->Items[x]);
delete UndoList;
for(int x = 0; x < RedoList->Count; ++x)
delete static_cast<TChangeOperation*>(RedoList->Items[x]);
delete RedoList;
}
void __fastcall TMyForm::AddButton()
{
TAddControl<TButton>*AddButtonOp = new
TAddControl<TButton>(UndoList, this, this);
TSetControlBounds *SetBoundsOp = new
TSetControlBounds(AddButtonOp->Component, Rect(0, 0, 100, 25));
TSetControlCaption *SetCaptionOp = new
TSetControlCaption(AddButtonOp->Component, "Some Caption");
//...
};
void __fastcall TMyForm::Undo(int NumItems)
{
if( (NumItems < 0) || (NumItems>UndoList->Count) )
NumItems = UndoList->Count;
while( NumItems>0 )
{
TChangeOperation *Op =
static_cast<TChangeOperation*>(UndoList->Last());
Op->Undo();
UndoList->Delete(UndoList->Count-1);
RedoList->Add(Op);
--NumItems;
}
}
void __fastcall TMyForm::Redo(int NumItems)
{
if( (NumItems < 0) || (NumItems>RedoList->Count) )
NumItems = RedoList->Count;
while( NumItems>0 )
{
TChangeOperation *Op =
static_cast<TChangeOperation*>(RedoList->Last());
Op->Redo();
RedoList->Delete(RedoList->Count-1);
UndoList->Add(Op);
--NumItems;
}
}
Now, obviously, for each change you want to support undoing/redoing, you
would have to write a new class for it. Also, as you may notice, once a
chance occurs, the details remain in memory, just moving back and forth
between lists. Some programs limit the number of undos/redos available.
For instance, you could limit the number of undos to, say, 100. When the
undo list reaches 100 item, for each additional item added, remove the first
item from the list so the number of items stays at 100.
Quote
so in my new implementation, i just save the control
that was edited. then for undo/redo, i just reload the
control from my stream. that would keep me from saving
all the other controls in the stream when they are not
needed.
That is still a bit much to store. If a control has multiple properties,
and you change only one property, you don't need to save a copy of the
entire control. Worse, saving a component to a stream only saves the
published properties anyway. If you change a non-published property, your
stream approach won't be able to undo the change. The list approach I
mention above can.
Gambit
 

Re:How to replace a component with a new one (at runtime)

Gambit,
That topic + your code would make for a great article (hint, hint)<g>.
Cheers,
Remy Lebeau (TeamB) wrote:
Quote
A better solution
might be to keep track of the individual changes, and then revert them back
one at a time as needed. One way to do that is
8< ---
For example (untested, error checking omitted for simplicity):
8< ---