Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to determine what caused OnClick event for checbox

3 views
Skip to first unread message

A

unread,
Dec 9, 2009, 9:25:03 AM12/9/09
to
Hi,

I'm trying to figure out which event caused OnClick on a particular
checkbox.
As OnClick is fired not only when mouse clicked but also on other occasions
for example - when you change the value of checkbox.checked

So what I need to determine is if the event came from mouse or from
something else (I need only to process it if it was mouse-clicked).
Can Sender be used to determine that?

Any ideas guys?


Skybuck Flying

unread,
Dec 9, 2009, 5:51:05 PM12/9/09
to

"A" <a@a.a> wrote in message news:hfoc0n$6sq$1...@gregory.bnet.hr...

This should be very easy to do...

Use the following events:

"MouseDown"
"MouseUp"
+
A boolean which keeps track of mouse button status.

Bye,
Skybuck.


A

unread,
Dec 10, 2009, 3:47:25 AM12/10/09
to
> Use the following events:
>
> "MouseDown"
> "MouseUp"
> +
> A boolean which keeps track of mouse button status.

yes but the i'd need also to install events to track key down (space, enter)
and so on.

i solved it by uninstalling and reattaching event like this:

cb.OnClick := nil;
cb.Checked := true;
cb.OnClick := OnClick;
cb.OnClick(nil);

and in the event itself i check the type of Sender if it is nil - i don't do
anything, if it is defined then the event occurs normally and is processed
as it should be - therefore by "sender" i have the distinction what caused
the event as i needed.


Maarten Wiltink

unread,
Dec 10, 2009, 4:27:51 AM12/10/09
to
"A" <a@a.a> wrote in message news:hfoc0n$6sq$1...@gregory.bnet.hr...

> I'm trying to figure out which event caused OnClick on a particular


> checkbox.
> As OnClick is fired not only when mouse clicked but also on other
> occasions for example - when you change the value of checkbox.checked

Yes. That's a bit of a holy war. Some people are very, very convinced
that if you do anything in code, no events should be fired (you can
do that in code, too, after all), while others think an event should
be fired whenever something interesting happens - no exceptions.

As you may have guessed, I'm in the last group. I tend to use events
at the application level (keeping program state consistent with the
state of the UI, not just 'making things happen when the user clicks[0]'),
so I rather depend on them happening always. I prefer writing my
event handlers so I can tweak them to do something slightly different
_sometimes_ to having to fire events manually (perhaps even calling
handlers directly for some very poorly designed controls) _always_.

Which brings us to my point. Why do you care what made your checkbox
decide to flip over? Your application only needs to adjust itself to
the new situation.

Take, as an example, a checkbox that enables/disables the editbox
next to it. In its OnClick handler, you'd write something like
'edtNextToMe.Enabled:=(Sender as TCheckBox).Checked;'. I grant that
OnClick isn't a very good description of what the event means, but
you can make it do exactly what you want to happen. Without knowing
or caring where the event comes from.

If you tell us what you're trying to do (what, not how), we may be
able to hand you a better way of doing things. Without trying to
subvert the system. There's usually a way to work _with_ the system
to achieve what you want.

Groetjes,
Maarten Wiltink

[0] Not to mention my habit of driving things by keyboard.


Skybuck Flying

unread,
Dec 10, 2009, 5:25:02 AM12/10/09
to
Yes that's another common solution.

If you want to seperate "gui events" from "code events" then you should
indeed "remove the event handler connections", then execute your specific
"gui code" to update the gui or some kind of logic... and then when you are
done "re-enable the gui event handlers/connections as needed".

This is needed to prevent recursion problems.

Like event a triggers event b, and event b triggers event a, and so forth.

Without disconnecting the event handlers that would go on forever...

Bye,
Skybuck.


Maarten Wiltink

unread,
Dec 10, 2009, 8:14:40 AM12/10/09
to
"A" <a@a.a> wrote in message news:hfqciv$vu9$1...@gregory.bnet.hr...
[...]

> cb.OnClick := nil;
> cb.Checked := true;
> cb.OnClick := OnClick;
> cb.OnClick(nil);
>
> and in the event itself i check the type of Sender if it is nil - i
> don't do anything, if it is defined then the event occurs normally
> and is processed as it should be ...

So the call cb.Onclick(nil) does nothing?

...I think I know a simpler way to make sure nothing happens.

