Board index » cppbuilder » avoid extra onclick event on runtime update of components

avoid extra onclick event on runtime update of components


2006-04-29 09:02:59 PM
cppbuilder28
Hi All,
well, this must be a common newbie question, alas I can't find an answer in
recent archives.
Problem is I need to check user input (we all need to, don't we? :D), e.g.
selection of an item in a RadioGroup, Checkbox etc.
After checking I sometimes need to change the user selection. If I change
the selection (e.g. ItemIndex or Checked property) the OnClick event is
fired again....
Not too much of a problem, if only I could see the difference between
user-input and my own changes...
How to solve this? I can think of a dozen workarounds but they all are...
well... workarounds.
I want to do this right as my userinterface will be quite complex. Where to
start?
Any (link to a) solution highly appreciated! Using BCB6
TIA,
Dre
 
 

Re:avoid extra onclick event on runtime update of components

"Dre" < XXXX@XXXXX.COM >wrote in message news: XXXX@XXXXX.COM ...
Quote
Problem is I need to check user input (we all need to, don't we? :D), e.g.
selection of an item in a RadioGroup, Checkbox etc.
After checking I sometimes need to change the user selection. If I change
the selection (e.g. ItemIndex or Checked property) the OnClick event is
fired again....
Not too much of a problem, if only I could see the difference between
user-input and my own changes...
void __fastcall TForm1::IntoxicatedRadioGroupClick(TObject *Sender)
{
int NewItemIndex = Random( IntoxicatedRadioGroup->Items->Count -1 );
IntoxicatedRadioGroup->OnClick = 0;
IntoxicatedRadioGroup->ItemIndex = NewItemIndex;
IntoxicatedRadioGroup->OnClick = IntoxicatedRadioGroupClick;
}
Todd
 

Re:avoid extra onclick event on runtime update of components

Todd,
Thanks! A much better workaround than the ones I had in mind!
BTW, do you know of more structural solutions e.g. through derived
components with an extra method to change ItemIndex through the backdoor so
OnClick won't notice? Would even save more coding...
Thanks for this one!
Andr?
 

{smallsort}

Re:avoid extra onclick event on runtime update of components

"Dre" < XXXX@XXXXX.COM >wrote:
Quote

[...] do you know of more structural solutions e.g. through
derived components with an extra method to change ItemIndex
through the backdoor so OnClick won't notice? Would even
save more coding...
If you do derive a new component you can add something that
prevents the OnClick from executing when ItemIndex is changed.
Exactly how you do it is a matter of choice because off the
top of my head, I can think of several ways to do it.
The one I like best is to use a property with a setter method
because of it's usage in code. For example:
private:
// Usually the setter method would be named with 'Set'
// + property name but that would be sure to conflict
// with the existing.
void __fastcall ChangeItemIndex( int );
public:
__property int MyItemIndex = { read = ItemIndex, write = ChangeItemIndex };
Usage:
IntoxicatedRadioGroup->MyItemIndex = SomeIndex;
Now you just have to code the setter method and again, you have choices with how you want to prevent the OnClick from
executing but in keeping with Todd's post:
//-------------------------------------------------------------
void __fastcall TMyRadioGroup::ChangeItemIndex( int Index )
{
if( ItemIndex != Index )
{
// don't range check - let component throw if invalid
TNotifyEvent OldOnClick = OnClick;
OnClick = NULL;
ItemIndex = Index;
OnClick = OldOnClick;
}
}
//-------------------------------------------------------------
~ JD
 

Re:avoid extra onclick event on runtime update of components

Thanks JD!
Great piece of cooperation!
Thanks a 1,000,000!
Now all I need to do is dive into deriving components ;^)
Dre
Quote
>[...] do you know of more structural solutions e.g. through
>derived components with an extra method to change ItemIndex
>through the backdoor so OnClick won't notice? Would even
>save more coding...

If you do derive a new component you can add something that
prevents the OnClick from executing when ItemIndex is changed.
Exactly how you do it is a matter of choice because off the
top of my head, I can think of several ways to do it.

The one I like best is to use a property with a setter method
because of it's usage in code. For example:

private:
// Usually the setter method would be named with 'Set'
// + property name but that would be sure to conflict
// with the existing.
void __fastcall ChangeItemIndex( int );
public:
__property int MyItemIndex = { read = ItemIndex, write =
ChangeItemIndex };


Usage:

IntoxicatedRadioGroup->MyItemIndex = SomeIndex;

Now you just have to code the setter method and again, you have choices
with how you want to prevent the OnClick from
executing but in keeping with Todd's post:

//-------------------------------------------------------------
void __fastcall TMyRadioGroup::ChangeItemIndex( int Index )
{
if( ItemIndex != Index )
{
// don't range check - let component throw if invalid
TNotifyEvent OldOnClick = OnClick;
OnClick = NULL;
ItemIndex = Index;
OnClick = OldOnClick;
}
}
//-------------------------------------------------------------

~ JD

 

Re:avoid extra onclick event on runtime update of components

"Dre" < XXXX@XXXXX.COM >wrote:
Quote

Now all I need to do is dive into deriving components
Peice of pie.
Click File | New | Unit and save it with an appropriate name.
Then make it look like:
//-------------------------------------------------------------
#ifndef WhatEverYouNamedItH
#define WhatEverYouNamedItH
//-------------------------------------------------------------
#include <Classes.hpp>
#include <StdCtrls.hpp>
//-------------------------------------------------------------
class TMyRadioGroup : public TRadioGroup
{
private:
void __fastcall ChangeItemIndex( int );
public:
__fastcall TMyRadioGroup(TComponent* Owner);
__property int MyItemIndex = { read = ItemIndex, write = ChangeItemIndex };
};
//-------------------------------------------------------------
#endif
//-------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "WhatEverYouNamedIt.h"
#pragma package(smart_init)
//-------------------------------------------------------------
__fastcall TMyRadioGroup::TMyRadioGroup( TComponent* Owner ) : TRadioGroup( Owner )
{
}
//-------------------------------------------------------------
void __fastcall TMyRadioGroup::ChangeItemIndex( int Index )
{
if( ItemIndex != Index )
{
TNotifyEvent OldOnClick = OnClick;
OnClick = NULL;
ItemIndex = Index;
OnClick = OldOnClick;
}
}
//-------------------------------------------------------------
To use it, include it's header in the form's unit and
dynamically allocate it in the form's constructor and
set the properties as needed. That's right, manually
set the properties that you had previousely set using
the Object Inspector (including it's Parent).
If you don't like that option, then wrap your newly derived
class into a component and install it into the IDE. If you
go this route, I advise that you install into a seperate (new)
package on a seperate (new) tab. This way, it's easy to remove
your mistakes until you get it right.
~ JD
 

Re:avoid extra onclick event on runtime update of components

JD,
I am extremely greatful for the time you spend on answering my questions!
I am afraid it will take ages for me to catch up and help answering any of
YOUR questions ;^))
Again, thanks a lot!
Dre
Quote
Peice of pie.

