Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Custom Child Controls Force All Parent Controls to Repaint


This question is answered. Helpful answers available: 1. Correct answers available: 1.


Permlink Replies: 8 - Last Post: Nov 30, 2016 1:14 PM Last Post By: Richard Zarr
Richard Zarr

Posts: 74
Registered: 7/1/98
Custom Child Controls Force All Parent Controls to Repaint  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 30, 2016 8:14 AM
We're having trouble trying to figure out why our custom controls cause the parent to repaint all the other child controls. Our control descends from TCustomTransparentControl. We use a background TBitmap (32 bit) with alpha channel and our paint method looks like this:

procedure TMyControl.Paint;
begin
 // if the background has not been setup, then paint it... (first time)
 if (Background.Width = 0) then DrawBackground;
 // copy using alpha blending (Background contains an alpha channel)
 AlphaBlend(
  Canvas.Handle, 0, 0, Width, Height,
  Background.Canvas.Handle, 0, 0, Width, Height, BackgroundBlend
 );
 inherited;
end;


If we need to update the control, we call a method called DrawControl which looks like this:

procedure TMyControl.DrawControl;
begin
 DoSpecialPreWorkStuff;  // calculates sizes / positions
 DrawBackground;  // draws the control on the background TBitmap using GDI+
 Invalidate; // repaints the control when Windows gets around to it
end;


If we place several of these custom controls into another parent control (e.g. a TWinControl descendant), and update only one control, all the other child controls are repainted. Normally this wouldn't bother us... except that we are asynchronously updating a VERY large amount of these child controls in an almost real-time fashion... so this is a MAJOR performance hit. If we are updating only one control every 16ms (e.g. video rate), then ALL of them are repainting as well... we've tried setting up clipping regions on the parent control to no avail. Due to the asynchronous nature of both the control updates and when Windows actually fires the WM_PAINT message we've had trouble tracking down what is causing this. Thoughts?

Regards,

Rick Z - Strasis Systems
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Custom Child Controls Force All Parent Controls to Repaint  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 30, 2016 8:53 AM   in response to: Richard Zarr in response to: Richard Zarr
Richard wrote:

We're having trouble trying to figure out why our custom controls
cause the parent to repaint all the other child controls. Our control
descends from TCustomTransparentControl. We use a background
TBitmap (32 bit) with alpha channel

Does your component require input focus? Or is it strictly graphical only?
TCustomTransparentControl is a TWinControl descendant. If you don't need
your component to have its own window, you should derive from TGraphicControl
instead. For non-windowed controls, a lot of performance issues can be solved
that way.

If we place several of these custom controls into another parent
control (e.g. a TWinControl descendant), and update only one
control, all the other child controls are repainted.

TCustomTransparentControl overrides Invalidate() to:

1. invalidate any of the Parent's child controls (and their children) that
are below it in z-order.

2. invalidate the Parent.

Normally this wouldn't bother us... except that we are asynchronously
updating a VERY large amount of these child controls in an almost
real-time fashion... so this is a MAJOR performance hit.

You will have to stay away from using TCustomTransparentControl.Invalidate().
Try calling Windows.InvalidateRect() directly instead. Or don't derive
from TCustomTransparentControl at all.

--
Remy Lebeau (TeamB)
Richard Zarr

Posts: 74
Registered: 7/1/98
Re: Custom Child Controls Force All Parent Controls to Repaint  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 30, 2016 9:18 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Richard wrote:

We're having trouble trying to figure out why our custom controls
cause the parent to repaint all the other child controls. Our control
descends from TCustomTransparentControl. We use a background
TBitmap (32 bit) with alpha channel

Does your component require input focus? Or is it strictly graphical only?
TCustomTransparentControl is a TWinControl descendant. If you don't need
your component to have its own window, you should derive from TGraphicControl
instead. For non-windowed controls, a lot of performance issues can be solved
that way.

If we place several of these custom controls into another parent
control (e.g. a TWinControl descendant), and update only one
control, all the other child controls are repainted.

TCustomTransparentControl overrides Invalidate() to:

1. invalidate any of the Parent's child controls (and their children) that
are below it in z-order.