Groetjes,
Maarten Wiltink


A

unread,
Dec 12, 2009, 3:42:46 AM12/12/09
to
> So the call cb.Onclick(nil) does nothing?
> ...I think I know a simpler way to make sure nothing happens.

no, it does some stuff but i needed to separate them from gui clicks. it
does some inits.
if you notice i did wrote that in OnClick handler i have processing like:

if (Sender = nil) do non-gui handler
else do gui handler

to be precise, gui click needed to change editbox value but the same handler
also load after i load configuration the checkbox needs change based on
config.


A

unread,
Dec 12, 2009, 4:07:52 AM12/12/09
to
> If you tell us what you're trying to do (what, not how), we may be
> able to hand you a better way of doing things. Without trying to
> subvert the system. There's usually a way to work _with_ the system
> to achieve what you want.

OK you wanted to know so here it is:

- onclick handler does some stuff that is required all the time (does some
reinits for SSL and so on). it's ok so it fires up on checkbox change
whether it was caused by gui or my code. i don't care if it fires up. i just
need to distinguish the code where it came from.

- checkbox enables/disables SSL value (true/false), edit box holds port
value (110/995/other)

- when ssl is checked/unchecked from gui - port is changed to default value
(110 or 995) and can be adjusted later. this is required ONLY for
mouseclicks. this is gui event we're talking about that is executed only for

- on loading configuration i load port value put it in editbox, then change
ssl checkbox also based on config and it fires up onclick. of course this
changes port to different value from the loaded from config. for some reason
edit must be changed first not later.

- i distinguish where the event came from by sender variable which is null
after i call it from config or sslcheckbox when clicked.

i guess i could try to store some variable somewhere like "guiclicked" and
use it instead of "sender" else but i really dislike that idea. i guess i
could go through torture for replacing ssl and edit box loading from config
but even if i change ssl first and then change edit who know that the event
is guaranteed to fire up right after i change ssl checkbox - in fact
probably it will do it in this order:
1.i change checkbox
2.i change editbox
3.event fires up and changes editbox again based on checkbox.

so, do you have better idea than uninstalling handler and doing it like
this:
1.uninstall handler
2.change checkbox (no event is fired up and editbox stays as it should)
3.install handler
4.call handler manually - sender = nil for easy distinguishing where it came
from in the handler itself and changing editbox only for gui clicks.


Maarten Wiltink

unread,
Dec 12, 2009, 4:09:24 AM12/12/09
to
"A" <a@a.a> wrote in message news:hfvl29$qkg$1...@gregory.bnet.hr...

>> So the call cb.Onclick(nil) does nothing?
>> ...I think I know a simpler way to make sure nothing happens.
>
> no, it does some stuff but i needed to separate them from gui clicks.
> it does some inits.
> if you notice i did wrote that in OnClick handler i have processing like:
>
> if (Sender = nil) do non-gui handler
> else do gui handler

Evidently I didn't notice. No big deal either way.


> to be precise, gui click needed to change editbox value but the same
> handler also load after i load configuration the checkbox needs
> change based on config.

That's the sort of difference I was talking about. There are much
better ways to make it than directly calling event handlers with
bogus Senders.

What I do in such circumstances is to add a flag to the surrounding
form and have the event handler look at that. You can usually use a
single flag for many such events. Mostly, I hide setting the flag
in Begin/EndUpdate calls (TForm doesn't yet have those, unlike many
other VCL classes) and check for an IsUpdating property (also new).
Note that the underlying mechanism is a _counter_, not a flag, to
support multiple nested Begin/EndUpdate pairs.

This lets you disable all GUI changes at once while loading your
configuration, then when you're done and the counter drops to zero
and it's time to update again, you can do it all at once (from
EndUpdate, that is). You can even keep separate 'Dirty' flags for
different GUI parts and only update the ones that need it.

As an additional benefit (and I do really consider it that),
IsUpdating state is now concentrated at the application (form,
really) level instead of spread out through diffuse calls to
various event handlers that may or may not match what is really
linked up to the controls.

Groetjes,
Maarten Wiltink


Maarten Wiltink

unread,
Dec 12, 2009, 4:28:18 AM12/12/09
to
"A" <a@a.a> wrote in message news:hfvmhd$te8$1...@gregory.bnet.hr...

