views:

1165

answers:

4

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.

A: 

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.

Ryan J. Mills
The visible listview is just a child of the `HWND` returned by `GetDesktopWindow()`, so that's probably not going to work as-is.
mghie
I did say I hadn't tested it. <grin>
Ryan J. Mills
As mghie anticipated, this does not work unfortunately. Thanks for the answer though, Ryan.
John Riche
+4  A: 

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.

mghie
I thought about it but the problem is that Windows won't return me the control behind the mouse then. Or will it ? Come to think about it, if the canvas is transparent, this might do the trick. I'll try that.Thank you for the very detailed and helpful answer.
John Riche
Well, selecting doesn't work and the top level window is always the one behind the mouse.
John Riche
Sorry, don't understand. Do you start selection by just left clicking the mouse on the desktop and start dragging it? If so, how do you catch this, with a global (low level) mouse hook? Or do you create the overlay (when in a special selection mode) and start selection with a mouse click on it? Maybe you can expand your question with some more detail.
mghie
Note that you can make transparent windows "click-through", which is probably what you need to do. Google for it, there was a DiLascia article some years ago on it.
mghie
This is exactly what I needed. Thank you for the very helpful answers.
John Riche
A: 

This is how we do it in our app:

    FBitmap.Canvas.Brush.Color := clWhite;
    FBitmap.Canvas.FillRect(FBitmap.Canvas.ClipRect);
Alan Clark
-1 that's not the "clearing" the OP asked for. Instead it justs paints a white rectangle.
Smasher
Ah, gotcha. Didn't read the question properly.
Alan Clark
A: 

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.

Despatcher
I'm already using a global mouse hook to retrieve the window handle but when I create a top level window, even if it is transparent, Windows always selects its handle :(
John Riche