【问题标题】:How to make a "popup" (hint, drop-down, popup) window in Winforms?如何在 Winforms 中制作“弹出”(提示、下拉、弹出)窗口?
【发布时间】:2012-01-29 23:03:05
【问题描述】:

我怎样才能在 WinForms 中创建一个“弹出”窗口?


由于我使用了我自己编造的词“popup”,让我举一个所谓的“popup”窗口的例子:

  • 工具提示窗口(可以延伸到其父窗体的边界之外,不出现在任务栏中,不是模态的,也不会窃取焦点):

  • 一个弹出菜单窗口(可以延伸到其父窗体的边界之外,不出现在任务栏中,不是模态的,并且不窃取焦点):

  • 下拉式窗口(可以延伸到其父窗体的边界之外,不会出现在任务栏中,不是模态的,并且不会窃取焦点):

  • 主菜单窗口(可以延伸到其父窗体的边界之外,不出现在任务栏中,不是模态的,也不会窃取焦点):

  • 更新 popup 窗口在使用鼠标或键盘进行交互时不会使自己成为the "active" window(“所有者”窗口仍然是活动窗口):

我在这个神秘的“弹出窗口”中寻找的属性是:

  • 可以扩展超出其父窗体的边界(即is not a child window
  • 未出现在任务栏中(即 Window 对应出现哪些窗口的启发式方法未启动,也没有 WS_EX_APPWINDOW 扩展窗口样式)
  • 不是模态(即doesn't disable its "owner"
  • 不偷专注
  • 始终在其“所有者”之上
  • 与之交互时不会成为“活动”窗口(所有者保持活动状态)

Windows 应用程序已经在设法创建此类窗口。如何在 WinForms 应用程序中执行此操作?

相关问题

  • 如何在本机代码中实现以上所有功能?
  • 如何在 Delphi 中创建弹出窗口?
  • 我有这个本机代码来显示“弹出”窗口 - 在 .NET 中执行相同操作需要哪些 P/Invokes?
  • 我在 .NET 中有一组 P/Invoke - 我可以重用常规 WinForm,覆盖某些方法,以达到相同的效果吗?
  • 我有 WinForm,我通过覆盖某些方法将其显示为“弹出窗口” - 是否有内置的 Control 可以充当我的弹出窗口?
  • How to simulate a drop-down window in WinForms?

尝试#1

我试过Show(onwer) + ShowWithoutActivation方法:

PopupForm dd = new PopupForm ();
dd.Show(this);

使用 PopupForm:

public class PopupForm: Form
{
    public PopupForm()
    {
        InitilizeComponent();
    }

    private void InitilizeComponent()
    {
        this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
        this.WindowState = FormWindowState.Normal;
        this.ShowInTaskbar = false;
    }

    protected override bool ShowWithoutActivation
    { get { return true; } }
}

几乎解决了这个问题,但后来我发现被提醒“弹出”窗口的另一个属性:它们不会从它们的“所有者”表单中获取焦点变成与鼠标或键盘交互时激活。

【问题讨论】:

  • 您希望这一切都是一个“弹出”或多个“弹出”方法?
  • @Moonlight 无论内容是什么,概念都应该相同。一旦我有了一个“弹出表单”,我就可以在里面放任何我想要的内容。
  • 我很难看出这和yesterday's question 之间的区别。我的回答将适用于您正在尝试做的事情。
  • @LarsTech 通过询问有关“下拉菜单”的问题,我将我的答案限制在“下拉菜单”。我想要一个具有更广泛适用性的问题(和答案)。这个问题的答案解决了“下拉”问题 - 但反之亦然。最后,我想问一个关于“弹出窗口”的问题,这样任何搜索“弹出窗口”的人都会找到这个,而不仅限于“下拉菜单”。

标签: winforms winapi popup popupwindow


【解决方案1】:

您想要一个拥有的窗口。在你的主要形式中:

private void showPopup_Click(object sender, EventArgs e)
{
    PopupForm popupForm = new PopupForm();
    // Make "this" the owner of form2
    popupForm.Show(this);
}

PopupForm 应该如下所示:

public partial class PopupForm : Form
{
    private bool _activating = false;

    public PopupForm()
    {
        InitializeComponent();
    }

    // Ensure the popup isn't activated when it is first shown
    protected override bool ShowWithoutActivation
    {
        get
        {
            return true;
        }
    }

    private const int WM_NCACTIVATE = 0x86;

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

    protected override void WndProc(ref Message m)
    {
        // The popup needs to be activated for the user to interact with it,
        // but we want to keep the owner window's appearance the same.
        if ((m.Msg == WM_NCACTIVATE) && !_activating && (m.WParam != IntPtr.Zero))
        {
            // The popup is being activated, ensure parent keeps activated appearance
            _activating = true;
            SendMessage(this.Owner.Handle, WM_NCACTIVATE, (IntPtr) 1, IntPtr.Zero);
            _activating = false;
            // Call base.WndProc here if you want the appearance of the popup to change
        }
        else
        {
            base.WndProc(ref m);
        }
    }
}

并确保PopupForm.ShowInTaskbar = false

【讨论】:

  • 有用。关闭。但是当用户点击它时,表单会获得焦点(而不是弹出窗口,例如 Windows 资源管理器)
  • @IanBoyd:您能否举一个更具体的示例来说明您可以与之交互的窗口,而无需激活该窗口?如果您希望弹出窗口完全忽略鼠标点击,请在显示 form2 后设置“form2.Enabled = False”。
  • Windows 资源管理器 - 过滤和分组列(屏幕截图附加到问题)
  • @IanBoyd:我认为这个新版本做到了。这是一种邋遢的技术,但似乎很常见。
  • 这仍然存在@IanBoyd 提到的问题:单击表单会窃取父级的焦点
【解决方案2】:

我很好奇组合框下拉菜单和菜单的工作原理,所以我做了一些研究。

有两种基本方法。

将弹出窗口创建为重叠窗口,归主窗口所有

如果弹出窗口具有嵌入式控件,或者您希望弹出窗口表现为无模式对话框,则需要此方法。

如果用户要与弹出窗口中的子控件交互,它必须接收激活。 (因此阻止激活的各种技术,例如处理 WM_MOUSEACTIVATE 是红鲱鱼。)当它收到激活时,Windows 将停用主窗口。解决此问题的方法是向父级发送 WM_NCACTIVATE 消息以更新其视觉外观而不更改其激活状态。这是.Net ToolStrip使用的方法,我的另一个答案用代码说明了它。

将弹出窗口创建为桌面窗口的子窗口

组合框下拉菜单和(我猜)菜单使用此方法,但您不能嵌入子控件,因此它不能广泛适用。

弹出窗口是一个子窗口,因此不会干扰激活。它总是在顶部。鼠标捕获用于检测弹出窗口外的点击并关闭它。

但是,这并不容易实现。激活保留在主应用程序中,因此它保持焦点并接收击键。这似乎排除了弹出窗口中的嵌入式控件,因为它们无法获得焦点。组合框通过将击键消息转发到其下拉列表来处理此问题。 (请注意,菜单、组合框下拉菜单等都完全是所有者绘制的,因为它们没有嵌入的窗口。)

【讨论】:

  • 在我的“祖先”问题中,Lars 建议您提到的东西 - ToolStrip。在我看来,您的研究为这个问题提供了最好的答案;使用 ToolStrip 是它的一种可能实现方式。
【解决方案3】:

将您的“弹出”窗口创建为桌面的子窗口,然后在不激活的情况下显示它。

hWnd = CreateWindowEx(..., WS_CHILDWINDOW | WS_VISIBLE | WS_BORDER | WS_CLIPSIBLINGS, ..., GetDesktopWindow(), ...);
SetWindowPos(hWnd, HWND_TOPMOST, ..., SWP_NOACTIVATE);

执行此操作后,即使您单击“弹出”窗口,您的原始窗口仍会保持激活状态。 “弹出”窗口可以有自己的子控件。你可以点击它上面的按钮。但是如果是编辑控件,就不能编辑了,不知道为什么。可能是因为原来的窗口上已经有一个光标在闪烁。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-14
    相关资源
    最近更新 更多