vbAccelerator - Contents of code file: CancellableEditPopupCS_PopupCancelNotifier.csusing System;
using System.Drawing;
using System.Windows.Forms;
namespace vbAccelerator.Components.Controls
{
/// <summary>
/// Represents the method that handles the <see
cref="PopupCancelNotifier.PopupCancel"/> event
/// raised by this class.
/// </summary>
public delegate void PopupCancelEventHandler(object sender,
PopupCancelEventArgs e);
/// <summary>
/// Arguments to a <see cref="PopupCancelEvent"/>. Provides a
/// reference to the popup form that is to be closed and
/// allows the operation to be cancelled.
/// </summary>
public class PopupCancelEventArgs : EventArgs
{
/// <summary>
/// Whether to cancel the operation
/// </summary>
private bool cancel = false;
/// <summary>
/// Mouse down location
/// </summary>
private Point location;
/// <summary>
/// Popup control.
/// </summary>
private Control popup = null;
/// <summary>
/// Constructs a new instance of this class.
/// </summary>
/// <param name="popup">The popup form</param>
/// <param name="location">The mouse location, if any, where the
/// mouse event that would cancel the popup occured.</param>
public PopupCancelEventArgs(Control popup, Point location)
{
this.popup = popup;
this.location = location;
this.cancel = false;
}
/// <summary>
/// Gets the popup control
/// </summary>
public Control Popup
{
get
{
return this.popup;
}
}
/// <summary>
/// Gets the location that the mouse down which would cancel this
/// popup occurred
/// </summary>
public Point CursorLocation
{
get
{
return this.location;
}
}
/// <summary>
/// Gets/sets whether to cancel closing the form. Set to
/// <c>true</c> to prevent the popup from being closed.
/// </summary>
public bool Cancel
{
get
{
return this.cancel;
}
set
{
this.cancel = value;
}
}
}
/// <summary>
///
/// A class which provides the functionality required to
/// cancel a popup window. This class wraps two pieces of
/// functionality:
///
/// <list type="number">Firstly, it checks whether the form (or the form
owner
/// for the control) receives a <c>WM_APPACTIVATE</c> message with
/// wParam = 0. This indicates the window has gone out
/// of focus because the user has clicked on another one.</list>
/// <list type="number">Secondly, it installs a <see
cref="System.Windows.Forms.IMessageFilter"/>
/// message filter implementation which checks for mouse presses anywhere
/// else in the application.
///
/// <remarks>
/// Copyright © 2003 Steve McMahon for vbAccelerator.com.
/// vbAccelerator is a Trade Mark of vbAccelerator Ltd. All Rights
/// Reserved. Please visit http://vbaccelerator.com/ for more
/// on this and other VB and .NET Framework code.
/// </remarks>
///
/// </summary>
public class PopupCancelNotifier : NativeWindow
{
private const int WM_ACTIVATEAPP = 0x01C;
/// <summary>
/// Raised when the popup control is about to be cancelled.
/// </summary>
public event PopupCancelEventHandler PopupCancel;
/// <summary>
/// The <see cref="System.Windows.Forms.IMessageFilter"/> object
/// which checks for mouse down outside the control
/// </summary>
private PopupCancelNotifierMessageFilter filter = null;
/// <summary>
/// Owning Form's Window handle to track for popup cancellation
/// </summary>
private IntPtr trackHandle = IntPtr.Zero;
/// <summary>
/// Control to track for popup cancellation
/// </summary>
private Control trackControl = null;
/// <summary>
/// Start tracking for a popup cancellation.
/// </summary>
/// <param name="ctl">The <c>Control</c> or <c>Form</c>
/// to use when tracking Window inactivation messages. This can
/// either be a control or a Form.</param>
public void StartTracking(Control ctl)
{
IntPtr handle = IntPtr.Zero;
Control ctlOwnerForm = ctl;
Control ctlTest = null;
while (!typeof(Form).IsAssignableFrom(ctlOwnerForm.GetType()))
{
ctlTest = ctlOwnerForm.Parent;
if (ctlTest == null)
{
break;
}
else
{
ctlOwnerForm = ctlTest;
}
}
this.trackControl = ctl;
filter.Popup = ctl;
this.trackHandle = ctlOwnerForm.Handle;
this.AssignHandle(trackHandle);
Application.AddMessageFilter(filter);
}
/// <summary>
/// Check for the WM_APPACTIVATE message and stop
/// tracking if the window is inactivated.
/// </summary>
/// <param name="msg">Message details for this window procedure
/// event.</param>
protected override void WndProc(ref Message msg)
{
base.WndProc(ref msg);
if (msg.Msg == WM_ACTIVATEAPP)
{
if (((int)msg.WParam) == 0)
{
PopupCancelEventArgs e = new PopupCancelEventArgs(
this.trackControl, Cursor.Position);
OnPopupCancel(e);
}
}
}
/// <summary>
/// Stop tracking. Called automatically if this class determines
/// the popup should be cancelled.
/// </summary>
public void StopTracking()
{
if (!this.trackHandle.Equals(IntPtr.Zero))
{
this.ReleaseHandle();
this.trackHandle = IntPtr.Zero;
Application.RemoveMessageFilter(filter);
filter.Popup = null;
}
}
/// <summary>
/// Pass through for the PopupCancel event of the Message Filter
/// </summary>
/// <param name="sender">The <see
cref="PopupCancelNotifierEventFilter"/></param>
/// <param name="e"><see cref="PopupCancelEventArgs"/> describing the
event
/// that will cancel the popup.</param>
private void filter_PopupCancel(object sender, PopupCancelEventArgs e)
{
OnPopupCancel(e);
}
/// <summary>
/// Notify when the popup should be cancelled,
/// and uninstall tracking.
/// </summary>
protected virtual void OnPopupCancel(PopupCancelEventArgs e)
{
if (PopupCancel != null)
{
PopupCancel(this, e);
}
if (!e.Cancel)
{
StopTracking();
}
}
/// <summary>
/// Constructs a new instance of the PopupCancelNotifier
/// class.
/// </summary>
public PopupCancelNotifier() : base()
{
this.filter = new PopupCancelNotifierMessageFilter(this);
this.filter.PopupCancel += new
PopupCancelEventHandler(filter_PopupCancel);
}
}
#region PopupWindowHelperMessageFilter
/// <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.
/// </summary>
public class PopupCancelNotifierMessageFilter : IMessageFilter
{
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;
/// <summary>
/// Raised when the Popup COntrol is about to be cancelled. The
/// <see cref="PopupCancelEventArgs.Cancel"/> property can be
/// set to <c>true</c> to prevent the control from being cancelled.
/// </summary>
public event PopupCancelEventHandler PopupCancel;
/// <summary>
/// The popup control
/// </summary>
private Control popup = null;
/// <summary>
/// The owning <see cref="PopupCancelNotifier"/> object.
/// </summary>
private PopupCancelNotifier owner = null;
/// <summary>
/// Constructs a new instance of this class and sets the owning
/// object.
/// </summary>
/// <param name="owner">The <see cref="PopupCancelNotifier"/> object
/// which owns this class.</param>
public PopupCancelNotifierMessageFilter(PopupCancelNotifier owner)
{
this.owner = owner;
}
/// <summary>
/// Gets/sets the popup <see cref="System.Windows.Forms.Control"/> which
is being displayed.
/// </summary>
public Control Popup
{
get
{
return this.popup;
}
set
{
this.popup = value;
}
}
/// <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 (this.popup != 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;
}
/// <summary>
/// Checks the mouse location and calls the OnCancelPopup method
/// if the mouse is outside the popup form.
/// </summary>
private void OnMouseDown()
{
Console.WriteLine("Filter:OnMouseDown");
if (this.popup != null)
{
// Get the cursor location
Point cursorPos = Cursor.Position;
// To control coordinates:
cursorPos = this.popup.PointToClient(cursorPos);
// Check if it is within the popup control
if (!popup.ClientRectangle.Contains(cursorPos))
{
// If not, then call to see if it should be closed
OnCancelPopup(new PopupCancelEventArgs(popup, cursorPos));
}
}
}
/// <summary>
/// Raises the <see cref="PopupCancel"/> event.
/// </summary>
/// <param name="e">The <see cref="PopupCancelEventArgs"/> associated
/// with the cancel event.</param>
protected virtual void OnCancelPopup(PopupCancelEventArgs e)
{
if (this.PopupCancel != null)
{
this.PopupCancel(this, e);
}
if (!e.Cancel)
{
owner.StopTracking();
// Clear reference for GC
popup = null;
}
}
}
#endregion
}
|
|