>> If you tell us what you're trying to do (what, not how), we may be
>> able to hand you a better way of doing things. Without trying to
>> subvert the system. There's usually a way to work _with_ the system
>> to achieve what you want.
>
> OK you wanted to know so here it is:

[...]


> - when ssl is checked/unchecked from gui - port is changed to default
> value (110 or 995) and can be adjusted later. this is required ONLY

> for mouseclicks. ...

I'll focus on this as I don't have much time and I only saw this message
right after I posted the other one.

Let the events happen. Don't fight them. When loading configuration,
load the port number after the SSL flag.

> [...] for some reason edit must be changed first not later.

You're talking how, not what, again.


[...]

> i guess i could try to store some variable somewhere like "guiclicked"
> and use it instead of "sender" else but i really dislike that idea.

That's your prerogative. I don't claim that my system is the only one
that works but it does work very well for me. The idea behind it, however,
is not to signal when changes are coming from outside but to signal
when changes come from inside. It makes a difference. It's best always
to react immediately to outside changes; they're not under your control.
The inside changes are, and you can group the response to them. It's all
about keeping things (for simplicity, read 'the GUI') consistent with
other things (read 'program state'). It's a relatively simple optimisation
to make lots of state changes by loading configuration with the GUI
disabled, then synchronise it in one step when done.


> [...] even if i change ssl first and then change edit who know that


> the event is guaranteed to fire up right after i change ssl checkbox

> ...

My event handlers are about making sure that 'things' (could be anything)
are consistent with internal program state as evidenced by various
properties, some backed by private fields and some by controls, like the
state of checkboxes. That sometimes requires an initialisation cycle at
startup, so my constructors tend to end with a call to every event
dispatcher that needs it.

So even if loading configuration doesn't toggle the checkbox, the
program will already have reacted once as though it had toggled.
Afterwards, whenever it toggles for whatever reason, consistency will
be restored.

Groetjes,
Maarten Wiltink


A

unread,
Dec 12, 2009, 5:20:31 AM12/12/09
to
> What I do in such circumstances is to add a flag to the surrounding
> form and have the event handler look at that. You can usually use a
> single flag for many such events. Mostly, I hide setting the flag
> in Begin/EndUpdate calls (TForm doesn't yet have those, unlike many
> other VCL classes) and check for an IsUpdating property (also new).
> Note that the underlying mechanism is a _counter_, not a flag, to
> support multiple nested Begin/EndUpdate pairs.

If I understood correctly you propose adding additional let's call it
"global" variable to keep track like a counter where the event came from.

e.g.

FromConfig := 1;
cb.Checked := true;


and in event itself:

if (FromConfig > 0) do non gui stuff

FromConfig := 0;
do gui stuff

that would require additional (global) variable to care about. do you really
think that is more elegant? it is in some ways but not entirely...
ok, i agree the calling code looks a bit cleaner though (the part that
changes checkbox state)

Tag value of checkbox could be used for that purpose that would make it a
bit more elegant but it is still an extra variabl
e.g.
cb.Tag := 1;
cb.Checked := true;

and then later in the event

if (this.Tag > 0) do non gui stuff
this.Tag := 0;
do gui stuff


Maarten Wiltink

unread,
Dec 14, 2009, 5:26:58 AM12/14/09
to
"A" <a@a.a> wrote in message news:hfvqpm$5oi$1...@gregory.bnet.hr...
[...]

> If I understood correctly you propose adding additional let's call
> it "global" variable to keep track like a counter where the event
> came from.

In a word, yes. And no. You want to cast the meaning into terms of
your understanding, and that is understandable but it's in many
ways not quite what they mean.

The least controversial is calling it 'global'. It is that, but you
can make this flag as global as you like, and it is important that
you make it exactly as global as it should be. No less, or you'll
end up with more flags than necessary (not to mention problems finding
the right one in the called code), and no more, or you'll find it
being used from places that have no business with it.

Your inclination seems to be to make it not global enough. I'll be
saying more about that.

But the flag does not say where the event came from. It only says
to hold up cleaning up after events for a bit. This is actually a
rather important distinction. I never simply disable events; I can't
afford to. Events tell me that program state has changed and there
are things that must be updated. It may be related bits of state, or
controls visible to the user; that's not really important. Important
is that 'restoring coonsistency' after an event is itself stateless.
I do not care what the old value of the checkbox was, I only make
sure that the current value is correctly reflected everywhere. That
also makes it something that can be postponed - but not indefinitely.
I can perform a string of operations that change state (like loading
a configuration), and only at the end of it smooth out everything
that needs to be changed along with it.

