1
votes

I'm trying to draw over the whole screen by using the desktop canvas and painting to it directly. The problem is I can't clear that desktop canvas. I've tried setting the canvas.pen.style to psNotXOR and draw over the old image but unfortunately, this is not reliable enough and some left overs are still present in some conditions.

My need is to draw a selection rectangle around a window / control when the mouse is over it.

4

4 Answers

5
votes

You don't write on what OS you have problems with the artefacts after clearing.

At least with desktop composition activated it is a very bad idea to draw directly to the desktop and to do XOR painting (see "Drawing To and Reading From the Screen -- Baaaad!" in this blog post). Apart from the negative performance implications you can't be sure what other painting happens at the same time and what effects and animations alter the displayed content, so a simple XOR may not be enough to completely erase everything.

One possible way to implement it would be a transparent overlay window of desktop size, and to draw your rubber band selector over that. Invalidating the whole window if the size changes should be enough, no need to erase the old selection line. If the overlay is removed the line will be gone too. Desktop composition will make sure that no flicker occurs. However, switching applications while selecting an area will be problematic, you need to catch this and immediately cancel the selection.

Edit:

I just tested it with Delphi 2009, and with the following test app:

  • a form with FormStyle set to fsStayOnTop and with AlphaBlend set to True
  • with an overridden CreateParams() method to add the WS_EX_TRANSPARENT extended style flag

I can pass all mouse clicks through to the underlying windows while being able to draw into a window on top of them. This should get you started.

0
votes

I've done some stuff like this in the past and I've never come up with an acceptable solution.

Having a think about it though you could send the desktop HWND an invalidate command.

Because the desktop is a modified listview control you should able to use something like

procedure InvalidateDesktop;
begin
  RedrawWindow(GetDesktopWindow, 0, 0, RDW_INVALIDATE);

  //if that doesn't work then try this:
  //Sendmessage(GetDesktopWindow, wm_setredraw, 1, 0);
end;

That might do what you want, I haven't tested it as I've just knocked it up for the example.

0
votes

the problem is that Windows won't return me the control behind the mouse

I think you need to hook the mouse event messages for this - the hooked message then gives you the window handle that the mouse is over.

Look up SetWindowsHookEx(WH_MOUSE,,,) and MOUSEHOOKSTRUCT.

-2
votes

This is how we do it in our app:

    FBitmap.Canvas.Brush.Color := clWhite;
    FBitmap.Canvas.FillRect(FBitmap.Canvas.ClipRect);