Avoid painting over nested controls - delphi

I am writing a toolbar-style control and use the ThemeServices unit to paint the background required by the toolbar. Like so:
ThemeServices.DrawElement(Canvas.Handle,
ThemeServices.GetElementDetails(trRebarRoot), ARect);
I then drop child controls onto the toolbar and voila - instant toolbar. Except that every now and again I notice that the nested (child) controls don't repaint as well. So my control paints its background and the child controls disappear until you move the mouse over them.
Some of the edit controls show their client area (no border) and some of them are just gone altogether.
This happens mostly when I place a window from another application over the toolbar and move it away.
I keep thinking this has to be very easy to cure, that perhaps I'm somehow preventing the paint message from reaching the child controls, or missing an override but I have been unable to figure it out.

For this to work properly so you do not end up over-painting the child controls from the WM_NCPaint, you can use the Window's GDI Region functions to create a clipping region which excludes the areas you do not want to paint.
You can do this by creating a Region using the CreateRectRgn (or similar) function with the size of your background.
Select this region in to the DC you are using via the SelectClipRgn function.
Next, loop through the top level child windows on your toolbar / panel and calling ExcludeClipRect with the appropriate coords for each child.
Finally when you paint, only the area's that have not been excluded will be painted.
Also be aware you might need to clear up your region (best check the GDI help).

That's normal for a canvas to have to repaint when covered by another windows control.
You should draw your tool bar in the OnPaint event of the container control.

OK, I found the problem myself. I was drawing the background in WM_NCPAINT because it is a gradiated background that cannot really be drawn bit by bit. I didn't realize that WM_NCPAINT is often called without the client controls being repainted. So I still paint the background in WM_NCPAINT but also in WM_PAINT. The latter is already clipped to exclude the border and to get the former to clip the client area, I called ExcludeClipRect.
This works like a treat.

Related

Paint TProgressBar behind text and icon in TSpeedButton