There are, of course, consequences. Some of my events live at a
higher level of abstraction than the VCL's TControl.OnClick. It
takes some getting used to, but as it turns out it's only proper.
You do have to be able to keep track inside your head of what's
flowing inside your application. It's not for first-week programmers.

Perhaps the hardest thing to keep straight is that often, there are
dependencies in the cleaning-up-after-events, so to prevent a lot
of 'pinballing', you have to fire them in the correct order. It
turns out that this doesn't matter when you're firing just one event
(and there are reasons why that has to be so), but the things that
happen after the last EndUpdate call have to be written in the right
order.

I think of that as the hierarchy underneath the Dirty flags. If an
event handler fires subsequent events, they have a dependency on it.
When event firing is deferred, every event not-fired sets its own
Dirty flag instead, and when event handling is re-enabled and you're
catching up with all the Dirty flags, you have to do them one by one
in such an order that you are making progress towards the Clean state.
Doing the work to make one Dirty flag go to False should not cause
others already done to go to True again.

Alternatively, you can dispense with all the Dirty flags and after
every final EndUpdate, fire _every_ event. This may sound tremendously
heavy-handed and inefficient and superfluous, but if you have more
meaningful events (application-level ones rather than the myriad
control-bound ones), there will be fewer of them and it does make your
code simpler. There will be more work in less code; consider what
costs more in the end.


> e.g.
>
> FromConfig := 1;

Yes. Or rather, 'BeginUpdate;' - which is implemented as
'Inc(FUpdateLevel);'


> cb.Checked := true;
>
>
> and in event itself:
>
> if (FromConfig > 0) do non gui stuff
>
> FromConfig := 0;
> do gui stuff

No. First of all, if you're postponing event handling until later,
NOTHING happens except for maybe setting a Dirty flag. There is no
separation into things that happen always and things that may or
may not happen. They either happen now or they happen later, but
they always happen eventually.

Second, the event handler is not the one to clear the delay flag
(it's not a flag anyway, it's a counter). And you're not messing with
the counter yourself; you call BeginUpdate. And in the same scope
that you called BeginUpdate, you _will_ call EndUpdate. Everything
goes to pieces if you don't. This is why you should be seeing code
like this everywhere:

BeginUpdate;
try
{ Work }
finally
EndUpdate;
end;

The try-finally makes sure that no matter what happens, if BeginUpdate
is called, so will EndUpdate. No exceptions (pun intended).

As a general rule, whenever you do something that has a beginning and
an end, you do those in the same scope. Acquiring and releasing locks,
creating and freeing objects, Begin/EndUpdate cycles, everything that
has to happen in pairs is made easiest by far when you can see at once
that the closing half of the pair will not be skipped.

Without wanting to sound like I'm substituting age for authority, you
can take my word for this. I am an old programmer and I know when and
how to make life easy for myself. Trust me. This is not the place to
get smart. It's too easy to break already. If you miss an EndUpdate,
no events will be handled anymore, and the way to prevent that is not
to clear the flag whenever you think you're done (you may be inside
another Begin/EndUpdate brace); it is to make very sure you never miss
an EndUpdate. Try-finally is your friend.


> that would require additional (global) variable to care about. do
> you really think that is more elegant? it is in some ways but not
> entirely...

