🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: borland.public.delphi.graphics
26 messages
26 total messages Started by David HAROUCHE Mon, 04 Aug 2008 18:27
TPaintBox and changing properties
#130
Author: David HAROUCHE
Date: Mon, 04 Aug 2008 18:27
23 lines
1426 bytes
What is the rule ?

I handle the Paint method of a TPaintBox :

procedure TMyBox.Paint;

begin
        inherited Paint;

       if "a property has changed"
          then handle some paintings
end;

"a property has changed" means the if you change Color, an Invalidate will 
be performed, like if I just call Invalidate within a method that doesnt 
affect any property...

How to make the difference between an Invalidate after a property changed 
and an Invalidate after a simple call ?

TPaintBox.Changed/Modified doesn't exist ?

DH 
Re: TPaintBox and changing properties
#131
Author: Jens Gruschel
Date: Mon, 04 Aug 2008 20:28
53 lines
2547 bytes
> procedure TMyBox.Paint;
> 
> begin
>         inherited Paint;
> 
>        if "a property has changed"
>           then handle some paintings
> end;
> 
> "a property has changed" means the if you change Color, an Invalidate will 
> be performed, like if I just call Invalidate within a method that doesnt 
> affect any property...

IMO the Paint method should just paint, nothing else, using the current 
properties. The property setters instead should invalidate the paint box.

procedure TMyBox.SetStyle(Value: TMyStyle);
begin
   if FMyStyle <> Value then begin
     FMyStyle := Value;
     Invalidate;
   end;
end;

For properties inherited from TPaintBox you have to override specific 
methods or handle certain messages, there are even some properties you 
cannot observe at all.

For the Color property (inherited from TControl) a CM_COLORCHANGED 
message is sent, so what you need is something like...

private
   procedure CMColorChanged(var Msg: TMessage); message CM_COLORCHANGED;

[...]

procedure TMyBox.CMColorChanged(var Msg: TMessage);
begin
   Invalidate;
   inherited;
end;

For other properties other messages are sent. If you have the VCL source 
code you can easily find out how you can catch such changes.

So the example setter method above can be enhanced: don't call 
Invalidate directly, instead call a virtual method "StyleChanged" others 
can override or send a user message others can handle in case some 
additional things have to done when such a property changes.

-- 
Jens Gruschel
http://www.pegtop.net
Re: TPaintBox and changing properties
#135
Author: Remy Lebeau (TeamB)
Date: Mon, 04 Aug 2008 20:56
34 lines
2070 bytes
"David HAROUCHE" <metanil@hotmail.com> wrote in message 
news:489749d3$2@newsgroups.borland.com...

> I handle the Paint method of a TPaintBox :

That is not the correct way to handle it.

>       if "a property has changed"
>          then handle some paintings

Ignore property changes like that.  Every time Paint() is called, the 
control has to redraw everything.  So just use whatever the current property 
values are, regardless of whether they have changed since the last time 
Paint() was called.

> "a property has changed" means the if you change Color, an Invalidate
> will be performed, like if I just call Invalidate within a method that
> doesnt affect any property...

Which is fine.  Invalidate() is a request for the control to re-draw its 
current state when the request gets processed.

> How to make the difference between an Invalidate after a property
> changed and an Invalidate after a simple call ?

You don't.  Nor should you be trying to anyway.  That is how the way to go.

> TPaintBox.Changed/Modified doesn't exist ?

Because it doesn't need to.


Gambit 
Re: TPaintBox and changing properties
#236
Author: Remy Lebeau (TeamB)
Date: Mon, 04 Aug 2008 20:58
11 lines
1287 bytes
"Jens Gruschel" <nospam@thisurldoesnotexist.com> wrote in message 
news:48976675$1@newsgroups.borland.com...

> For the Color property (inherited from TControl) a CM_COLORCHANGED message 
> is sent, so what you need is something like...

TControl already handles that for you.


Gambit 
Re: TPaintBox and changing properties
#237
Author: Jens Gruschel
Date: Mon, 04 Aug 2008 21:04
12 lines
1444 bytes
>> For the Color property (inherited from TControl) a CM_COLORCHANGED message 
>> is sent, so what you need is something like...
> 
> TControl already handles that for you.

Right, TControl calls Invalidate in its own CM_COLORCHANGED handler. Was 
only meant as an example that one can react to changes of such 
properties. Obviously it wasn't a good example :-)

