12

通常、Form.Visibleを使用して、ウィンドウが表示されているかどうかを確認します。ただし、画面上のウィンドウが他のウィンドウの下にあるため、実際には見えない場合があります。

では、ウィンドウが実際に表示されているかどうかをC#Windowsフォームで確認するにはどうすればよいですか?

これを実現したいと思います。キーボードでCtrl+Kをクリックし、ウィンドウが画面に表示されても何も起こりません。ただし、他のウィンドウの下にある場合は、一番上に表示されます(前面に移動します)。

敬具

4

10 に答える 10

8

私はウェブをグーグルで検索しましたが、ウィンドウの一部が本当にユーザーに表示されているかどうかを確認するための正解は見つかりませんでした。マウスが現在ウィンドウの表示部分の上にある場合、実際にフォームを「ヒットテスト」する方法が必要でした。達成するのに数日かかったコードを共有したいと思いました:

public class VisibilityTester
{
    private delegate bool CallBackPtr(int hwnd, int lParam);
    private static CallBackPtr callBackPtr;

    /// <summary>
    /// The enumerated pointers of actually visible windows
    /// </summary>
    public static List<IntPtr> enumedwindowPtrs = new List<IntPtr>();
    /// <summary>
    /// The enumerated rectangles of actually visible windows
    /// </summary>
    public static List<Rectangle> enumedwindowRects = new List<Rectangle>();

    /// <summary>
    /// Does a hit test for specified control (is point of control visible to user)
    /// </summary>
    /// <param name="ctrlRect">the rectangle (usually Bounds) of the control</param>
    /// <param name="ctrlHandle">the handle for the control</param>
    /// <param name="p">the point to test (usually MousePosition)</param>
    /// <param name="ExcludeWindow">a control or window to exclude from hit test (means point is visible through this window)</param>
    /// <returns>boolean value indicating if p is visible for ctrlRect</returns>
    public static bool HitTest(Rectangle ctrlRect, IntPtr ctrlHandle, Point p, IntPtr ExcludeWindow)
    {
        // clear results
        enumedwindowPtrs.Clear();
        enumedwindowRects.Clear();

        // Create callback and start enumeration
        callBackPtr = new CallBackPtr(EnumCallBack);
        EnumDesktopWindows(IntPtr.Zero, callBackPtr, 0);

        // Go from last to first window, and substract them from the ctrlRect area
        Region r = new Region(ctrlRect);

        bool StartClipping = false;
        for (int i = enumedwindowRects.Count - 1; i >= 0; i--)
        {
            if (StartClipping && enumedwindowPtrs[i] != ExcludeWindow)
            {
                r.Exclude(enumedwindowRects[i]);
            }

            if (enumedwindowPtrs[i] == ctrlHandle) StartClipping = true;
        }

        // return boolean indicating if point is visible to clipped (truly visible) window
        return r.IsVisible(p);
    }

    /// <summary>
    /// Window enumeration callback
    /// </summary>
    private static bool EnumCallBack(int hwnd, int lParam)
    {
        // If window is visible and not minimized (isiconic)
        if (IsWindow((IntPtr)hwnd) && IsWindowVisible((IntPtr)hwnd) && !IsIconic((IntPtr)hwnd))
        { 
            // add the handle and windowrect to "found windows" collection
            enumedwindowPtrs.Add((IntPtr)hwnd);

            RECT rct;

            if (GetWindowRect((IntPtr)hwnd, out rct))
            {
                // add rect to list
                enumedwindowRects.Add(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top));
            }
            else
            {
                // invalid, make empty rectangle
                enumedwindowRects.Add(new Rectangle(0, 0, 0, 0));
            }
        }

        return true;
    }


    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool IsWindowVisible(IntPtr hWnd);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool IsWindow(IntPtr hWnd);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool IsIconic(IntPtr hWnd);

    [DllImport("user32.dll")]
    private static extern int EnumDesktopWindows(IntPtr hDesktop, CallBackPtr callPtr, int lPar);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int Left;        // x position of upper-left corner
        public int Top;         // y position of upper-left corner
        public int Right;       // x position of lower-right corner
        public int Bottom;      // y position of lower-right corner

        public override string ToString()
        {
            return Left + "," + Top + "," + Right + "," + Bottom;
        }
    }
}
于 2010-12-05T22:26:46.233 に答える
4

