Определите, является ли программа активным окном в .NET

У меня есть приложение C#/.NET, и я хочу реализовать следующее поведение:

У меня есть всплывающее меню. Всякий раз, когда пользователь щелкает что-либо внутри приложения, которое не является всплывающим меню, я хочу, чтобы всплывающее меню закрывалось.

Однако всякий раз, когда пользователь не находится в приложении, я не хочу, чтобы что-то происходило.

Я пытаюсь управлять этим с помощью события LostFocus, но у меня возникают проблемы с определением того, является ли мое приложение активным окном. Код выглядит примерно так.

    private void Button_LostFocus(object sender, System.EventArgs e)
    {
        if (InActiveWindow()) {
           CloseMenu()
        }
        else {
           // not in active window, do nothing
        }
    }

Мне нужно знать, как реализовать метод InActiveWindow().


person Cory    schedule 21.05.2009    source источник


Ответы (3)


Вы можете P/Invoke в GetForegroundWindow() и сравните HWND, возвращаемый свойству form.Handle приложения.

Получив дескриптор, вы также можете P/Invoke GetAncestor (), чтобы получить окно владельца root. Это должен быть дескриптор главного окна запуска вашего приложения, если оно есть в вашем приложении.

person Reed Copsey    schedule 21.05.2009

Я наткнулся на ваш вопрос во время работы над проектом и на основе Ответ Рида Копси, я написал этот быстрый код, который, похоже, хорошо справляется со своей задачей.

Вот код:

Public Class Form1
    '''<summary>
    '''Returns a handle to the foreground window.
    '''</summary>
    <Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
    Private Shared Function GetForegroundWindow() As IntPtr
    End Function

    '''<summary>
    '''Gets a value indicating whether this instance is foreground window.
    '''</summary>
    '''<value>
    '''<c>true</c> if this is the foreground window; otherwise, <c>false</c>.
    '''</value>
    Private ReadOnly Property IsForegroundWindow As Boolean
        Get
            Dim foreWnd = GetForegroundWindow()
            Return ((From f In Me.MdiChildren Select f.Handle).Union(
                    From f In Me.OwnedForms Select f.Handle).Union(
                    {Me.Handle})).Contains(foreWnd)
        End Get
    End Property
End Class

У меня не было слишком много времени, чтобы преобразовать его в C#, так как я работаю над проектом, крайний срок которого составляет 2 дня, но я думаю, что вы сможете быстро преобразовать его.

Вот версия C# кода VB.NET:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern IntPtr GetForegroundWindow();

    ///<summary>Gets a value indicating whether this instance is foreground window.</summary>
    ///<value><c>true</c> if this is the foreground window; otherwise, <c>false</c>.</value>
    private bool IsForegroundWindow
    {
        get
        {
            var foreWnd = GetForegroundWindow();
            return ((from f in this.MdiChildren select f.Handle)
                .Union(from f in this.OwnedForms select f.Handle)
                .Union(new IntPtr[] { this.Handle })).Contains(foreWnd);
        }
    }
}
person Alex Essilfie    schedule 01.01.2011

Кажется, самая большая причина, по которой это сложно, заключается в том, что всплывающее окно теряет фокус до того, как основная форма деактивируется, поэтому активное окно всегда будет в приложении во время этого события. На самом деле, вы хотите знать, будет ли это окно активным после завершения события.

Вы можете настроить какую-то схему, при которой вы помните, что всплывающее окно теряет фокус, отбрасываете тот факт, что вам нужно будет его закрыть, и в событии LostFocus или Deactivate главной формы приложения отменяете примечание, которое говорит вам, что вам нужно закрыть его; но проблема в том, когда вы обработаете заметку?

Я думаю, что может быть проще, по крайней мере, если всплывающее окно является прямым дочерним элементом основной формы (что, как я подозреваю, в вашем случае может быть), перехватить событие Focus или, может быть, даже Click основной формы и использовать его близко всплывающее окно, если оно открыто (возможно, просматривая его список дочерних форм для любых, которые реализуют интерфейс ICloseOnLostFocus, чтобы всплывающее окно имело возможность участвовать в принятии решения и делать все, что ему нужно).

Хотел бы я знать о лучшем документе, объясняющем, что на самом деле означают все эти события и как они упорядочены по отношению друг к другу, MSDN оставляет желать лучшего в их описании.

person Doug McClean    schedule 24.06.2009