Board index » cppbuilder » Advice in event handling of custom components

Advice in event handling of custom components


2007-06-20 02:25:43 PM
cppbuilder8
Hi,
I have the following scenario:
1) I have a custom component that's derived from TLabel, which I named
TMultiLabel. It contains 2 extra properties - a CaptionIndex which is an
integer and a CaptionList which is a TStringList that will hold a list
of captions to be displayed by this TMultiLabel.
2) Now, I need to have something, such as a user windows message like
WM_USER_INDEX_CHANGE (just a guess of how I should name this message)
that once sent, should make *ALL* the TMultiLabel components I placed in
a Form to change their CaptionIndex which will then change their Caption
according to the CaptionIndex and the content of their CaptionList.
3) I already have the part of changing the components' Caption according
to the CaptionIndex covered (via the SetCaptionIndex property function).
4) What I'm puzzled of, is there any way to achieve the part where I
brodcast a meesage and causing *ALL* the TMultiLabel components to react
to this message (Change it's CaptionIndex)? If so, how should I design
such event/message, and the event handler?
currently I'm handling this manually, which means I'll have to write a
code to change the CaptionIndex of each and every one of the TMultiLable
components I placed on the Form. This will be very tedious as I add new
lables to the Form, I'll have to keep track of those newly added labels,
and add them to the handler.
Any advice or instructions are appreciated and thanks in advance.
Noob Coder.
 
 

Re:Advice in event handling of custom components

"noobcoder" < XXXX@XXXXX.COM >wrote in message
Quote
What I'm puzzled of, is there any way to achieve the part where
I brodcast a meesage and causing *ALL* the TMultiLabel
components to react to this message (Change it's CaptionIndex)?
There is no single way to do such a large broadcast. You would need
to manually loop through all of the controls on the parent form,
checking each one to see if it is a TMultiLabel.
A better approach would be to create a singleton object that each
TMultiLabel can register itself with on creation and unregister on
destruction. That way, the singleton knows all of the active
instances and can easily loop through them all directly.
Gambit
 

Re:Advice in event handling of custom components

Remy Lebeau (TeamB) дµÀ:
Quote
There is no single way to do such a large broadcast. You would need
to manually loop through all of the controls on the parent form,
checking each one to see if it is a TMultiLabel.
As much as I have guessed. Thanks for the confirmation anyway.
Quote
A better approach would be to create a singleton object that each
TMultiLabel can register itself with on creation and unregister on
destruction. That way, the singleton knows all of the active
instances and can easily loop through them all directly.
Currently I'm doing it this way (just found out of this method after I
posted my original message):
//--start code-----------------------------------------------------
TMultiLabel *label;
for(int i=0; i<this->ComponentCount; i++)
{
if(this->Components[i]->ClassNameIs("TMultiLabel"))
{
label = dynamic_cast <TMultiLabel *>(this->Components[i]);
label->CaptionIndex = myIndex;
}
}
//--end code-------------------------------------------------------
from my test runs, seems like it is working fine. Please let me know if
there is anything incorrect in my method, I'd appreciate that.
Noob Coder
Quote

Gambit


 

{smallsort}

Re:Advice in event handling of custom components

"noobcoder" < XXXX@XXXXX.COM >wrote in message
Quote
Currently I'm doing it this way (just found out of this method
after I posted my original message):
That is not handling any nested components, and it does not handle any
descendant classes, either. If you are going to implement such a
manual loop, then you should implement it as a recursive function, ie:
void __fastcall SetMultiLabelCaptionIndexes(TWinControl
*ParentControl, int Index)
{
for(int i = 0; i < ParentControl->ControlCount; ++i)
{
TControl *pControl = ParentControl->Controls[i];
TMultiLabel *label = dynamic_cast<TMultiLabel*>(pControl);
if( label )
label->CaptionIndex = Index;
TWinControl *pWC = dynamic_cast<TWinControl*>(pControl);
if( pWC )
SetMultiLabelCaptionIndexes(pWC, Index);
}
}
Then you can call it like this:
void __fastcall TForm1::DoSomething()
{
SetMultiLabelCaptionIndexes(this, myIndex);
}
Depending on how many controls you actually have on your form, though,
this can become very inefficient. I still think your best approach is
the singleton approach. You can implement it inside your component
package directly. For example (untested):
--- MultiLabel.h ---
class TMultiLabel : public TLabel
{
private:
TStrings* FCaptionList;
int FCaptionIndex;
void __fastcall CaptionListChanged(TObject *Sender);
void __fastcall SetCaptionList(TStrings *Value);
void __fastcall SetCaptionIndex(int Value);
public:
__fastcall TMultiLabel(TComponent *Owner);
__fastcall ~TMultiLabel();
__published:
__property TStrings* CaptionList = {read=FCaptionList,
write=SetCaptionList};
__property int CaptionIndex = {read=FCaptionIndex,
write=SetCaptionIndex, default=-1};
};
--- MultiLabel.cpp ---
#include "MultiLabel.h"
class TMultiLabelRegistrar
{
private:
TList *FList;
bool FBroadcasting;
public:
TMultiLabelRegistrar();
~TMultiLabelRegistrar();
void CaptionIndexChanged(TMultiLabel *Label, int Value);
void Register(TMultiLabel *Label);
void Unregister(TMultiLabel *Label);
};
TMultiLabelRegistrar *Registrar = NULL:
TMultiLabelRegistrar::TMultiLabelRegistrar()
{
FList = new TList;
}
TMultiLabelRegistrar::~TMultiLabelRegistrar()
{
delete FList;
}
void TMultiLabelRegistrar::CaptionIndexChanged(TMultiLabel *Label,
int Value)
{
if( Broadcasting ) return;
Broadcasting = true;
try
{
for(int i = 0; i < FList->Count; ++i)
{
TMultiLabel *pLabel =
static_cast<TMultiLabel*>(FList->Items[i]);
if( pLabel != Label )
pLabel->CaptionIndex = Value;
}
}
__finally
{
Broadcasting = false;
}
}
void TMultiLabelRegistrar::Register(TMultiLabel *Label)
{
FList->Add(Label);
}
void TMultiLabelRegistrar::Unregister(TMultiLabel *Label)
{
FList->Remove(Label);
if( FList->Count == 0 )
{
delete Registrar;
Registrar = NULL;
}
}
__fastcall TMultiLabel::TMultiLabel(TComponent *Owner)
: TLabel(Owner)
{
FCaptionIndex = -1;
FCaptionList = new TStringList;
static_cast<TStringList*>(FCaptionList)->OnChange =
CaptionListChanged;
if( !Registrar )
Registrar = new TMultiLabelRegistrar;
Registrar->Register(this);
}
__fastcall ~TMultiLabel()
{
delete FCaptionList;
if( Registrar )
Registrar->Unregister(this);
}
void __fastcall TMultiLabel::CaptionListChanged(TObject *Sender)
{
if( (FCaptionIndex>= 0) && (FCaptionIndex <
FCaptionList->Count) )
Caption = FCaptionList->Strings[FCaptionIndex];
else
SetCaptionIndex(-1);
}
void __fastcall TMultiLabel::SetCaptionList(TStrings *Value)
{
FCaptionList->Assign(Value);
}
void __fastcall TMultiLabel::SetCaptionIndex(int Value)
{
if( Value < -1 )
Value = -1;
else if( Value>= FCaptionList->Count )
Value = (FCaptionList->Count - 1);
if( FCaptionIndex != Value )
{
FCaptionIndex = Value;
if( (FCaptionIndex>= 0) && (FCaptionIndex <
FCaptionList->Count) )
Caption := FCaptionList->Strings[FCaptionIndex];
else
Caption := "";
Registrar->CaptionIndexChanged(this, FCaptionIndex);
}
}
Gambit
 

Re:Advice in event handling of custom components

Quote
2) Now, I need to have something, such as a user windows message like
WM_USER_INDEX_CHANGE (just a guess of how I should name this message)
that once sent, should make *ALL* the TMultiLabel components I placed in
a Form to change their CaptionIndex which will then change their Caption
according to the CaptionIndex and the content of their CaptionList.
As you need to change all the labels in the form, which is the owner of the
label, I would do the following:
In the setter of CaptionIndex have a loop checking each Component in the
public list "Components" if "Components[i] is TMultiLabel" and if true
change the index.
Regards Horst