Well, you need an additional variable, too (you're using Sender, which
isn't even yours). I think it could be proven that you can't make it
work without an extra variable, so the next question is where to place
it. Candidates are the calling code, the form class, and the application
as a whole (outside the form classes).

There will often be more than one place that functions as the calling
code, so you'd have to pass the flag along as a parameter to make it
work. That's essentially what you're doing now, and you're coopting
a parameter for it that was meant to do something else. At some time
in the future, you might start using Actions, and you might find that
your event handlers don't work so well when you don't have a meaningful
Sender. (Actions are great, by the way. Just make sure you Update them
in the right place.)

So in my opinion, the calling code is usually the wrong place (because
there are too many of them), and putting the variable in a publicly-
visible unit is usually wrong, too (because it becomes too widely visible),
and the form class is usually about right. Not because it's the form
class per se, but because forms are mostly about the right-sized unit
for it.


> ok, i agree the calling code looks a bit cleaner though (the part
> that changes checkbox state)

And that is very, very important. All code should look clean, not
for the compiler (it doesn't care), but for you. And for your
colleagues. And after working on other projects for half a year,
for you again.

It is a fallacy that any code that compiles is good enough. The most
important target audience for code in a project of any significant
duration is not the compiler, it is humans. Code should be readable to
people (not necessarily normal people; it is allowed to assume some
measure of intelligence), and the best thing you can do to make it
readable is to limit the amount of code you have to read before being
able to understand it.

This is not the only, but one very important reason why it is good
to keep the setting and resetting of things together - when reading
it for the first time, you can look at a single block of code and
have a reasonable hope that it is more or less self-contained, that
you don't have to go hunting around another ten thousand lines of
code for other places where this flag is touched.

But while keeping code short and clean is important, we are programmers
and we do have to write code. If you have well-organised code you can
choose where to write code, and you can choose to have it in only one
place. That keeps your overall code shorter, makes your program more
modular and more readable, and makes maintenance a hundred times easier.
Because instead of having to find the hundred places where a bit of
code is repeated, there is a single procedure that is called a hundred
times, and can be amended just once.

This procedure, that does everything that needs to be done when
'something interesting happens', is an event dispatcher. It is what
gets called whenever an abstract 'event' occurs. It can call event
handlers, do bookkeeping, fire follow-up events (by calling their
dispatchers), and in really good object-oriented programming it is
virtual and can be overridden when the event needs to do additional
things in a derived class.

And it will be independent of where the event came from.

Groetjes,
Maarten Wiltink


A

unread,
Dec 14, 2009, 6:46:06 AM12/14/09
to
>> e.g.
>>
>> FromConfig := 1;
>
> Yes. Or rather, 'BeginUpdate;' - which is implemented as
> 'Inc(FUpdateLevel);'

> BeginUpdate;


> try
> { Work }
> finally
> EndUpdate;
> end;

Can you please put this into an example code how would you implement it? No
need to write heavy explanation I can figure it from the code itself much
faster than from few pages of text.

Here are my points:

- Begin/End update have no meaning into this context. EndUpdate may fire
onclick event. Does not mean anything. I update Checked state on single
checkbox not on a large set of gui objects.

- I still need to update FUpdateLevel (or use it somehow) somewhere - where
do you propose I update it and what FUpdateLevel even means? How it is used
to distinguish the originator of the event? The scope or placement of this
variable now is not of much relevance. Like i said, I could use Tag (it is
"mine" to use).

- try / finally also have no meaning here - no exception is fired on
cb.Checked := true; anyway. try/finally are used for exception handling -
which there is none here.

So without a large text and explanation... please write a few lines of
code... how would you:
a) change checbox state
b) update flags/whatever
c) do the event itself

That would make much more sense (at least to me). Give me an example that
can be used here... not a large theory book. I'm a practical person not a
theorist.


Maarten Wiltink

unread,
Dec 14, 2009, 10:04:04 AM12/14/09
to
"A" <a@a.a> wrote in message news:hg58i7$aj0$1...@gregory.bnet.hr...

>>> e.g.
>>>
>>> FromConfig := 1;
>>
>> Yes. Or rather, 'BeginUpdate;' - which is implemented as
>> 'Inc(FUpdateLevel);'
>
>> BeginUpdate;
>> try
>> { Work }
>> finally
>> EndUpdate;
>> end;
>
> Can you please put this into an example code how would you implement
> it? No need to write heavy explanation I can figure it from the code
> itself much faster than from few pages of text.

I'm quite impressed that you're still responding to them. You may be
glazing over after ten lines but apparently you haven't yet given up.
Good job.

(untested)

type
TClassWithDeferredEvents = class(...)
private
FData1: Boolean;
FDataN: Integer;
FUpdateLevel: Integer;

procedure SetData1(const Value: Boolean);
procedure SetDataN(const Value: Integer);
function GetIsUpdating: Boolean;
protected
property IsUpdating: Boolean read GetIsUpdating;

procedure DoData1Changed; virtual;
procedure DoDataNChanged; virtual;
procedure DoLock; virtual;
procedure DoUnlock; virtual;
public
constructor Create;
destructor Destroy; override;

property Data1: Boolean read FData1 write SetData1 default
DefaultData1;
property DataN: Integer read FDataN write SetDataN default
DefaultDataN;

