views:

716

answers:

3

I need a way to for a custom control (descended from TCustomControl) to tell if it is currently visible. I'm not talking about the .Visible property; I mean whether or not it's actually being displayed on-screen at the moment. Does anyone know how to do this?

+1  A: 

Could you attach code to the OnPaint event? This is called very often and I think is only called when the control is actually going to be painted (eg is visible in the way you mean).

Toby Allen
I'd go with this as a best indicator. You can never be sure because in Vista all apps draw to an off-screen bitmap which is then composed in the graphics card with the overlays etc.
mj2008
But presumably its still only drawn off screen if it is going to be shown onscreen at the moment?
Toby Allen
I don't think it's safe to assume that WM_PAINT will only come when those pixels are about to hit the screen. With Desktop Composition (http://msdn.microsoft.com/en-us/library/aa969540%28VS.85%29.aspx), Windows keeps a cache of drawn windows for effects like Windows Flip (http://www.microsoft.com/windows/windows-vista/features/flip-3d.aspx) that may require *all* windows at once. Because of this, I'd expect that there might be some background cache-refreshes.
Mattias Andersson
+2  A: 

I think this is what TWinControl.Showing is for. I am not sure how reliable it is.

Henk Holterman
It does not work for a Form for instance. A Form can be completely behind another one and still have Showing True.
François
Like I said, I had no idea how thorough it was.
Henk Holterman
Showing only tells you whether all parent controls are visible. It differentiates some situations in design mode, where f.e. non visible components are visible. So it tells you whether the component if shown at the moment would be visible.
Ralph Rickenbach
+5  A: 

A few years back I had the same kind of problem for a Form: I was looking for a way to determine if a Form is actually visible (even only partially) to the user.
In particular when it was supposed to be visible and Showing was True but the window was actually entirely behind another one.
Here's the code, it could be adapted for a WinControl...

{----------------------------------------------------------}
function IsMyFormCovered(const MyForm: TForm): Boolean;
var
   MyRect: TRect;
   MyRgn, TempRgn: HRGN;
   RType: Integer;
   hw: HWND;
begin
  MyRect := MyForm.BoundsRect;            // screen coordinates
  MyRgn := CreateRectRgnIndirect(MyRect); // MyForm not overlapped region
  hw := GetTopWindow(0);                  // currently examined topwindow
  RType := SIMPLEREGION;                  // MyRgn type

// From topmost window downto MyForm, build the not overlapped portion of MyForm
  while (hw<>0) and (hw <> MyForm.handle) and (RType <> NULLREGION) do
  begin
    // nothing to do if hidden window
    if IsWindowVisible(hw) then
    begin
      GetWindowRect(hw, MyRect);
      TempRgn := CreateRectRgnIndirect(MyRect);// currently examined window region
      RType := CombineRgn(MyRgn, MyRgn, TempRgn, RGN_DIFF); // diff intersect
      DeleteObject( TempRgn );
    end; {if}
    if RType <> NULLREGION then // there's a remaining portion
      hw := GetNextWindow(hw, GW_HWNDNEXT);
  end; {while}

  DeleteObject(MyRgn);
  Result := RType = NULLREGION;
end;

function IsMyFormVisible(const MyForm : TForm): Boolean;
begin
  Result:= MyForm.visible and
           isWindowVisible(MyForm.Handle) and
           not IsMyFormCovered(MyForm);
end;
François
Thanks! That's precisely what I was looking for.
Mason Wheeler