BorlandTalk.com Forum Index BorlandTalk.com
Borland discussion newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

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

 
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> C++ Builder (VCL Components Usage)
View previous topic :: View next topic  
Author Message
mickey
Guest





PostPosted: Wed Mar 23, 2005 12:33 pm    Post subject: How to replace a component with a new one (at runtime) Reply with 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! Smile
Back to top
Brian Plotkin
Guest





PostPosted: Thu Mar 24, 2005 12:12 am    Post subject: Re: How to replace a component with a new one (at runtime) Reply with quote



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! Smile

Back to top
mickey
Guest





PostPosted: Thu Mar 24, 2005 12:40 am    Post subject: Re: How to replace a component with a new one (at runtime) Reply with quote





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 <brian.plotkin (AT) us (DOT) fujitsu.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! Smile


Back to top
Remy Lebeau (TeamB)
Guest





PostPosted: Thu Mar 24, 2005 1:15 am    Post subject: Re: How to replace a component with a new one (at runtime) Reply with quote


"Brian Plotkin" <brian.plotkin (AT) us (DOT) fujitsu.com> wrote


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



Back to top
Remy Lebeau (TeamB)
Guest





PostPosted: Thu Mar 24, 2005 1:17 am    Post subject: Re: How to replace a component with a new one (at runtime) Reply with quote


"mickey" <mickeymicks (AT) hotmail (DOT) com> wrote


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



Back to top
mickey
Guest





PostPosted: Thu Mar 24, 2005 4:08 am    Post subject: Re: How to replace a component with a new one (at runtime) Reply with quote



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)" <no.spam (AT) no (DOT) spam.com> wrote:
Quote:

"mickey" <mickeymicks (AT) hotmail (DOT) com> wrote in message
news:4241621c$1 (AT) newsgroups (DOT) borland.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




Back to top
Remy Lebeau (TeamB)
Guest





PostPosted: Thu Mar 24, 2005 10:50 am    Post subject: Re: How to replace a component with a new one (at runtime) Reply with quote


"mickey" <mickeymicks (AT) hotmail (DOT) com> wrote


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



Back to top
Damon Chandler (TeamB)
Guest





PostPosted: Sat Mar 26, 2005 4:17 am    Post subject: Re: How to replace a component with a new one (at runtime) Reply with quote

Gambit,
That topic + your code would make for a great article (hint, hint)<g>.

Cheers,
--
Damon (TeamB)
C++Builder Developer's Journal (http://bcbjournal.com)
BCB Commonly Asked Questions (http://bcbjournal.com/bcbcaq)


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< ---


Back to top
Display posts from previous:   
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> C++ Builder (VCL Components Usage) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.