procedure BeginUpdate;
procedure EndUpdate;
end;

constructor TClassWithDeferredEvents.Create;
begin
FData1:=DefaultData1;
FDataN:=DefaultDataN;
FUpdateLevel:=0;

inherited Create;

DoUnlock; { Force initial state }
end;

destructor TClassWithDeferredEvents.Destroy;
begin
{ IsUpdating should be False here. }
{ But no exceptions ever must escape from destructors. }

inherited Destroy;
end;

function TClassWithDeferredEvents.GetIsUpdating: Boolean;
begin
Result:=(0<FUpdateLevel);
end;

procedure TClassWithDeferredEvents.SetData1(const Value: Boolean);
begin
if (Data1<>Value) then begin
FData1:=Value;
DoData1Changed;
end;
end;

procedure TClassWithDeferredEvents.SetDataN(const Value: Boolean);
begin
if (DataN<>Value) then begin
FDataN:=Value;
DoDataNChanged;
end;
end;

procedure TClassWithDeferredEvents.DoData1Changed;
begin
if IsUpdating then Exit;

{ e.g. if not Data1 then DataN:=0; }
{
Alternatively:
if IsUpdating
then DirtyData1:=True
else begin
// Other updates
DirtyData1:=False;
end;
}
end;

procedure TClassWithDeferredEvents.DoDataNChanged;
begin
if IsUpdating then Exit;

{ e.g. if DataN<>0 then Data1:=True; }
{ e.g. if Assigned(OnDataNChanged) then OnDataNChanged(Self); }
end;

procedure TClassWithDeferredEvents.DoLock;
begin
{ Usually no code is needed here }
end;

procedure TClassWithDeferredEvents.DoUnlock;
begin
DoData1Changed;
DoDataNChanged;
end;

procedure TClassWithDeferredEvents.BeginUpdate;
begin
if not IsUpdating then DoLock;
Inc(FUpdateLevel);
Assert(IsUpdating);
end;

procedure TClassWithDeferredEvents.EndUpdate;
begin
Assert(IsUpdating);
Dec(FUpdateLevel);
if not IsUpdating then DoUnlock;
end;

Data1 might be shown to the user as a checkbox, and DataN as a spinedit.
Changing the value in one might change the value or state the other.
There might be many other data items and correlations.


> Here are my points:
>
> - Begin/End update have no meaning into this context. EndUpdate may fire
> onclick event. Does not mean anything. I update Checked state on single
> checkbox not on a large set of gui objects.

Begin/EndUpdate is borrowed from what the VCL calls it. Its intended
meaning is to start and end a cycle of updates (to state) with updates
(to screen) disabled for the duration, then handled all at once at the
end.

The update to the checkbox may cascade. Imagine a groupbox with a
checkbox on the border to enable/disable everything in it. A _lot_
may happen when a simple Boolean property toggles.

(Or it may not. Then it could make sense to keep it out of the whole
mutual updating scheme. But your problem was that too many ill-defined
things happened when a Boolean value toggled.)

EndUpdate may fire an OnClick event. Yes. At the end of a modification
cycle with suppressed events, the events should be allowed to resurface.
That's precisely correct. If you want only events for what actually
changed, keep Dirty flags and don't fire the events that don't need it.
(Firing an event clears the Dirty flag in addition to any other work
required.)


> - I still need to update FUpdateLevel (or use it somehow) somewhere -
> where do you propose I update it and what FUpdateLevel even means?

FUpdateLevel is the nesting level of Begin/EndUpdate calls. It's
possible that you are in the middle of a Begin/EndUpdate cycle and
enter another one. Then the counter goes to 2. Only when it goes down
to zero again do events start getting fired again.


> How it is used to distinguish the originator of the event?

You don't. The event firing means something needs to be updated. That
may be to secure an initial condition (the DoUnlock call in the
constructor), or because a value was set in code, or because a control
was twiddled in the user interface. I don't care where the event comes
from. I write code that doesn't either.


> The scope or placement of thisvariable now is not of much relevance.


> Like i said, I could use Tag (it is "mine" to use).

Where it ends up is not very important, whether it ends up in the
right place is. Every control has its own Tag; the sample code above
has a single flag for the entire class. The properties influence
each other (by design), so events should be enabled and disabled for
all of them at once.