フォームのメソッドを呼び出して、Activateまだ前面に表示されていない場合は前面に表示できます。

ただし、別のプログラムがアクティブな場合は、通常、デスクトップボタンが点滅するだけであることに注意してください(どこから呼び出したかによって異なります)。これは、フォーカスの盗難に対するWindowsの標準的な保護であり、回避しようとしないでください

于 2009-10-30T14:06:00.450 に答える
4

Windows APIを使用して、すべてのウィンドウを列挙し、それらのZオーダーを取得して、ウィンドウのZオーダーと比較することができます。私は誰かがすでにここでこれをしたと思います。

于 2009-10-30T14:17:34.420 に答える
3

尋ねられた質問に答えるには、WindowFromPointAPI関数を呼び出して、フォームのさまざまなポイントでウィンドウを見つけ、その時点で期待されるもののハンドルが返されるかどうかを確認します。

于 2009-10-30T14:10:54.107 に答える
2

私は実際にSLaksの提案を実装しようとしました。C#ではなくVB.NETで作成しましたが

Friend Structure PointStruct
    Public x As Int32
    Public y As Int32
End Structure

<System.Runtime.InteropServices.DllImport("user32.dll")> _
Friend Function WindowFromPoint(ByVal Point As PointStruct) As IntPtr
End Function

''' <summary>
''' Checks if a control is actually visible to the user completely
''' </summary>
''' <param name="control">The control to check.</param>
''' <returns>True, if the control is completely visible, false else.</returns>
''' <remarks>This is not 100% accurate, but feasible enough for my purpose.</remarks>
Public Function IsControlVisibleToUser(ByVal control As Windows.Forms.Control) As Boolean
    If Not control.Visible Then Return False

    Dim bAllPointsVisible As Boolean = True
    Dim lPointsToCheck As New List(Of Point)
    'Add the points to check. In this case add the edges and some border points
    'between the edges.
    'Strangely, the exact edge points always return the false handle.
    'So we add a pixel into the control.
    lPointsToCheck.Add(New Point(control.Left + 1, control.Top + 1))
    lPointsToCheck.Add(New Point(control.Right - 1, control.Top + 1))
    lPointsToCheck.Add(New Point(control.Right - 1, control.Bottom - 1))
    lPointsToCheck.Add(New Point(control.Left + 1, control.Bottom - 1))
    lPointsToCheck.Add(New Point(control.Left + control.Width / 2, control.Top + 1))
    lPointsToCheck.Add(New Point(control.Right - 1, control.Top + control.Height / 2))
    lPointsToCheck.Add(New Point(control.Right - control.Width / 2, control.Bottom - 1))
    lPointsToCheck.Add(New Point(control.Left + 1, control.Bottom - control.Height / 2))
    'lPointsToCheck.Add(New Point(control.Left + control.Width / 2, control.Top + control.Height / 2))

    'Check each point. If all points return the handle of the control,
    'the control should be visible to the user.
    For Each oPoint In lPointsToCheck
        Dim sPoint As New PointStruct() With {
            .x = oPoint.X, _
            .y = oPoint.Y _
        }
        bAllPointsVisible = bAllPointsVisible And ( _
            (WindowFromPoint(sPoint) = control.Handle) _
        )
    Next

    Return bAllPointsVisible
End Function
于 2013-05-26T15:31:18.237 に答える
1

また、ウィンドウに対応するAutomationElementからClickablePointプロパティを取得することもできます。これが完全に正確であるかどうかは100%わかりませんが..99%のケースで機能し、問題が存在する残りの1%をチェックしています(私の側にあるか、悪いユーザーである可能性があります)。取り扱い、または。)

于 2011-05-24T14:23:38.770 に答える
0

うーん...奇妙な質問。:P

おそらく、フォームの場所を尋ねることができ、2つのフォームが重なり合っている場合(それらの座標を計算し、簡単なメソッドを作成する)、1つのフォームにFocus()があるかどうかを確認します。フォーカスがある場合、他のフォームは「非表示」である必要があります(他のフォームの下にあるため、ユーザーはそれを見ることができません)。

明らかに、この方法はせいぜいハッキーですが、それはあなたが使い始めることができるものです。

于 2009-10-30T14:00:37.807 に答える
0