Click File | New | Unit and save it with an appropriate name.
Then make it look like:


//-------------------------------------------------------------
#ifndef WhatEverYouNamedItH
#define WhatEverYouNamedItH
//-------------------------------------------------------------
#include <Classes.hpp>
#include <StdCtrls.hpp>
//-------------------------------------------------------------
class TMyRadioGroup : public TRadioGroup
{
private:
void __fastcall ChangeItemIndex( int );
public:
__fastcall TMyRadioGroup(TComponent* Owner);
__property int MyItemIndex = { read = ItemIndex, write =
ChangeItemIndex };
};
//-------------------------------------------------------------
#endif


//-------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "WhatEverYouNamedIt.h"
#pragma package(smart_init)
//-------------------------------------------------------------
__fastcall TMyRadioGroup::TMyRadioGroup( TComponent* Owner ) :
TRadioGroup( Owner )
{
}
//-------------------------------------------------------------
void __fastcall TMyRadioGroup::ChangeItemIndex( int Index )
{
if( ItemIndex != Index )
{
TNotifyEvent OldOnClick = OnClick;
OnClick = NULL;
ItemIndex = Index;
OnClick = OldOnClick;
}
}
//-------------------------------------------------------------

To use it, include it's header in the form's unit and
dynamically allocate it in the form's constructor and
set the properties as needed. That's right, manually
set the properties that you had previousely set using
the Object Inspector (including it's Parent).

If you don't like that option, then wrap your newly derived
class into a component and install it into the IDE. If you
go this route, I advise that you install into a seperate (new)
package on a seperate (new) tab. This way, it's easy to remove
your mistakes until you get it right.

~ JD

 

Re:avoid extra onclick event on runtime update of components

JD,
Thanks again! Derived my own onclick-components and made them available at
designtime. I'm almost tempted to remove the standard ones... G, my first
'own'-components....
[8'D)
Indeed: with good help it's a piece of pie!
Dre
 

Re:avoid extra onclick event on runtime update of components

"Dre" < XXXX@XXXXX.COM >wrote in message
Quote
If I change the selection (e.g. ItemIndex or Checked property)
the OnClick event is fired again....
That depends on the particular component. Some do and some don't.
Quote
Not too much of a problem, if only I could see the difference between
user-input and my own changes...
You have two options:
1) when setting the property by code, set a flag somewhere that the OnClick
event handler can look for. Set the flag before making the change, and then
clear the flag when finished. When the event is triggered, if the flag is
set then the handler can exit immediately without doing anything.
2) before making the change, disable the event handler itself by assigning
it to NULL, and then reassign it to its old value after the change is
finished.
Gambit
 

