如何判断 Delphi 控件当前是否可见?

10

我需要一种方法来使自定义控件(派生自TCustomControl)能够判断它当前是否可见。我指的不是.Visible属性,而是指它是否正在屏幕上实际显示。有人知道如何做到这一点吗?


你的意思是它是否被另一个窗口覆盖了吗? - anon
我的意思是它是否被绘制到屏幕上。被覆盖可能是一个原因。另一个原因可能是它被放置在一个已创建但尚未显示的窗体上。 - Mason Wheeler
3个回答

17
几年前,我遇到了一个类似的问题:我正在寻找一种方法来确定表单是否实际可见(即使只有部分)。 特别是当它应该可见且Showing为True但窗口实际上完全在另一个窗口后面时。这里是代码,它可以适应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;

1
谢谢!这正是我在寻找的。 - Mason Wheeler
你是一个英雄。 - Xel Naga
这段代码有一个问题。如果窗体移动到屏幕边界之外,结果总是False。即使窗体部分在屏幕内(被覆盖)。 - undefined

2

你可以将代码附加到OnPaint事件上吗?这个事件会被频繁调用,但只有在控件实际绘制(例如以您指定的方式可见)时才会被调用。


我会选择这个作为最佳指标。你永远无法确定,因为在Vista中,所有应用程序都会绘制到一个离屏位图中,然后与叠加层等一起在图形卡上合成。 - mj2008
但是假设它现在只有在屏幕上显示时才会被绘制到屏幕外? - Toby Allen
1
我认为假设 WM_PAINT 只会在这些像素即将到达屏幕时才出现是不安全的。通过桌面组合(http://msdn.microsoft.com/en-us/library/aa969540%28VS.85%29.aspx),Windows 保留了绘制窗口的缓存,以实现 Windows Flip(http://www.microsoft.com/windows/windows-vista/features/flip-3d.aspx)等效果,这可能需要一次性显示*所有*窗口。因此,我预计可能会有一些后台缓存刷新。 - Mattias Andersson

0
Francesca的答案适用于大多数情况。然而,如果表单移动到屏幕边界之外,它就无法起作用。
以下是更好的答案:
function IsMyFormCovered(const MyForm: TForm): Boolean;
var
  RectForm, RectForeground: TRect;
  hWndForeground: HWND;
begin
  GetWindowRect(MyForm.Handle, RectForm);
  hWndForeground := GetForegroundWindow;
  if (hWndForeground <> MyForm.Handle) and (hWndForeground <> 0) then
  begin
    GetWindowRect(hWndForeground, RectForeground);
    if (RectForm.Left < RectForeground.Right) and
       (RectForm.Right > RectForeground.Left) and
       (RectForm.Top < RectForeground.Bottom) and
       (RectForm.Bottom > RectForeground.Top) then
      Result := True
    else
      Result := False;
  end else
    Result := False;
end;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接