-- 
Jens Gruschel
http://www.pegtop.net
Re: TPaintBox and changing properties
#248
Author: David HAROUCHE
Date: Mon, 04 Aug 2008 23:34
6 lines
1103 bytes
>> Ignore property changes like that.  Every time Paint() is called, the 
>> control has to redraw everything.

redraw, in my context, is a 'if I need to rebuild a bitmap'...

DH 
Re: TPaintBox and changing properties
#249
Author: David HAROUCHE
Date: Mon, 04 Aug 2008 23:37
3 lines
1126 bytes
I didnt want a TControl...

DH 
Re: TPaintBox and changing properties
#250
Author: Remy Lebeau (TeamB)
Date: Mon, 04 Aug 2008 23:53
10 lines
1326 bytes
"David HAROUCHE" <metanil@hotmail.com> wrote in message 
news:4897927f@newsgroups.borland.com...

> I didnt want a TControl...

TPaintBox derives from TControl


Gambit 
Re: TPaintBox and changing properties
#252
Author: Remy Lebeau (TeamB)
Date: Mon, 04 Aug 2008 23:58
18 lines
1751 bytes
"David HAROUCHE" <metanil@hotmail.com> wrote in message 
news:48979223$1@newsgroups.borland.com...

> redraw, in my context, is a 'if I need to rebuild a bitmap'...

Then you took the wrong direction when implementing your TPaintBox.  The 
sole responsibility of Paint() is to redraw the TPaintBox's current state 
onto its Canvas for display onscreen.  Nothing else.  If you want the 
TPaintBox to show a TBitmap, then manage/update the TBitmap separately and 
then Invalidate() the TPaintBox whenever the TBitmap is changed.  Paint() 
should draw the TBitmap as-is onto the Canvas.

In which case, you would just be duplicating what TImage does, and should 
have used that instead of a TPaintBox.


Gambit 
Re: TPaintBox and changing properties
#253
Author: David HAROUCHE
Date: Tue, 05 Aug 2008 00:54
5 lines
1207 bytes
>>  The sole responsibility of Paint() is to redraw the TPaintBox's current 
>> state onto its Canvas for display onscreen.  Nothing else.
Im taking the right direction right now, Sir...

DH 
Re: TPaintBox and changing properties
#254
Author: David HAROUCHE
Date: Tue, 05 Aug 2008 00:57
3 lines
1133 bytes
I needed the OnPaint Event...

DH 
Re: TPaintBox and changing properties
#266
Author: Jens Gruschel
Date: Tue, 05 Aug 2008 16:35
8 lines
1440 bytes
> I needed the OnPaint Event...

Doesn't matter. TPaintBox has an OnPaint event *and* receives a 
CM_COLORCHANGED message, because TPaintBox *is* a TControl.

-- 
Jens Gruschel
http://www.pegtop.net
Re: TPaintBox and changing properties
#269
Author: Remy Lebeau (TeamB)
Date: Tue, 05 Aug 2008 18:14
11 lines
1370 bytes
"David HAROUCHE" <metanil@hotmail.com> wrote in message 
news:4897a474$1@newsgroups.borland.com...

> Im taking the right direction right now, Sir...

From your earlier descriptions, it did not sound like you were.   What does 
your component actual do?


Gambit
Re: TPaintBox and changing properties
#273
Author: David HAROUCHE
Date: Tue, 05 Aug 2008 20:07
62 lines
3296 bytes
I do have a set of Turbo Pascal classes I migrated to VCL Style TComponent, 
TPersistent and TPaintBox...

They all have an "OutOfDate" behavior that is a flag to indicate if it has 
been updated (and so may be need redraw)...

The redraw consists of building its TBitmap from all TBitmap of the 
TPaintBox's subcomponents (here, subcomponents are a TList of 
TComponents)...

I have set the redraw as a thread, doing its job (Execute) as a loop for all 
subcomponents to redraw themself on the TPaintBox's TBitmap... The Paint 
method looks like :

procedure TMyPaintBox.Paint;

begin
    inherited Paint;

   // Check if OutOfDate and stop any running redraw
   if self.OutOfDate
            then begin
                           if Assigned(self.Thread)
                                then self.Thread.Terminate;

                           SysUtils.FreeAndNil(self.Thread);
                    end;

   // Check for a new redraw
   if not Assigned(self.Thread)
            then begin
                            self.Thread := TMyThread(self); // the thread 