Re:avoid extra onclick event on runtime update of components

Remy,
Quote
>If I change the selection (e.g. ItemIndex or Checked property)
>the OnClick event is fired again....

That depends on the particular component. Some do and some don't.
Thats right. By now I created descendants for TCheckButton, TRadioButton,
TRadioGroup (preventing call for OnClick handler) and for TTrackBar and
TEdit (preventing call for OnChange handler). Maybe I need to derive some
more as I check each component before implementing in my UI
Quote
>Not too much of a problem, if only I could see the difference between
>user-input and my own changes...

You have two options:

1) when setting the property by code, set a flag somewhere that the
OnClick
event handler can look for. Set the flag before making the change, and
then
clear the flag when finished. When the event is triggered, if the flag is
set then the handler can exit immediately without doing anything.

2) before making the change, disable the event handler itself by assigning
it to NULL, and then reassign it to its old value after the change is
finished.
Option 1) is what I really wanted to avoid: I simply knew this was asking
for coding errors. This is why I asked my question in the first place
Option 2) was the way to go. Luckily I got the information on how to derive
my own components. I can now set Checked/ItemIndex/Position/Text through an
extra write-only property in each component... |[8'D)
Thanks!
Dre
 

Re:avoid extra onclick event on runtime update of components

"Dre" < XXXX@XXXXX.COM >wrote in message
Quote
Thats right. By now I created descendants for TCheckButton,
TRadioButton, TRadioGroup (preventing call for OnClick handler)
and for TTrackBar and TEdit (preventing call for OnChange handler).
Maybe I need to derive some more as I check each component
before implementing in my UI
In your descendants, you can override the virtual Click() method and have it
call the inherited Click() only if you are not in the middle of updating the
properties programmably.
Quote
Option 1) is what I really wanted to avoid
There is no way to avoid the extra clicks. They are occuring at the OS
level, and the VCL is merely translating them into events that your code
reacts to. So have to do some kind of manual filtering in order to discard
the events you don't want.
In the case of TButtonControl-derived controls, it does have a protected
ClicksDisabled property for filtering out unnecessary events. For example,
TButtonControl.WndProc() uses that property to prevent click messages in
response to WM_LBUTTONDOWN, WM_LBUTTONDBLCLK., and CN_COMMAND messages.
Also, TCheckBox and TRadioButton use that property when the state of the
control is being changed programmably (the properly is false by default, so
you will have to set it to true manually before changing the State and
Checked properties, respectively).
Gambit
 

Re:avoid extra onclick event on runtime update of components

Remy,
Quote
>Thats right. By now I created descendants for TCheckButton,
>TRadioButton, TRadioGroup (preventing call for OnClick handler)
>and for TTrackBar and TEdit (preventing call for OnChange handler).
>Maybe I need to derive some more as I check each component
>before implementing in my UI

