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

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

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

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

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

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

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

3 ответа

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

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

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

Вот код:

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 дня, но я верю, что вы можете быстро выполнить преобразование.

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

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);
        }
    }
}

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

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

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

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

Другие вопросы по тегам