will call Invalidate when Terminated
                            self.Thread.Execute;
                   end
            else
   // Redraw has been finished
   if self.Thread.Terminated
            then begin
                           SysUtils.FreeAndNil(self.Thread);

                            self.Canvas.Draw(0, 0, self.MyBitmap);

                            self.OutOfDate := False;
                   end;
end;

That works fine : if I change a property of a subproperty of TMyPaintBox, 
using my own mechanism ("OutOfDate"), then the PaintBox redraws it self.

But if I change the Color property, that fails...due to the VCL, only 
calling Invalidate, so that Paint is fired, but no flags tells me if a 
property have been modified...as I dont handle the VCL properties...

What I will do is to "forget" the color property, and use a "WallPaper" 
subproperty with a Color property, so that it will be handled by my 
"OutOfDate" mechanism...

But what I miss is the "natural" mechanism for validating the change of one 
native property of TPaintBox...

end; 
Re: TPaintBox and changing properties
#272
Author: David HAROUCHE
Date: Tue, 05 Aug 2008 20:08
3 lines
1182 bytes
Look at the last second answer for my needs...

DH 
Re: TPaintBox and changing properties
#274
Author: David HAROUCHE
Date: Tue, 05 Aug 2008 20:16
9 lines
1067 bytes
To solve the problem, I found !

I just have overriden the Color property with my self setters and getters 
and use my OutOfDate mechanism...


hihihih

DH 
Re: TPaintBox and changing properties
#275
Author: Remy Lebeau (TeamB)
Date: Tue, 05 Aug 2008 21:07
83 lines
4458 bytes
"David HAROUCHE" <metanil@hotmail.com> wrote in message 
news:4898b29e@newsgroups.borland.com...

> I do have a set of Turbo Pascal classes I migrated to VCL Style
> TComponent, TPersistent and TPaintBox...
>
> They all have an "OutOfDate" behavior that is a flag to indicate
> if it has been updated (and so may be need redraw)...

That kind of model does not migrate well to what you are attempting to do 
with Paint().

> The redraw consists of building its TBitmap from all TBitmap of the 
> TPaintBox's subcomponents (here, subcomponents are a TList of 
> TComponents)...

Paint() is *NOT* the correct place to do that kind of logic.

> I have set the redraw as a thread, doing its job (Execute) as a
> loop for all subcomponents to redraw themself on the TPaintBox's
> TBitmap...

Why do you need a thread for that?When a particular subcomponent changes, 
just update the TBitmap immediately and then Invalidate() the TPaintBox. 
When Paint() is called at a later time, it can then draw whatever the 
TBitmap currently contains.  No threading is necessary.  Especially 
considering that you are using the thread completely wrong to begin with. 
DO NOT EVER call a thread's Execute() method directly!!!  You are completely 
defeating the purpose of using a thread.

> The Paint method looks like :

That implementation is completely wrong for what you are attempting.  You 
need to do something like this instead:

    procedure TMyPaintBox.Paint;
    begin
        inherited Paint;
        Self.Canvas.Draw(0, 0, Self.MyBitmap); // <-- that is ALL you need 
in Paint() !!!
    end;

Assign an OnChange event handler to Self.MyBitmap and have it call 
Invalidate().  Whenever the TBitmap or any of the TPaintBox's properties are 
changed, Paint() is called automatically to display the updated TBitmap. 
All you have to do is update the TBitmap when any of the subcomponents 
change.  NOTHING ELSE.

> That works fine

No, it doesn't.  There are many flaws in your code.  I'm surprised your code 
hasn't completely crashed yet.

> if I change a property of a subproperty of TMyPaintBox, using my own 
> mechanism ("OutOfDate"), then the PaintBox
> redraws it self.

Not for the right reasons, though.

> But if I change the Color property, that fails...due to the VCL,
> only calling Invalidate, so that Paint is fired, but no flags tells me
> if a property have been modified...as I dont handle the VCL
> properties...

No such flag is necessarily in the first place.  You implemented your 
component completely wrong.  If you fix your component to work correctly, 
you will find that the changing the Color property will start working fine.

> What I will do is to "forget" the color property, and use a "WallPaper" 
> subproperty with a Color property, so that it will be handled by my 
> "OutOfDate" mechanism...

Or, you could get rid of your OutOfDate mechanism instead.  It does not 
belong in that component to begin with.

> But what I miss is the "natural" mechanism for validating the
> change of one native property of TPaintBox...

Becaise you are approaching this from the completely wrong design choices.


