6
votes

How can i make, what i will call, a "popup" window" in WinForms?


Since i used my own made-up word "popup", let me give examples of this so-called "popup" window:

  • a tooltip window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):

    enter image description here

  • a popup menu window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):

    enter image description here

  • a drop-down window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):

    enter image description here

  • A main menu window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):

    enter image description here

  • Update A popup window not make itself the "active" window when interacted with using a mouse or keyboard (the "owner" window remains the active window):

enter image description here

The attributes that i'm looking for in this mythical "popup" are that it:

  • can extend outside the boundaries of its parent form (i.e. is not a child window)
  • doesn't appear in the taskbar (i.e. Window's heuristics of which windows should appear doesn't kick in, nor does it have WS_EX_APPWINDOW extended window style)
  • is not modal (i.e. doesn't disable its "owner")
  • doesn't steal focus
  • is always on-top of of it's "owner"
  • does not become the "active" window when interacted with (the owner remains active)

Windows applications are already managing to create such windows. How can i do it in a WinForms application?

Related questions

  • How do i achieve all the above in native code?
  • How do i create a popup window in Delphi?
  • i have this native code to show a "popup" window - what P/Invokes are required to perform the same actions in .NET?
  • i have a set of P/Invoke's in .NET - can i reuse a regular WinForm, overriding certain methods, to achieve the same effect?
  • i have WinForm that i'm showing as a "popup" by overriding certain methods - is there a built-in Control that can act as a popup for me?
  • How to simulate a drop-down window in WinForms?

Attempt#1

i tried the Show(onwer) + ShowWithoutActivation method:

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

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

Very nearly solved the problem, but then i discovered was reminded of another property of "popup" windows: they do not take focus from their "owner" form become active when interacted with by mouse or keyboard.

3
you want it all to be one "popup" or severall "popup" methods?Moonlight
@Moonlight The concepts should be identical, no matter what the contents are. Once i have a "popup form", i can put whatever contents in it i wish.Ian Boyd
I have a hard time seeing the difference between this and yesterday's question. My answer there will work for what you are trying to do.LarsTech
@LarsTech By asking a question about "drop-downs" i was limiting my answers to "drop-downs". i wanted a question (and answers) with wider applicibility. An answer to this question solves the "drop-down" problem - but not necessarily vice-versa. Finally, i wanted a question about "popups", so that anyone searching for "popups" will find this, and not be limited to just "dropdowns".Ian Boyd

3 Answers

3
votes

You want an owned window. In your main form:

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

PopupForm should look like this:

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

And make sure that PopupForm.ShowInTaskbar = false.

3
votes

I was curious as to how combobox dropdowns and menus work, so I did some more research.

There are two basic approaches.

Create the popup as an overlapped window, owned by the main window

This method is required if the popup has embedded controls, or if you want the popup to behave as a modeless dialog.

If the user is going to interact with child controls in the popup window, it must receive activation. (So the various techniques for blocking activation, such as handling WM_MOUSEACTIVATE are red herrings.) And when it receives activation, Windows will deactivate the main window. The fix for this is to send a WM_NCACTIVATE message to the parent to update its visual appearance without changing its activation status. This is the approach used by the .Net ToolStrip, and my other answer illustrates it with code.

Create the popup as a child of the Desktop window

This method is used by combobox dropdowns and (I guess) menus, but you can't embed child controls so it's not widely applicable.

The popup is a child window so it doesn't interfere with activation. It is always on top. Mouse capture is used to detect clicks outside the popup and dismiss it.

However, this isn't straightforward to implement. Activation remains with the main application, so it keeps the focus and receives keystrokes. This seems to preclude embedded controls in the popup because they can't receive focus. The combobox handles this by forwarding keystroke messages to its dropdown list. (Note that menus, combobox dropdowns, etc. are all entirely owner-draw in the sense that they have no embedded windows.)

0
votes

Create your "popup" window as a child window of desktop, then show it without activating it.

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

After doing this, your original window remains activated even if you click on the "popuped" window. The "popup" window can have its own children controls. You can click the button on it. But if it is an edit control, you cannot edit it, I don't know why. Maybe because there is already a cursor on your original window, blinking.