Как сделать "всплывающее" (подсказка, выпадающее, всплывающее) окно в Winforms?

как я могу сделать, что я буду называть, "всплывающее окно" в WinForms?


так как я использовал свое собственное придуманное слово "popup", позвольте мне привести примеры этого так называемого "всплывающего" окна:

  • a окне всплывающей подсказки (может выходить за пределы родительской формы, не отображается на панели задач, не является модальным и не крадет фокус):

    enter image description here

  • a всплывающее окно меню окно (может выходить за пределы родительской формы, не отображается на панели задач, не является модальным и не крадет фокус):

    enter image description here

  • a выпадающее окно (может выходить за пределы родительской формы, не отображается на панели задач, не является модальным и не крадет фокус):

    enter image description here

  • A главное меню окно (смогите удлинить вне границы его родительской формы, не отображаются на панели задач, не модальны и не крадут фокус):

    enter image description here

  • обновление A всплывающие окно не заставляют себя "активные" окна когда общались с помощью мыши или клавиатуры (окно "владелец" остается активным окном):

enter image description here

атрибуты, которые я ищу в этом мифическом" всплывающем окне вот оно:

  • can расширения вне границ родительской формы (т. е. не является дочерним окном)
  • не появляется в задач (т. е. эвристика окна, в котором должны появиться окна, не срабатывает, и у него нет расширенного стиля окна WS_EX_APPWINDOW)
  • не модальные (т. е. не отключает своего "владельца")
  • не воровать фокус
  • всегда on-top из его "рук"
  • не становится "активным" окном при взаимодействии с (владелец остается активным)

приложения Windows уже управляют созданием таких окон. Как это сделать в приложении WinForms?

вопросы

  • как я могу достичь всего вышеперечисленного в собственном коде?
  • как создать всплывающее окно в Дельфи?
  • у меня есть этот собственный код, чтобы показать "всплывающее" окно - какие P / Invokes требуются для выполнения тех же действий в .NET?
  • у меня есть набор P / Invoke в .NET - могу ли я повторно использовать обычную WinForm, переопределяя определенные методы, для достижения того же эффекта?
  • у меня есть WinForm, который я показываю как "всплывающее окно", переопределяя определенные методы - есть ли встроенный Control что может выступать в качестве всплывающего для меня?
  • как имитировать раскрывающийся список окно в 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; } }
}

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

3 ответов


вы хотите иметь собственное окно. В основной форме:

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.


Мне было любопытно, как выпадающие списки combobox и меню работают, поэтому я сделал еще несколько исследований.

существует два основных подхода.

создайте всплывающее окно в виде перекрывающегося окна, принадлежащего главному окну

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

Если пользователь будет взаимодействовать с дочерними элементами управления во всплывающем окне, это должны получить активацию. (Таким образом, различные методы блокировки активации, такие как обработка WM_MOUSEACTIVATE, являются красными селедками.) И когда он получает активацию, Windows деактивирует главное окно. Исправить это-отправить сообщение WM_NCACTIVATE родителю, чтобы обновить его внешний вид без изменения состояния активации. Это подход, используемый .Net ToolStrip, и мой другой ответ иллюстрирует его с помощью кода.

создайте всплывающее окно как дочернее Окно рабочего стола

этот метод используется выпадающими списками combobox и (я думаю) меню, но вы не можете вставлять дочерние элементы управления, поэтому он не широко применим.

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

однако это не просто реализовать. Активация остается с основным приложением, поэтому он сохраняет фокус и получает нажатия клавиш. Это, похоже, исключает встроенные элементы управления во всплывающем окне, потому что они не могут получить фокус. Combobox обрабатывает это, перенаправляя сообщения нажатия клавиш в раскрывающийся список. (Обратите внимание, что меню, выпадающие списки combobox и т. д. все они полностью принадлежат владельцу-рисуют в том смысле, что у них нет встроенных окон.)


создайте всплывающее окно как дочернее окно рабочего стола, а затем покажите его без активации.

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

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