//######################################################################################### //★★★★★★★ http://www.cnpopsoft.com [华普软件] ★★★★★★★ //★★★★★★★ 华普软件 - VB & C#.NET 专业论文与源码荟萃! ★★★★★★★ //######################################################################################### /* * Copyright ?2005, Mathew Hall * All rights reserved. * * DropDownCellEditor.ActivationListener, DropDownCellEditor.ShowDropDown() and * DropDownCellEditor.HideDropDown() contains code based on Steve McMahon's * PopupWindowHelper (see http://www.vbaccelerator.com/home/NET/Code/Controls/Popup_Windows/Popup_Windows/article.asp) * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using XPTable.Events; using XPTable.Models; using XPTable.Renderers; using XPTable.Win32; namespace XPTable.Editors { /// /// A base class for editing Cells that contain drop down buttons /// public abstract class DropDownCellEditor : CellEditor, IEditorUsesRendererButtons { #region Class Data /// /// The container that holds the Control displayed when editor is dropped down /// private DropDownContainer dropDownContainer; /// /// Specifies whether the DropDownContainer is currently displayed /// private bool droppedDown; /// /// Specifies the DropDown style /// private DropDownStyle dropDownStyle; /// /// The user defined width of the DropDownContainer /// private int dropDownWidth; /// /// Listener for WM_NCACTIVATE and WM_ACTIVATEAPP messages /// private ActivationListener activationListener; /// /// The Form that will own the DropDownContainer /// private Form parentForm; /// /// Specifies whether the mouse is currently over the /// DropDownContainer /// private bool containsMouse; #endregion #region Constructor /// /// Initializes a new instance of the DropDownCellEditor class with default settings /// public DropDownCellEditor() : base() { TextBox textbox = new TextBox(); textbox.AutoSize = false; textbox.BackColor = SystemColors.Window; textbox.BorderStyle = BorderStyle.None; textbox.MouseEnter += new EventHandler(textbox_MouseEnter); this.Control = textbox; this.dropDownContainer = new DropDownContainer(this); this.droppedDown = false; this.DropDownStyle = DropDownStyle.DropDownList; this.dropDownWidth = -1; this.parentForm = null; this.activationListener = new ActivationListener(this); this.containsMouse = false; } #endregion #region Methods /// /// Prepares the CellEditor to edit the specified Cell /// /// The Cell to be edited /// The Table that contains the Cell /// A CellPos representing the position of the Cell /// The Rectangle that represents the Cells location and size /// Specifies whether the ICellEditors /// starting value has already been set by the user /// true if the ICellEditor can continue editing the Cell, false otherwise public override bool PrepareForEditing(Cell cell, Table table, CellPos cellPos, Rectangle cellRect, bool userSetEditorValues) { if (!(table.ColumnModel.Columns[cellPos.Column] is DropDownColumn)) { throw new InvalidOperationException("Cannot edit Cell as DropDownCellEditor can only be used with a DropDownColumn"); } return base.PrepareForEditing (cell, table, cellPos, cellRect, userSetEditorValues); } /// /// Starts editing the Cell /// public override void StartEditing() { this.TextBox.KeyPress += new KeyPressEventHandler(OnKeyPress); this.TextBox.LostFocus += new EventHandler(OnLostFocus); base.StartEditing(); this.parentForm = this.EditingTable.FindForm(); if (this.DroppedDown) { this.ShowDropDown(); } this.TextBox.Focus(); } /// /// Stops editing the Cell and commits any changes /// public override void StopEditing() { this.TextBox.KeyPress -= new KeyPressEventHandler(OnKeyPress); this.TextBox.LostFocus -= new EventHandler(OnLostFocus); base.StopEditing(); this.DroppedDown = false; this.parentForm = null; } /// /// Stops editing the Cell and ignores any changes /// public override void CancelEditing() { this.TextBox.KeyPress -= new KeyPressEventHandler(OnKeyPress); this.TextBox.LostFocus -= new EventHandler(OnLostFocus); base.CancelEditing(); this.DroppedDown = false; this.parentForm = null; } /// /// Displays the drop down portion to the user /// protected virtual void ShowDropDown() { Point p = this.EditingTable.PointToScreen(this.TextBox.Location); p.Y += this.TextBox.Height + 1; Rectangle screenBounds = Screen.GetBounds(p); if (p.Y + this.dropDownContainer.Height > screenBounds.Bottom) { p.Y -= this.TextBox.Height + this.dropDownContainer.Height + 1; } if (p.X + this.dropDownContainer.Width > screenBounds.Right) { ICellRenderer renderer = this.EditingTable.ColumnModel.GetCellRenderer(this.EditingCellPos.Column); int buttonWidth = ((DropDownCellRenderer) renderer).ButtonWidth; p.X = p.X + this.TextBox.Width + buttonWidth - this.dropDownContainer.Width; } this.dropDownContainer.Location = p; this.parentForm.AddOwnedForm(this.dropDownContainer); this.activationListener.AssignHandle(this.parentForm.Handle); this.dropDownContainer.ShowDropDown(); this.dropDownContainer.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: NativeMethods.keybd_event((byte) Keys.Tab, 0, 0, 0); NativeMethods.keybd_event((byte) Keys.Tab, 0, KeyEventFFlags.KEYEVENTF_KEYUP, 0); // Send a reverse Tab command: NativeMethods.keybd_event((byte) Keys.ShiftKey, 0, 0, 0); NativeMethods.keybd_event((byte) Keys.Tab, 0, 0, 0); NativeMethods.keybd_event((byte) Keys.Tab, 0, KeyEventFFlags.KEYEVENTF_KEYUP, 0); NativeMethods.keybd_event((byte) Keys.ShiftKey, 0, KeyEventFFlags.KEYEVENTF_KEYUP, 0); } /// /// Conceals the drop down portion from the user /// protected virtual void HideDropDown() { this.dropDownContainer.HideDropDown(); this.parentForm.RemoveOwnedForm(this.dropDownContainer); this.activationListener.ReleaseHandle(); this.parentForm.Activate(); } /// /// Gets whether the editor should stop editing if a mouse click occurs /// outside of the DropDownContainer while it is dropped down /// /// The Control that will receive the message /// The current position of the mouse cursor /// true if the editor should stop editing, false otherwise protected virtual bool ShouldStopEditing(Control target, Point cursorPos) { return true; } /// /// Filters out a mouse message before it is dispatched /// /// The Control that will receive the message /// A WindowMessage that represents the message to process /// Specifies the WParam field of the message /// Specifies the LParam field of the message /// true to filter the message and prevent it from being dispatched; /// false to allow the message to continue to the next filter or control public override bool ProcessMouseMessage(Control target, WindowMessage msg, int wParam, int lParam) { if (this.DroppedDown) { if (msg == WindowMessage.WM_LBUTTONDOWN || msg == WindowMessage.WM_RBUTTONDOWN || msg == WindowMessage.WM_MBUTTONDOWN || msg == WindowMessage.WM_XBUTTONDOWN || msg == WindowMessage.WM_NCLBUTTONDOWN || msg == WindowMessage.WM_NCRBUTTONDOWN || msg == WindowMessage.WM_NCMBUTTONDOWN || msg == WindowMessage.WM_NCXBUTTONDOWN) { Point cursorPos = Cursor.Position; if (!this.DropDown.Bounds.Contains(cursorPos)) { if (target != this.EditingTable && target != this.TextBox) { if (this.ShouldStopEditing(target, cursorPos)) { this.EditingTable.StopEditing(); } } } } else if (msg == WindowMessage.WM_MOUSEMOVE) { Point cursorPos = Cursor.Position; if (this.DropDown.Bounds.Contains(cursorPos)) { if (!this.containsMouse) { this.containsMouse = true; this.EditingTable.RaiseCellMouseLeave(this.EditingCellPos); } } else { this.containsMouse = true; } } } return false; } /// /// Filters out a key message before it is dispatched /// /// The Control that will receive the message /// A WindowMessage that represents the message to process /// Specifies the WParam field of the message /// Specifies the LParam field of the message /// true to filter the message and prevent it from being dispatched; /// false to allow the message to continue to the next filter or control public override bool ProcessKeyMessage(Control target, WindowMessage msg, int wParam, int lParam) { if (msg == WindowMessage.WM_KEYDOWN) { if (((Keys) wParam) == Keys.F4) { if (this.TextBox.Focused || this.DropDown.ContainsFocus) { this.DroppedDown = !this.DroppedDown; return true; } } } return false; } #endregion #region Properties /// /// Gets the TextBox used to edit the Cells contents /// protected TextBox TextBox { get { return this.Control as TextBox; } } /// /// Gets the container that holds the Control displayed when editor is dropped down /// protected DropDownContainer DropDown { get { return this.dropDownContainer; } } /// /// Gets or sets whether the editor is displaying its drop-down portion /// public bool DroppedDown { get { return this.droppedDown; } set { if (this.droppedDown != value) { this.droppedDown = value; if (value) { this.ShowDropDown(); } else { this.HideDropDown(); } } } } /// /// Gets or sets the width of the of the drop-down portion of the editor /// public int DropDownWidth { get { if (this.dropDownWidth != -1) { return this.dropDownWidth; } return this.dropDownContainer.Width; } set { this.dropDownWidth = value; this.dropDownContainer.Width = value; } } /// /// Gets the user defined width of the of the drop-down portion of the editor /// internal int InternalDropDownWidth { get { return this.dropDownWidth; } } /// /// Gets or sets a value specifying the style of the drop down editor /// public DropDownStyle DropDownStyle { get { return this.dropDownStyle; } set { if (!Enum.IsDefined(typeof(DropDownStyle), value)) { throw new InvalidEnumArgumentException("value", (int) value, typeof(DropDownStyle)); } if (this.dropDownStyle != value) { this.dropDownStyle = value; this.TextBox.ReadOnly = (value == DropDownStyle.DropDownList); } } } /// /// Gets or sets the text that is selected in the editable portion of the editor /// public string SelectedText { get { if (this.DropDownStyle == DropDownStyle.DropDownList) { return ""; } return this.TextBox.SelectedText; } set { if (this.DropDownStyle != DropDownStyle.DropDownList && value != null) { this.TextBox.SelectedText = value; } } } /// /// Gets or sets the number of characters selected in the editable portion /// of the editor /// public int SelectionLength { get { return this.TextBox.SelectionLength; } set { this.TextBox.SelectionLength = value; } } /// /// Gets or sets the starting index of text selected in the editor /// public int SelectionStart { get { return this.TextBox.SelectionStart; } set { this.TextBox.SelectionStart = value; } } /// /// Gets or sets the text associated with the editor /// public string Text { get { return this.TextBox.Text; } set { this.TextBox.Text = value; } } #endregion #region Events /// /// Handler for the editors TextBox.KeyPress event /// /// The object that raised the event /// A KeyPressEventArgs that contains the event data protected virtual void OnKeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar == AsciiChars.CarriageReturn /*Enter*/) { if (this.EditingTable != null) { this.EditingTable.StopEditing(); } } else if (e.KeyChar == AsciiChars.Escape) { if (this.EditingTable != null) { this.EditingTable.CancelEditing(); } } } /// /// Handler for the editors TextBox.LostFocus event /// /// The object that raised the event /// An EventArgs that contains the event data protected virtual void OnLostFocus(object sender, EventArgs e) { if (this.TextBox.Focused || this.DropDown.ContainsFocus) { return; } if (this.EditingTable != null) { this.EditingTable.StopEditing(); } } /// /// Handler for the editors drop down button MouseDown event /// /// The object that raised the event /// A CellMouseEventArgs that contains the event data public virtual void OnEditorButtonMouseDown(object sender, CellMouseEventArgs e) { this.DroppedDown = !this.DroppedDown; } /// /// Handler for the editors drop down button MouseUp event /// /// The object that raised the event /// A CellMouseEventArgs that contains the event data public virtual void OnEditorButtonMouseUp(object sender, CellMouseEventArgs e) { } /// /// Handler for the editors textbox MouseEnter event /// /// The object that raised the event /// An EventArgs that contains the event data private void textbox_MouseEnter(object sender, EventArgs e) { this.EditingTable.RaiseCellMouseLeave(this.EditingCellPos); } #endregion #region ActivationListener /// /// Listener for WM_NCACTIVATE and WM_ACTIVATEAPP messages /// internal class ActivationListener : XPTable.Win32.NativeWindow { /// /// The DropDownCellEditor that owns the listener /// private DropDownCellEditor owner; /// /// Initializes a new instance of the DropDownCellEditor class with the /// specified DropDownCellEditor owner /// /// The DropDownCellEditor that owns the listener public ActivationListener(DropDownCellEditor owner) : base() { this.owner = owner; } /// /// Gets or sets the DropDownCellEditor that owns the listener /// public DropDownCellEditor Editor { get { return this.owner; } set { this.owner = value; } } /// /// Processes Windows messages /// /// The Windows Message to process protected override void WndProc(ref Message m) { base.WndProc(ref m); if (this.owner != null && this.owner.DroppedDown) { if (m.Msg == (int) WindowMessage.WM_NCACTIVATE) { if (((int) m.WParam) == 0) { NativeMethods.SendMessage(this.Handle, (int) WindowMessage.WM_NCACTIVATE, 1, 0); } } else if (m.Msg == (int) WindowMessage.WM_ACTIVATEAPP) { if ((int)m.WParam == 0) { this.owner.DroppedDown = false; NativeMethods.PostMessage(this.Handle, (int) WindowMessage.WM_NCACTIVATE, 0, 0); } } } } } #endregion } }