> - try / finally also have no meaning here - no exception is fired on
> cb.Checked := true; anyway. try/finally are used for exception
> handling - which there is none here.

Try/*except* is used for exception _handling_. Try/finally is used to
make sure that if there has been a BeginUpdate, the EndUpdate does not
become lost. The exception still gets raised.

Checking a box may not raise an exception today but I've seen too many
event handlers Abort if they didn't like what was happening. Note also
that many things may be done between the Begin- and EndUpdate. Loading
a complete screenful of data into controls is not wrong or unexpected.
The intent is that if you're changing only a single property, you don't
need to wrap it in Begin/EndUpdate.


> So without a large text and explanation... please write a few lines
> of code... how would you:
> a) change checbox state

Data1:=Chance(.50);

If you are changing a lot of property values together and want to
avoid flickering, wrap the whole list in Begin/EndUpdate.


> b) update flags/whatever

BeginUpdate;
try
{ Work; }
finally
EndUpdate;
end;

> c) do the event itself

DoDataNChanged;


> That would make much more sense (at least to me). Give me an example
> that can be used here... not a large theory book. I'm a practical
> person not a theorist.

My examples will still remain theoretical, I'm afraid. You talk about
checkboxes; I talk about Boolean properties. Bear in mind that they
are logically equivalent; a Boolean property in the source could still
be presented as a checkbox to the user. With code like the following
(requires a checkbox somewhere; you can also hardwire it if you're in a
form class and already have one), the property keeps its value in a
checkbox instead of a private field:

type
TVisualClass = class(...)
private
FData: TCheckbox;

function GetData: Boolean;
procedure SetData(const Value: Boolean);
protected
procedure DoDataChanged; virtual;

procedure DataToggled(Sender: TObject);
public
constructor Create(const Checkbox: TCheckbox);
destructor Destroy; override;
end;

constructor TVisualClass.Create(const Checkbox: TCheckbox);
begin
Assert(Assigned(Checkbox));

FData:=Checkbox;
FData.OnClick:=DataToggled;
{ It's possible to save the old event handler is there is one, }
{ and chain calls to it, but I've not done so here. }

inherited Create;

DoDataChanged;
end;

destructor TVisualClass.Destroy;
begin
FData.OnClick:=nil;

inherited Destroy;
end;

function TVisualClass.GetData: Boolean;
begin
Result:=FData.Checked;
end;

procedure TVisualClass.SetData(const Value: Boolean);
begin
FData.Checked:=Value;
{ Depend on checkbox to fire event if value is changed }
end;

procedure TVisualClass.DoDataChanged;
begin
{ Do interesting stuff here }
end;

procedure TVisualClass.DataToggled(Sender: TObject);
begin
Assert(Sender=FData); { Sanity check; optional }
DoDataChanged; { Route event to property }
end;

Is it crazy to do this? Not to me; I've really done it in the past. At
a time when I needed intricate systems of mutually dependent properties,
I could think of property dependencies on even days and UI concerns on
odd days.

Groetjes,
Maarten Wiltink


A

unread,
Dec 14, 2009, 2:37:28 PM12/14/09
to
> Is it crazy to do this? Not to me; I've really done it in the past. At
> a time when I needed intricate systems of mutually dependent properties,
> I could think of property dependencies on even days and UI concerns on
> odd days.

i think you might be a little overdoing this!

i am all for improving code and making it more efficient but all this for a
check where the event came from? that is a bit raping it... maybe more than
just a bit. you gotta find the balance between being productive and being
just perfectionist.


Maarten Wiltink

unread,
Dec 14, 2009, 3:46:56 PM12/14/09
to
"A" <a@a.a> wrote in message news:hg645t$u7c$1...@gregory.bnet.hr...
[...]

> i think you might be a little overdoing this!

I'm good at that.


> i am all for improving code and making it more efficient but all

> this for a check where the event came from? ...

You keep saying that. And I keep telling you that I *never* think
about where an event comes from. I'm doing this because it makes it
easy for me to build mutually dependent properties, and modular
programs.

You described your situation earlier: there's a Boolean and an Integer,
and when the Boolean is toggled the Integer is reset to a default
value, but only after initialisation.

Fine. Set a flag during initialisation that inhibits event propagation.
In your case, you don't need the events fired after all when the flag
reverts, so remove that from DoUnlock. Remove any dead code if it
bothers you; it's not gospel. Do whatever works.

Just, please, stop telling me that you want to know where events
came from, because it *doesn't matter*. All that matters is what you
want to happen. It's your code, you can make it do - or not do -
anything you want, whenever you want. It's trying to put meaning into
it that isn't there that's wrong.

Groetjes,
Maarten Wiltink


Skybuck Flying

unread,
Dec 14, 2009, 9:23:53 PM12/14/09
to
Question for you Maarten:

"Don't you think it would be much easier to simple disable all gui component
event handlers and then update the gui components ?"

Example:

On program startup gui settings need to be loaded from a file... all gui
component event handlers are disabled... the gui components are updated with
loaded settings.

Then gui is enabled again...

Why go through so much trouble just to update a gui component ?

If a gui component needs to be updated while running then same technique
could be used:

"Disable all gui component event handlers, update necessary gui components,
re-enable all gui components".

It's a bit brute force but simple technique... little chance of bugs...
while your method seems a bit "bug prone ?" ;) :)

Bye,
Skybuck.


Maarten Wiltink

unread,
Dec 15, 2009, 4:25:22 AM12/15/09
to
"Skybuck Flying" <IntoTh...@hotmail.com> wrote in message
news:59b33$4b26f330$d53371df$31...@cache3.tilbu1.nb.home.nl...

> Question for you Maarten:
>
> "Don't you think it would be much easier to simple disable all gui
> component event handlers and then update the gui components ?"
>
> Example:
>
> On program startup gui settings need to be loaded from a file... all
> gui component event handlers are disabled... the gui components are
> updated with loaded settings.
>
> Then gui is enabled again...
>
> Why go through so much trouble just to update a gui component ?

Because it's not about GUI components. Note the complete absence of
them from the first stretch of sample code I gave. It dealt strictly
with values in properties, and what happens when any one of them
changes.

The second bit of sample code could easily be adapted to mirror a
property into a list containing any number of controls. Can you imagine
the horrors of trying to do that directly in controls' event handlers?

It's all about modularity. The start is a data model with values and
interrelations. That's very easy to translate into properties. Then
you hook up controls to the properties - again very easy. Suddenly,
you have a complete, responsive, efficient user interface in which
all the expected dependencies between the controls just work.

Sure, it's probably more than the absolute minimum of code, but most
of it is boilerplate. That's a _good_ thing. You don't have to think
very hard while typing it. The data model, the design, becomes the
hardest part, and I think that's as it should be.

Groetjes,
Maarten Wiltink


Skybuck Flying

unread,
Dec 16, 2009, 6:43:24 AM12/16/09
to
Well I didn't get most of your code, it also said "untested"... I understand
a little bit of the concepts you tried to implement and showed in code...
but I did not understand it fully... nor do I understand what problem it is
trying to solve ;) Maybe this vagueness is on purpose to keep your
"invention" secret... I can understand that...

But if your intention is to share your idea's and "inventions" with others
than I urge you to create a fully working example/test program that shows
the problem and the solution.

I think your "invention" might be a nice idea for Embarca-etc-ro to
implement it in Delphi to solve this nasty "event recursion problem" once
and for all ?!? ;)

(If it works that is... and for that a working example would be needed ! ;)
:))

Bye,
Skybuck =D


Maarten Wiltink

unread,
Dec 16, 2009, 7:38:05 AM12/16/09
to
"Skybuck Flying" <IntoTh...@hotmail.com> wrote in message
news:6518d$4b28c7d0$d53371df$24...@cache4.tilbu1.nb.home.nl...

> Well I didn't get most of your code, it also said "untested"...

That means I do not guarantee it to be without accidental syntax
errors. It does not mean that I introduced them deliberately.


> I understand a little bit of the concepts you tried to implement and
> showed in code... but I did not understand it fully... nor do I
> understand what problem it is trying to solve ;)

Using interdependent properties without runaway event cascades.
Programming with events meaningful at the application level.


> Maybe this vagueness is on purpose to keep your
> "invention" secret... I can understand that...

I was trying to explain something. Not sell it.

Groetjes,
Maarten Wiltink


Skybuck Flying

unread,
Dec 17, 2009, 12:11:54 AM12/17/09
to
> I was trying to explain something. Not sell it.

Well you failed badly at that ! ;) :)

FAIL :)

Bye,
Skybuck =D


0 new messages