/// <summary> /// Shows the specified Form as a popup window, keeping the /// Owner's title bar active and preparing to cancel the popup /// should the user click anywhere outside the popup window. /// <para>Typical code to use this message is as follows:</para> /// <code> /// frmPopup popup = new frmPopup(); /// Point location = this.PointToScreen(new Point(button1.Left, button1.Bottom)); /// popupHelper.ShowPopup(this, popup, location); /// </code> /// <para>Put as much initialisation code as possible /// into the popup form's constructor, rather than the <see cref="System.Windows.Forms.Load"/> /// event as this will improve visual appearance.</para> /// </summary> /// <param name="Owner">Main form which owns the popup</param> /// <param name="Popup">Window to show as a popup</param> /// <param name="v">Location relative to the screen to show the popup at.</param> public void ShowDropDown(Form Owner, Form DropDown, Point Location) { _owner = Owner; _dropDown = DropDown; Application.AddMessageFilter(_filter); // Start checking for the popup being cancelled DropDown.StartPosition = FormStartPosition.Manual; // Set the location of the popup form: DropDown.Location = Location; Owner.AddOwnedForm(DropDown); // Make it owned by the window that's displaying it: DropDownClosedHandler = new EventHandler(Popup_Closed); // Respond to the Closed event in case the popup is closed by its own internal means DropDown.Closed += DropDownClosedHandler; _dropDownShowing = true; // Show the popup: DropDown.Show(); DropDown.Activate(); // A little bit of fun. We've shown the popup, but because we've kept the main window's // title bar in focus the tab sequence isn't quite right. This can be fixed by sending a tab, // but that on its own would shift focus to the second control in the form. So send a tab, // followed by a reverse-tab. UIApiCalls.keybd_event((byte)Keys.Tab, 0, 0, 0); UIApiCalls.keybd_event((byte)Keys.Tab, 0, UIApiCalls.KEYEVENTF_KEYUP, 0); UIApiCalls.keybd_event((byte)Keys.ShiftKey, 0, 0, 0); UIApiCalls.keybd_event((byte)Keys.Tab, 0, 0, 0); UIApiCalls.keybd_event((byte)Keys.Tab, 0, UIApiCalls.KEYEVENTF_KEYUP, 0); UIApiCalls.keybd_event((byte)Keys.ShiftKey, 0, UIApiCalls.KEYEVENTF_KEYUP, 0); _filter.DropDown = DropDown; // Start filtering for mouse clicks outside the popup }
/// <summary> /// Subclasses the owning form's existing Window Procedure to enables the /// title bar to remain active when a popup is show, and to detect if /// the user clicks onto another application whilst the popup is visible. /// </summary> /// <param name="m">Window Procedure Message</param> protected override void WndProc(ref Message m) { base.WndProc(ref m); if (DropDownShowing) { if (m.Msg == UIApiCalls.WM_NCACTIVATE) { if (((int)m.WParam) == 0) // Check if the title bar will made inactive: { // Note it's no good to try and consume this message; if you try to do that you'll end up with windows UIApiCalls.SendMessage(this.Handle, UIApiCalls.WM_NCACTIVATE, 1, IntPtr.Zero); // If so reactivate it. } } else if (m.Msg == UIApiCalls.WM_ACTIVATEAPP) { if ((int)m.WParam == 0) // Check if the application is being deactivated. { CloseDropDown(); // It is so cancel the popup: UIApiCalls.PostMessage(this.Handle, UIApiCalls.WM_NCACTIVATE, 0, IntPtr.Zero); // And put the title bar into the inactive state: } } } }