Is it possible to paint a TProgressBar on a TSpeedButton, behind text and icon ?
I have no idea how to get started on this (assuming it's possible).
How would I go about it ?
In this particular case I use the button to start and stop a process, and it would be nice to display the process in that button as well.
No, this is not possible with the standard TSpeedButton without creating your own descendant.
TSpeedButton does all of its drawing in response to the WM_PAINT message, and there is no way for you to inject another control behind the content that is drawn, because the drawing would erase the area where your control is drawing itself. You can see this yourself; you have the source code for TSpeedButton in almost every Delphi and C++ Builder version.
In addition, a TSpeedButton is a graphical control, not a windowed control (it derives from TGraphicControl instead of TWinControl), so it does not have a window handle to be used as the parent for other controls.

How to prevent a firemonkey form to draw itself (for calling an own drawing function)?

I try to convert an existing OpenGL-Application from VCL-based forms to FMX-based forms in C++ Builder XE3.
For this purpose I use the handle of a Firemonkey-form to create the OpenGL device context and make this form a child of another form to mimic the panel inside the form which I had in my VCL-based application.
The problem is now that after a resize-event or in general after calling Invalidate() the form is rendered with its background. This causes a flickering or even worse: sometimes the background is shown instead of my OpenGL rendered scene.
How can I prevent the repaint of the region at the position of my Firemonkey-OpenGL-form. Alternatively, can I define an Ownerdraw-function or something else?
Please note, that it is no option for me to use a Firemonkey-3d-Form.
Have tried giving the form a transparent background? Then you can paint whatever you want 'over the top'.
The quick way is to set the forms Transparent property to True, but this creates issues with ComboBoxes.
Another way is to place a TRectangle on the form. Set it's StyleName to 'backgroundstyle'. Set it's Fill.Color to claNull. Set Stroke.Thickness to 0. This will replace the default background styling for the form.
If you want to paint your own borders too you'll need to investigate non-client area styling but I don't have a pointer to a good resource at present.
After hours of experimenting I found the solution: The "TCommonCustomForm" has the needed properties: (1) it does not draw itself, (2) it has a handle which can be used for OpenGL initialization.
I hope this information will help others.

Add shortcut to a TForm or Panel.Transparent?

To thwart the nit-pickers, let me start with, I searched here with this and could not find an answer, and yes, also I did scroll through the "Similar questions."...
Adding shortcuts to a TForm
I want to drag and drop some shortcuts from the Desktop to a TForm in my application. I am using Anders Melander's brilliant Drag Drop Suite (DDS).
I tried putting a TImage on the form but the DDS does not drop to an Image so I added a TPanel with a TImage on it. I could then drop on the panel and assign the image to the TImage.Picture. Problem was the Panel has no Transparent Property so the shortcut on the form looks clunky with the visible Panel behind it.
I need to be able to drop to the TImage or make the underlying TPanel transparent.
Can anyone help with code-snippets for either of those options, or better yet, a method of dropping a Shortcut directly on to my Form.
Thanks
Coincidentally I needed to make a TWinControl (the base for every visible control with a window handle, including TPanel) transparent. I found numerous results and applied them to this answer.
It's been a while since I implemented drag and drop, but I assume you call some API and pass it the handle of the panel? That answers the question why you can't use TImage. TImage is a graphic control, a control without a handle, that relies on its parent for recieving messages and drawing itself.
It should be possible to use the form, though, since that has a handle too.
If the TImage is directly on the TForm, then let the TForm handle the drop, no TPanel needed. OLE Drag&Drop operations (which Ander's components implement) provide coordinates where dragging and dropping occurs. The TForm should be able to detect when a drag is over the area occupied by the TImage and what type of data is being dragged, and only allow dropping of supported types within that area, extracting the dropped data and updating the TImageas needed, and denying anything else that does not match that criteria.

How to make control invisible but responsive to mouse events?

I want to create a control (derived from TCustomControl) that is invisible but reacts to normal events (I want to use it to show a hint when moving the mouse over a custom element). I thought overriding the paint method and leaving it empty would do the job but unfortunately a rectangle is drawn where the component is.
How can I make the control completely invisible?
You can inherit from TGraphicControl instead of from TCustomControl, and leave the paint handler empty. Nothing will be drawn.
If you need a windowed control, then you should make sure that it has no border and uses the parent background. See this question for info on how to do that. You may need to override CreateParams() as well, to remove the border style bits.
If the control is not visible then process click messages in the parent, do a simple test for those being in the control's rectangle and use PostMessage to forward the message to the control. Such code may be more readable than empty paint handlers.
Bri

AlphaBlend a child form

I'm looking after a way to AlphaBlend a child form, if possible using layered windows as there will be interactive controls behind it.
The problem is I have a component in a regular TForm that paints multiple visual layers (drawings, pictures...). At some point I need to instantiate an editor control on this form (in-place), this control will involve a variety of standard input controls outside of my control (edit box, check box, etc.), however I would like to overlay the layers in front of the layer being edited using alpha blending (and WS_EX_TRANSPARENT to make it click-through).
I first thought of using child forms for that (borderless TForms parented to the component), and that works alright, up to the point where the AlphaBlended TForm isn't alphablended at all, but turns opaque as soon as parented...
I then tried to unparent the alphablended form, set it to fsStayOnTop, and by reacting to the relevant events, keep it in front of the component on screen, that works, but that isn't a truly satisfying solution: the alphablended StayOnTop form is also in front of other modal and modeless forms of the application, should the user decide to move them in front of the component...
So, any other ideas on a way to have a child form be alphablended? (or behave like it is)
According to MSDN you are out of luck, as WS_EX_LAYERED cannot be used for child windows.
Maybe you could hide all editor forms when your form / application loses focus, that would at least keep them from being on top of other windows. Still, it's unsatisfactory...

Resources