OnPaintメソッドをオーバーライドすることで、ウィンドウが表示されているかどうかを確認できるはずです。実際のペイントを行うには、基本クラスに制御を渡す必要がありますが、ペイントメッセージが受信されたかどうかを検出することはできます。更新:いいえ、これは機能しません、申し訳ありません!

原則として、Activateメソッドはウィンドウを前面に表示する必要がありますが、実際には、他のプロセスに入力フォーカスがある場合、これは常に問題があります。本当に誰かにウィンドウを見せたい場合は、一番上のビットを設定しますが、彼らがイライラすることを期待してください!ウィンドウに注意を向ける確実な方法の1つは、ウィンドウを閉じてから再度開くことです。

探しているものを実現する1つの方法は、通知アイコンを使用することです。これにより、WindowsUIガイドラインに準拠した方法でユーザーの注意を引くことができます。

于 2009-10-31T06:56:34.980 に答える
0

これは、 @ MarvinDickhausの回答のコードの変更されたC#バージョンです。

特定のポイントのみをチェックすることで、ウィンドウまたはコントロールが表示されているか、部分的に表示されているかをテストできます。

主な基本的な関心は、完全にまたは部分的に覆われたフォームを前面に出すことができることです。

13点を使用しますが、さらに追加することができ、任意の数を渡してその数から座標を計算するように方法を改善できます。Windows 10フォームの場合、デフォルトのマージンは15に設定されています。

static partial class NativeMethods
{
  [StructLayout(LayoutKind.Sequential)]
  public struct PointStruct
  {
    public int X;
    public int Y;
    public PointStruct(int x, int y)
    {
      X = x;
      Y = y;
    }
  }
  [DllImport("user32.dll")]
  static public extern IntPtr WindowFromPoint(PointStruct Point);
}
static public IEnumerable<T> GetAllControls<T>(this Control control)
{
  var controls = control.Controls.OfType<T>();
  return control.Controls.Cast<Control>()
                         .SelectMany(c => c.AllControls<T>())
                         .Concat(controls);
}
static public List<Point> GetGridPoints(this Control control, int margin = 15)
{
  int widthDiv2 = control.Width / 2;
  int heightDiv2 = control.Height / 2;
  int widthDiv4 = widthDiv2 / 4;
  int heightDiv4 = heightDiv2 / 4;
  var points = new List<Point>();
  // Center
  points.Add(new Point(control.Left + widthDiv2, control.Top + heightDiv2));
  // Corners
  points.Add(new Point(control.Left + margin, control.Top + margin));
  points.Add(new Point(control.Right - margin, control.Top + margin));
  points.Add(new Point(control.Left + margin, control.Bottom - margin));
  points.Add(new Point(control.Right - margin, control.Bottom - margin));
  // Borders
  points.Add(new Point(control.Left + widthDiv4, control.Top + heightDiv4));
  points.Add(new Point(control.Right - widthDiv4, control.Top + heightDiv4));
  points.Add(new Point(control.Left + widthDiv4, control.Bottom - heightDiv4));
  points.Add(new Point(control.Right - widthDiv4, control.Bottom - heightDiv4));
  // Inner
  points.Add(new Point(control.Left + widthDiv2, control.Top + margin));
  points.Add(new Point(control.Left + widthDiv2, control.Bottom - margin));
  points.Add(new Point(control.Left + margin, control.Top + heightDiv2));
  points.Add(new Point(control.Right - margin, control.Top + heightDiv2));
  return points;
}
static public bool IsVisibleOnTop(this Control control, int requiredPercent = 100, int margin = 15)
{
  if ( !control.Visible ) return false;
  var controls = control.GetAllControls<Control>().Select(c => c.Handle).ToList();
  var points = control.GetGridPoints(margin);
  bool all = requiredPercent == 100;
  int found = 0;
  int required = points.Count();
  if (!all) required = required * requiredPercent / 100;
  foreach ( var point in points )
  {
    var handle = NativeMethods.WindowFromPoint(new NativeMethods.PointStruct(point.X, point.Y));
    if ( handle == control.Handle || controls.Contains(handle) )
    {
      if ( ++found == required ) return true;
    }
    else
    {
      if ( all ) return false;
    }
  }
  return false;
}
于 2021-05-20T19:47:53.337 に答える
-4

Form.AlwaysOnTopプロパティをに設定するだけtrueです。

于 2012-04-01T21:24:40.367 に答える