Gambit 
Re: TPaintBox and changing properties
#277
Author: Jens Gruschel
Date: Tue, 05 Aug 2008 21:15
10 lines
1385 bytes
> But if I change the Color property, that fails...due to the VCL, only 
> calling Invalidate

I already told you how you can detect changes of the Color property, 
haven't you seen?


-- 
Jens Gruschel
http://www.pegtop.net
Re: TPaintBox and changing properties
#276
Author: Jens Gruschel
Date: Tue, 05 Aug 2008 21:17
18 lines
1475 bytes
> I just have overriden the Color property with my self setters and getters 
> and use my OutOfDate mechanism...

No, that's suboptimal. Better do it this way:

private
   procedure CMColorChanged(var Msg: TMessage); message CM_COLORCHANGED;

[...]

procedure TMyBox.CMColorChanged(var Msg: TMessage);
begin
   DoWhateverMustBeDoneWhenTheColorPropertyHasChanged;
end;

-- 
Jens Gruschel
http://www.pegtop.net
Re: TPaintBox and changing properties
#280
Author: David HAROUCHE
Date: Tue, 05 Aug 2008 22:30
41 lines
3004 bytes
>> That kind of model does not migrate well to what you are attempting to do 
>> with Paint().
I am just trying to update the displayed bitmap...but I can't know when 
Color has been changed !

>> Why do you need a thread for that?When a particular subcomponent changes,
> just update the TBitmap immediately and then Invalidate() the TPaintBox. 
> When Paint() is called at a later time, it can then draw whatever the 
> TBitmap currently contains.
I do draw a "relief" TBitmap, that is a 3D TBitmap...
That means that a subcomponent could "turn on itself or move into space", so 
I will have to redraw it...
But then comes the overlapping of graphical objects...
So I manage the display as building a buffered bitmap, and when completely 
rebuild, then draw itwithin an Invalidate...

I thread it so it takes less ressources, and I can stop and restart it while 
it draws to reflect a change in subcomponents...

>> Assign an OnChange event handler to Self.MyBitmap and have it call
I can't do that, the TBitmap could change have 1 pixel change for each 
subcomponent ! I do manage the change of the component's property, not its 
own bitmap !

>> I'm surprised your code hasn't completely crashed yet.
Me too ! But now that I only consider my OutOfDate mechanism, I make it work 
fine !

>>> if I change a property of a subproperty of TMyPaintBox, using my own 
>>> mechanism ("OutOfDate"), then the PaintBox
>>> redraws it self.
>>
>> Not for the right reasons, though.
At the end of the OutOfDate process, that calls an Invalidate...

>> Becaise you are approaching this from the completely wrong design 
>> choices.
What I was first looking for was to keep Pascal as the programming language, 
and to USE the VCL, not to integrate with...

DH 
Re: TPaintBox and changing properties
#279
Author: David HAROUCHE
Date: Tue, 05 Aug 2008 22:31
7 lines
1325 bytes
Yes I've seen, but that's not only the Color property I'd like to manage, 
but any of them...

But I succeded using a Color property of a subproperty in TPaintBox... So I 
do manage the change by myself !

DH 
Re: TPaintBox and changing properties
#278
Author: David HAROUCHE
Date: Tue, 05 Aug 2008 22:49
9 lines
1177 bytes
Success !

I fixed it  !

Was an Invalidate missing in the setter of my OutOfDate !

Thanx

DH 
Re: TPaintBox and changing properties
#281
Author: Remy Lebeau (TeamB)
Date: Tue, 05 Aug 2008 23:47
74 lines
4463 bytes
"David HAROUCHE" <metanil@hotmail.com> wrote in message 
news:4898d829$1@newsgroups.borland.com...

> I am just trying to update the displayed bitmap...but I can't know when 
> Color has been changed !

Yes, you can, and you have already been told how to do it - intercept the 
CM_COLORCHANGED message.

> I do draw a "relief" TBitmap, that is a 3D TBitmap...

What I said earlier still stands.

> That means that a subcomponent could "turn on itself
> or move into space", so I will have to redraw it...

Unless your subcomponents are smart enough to know exactly what areas of the 
TBitmap they get rendered in, and/or are smart enough to access the 
TPaintBox's properties to get the current values, like Color, then you have 
to redraw the entire TBitmap each time any of the subcomponents changes.  So 
either way, we're back to the situation where you are responsible for 
updating the TBitmap when needed and then call Invalidate() to display it 
as-is.