2. invalidate the Parent.

Normally this wouldn't bother us... except that we are asynchronously
updating a VERY large amount of these child controls in an almost
real-time fashion... so this is a MAJOR performance hit.

You will have to stay away from using TCustomTransparentControl.Invalidate().
Try calling Windows.InvalidateRect() directly instead. Or don't derive
from TCustomTransparentControl at all.

--
Remy Lebeau (TeamB)

Hi Remy,

This explains our issue... unfortunately our controls require input focus and need to receive mouse clicks. We'll try the InvalidateRect() first... otherwise we'll need to change our approach. We'll report back here on what we did to resolve this... thanks again for the quick response!

Regards

Rick Z - Strasis Systems
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Custom Child Controls Force All Parent Controls to Repaint  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 30, 2016 9:36 AM   in response to: Richard Zarr in response to: Richard Zarr
Richard wrote:

This explains our issue... unfortunately our controls require
input focus and need to receive mouse clicks.

Graphical controls can receive mouse input, but not keyboard input. When
the user clicks on a graphical control, they are actually clicking on the
parent control. So TWinControl checks if the coordinates of a mouse event
fall within the bounds of a graphical child control, and if so then passes
the event to that child for further processing. If you look at other graphical
controls like TLabel, TImage, etc, they have OnMouse... events available.

--
Remy Lebeau (TeamB)
Richard Zarr

Posts: 74
Registered: 7/1/98
Re: Custom Child Controls Force All Parent Controls to Repaint  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 30, 2016 10:46 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Richard wrote:

This explains our issue... unfortunately our controls require
input focus and need to receive mouse clicks.

Graphical controls can receive mouse input, but not keyboard input. When
the user clicks on a graphical control, they are actually clicking on the
parent control. So TWinControl checks if the coordinates of a mouse event
fall within the bounds of a graphical child control, and if so then passes
the event to that child for further processing. If you look at other graphical
controls like TLabel, TImage, etc, they have OnMouse... events available.

--
Remy Lebeau (TeamB)

Hi Remy,

Looking deeper, our controls do require focus and keyboard input. This is to allow manual selection of the controls separate from mouse clicks - tabbing and space bar for instance.

The InvalidateRect() call does work in stopping the overall redrawing of all components. A new problem however is when asynchronous events are coming in and are in the middle of rendering when the control is being destroyed. This fails:

var
 R : TRect;
begin
 
 // drawing stuff is here
 
 // blows up on either of these two lines depending on when the control is being destroyed...
 R := ClientRect;
 InvalidateRect(Handle, @R, false);
end


Any idea how to trap when to exit before the ClientRect fails - error is: "Control X has no parent window". I have a try-except block around the whole section, but this feels sloppy...

Regards,

RZ
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Custom Child Controls Force All Parent Controls to Repaint  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 30, 2016 11:05 AM   in response to: Richard Zarr in response to: Richard Zarr
Richard wrote:

The InvalidateRect() call does work in stopping the overall redrawing
of all components. A new problem however is when asynchronous
events are coming in and are in the middle of rendering when the
control is being destroyed.

Why are you invoking your rendering code while the control is being destroyed?
That implies a logic bug in your code. If everything is processing in the
main UI thread, you would not be able to destroy the control while rendering,
and vice versa. Or are you rendering in a worker thread?

In any case, you can have your component check for either "HandleAlloacated
= True" or "not (csDestroying in ComponentState)" before invoking any code
that replies on the Handle property.

Any idea how to trap when to exit before the ClientRect fails
- error is: "Control X has no parent window".

That error means you are accessing the Handle property when it is 0 and the
Parent property is nil.

--
Remy Lebeau (TeamB)
Richard Zarr

Posts: 74
Registered: 7/1/98
Re: Custom Child Controls Force All Parent Controls to Repaint  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 30, 2016 11:56 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Richard wrote:

The InvalidateRect() call does work in stopping the overall redrawing
of all components. A new problem however is when asynchronous
events are coming in and are in the middle of rendering when the
control is being destroyed.

