using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; namespace JwKdsV.Component { public class DatePicker : DateTimePicker { private PopupWindowHelper popupHelper; /// /// Color of the DatePicker header /// [Description("Color of the DatePicker header")] [Browsable(true), Category("DatePicker")] public Color HeaderColor { get; set; } Button btn = new Button(); public DatePicker() : base() { this.ShowCheckBox = false; this.ShowUpDown = false; popupHelper = new PopupWindowHelper(); btn.Dock = System.Windows.Forms.DockStyle.Right; btn.Image = Properties.Resources.calendar; btn.Width = 36; this.Controls.Add(btn); btn.Click += ButtonClick; } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); ButtonClick(btn, e); } private void ButtonClick(object sender, EventArgs e) { var btn = sender as Button; Form f = FindForm(); var popup = new DateTimePickerEx2(HeaderColor); popup.cmb_Cal = this; popup.CurrentDate = Convert.ToDateTime(Value); Value = popup.CurrentDate; Point location = PointToScreen(new Point(btn.Left - Width + btn.Width, btn.Bottom)); popupHelper.ShowPopup(f, popup, location); } } #region Event Argument Classes /// /// Contains event information for a event. /// public class PopupClosedEventArgs : EventArgs { /// /// The popup form. /// private Form popup = null; /// /// Gets the popup form which is being closed. /// public Form Popup { get { return this.popup; } } /// /// Constructs a new instance of this class for the specified /// popup form. /// /// Popup Form which is being closed. public PopupClosedEventArgs(Form popup) { this.popup = popup; } } /// /// Arguments to a . Provides a /// reference to the popup form that is to be closed and /// allows the operation to be cancelled. /// public class PopupCancelEventArgs : EventArgs { /// /// Whether to cancel the operation /// private bool cancel = false; /// /// Mouse down location /// private Point location; /// /// Popup form. /// private Form popup = null; /// /// Constructs a new instance of this class. /// /// The popup form /// The mouse location, if any, where the /// mouse event that would cancel the popup occured. public PopupCancelEventArgs(Form popup, Point location) { this.popup = popup; this.location = location; this.cancel = false; } /// /// Gets the popup form /// public Form Popup { get { return this.popup; } } /// /// Gets the location that the mouse down which would cancel this /// popup occurred /// public Point CursorLocation { get { return this.location; } } /// /// Gets/sets whether to cancel closing the form. Set to /// true to prevent the popup from being closed. /// public bool Cancel { get { return this.cancel; } set { this.cancel = value; } } } #endregion #region Delegates /// /// Represents the method which responds to a event. /// public delegate void PopupClosedEventHandler(object sender, PopupClosedEventArgs e); /// /// Represents the method which responds to a event. /// public delegate void PopupCancelEventHandler(object sender, PopupCancelEventArgs e); #endregion #region PopupWindowHelper /// /// 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. /// public class PopupWindowHelper : NativeWindow { #region Unmanaged Code [DllImport("user32", CharSet = CharSet.Auto)] private extern static int SendMessage(IntPtr handle, int msg, int wParam, IntPtr lParam); [DllImport("user32", CharSet = CharSet.Auto)] private extern static int PostMessage(IntPtr handle, int msg, int wParam, IntPtr lParam); private const int WM_ACTIVATE = 0x006; private const int WM_ACTIVATEAPP = 0x01C; private const int WM_NCACTIVATE = 0x086; [DllImport("user32")] private extern static void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); private const int KEYEVENTF_KEYUP = 0x0002; #endregion #region Member Variables /// /// Event Handler to detect when the popup window is closed /// private EventHandler popClosedHandler = null; /// /// Message filter to detect mouse clicks anywhere in the application /// whilst the popup window is being displayed. /// private PopupWindowHelperMessageFilter filter = null; /// /// The popup form that is being shown. /// private Form popup = null; /// /// The owner of the popup form that is being shown: /// private Form owner = null; /// /// Whether the popup is showing or not. /// private bool popupShowing = false; /// /// Whether the popup has been cancelled, notified by PopupCancel, /// rather than closed. /// private bool skipClose = false; #endregion /// /// Raised when the popup form is closed. /// public event PopupClosedEventHandler PopupClosed; /// /// Raised when the Popup Window is about to be cancelled. The /// property can be /// set to true to prevent the form from being cancelled. /// public event PopupCancelEventHandler PopupCancel; /// /// 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. /// Typical code to use this message is as follows: /// /// frmPopup popup = new frmPopup(); /// Point location = this.PointToScreen(new Point(button1.Left, button1.Bottom)); /// popupHelper.ShowPopup(this, popup, location); /// /// Put as much initialisation code as possible /// into the popup form's constructor, rather than the /// event as this will improve visual appearance. /// /// Main form which owns the popup /// Window to show as a popup /// Location relative to the screen to show the popup at. public void ShowPopup(Form owner, Form popup, Point location) { this.owner = owner; this.popup = popup; // Start checking for the popup being cancelled Application.AddMessageFilter(filter); // Set the location of the popup form: popup.StartPosition = FormStartPosition.Manual; popup.Location = location; // Make it owned by the window that's displaying it: owner.AddOwnedForm(popup); // Respond to the Closed event in case the popup // is closed by its own internal means popClosedHandler = new EventHandler(popup_Closed); popup.Closed += popClosedHandler; // Show the popup: this.popupShowing = true; popup.Show(); popup.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. // Send a Tab command: keybd_event((byte)Keys.Tab, 0, 0, 0); keybd_event((byte)Keys.Tab, 0, KEYEVENTF_KEYUP, 0); // Send a reverse Tab command: keybd_event((byte)Keys.ShiftKey, 0, 0, 0); keybd_event((byte)Keys.Tab, 0, 0, 0); keybd_event((byte)Keys.Tab, 0, KEYEVENTF_KEYUP, 0); keybd_event((byte)Keys.ShiftKey, 0, KEYEVENTF_KEYUP, 0); // Start filtering for mouse clicks outside the popup filter.Popup = popup; } /// /// Responds to the /// event from the popup form. /// /// Popup form that has been closed. /// Not used. private void popup_Closed(object sender, EventArgs e) { ClosePopup(); } /// /// 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. /// /// Window Procedure Message protected override void WndProc(ref Message m) { base.WndProc(ref m); if (this.popupShowing) { // check for WM_ACTIVATE and WM_NCACTIVATE if (m.Msg == WM_NCACTIVATE) { // Check if the title bar will made inactive: if (((int)m.WParam) == 0) { // If so reactivate it. SendMessage(this.Handle, WM_NCACTIVATE, 1, IntPtr.Zero); // Note it's no good to try and consume this message; // if you try to do that you'll end up with windows // that don't respond. } } else if (m.Msg == WM_ACTIVATEAPP) { // Check if the application is being deactivated. if ((int)m.WParam == 0) { // It is so cancel the popup: ClosePopup(); // And put the title bar into the inactive state: PostMessage(this.Handle, WM_NCACTIVATE, 0, IntPtr.Zero); } } } } /// /// Called when the popup is being hidden. /// public void ClosePopup() { if (this.popupShowing) { if (!skipClose) { // Raise event to owner OnPopupClosed(new PopupClosedEventArgs(this.popup)); } skipClose = false; // Make sure the popup is closed and we've cleaned // up: this.owner.RemoveOwnedForm(this.popup); this.popupShowing = false; this.popup.Closed -= popClosedHandler; this.popClosedHandler = null; this.popup.Close(); // No longer need to filter for clicks outside the // popup. Application.RemoveMessageFilter(filter); // 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: this.owner.Activate(); // Null out references for GC this.popup = null; this.owner = null; } } /// /// Raises the event. /// /// describing the /// popup form that is being closed. protected virtual void OnPopupClosed(PopupClosedEventArgs e) { if (this.PopupClosed != null) { this.PopupClosed(this, e); } } private void popup_Cancel(object sender, PopupCancelEventArgs e) { OnPopupCancel(e); } /// /// Raises the event. /// /// describing the /// popup form that about to be cancelled. protected virtual void OnPopupCancel(PopupCancelEventArgs e) { if (this.PopupCancel != null) { this.PopupCancel(this, e); if (!e.Cancel) { skipClose = true; } } } /// /// Default constructor. /// /// Use the /// method to attach this class to the form you want to show popups from. public PopupWindowHelper() { filter = new PopupWindowHelperMessageFilter(this); filter.PopupCancel += new PopupCancelEventHandler(popup_Cancel); } } #endregion #region PopupWindowHelperMessageFilter /// /// A Message Loop filter which detect mouse events whilst the popup form is shown /// and notifies the owning class when a mouse /// click outside the popup occurs. /// public class PopupWindowHelperMessageFilter : 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; /// /// Raised when the Popup Window is about to be cancelled. The /// property can be /// set to true to prevent the form from being cancelled. /// public event PopupCancelEventHandler PopupCancel; /// /// The popup form /// private Form popup = null; /// /// The owning object. /// private PopupWindowHelper owner = null; /// /// Constructs a new instance of this class and sets the owning /// object. /// /// The object /// which owns this class. public PopupWindowHelperMessageFilter(PopupWindowHelper owner) { this.owner = owner; } /// /// Gets/sets the popup form which is being displayed. /// public Form Popup { get { return this.popup; } set { this.popup = value; } } /// /// 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. /// /// Windows Message about to be processed by the /// message loop /// true to filter the message, false otherwise. /// This implementation always returns false. 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; } /// /// Checks the mouse location and calls the OnCancelPopup method /// if the mouse is outside the popup form. /// private void OnMouseDown() { // Get the cursor location Point cursorPos = Cursor.Position; // Check if it is within the popup form if (!popup.Bounds.Contains(cursorPos)) { // If not, then call to see if it should be closed OnCancelPopup(new PopupCancelEventArgs(popup, cursorPos)); } } /// /// Raises the event. /// /// The associated /// with the cancel event. protected virtual void OnCancelPopup(PopupCancelEventArgs e) { if (this.PopupCancel != null) { this.PopupCancel(this, e); } if (!e.Cancel) { //IsModal = false; owner.ClosePopup(); //Debug.WriteLine(owner.IsModal.ToString()); // Clear reference for GC popup = null; } } } #endregion }