> But then comes the overlapping of graphical objects...
> So I manage the display as building a buffered bitmap, and when
> completely rebuild, then draw itwithin an Invalidate...

That is not what your earlier code was trying to do.

> I thread it so it takes less ressources

But you did not thread it correctly.

> and I can stop and restart it while it draws to reflect
> a change in subcomponents...

No, you can't, because you were not running the thread correctly to begin 
with.  If you are going to use a thread, then there is no reason to stop it 
anyway.  Just have it query subcomponents for changes, or have them signal 
the thread when changes occur.  Let the thread notify the TPaintBox when the 
TBitmap changes so it can Invalidate() itself.  But in any case, Paint() 
still SHOULD NOT MANAGE the TBitmap in ANY way!  The responsibility of 
Paint() is to display the TBitmap as-is, NOTHING ELSE.  That is how drawing 
visual controls in the VCL works in general.  If you are going to use an 
intermediate TBitmap to supplement the drawing, the management of the 
TBitmap is meant to be keapt separate from the on-screen drawing.

>>> Assign an OnChange event handler to Self.MyBitmap and have it call
> I can't do that, the TBitmap could change have 1 pixel change for each 
> subcomponent !

So?  Simply have the subcomponents flag a value inside the TPaintBox to tell 
it that they are busy.  Any intermediate OnChange events that occur can then 
be ignored so Invalidate() it not called right away.  Many VCL components 
implement Begin/EndUpdate() type of methods for that same purpose.  It is 
very effective in reducing drawing overhead during busy operations.

>>> I'm surprised your code hasn't completely crashed yet.
> Me too ! But now that I only consider my OutOfDate mechanism,
> I make it work fine !

It is not fine if you are still using that thread code you showed earlier.

> What I was first looking for was to keep Pascal as the programming
> language, and to USE the VCL, not to integrate with...

Even with that in mind, you're still approaching this all wrong.  There are 
better ways to accomplish what you are attempting, whi;e being able to keep 
your logic separated from the VCL.


Gambiut 
Re: TPaintBox and changing properties
#282
Author: David HAROUCHE
Date: Wed, 06 Aug 2008 00:13
13 lines
1599 bytes
>> Yes, you can, and you have already been told how to do it - intercept the 
>> CM_COLORCHANGED message.
Ok

>> Unless your subcomponents are smart enough to know exactly what areas of 
>> the
The 3D TBitmap does it all by itself...holding the nearest distance/color 
for any 2D projected 3D point...

>> But you did not thread it correctly.
Now I do

DH 
Re: TPaintBox and changing properties
#287
Author: Jens Gruschel
Date: Wed, 06 Aug 2008 08:44
35 lines
2369 bytes
> Yes I've seen, but that's not only the Color property I'd like to manage, 
> but any of them...

Other properties can be observed in a similar way. Just tell us which 
property changes you want to detect.

> But I succeded using a Color property of a subproperty in TPaintBox... So I 
> do manage the change by myself !

You can do this of course, but IMO it's better to use the properties 
already existing. One advantage is that it allows to work with a base 
class. Imagine someone wants to recolor all paint boxes of a form 
including yours:

// if you introduce your own property
for I := 0 to SomeForm.ControlCount - 1 do begin
   Control := SomeForm.Controls[I];
   if Control is TDavidsPaintBox then
     TDavidsPaintBox(Control).DavidsColor := clBlue;
   else if Control is TPaintBox then
     TPaintBox(Control).Color := clBlue;
end;

vs.

// if you use the existing property
for I := 0 to SomeForm.ControlCount - 1 do begin
   Control := SomeForm.Controls[I];
   if Control is TPaintBox then
     TPaintBox(Control).Color := clBlue;
end;

-- 
Jens Gruschel
http://www.pegtop.net
Re: TPaintBox and changing properties
#289
Author: David HAROUCHE
Date: Wed, 06 Aug 2008 11:51
18 lines
1856 bytes
>> Other properties can be observed in a similar way. Just tell us which 
>> property changes you want to detect.
Ok

>> You can do this of course, but IMO it's better to use the properties 
>> already existing. One advantage is that it allows to work with a base 
>> class. Imagine someone wants to recolor all paint boxes of a form 
>> including yours:
Yes & no !

My classes are all from TPersistent and come from Turbo Pascal, so I use a 
Minimalist VCL...

In term of portability, it makes sense to only use TPaintBox for what it is. 
But it wasnt a good thing not to be able to use its Color property...That is 
now solved !

Thx
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads