3

我有一个 C#/.NET 应用程序,我想实现以下行为:

我有一个弹出菜单。每当用户单击应用程序中不是弹出菜单的任何内容时,我都希望关闭弹出菜单。

但是,每当用户不在应用程序中时,我都不希望发生任何事情。

我试图通过 LostFocus 事件来管理它,但我无法确定我的应用程序是否是活动窗口。代码看起来像这样。

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

我需要知道的是如何实现 InActiveWindow() 方法。

4

3 回答 3

4

您可以 P/Invoke 进入GetForegroundWindow(),并将返回的 HWND 与应用程序的 form.Handle 属性进行比较。

获得句柄后,您还可以 P/Invoke GetAncestor()以获取根所有者窗口。如果它在您的应用程序中,这应该是您应用程序的主启动窗口的句柄。

于 2009-05-21T16:02:58.527 回答
2

我在做一个项目时偶然发现了你的问题,根据Reed Copsey 的回答,我编写了这个似乎可以很好地完成工作的快速代码。

这是代码:

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);
        }
    }
}
于 2011-01-01T12:12:40.843 回答
1

这似乎是棘手的最大原因是因为弹出窗口在主窗体被停用之前失去焦点,因此活动窗口在此事件发生时将始终位于应用程序中。真的,您想知道事件结束后它是否仍然是活动窗口。

您可以设置某种方案,让您记住弹出窗口正在失去焦点,搁置您需要关闭它的事实,并在应用程序主窗体的LostFocusDeactivate事件中取消告诉您需要关闭的注释它; 但问题是你什么时候处理这​​张纸条?

我认为这可能更容易,至少如果弹出窗口是主窗体的直接子窗体(我怀疑在你的情况下可能是)挂钩主窗体的事件Focus或什至Click事件并使用它关闭弹出窗口如果它是打开的(可能通过扫描其子表单列表来查找任何实现 ICloseOnLostFocus 接口的表单,以便弹出窗口有机会参与决策并执行它需要做的任何其他事情)。

我希望我知道一个更好的文档来解释所有这些事件的实际含义以及它们如何相互排序,MSDN 在描述它们方面还有很多不足之处。

于 2009-06-24T01:26:39.873 回答