Why are you invoking your rendering code while the control is being destroyed?
That implies a logic bug in your code. If everything is processing in the
main UI thread, you would not be able to destroy the control while rendering,
and vice versa. Or are you rendering in a worker thread?

In any case, you can have your component check for either "HandleAlloacated
= True" or "not (csDestroying in ComponentState)" before invoking any code
that replies on the Handle property.

Any idea how to trap when to exit before the ClientRect fails
- error is: "Control X has no parent window".

That error means you are accessing the Handle property when it is 0 and the
Parent property is nil.

--
Remy Lebeau (TeamB)

Hi Remy,

You are correct... there are worker threads that are being stopped and destroyed in the component's destructor - thus the possibility of rendering code firing due to the asynchronous nature of when the thread is stopped. We've created a new method called InvalidateControl which does this:

procedure TMyControl.InvalidateControl;
var
 R : TRect;
begin
 if (WindowHandle = 0) then exit;  // we'll take a look at using "HandleAlloacated = false" or "(csDestroying in ComponentState)" instead
 try
  R := ClientRect;
  InvalidateRect(Handle, @R, false);
 except
  exit; // trap just in case the window handle goes during the above methods
 end;
end;


As always, you guys are extremely knowledgeable - once we made this change, the performance went through the roof! Thanks!!

Regards,

Rick Z - Strasis Systems
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Custom Child Controls Force All Parent Controls to Repaint
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 30, 2016 12:12 PM   in response to: Richard Zarr in response to: Richard Zarr
Richard wrote:

You are correct... there are worker threads that are being stopped
and destroyed in the component's destructor - thus the possibility
of rendering code firing due to the asynchronous nature of when the
thread is stopped.

That sounds suspicious. Can you show your actual thread logic? Not the
actual drawing code, but just how the thread interacts with the component.
Seems like a solution would be to have the destructor signal the threads
to stop producing new requests and then discard any requests that have already
been posted before they can be processed.

--
Remy Lebeau (TeamB)
Richard Zarr

Posts: 74
Registered: 7/1/98
Re: Custom Child Controls Force All Parent Controls to Repaint  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 30, 2016 1:14 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Richard wrote:

You are correct... there are worker threads that are being stopped
and destroyed in the component's destructor - thus the possibility
of rendering code firing due to the asynchronous nature of when the
thread is stopped.

That sounds suspicious. Can you show your actual thread logic? Not the
actual drawing code, but just how the thread interacts with the component.
Seems like a solution would be to have the destructor signal the threads
to stop producing new requests and then discard any requests that have already
been posted before they can be processed.

--
Remy Lebeau (TeamB)

Hi Remy,

The worker thread is running a video decoder, so depending on where the decoder is in the frame, the time will vary until the thread terminates. Here's the destructor code:

destructor TVideoDisplay.Destroy;
begin
 FRunning := false;  // used to stop drawing during close
 FLostSigTmr.Enabled := false;
 if Assigned(FStream) then
  begin
   FStream.Stop;
   FStream.WaitFor;
   FreeAndNil(FStream);
  end;
 FVideo.Free;
 
 // free all the other analytics classes here...
 
 inherited;
end;


There are two classes we created - one is the stream class. It handles all the stream packet parsing from the source. It feeds several other classes one of which is a video decoder. The decoder runs in the stream thread. There is also an analytics engine which also runs in the stream thread, but we will be changing that due to processing overhead. When a frame is completed, the presentation time code is adjusted and we use a high resolution timer to delay the presentation of the frame - we copy the frame buffer to a bitmap which is then painted when we call the new InvalidateControl method. There's also a series of overlay bitmaps that are updated asynchronously from the video stream due to the analytics processing. So lots of moving parts have to stop before we can release the resources. We'll probably be adding a FIFO in the frame presentation section to synchronize the overlays with the video (and audio thread) - we'll most likely supply a master clock that all the video frame presentation sections sync to. This may likely stop the issues with the errors since the drawing will be deterministic and we can stop it in the destructor by either disconnecting the event handler or flagging the FIFO to stop reading (a separate thread).

This is not a simple control... thanks for your feedback!!

Regards,

Rick Z
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02