This commit is contained in:
110
PROMS/DropDownPanel/Helper/DropDownMessageFilter.cs
Normal file
110
PROMS/DropDownPanel/Helper/DropDownMessageFilter.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AT.STO.UI.Win
|
||||
{
|
||||
/// <summary>
|
||||
/// A Message Loop filter which detect mouse events whilst the popup form is shown
|
||||
/// and notifies the owning <see cref="PopupWindowHelper"/> class when a mouse
|
||||
/// click outside the popup occurs.
|
||||
///
|
||||
/// Thousand thanks to Steve McMahon:
|
||||
/// http://www.vbaccelerator.com/home/NET/Code/Controls/Popup_Windows/Popup_Windows/Popup_Form_Demonstration.asp
|
||||
/// </summary>
|
||||
internal class DropDownMessageFilter : IMessageFilter
|
||||
{
|
||||
#region Private Constants
|
||||
private const int WM_LBUTTONDOWN = 0x201;
|
||||
private const int WM_RBUTTONDOWN = 0x204;
|
||||
private const int WM_MBUTTONDOWN = 0x207;
|
||||
private const int WM_NCLBUTTONDOWN = 0x0A1;
|
||||
private const int WM_NCRBUTTONDOWN = 0x0A4;
|
||||
private const int WM_NCMBUTTONDOWN = 0x0A7;
|
||||
#endregion
|
||||
#region Private Variable Declarations
|
||||
private Form _dropDown = null;
|
||||
private DropDownWindowHelper _owner = null;
|
||||
#endregion
|
||||
#region Event Declarations
|
||||
public event DropDownCancelEventHandler DropDownCancel;
|
||||
#endregion
|
||||
#region Constructor / Destructor
|
||||
/// <summary>
|
||||
/// Constructs a new instance of this class and sets the owning
|
||||
/// object.
|
||||
/// </summary>
|
||||
/// <param name="Owner">The <see cref="DropDownWindowHelper"/> object
|
||||
/// which owns this class.</param>
|
||||
public DropDownMessageFilter(DropDownWindowHelper Owner)
|
||||
{
|
||||
_owner = Owner;
|
||||
}
|
||||
#endregion
|
||||
#region Public Properties
|
||||
/// <summary>
|
||||
/// Gets/sets the dropdown form which is being displayed.
|
||||
/// </summary>
|
||||
public Form DropDown
|
||||
{
|
||||
get { return _dropDown; }
|
||||
set { _dropDown = value; }
|
||||
}
|
||||
#endregion
|
||||
#region Private Methods
|
||||
private void OnMouseDown()
|
||||
{
|
||||
Point cursorPos = Cursor.Position; // Get the cursor location
|
||||
|
||||
if (!_dropDown.Bounds.Contains(cursorPos)) // Check if it is within the popup form
|
||||
{
|
||||
OnDropDownCancel(new DropDownCancelEventArgs(_dropDown, cursorPos)); // If not, then call to see if it should be closed
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region DropDownCancelEvent Implementation
|
||||
protected virtual void OnDropDownCancel(DropDownCancelEventArgs e)
|
||||
{
|
||||
if (this.DropDownCancel != null)
|
||||
{
|
||||
this.DropDownCancel(this, e);
|
||||
}
|
||||
|
||||
if (!e.Cancel)
|
||||
{
|
||||
_owner.CloseDropDown();
|
||||
_dropDown = null; // Clear reference for GC
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region IMessageFilter Implementation
|
||||
/// <summary>
|
||||
/// Checks the message loop for mouse messages whilst the popup
|
||||
/// window is displayed. If one is detected the position is
|
||||
/// checked to see if it is outside the form, and the owner
|
||||
/// is notified if so.
|
||||
/// </summary>
|
||||
/// <param name="m">Windows Message about to be processed by the
|
||||
/// message loop</param>
|
||||
/// <returns><c>true</c> to filter the message, <c>false</c> otherwise.
|
||||
/// This implementation always returns <c>false</c>.</returns>
|
||||
public bool PreFilterMessage(ref Message m)
|
||||
{
|
||||
if (_dropDown != null)
|
||||
{
|
||||
switch (m.Msg)
|
||||
{
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_NCLBUTTONDOWN:
|
||||
case WM_NCRBUTTONDOWN:
|
||||
case WM_NCMBUTTONDOWN: OnMouseDown(); break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
203
PROMS/DropDownPanel/Helper/DropDownWindowHelper.cs
Normal file
203
PROMS/DropDownPanel/Helper/DropDownWindowHelper.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace AT.STO.UI.Win
|
||||
{
|
||||
/// <summary>
|
||||
/// A class to assist in creating popup windows like Combo Box drop-downs and Menus.
|
||||
/// This class includes functionality to keep the title bar of the popup owner form
|
||||
/// active whilst the popup is displayed, and to automatically cancel the popup
|
||||
/// whenever the user clicks outside the popup window or shifts focus to another
|
||||
/// application.
|
||||
///
|
||||
/// Thousand thanks to Steve McMahon:
|
||||
/// http://www.vbaccelerator.com/home/NET/Code/Controls/Popup_Windows/Popup_Windows/Popup_Form_Demonstration.asp
|
||||
/// </summary>
|
||||
internal class DropDownWindowHelper : NativeWindow
|
||||
{
|
||||
#region Private Variable Declarations
|
||||
private EventHandler DropDownClosedHandler = null;
|
||||
|
||||
private Form _dropDown = null;
|
||||
private bool _dropDownShowing = false;
|
||||
private DropDownMessageFilter _filter = null;
|
||||
private Form _owner = null;
|
||||
private bool _skipClose = false;
|
||||
#endregion
|
||||
#region Event Declarations
|
||||
public event DropDownCancelEventHandler DropDownCancel;
|
||||
public event DropDownClosedEventHandler DropDownClosed;
|
||||
#endregion
|
||||
#region Constructor / Destructor
|
||||
/// <summary>
|
||||
/// Default constructor.
|
||||
/// </summary>
|
||||
/// <remarks>Use the <see cref="System.Windows.Forms.NativeWindow.AssignHandle"/>
|
||||
/// method to attach this class to the form you want to show popups from.</remarks>
|
||||
public DropDownWindowHelper()
|
||||
{
|
||||
_filter = new DropDownMessageFilter(this);
|
||||
_filter.DropDownCancel += new DropDownCancelEventHandler(Popup_Cancel);
|
||||
}
|
||||
#endregion
|
||||
#region Event Handler
|
||||
private void Popup_Cancel(object sender, DropDownCancelEventArgs e)
|
||||
{
|
||||
OnDropDownCancel(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Responds to the <see cref="System.Windows.Forms.Form.Closed"/>
|
||||
/// event from the popup form.
|
||||
/// </summary>
|
||||
/// <param name="sender">Popup form that has been closed.</param>
|
||||
/// <param name="e">Not used.</param>
|
||||
private void Popup_Closed(object sender, EventArgs e)
|
||||
{
|
||||
CloseDropDown();
|
||||
}
|
||||
|
||||
/// <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:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Public Methods
|
||||
/// <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>
|
||||
/// Called when the popup is being hidden.
|
||||
/// </summary>
|
||||
public void CloseDropDown()
|
||||
{
|
||||
if (DropDownShowing)
|
||||
{
|
||||
if (!_skipClose)
|
||||
{
|
||||
OnPDropDownClosed(new DropDownClosedEventArgs(_dropDown));
|
||||
}
|
||||
|
||||
_skipClose = false;
|
||||
|
||||
_owner.RemoveOwnedForm(_dropDown); // Make sure the popup is closed and we've cleaned up:
|
||||
_dropDownShowing = false;
|
||||
_dropDown.Closed -= DropDownClosedHandler;
|
||||
DropDownClosedHandler = null;
|
||||
_dropDown.Close();
|
||||
|
||||
Application.RemoveMessageFilter(_filter); // No longer need to filter for clicks outside the popup.
|
||||
|
||||
// If we did something from the popup which shifted focus to a new form, like showing another popup
|
||||
// or dialog, then Windows won't know how to bring the original owner back to the foreground, so
|
||||
// force it here:
|
||||
_owner.Activate();
|
||||
|
||||
_dropDown = null; // Null out references for GC
|
||||
_owner = null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Public Properties
|
||||
/// <summary>
|
||||
/// Indicator weither the DropDown is showing.
|
||||
/// </summary>
|
||||
public bool DropDownShowing
|
||||
{
|
||||
get { return _dropDownShowing; }
|
||||
}
|
||||
#endregion
|
||||
#region Event Implementation
|
||||
protected virtual void OnDropDownCancel(DropDownCancelEventArgs e)
|
||||
{
|
||||
if (this.DropDownCancel != null)
|
||||
{
|
||||
this.DropDownCancel(this, e);
|
||||
|
||||
if (!e.Cancel)
|
||||
{
|
||||
_skipClose = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnPDropDownClosed(DropDownClosedEventArgs e)
|
||||
{
|
||||
if (this.DropDownClosed != null)
|
||||
{
|
||||
this.DropDownClosed(this, e);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user