In your descendants, you can override the virtual Click() method and have
it
call the inherited Click() only if you are not in the middle of updating
the
properties programmably.
Maybe I didn't explain this right: I created an extra property with
associated function (as kindly explained by others in this thread)
// If I want to change Checked the normal way:
TMyCheckBtn->Checked = !TMyCheckBtn->Checked;
// that will generate an OnClick event
// But with my derived component I can also choose to:
TMyCheckBtn->SilentChecked = !TMyCheckBtn->Checked;
// Changing the SilentChecked property actually changes Checked (SilentCheck
is write only).
// but the OnClick/OnChange handler is set to NULL first and back to
OnClick/OnChange afterwards
I can now check and change multiple visible components with
SilentChecked/SilentItemIndex/SilentPosition/SilentText
without checking for unnecessary handling of OnClick/OnChange events. The
components will remain aware of changes so they are properly updated. This
was a 30 minutes job (slow, but hey, never derived my own components ;'D ).
and I'll 'never' have to code any checking, workarounds from now on....
well...sounds like a should-be-standard improvement to me ;^)
Quote

>Option 1) is what I really wanted to avoid

There is no way to avoid the extra clicks. They are occuring at the OS
level, and the VCL is merely translating them into events that your code
reacts to. So have to do some kind of manual filtering in order to
discard
the events you don't want.
Thanks, I understood this part. I just wanted to get rid of some
dinosaurs...
Quote
In the case of TButtonControl-derived controls, it does have a protected
ClicksDisabled property for filtering out unnecessary events. For
example,
TButtonControl.WndProc() uses that property to prevent click messages in
response to WM_LBUTTONDOWN, WM_LBUTTONDBLCLK., and CN_COMMAND messages.
Also, TCheckBox and TRadioButton use that property when the state of the
control is being changed programmably (the properly is false by default,
so
you will have to set it to true manually before changing the State and
Checked properties, respectively).
So instead of temporarely disabling certain handlers (OnClick or Onchange
depending on the component) by copying the pointer, set it to NULL and
copying the pointer back I simply set ClicksDisabled = true and afterwards
ClicksDisabled = false? Sounds like a better way of doing things IF
OnChange is 'silenced' too. I'll check that soon.
Quote
Gambit
Thanks Remy / Gambit!
PS: which name do you prefer?
 

Re:avoid extra onclick event on runtime update of components

"Dre" < XXXX@XXXXX.COM >wrote in message
Quote
So instead of temporarely disabling certain handlers (OnClick or
Onchange depending on the component) by copying the pointer,
set it to NULL and copying the pointer back I simply set
ClicksDisabled = true and afterwards ClicksDisabled = false?
Yes.
Quote
Sounds like a better way of doing things IF OnChange is
'silenced' too.
ClicksDisabled does not disable the OnChange event in TRadioButton. I do
not know about the other components.
Gambit
 

Re:avoid extra onclick event on runtime update of components

Gambit,
Quote
>So instead of temporarely disabling certain handlers (OnClick or
>Onchange depending on the component) by copying the pointer,
>set it to NULL and copying the pointer back I simply set
>ClicksDisabled = true and afterwards ClicksDisabled = false?

Yes.

>Sounds like a better way of doing things IF OnChange is
>'silenced' too.

ClicksDisabled does not disable the OnChange event in TRadioButton. I do
not know about the other components.
Thanks, I'll check this as it might further improve 'my' components 8^)
Dre
 

Re:avoid extra onclick event on runtime update of components

Gambit,
Quote
>So instead of temporarely disabling certain handlers (OnClick or
>Onchange depending on the component) by copying the pointer,
>set it to NULL and copying the pointer back I simply set
>ClicksDisabled = true and afterwards ClicksDisabled = false?

Yes.

>Sounds like a better way of doing things IF OnChange is
>'silenced' too.

ClicksDisabled does not disable the OnChange event in TRadioButton. I do
not know about the other components.
ClicksDisabled is available only for (descendants of) TButton. So for my
current derived components:
- TRadioGroup, TTrackbar, TEdit (both should temporarily disable their
OnChange event) can't use this
- TCheckBox and TRadioButton CAN.
This raises the question, should I stick with the same 'old' method for all
components?
- to avoid introducing possibly different behaviour between components
- changing the ClicksDisabled property actualy calls some functions and this
might be more overhead than simply copying/assigning a pointer?
Dre