//#########################################################################################
//★★★★★★★ http://www.cnpopsoft.com [华普软件] ★★★★★★★
//★★★★★★★ 华普软件 - VB & C#.NET 专业论文与源码荟萃! ★★★★★★★
//#########################################################################################
/*
* Copyright ?2005, Mathew Hall
* All rights reserved.
*
* 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.Drawing;
using System.Globalization;
using System.Windows.Forms;
using XPTable.Events;
using XPTable.Models;
using XPTable.Renderers;
using XPTable.Win32;
namespace XPTable.Editors
{
///
/// A class for editing Cells that contain numbers
///
public class NumberCellEditor : CellEditor, IEditorUsesRendererButtons
{
#region Class Data
///
/// ID number for the up button
///
protected static readonly int UpButtonID = 1;
///
/// ID number for the down button
///
protected static readonly int DownButtonID = 2;
///
/// The current value of the editor
///
private decimal currentValue;
///
/// The value to increment or decrement when the up or down buttons are clicked
///
private decimal increment;
///
/// The maximum value for the editor
///
private decimal maximum;
///
/// The inximum value for the editor
///
private decimal minimum;
///
/// A string that specifies how editors value is formatted
///
private string format;
///
/// The amount the mouse wheel has moved
///
private int wheelDelta;
///
/// Indicates whether the arrow keys should be passed to the editor
///
private bool interceptArrowKeys;
///
/// Specifies whether the editors text value is changing
///
private bool changingText;
///
/// Initial interval between timer events
///
private const int TimerInterval = 500;
///
/// Current interval between timer events
///
private int interval;
///
/// Indicates whether the user has changed the editors value
///
private bool userEdit;
///
/// The bounding Rectangle of the up and down buttons
///
private Rectangle buttonBounds;
///
/// The id of the button that was pressed
///
private int buttonID;
///
/// Timer to to fire button presses at regular intervals while
/// a button is pressed
///
private Timer timer;
#endregion
#region Constructor
///
/// Initializes a new instance of the NumberCellEditor class with default settings
///
public NumberCellEditor()
{
TextBox textbox = new TextBox();
textbox.AutoSize = false;
textbox.BorderStyle = BorderStyle.None;
this.Control = textbox;
this.currentValue = new decimal(0);
this.increment = new decimal(1);
this.minimum = new decimal(0);
this.maximum = new decimal(100);
this.format = "G";
this.wheelDelta = 0;
this.interceptArrowKeys = true;
this.userEdit = false;
this.changingText = false;
this.buttonBounds = Rectangle.Empty;
this.buttonID = 0;
this.interval = TimerInterval;
}
#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 NumberColumn))
{
throw new InvalidOperationException("Cannot edit Cell as NumberCellEditor can only be used with a NumberColumn");
}
if (!(table.ColumnModel.GetCellRenderer(cellPos.Column) is NumberCellRenderer))
{
throw new InvalidOperationException("Cannot edit Cell as NumberCellEditor can only be used with a NumberColumn that uses a NumberCellRenderer");
}
this.Minimum = ((NumberColumn) table.ColumnModel.Columns[cellPos.Column]).Minimum;
this.Maximum = ((NumberColumn) table.ColumnModel.Columns[cellPos.Column]).Maximum;
this.Increment = ((NumberColumn) table.ColumnModel.Columns[cellPos.Column]).Increment;
return base.PrepareForEditing (cell, table, cellPos, cellRect, userSetEditorValues);
}
///
/// Sets the initial value of the editor based on the contents of
/// the Cell being edited
///
protected override void SetEditValue()
{
// make sure we start with a valid value
this.Value = this.Minimum;
// attempt to get the cells data
this.Value = Convert.ToDecimal(this.EditingCell.Data);
}
///
/// Sets the contents of the Cell being edited based on the value
/// in the editor
///
protected override void SetCellValue()
{
this.EditingCell.Data = this.Value;
}
///
/// Starts editing the Cell
///
public override void StartEditing()
{
this.TextBox.MouseWheel += new MouseEventHandler(OnMouseWheel);
this.TextBox.KeyDown += new KeyEventHandler(OnTextBoxKeyDown);
this.TextBox.KeyPress += new KeyPressEventHandler(OnTextBoxKeyPress);
this.TextBox.LostFocus += new EventHandler(OnTextBoxLostFocus);
base.StartEditing();
this.TextBox.Focus();
}
///
/// Stops editing the Cell and commits any changes
///
public override void StopEditing()
{
this.TextBox.MouseWheel -= new MouseEventHandler(OnMouseWheel);
this.TextBox.KeyDown -= new KeyEventHandler(OnTextBoxKeyDown);
this.TextBox.KeyPress -= new KeyPressEventHandler(OnTextBoxKeyPress);
this.TextBox.LostFocus -= new EventHandler(OnTextBoxLostFocus);
base.StopEditing();
}
///
/// Stops editing the Cell and ignores any changes
///
public override void CancelEditing()
{
this.TextBox.MouseWheel -= new MouseEventHandler(OnMouseWheel);
this.TextBox.KeyDown -= new KeyEventHandler(OnTextBoxKeyDown);
this.TextBox.KeyPress -= new KeyPressEventHandler(OnTextBoxKeyPress);
this.TextBox.LostFocus -= new EventHandler(OnTextBoxLostFocus);
base.CancelEditing();
}
///
/// Sets the location and size of the CellEditor
///
/// A Rectangle that represents the size and location
/// of the Cell being edited
protected override void SetEditLocation(Rectangle cellRect)
{
// calc the size of the textbox
ICellRenderer renderer = this.EditingTable.ColumnModel.GetCellRenderer(this.EditingCellPos.Column);
int buttonWidth = ((NumberCellRenderer) renderer).ButtonWidth;
this.TextBox.Size = new Size(cellRect.Width - 1 - buttonWidth, cellRect.Height-1);
// calc the location of the textbox
this.TextBox.Location = cellRect.Location;
this.buttonBounds = new Rectangle(this.TextBox.Left + 1, this.TextBox.Top, buttonWidth, this.TextBox.Height);
if (((NumberColumn) this.EditingTable.ColumnModel.Columns[this.EditingCellPos.Column]).UpDownAlign == LeftRightAlignment.Left)
{
this.TextBox.Location = new Point(cellRect.Left + buttonWidth, cellRect.Top);
this.buttonBounds.Location = new Point(cellRect.Left, cellRect.Top);
}
}
///
/// Simulates the up button being pressed
///
protected void UpButton()
{
if (this.UserEdit)
{
this.ParseEditText();
}
decimal num = this.currentValue;
if (num > (new decimal(-1, -1, -1, false, 0) - this.increment))
{
num = new decimal(-1, -1, -1, false, 0);
}
else
{
num += this.increment;
if (num > this.maximum)
{
num = this.maximum;
}
}
this.Value = num;
}
///
/// Simulates the down button being pressed
///
protected void DownButton()
{
if (this.UserEdit)
{
this.ParseEditText();
}
decimal num = this.currentValue;
if (num < (new decimal(-1, -1, -1, true, 0) + this.increment))
{
num = new decimal(-1, -1, -1, true, 0);
}
else
{
num -= this.increment;
if (num < this.minimum)
{
num = this.minimum;
}
}
this.Value = num;
}
///
/// Updates the editors text value to the current value
///
protected void UpdateEditText()
{
if (this.UserEdit)
{
this.ParseEditText();
}
this.ChangingText = true;
this.Control.Text = this.currentValue.ToString(this.Format);
}
///
/// Checks the current value and updates the editors text value
///
protected virtual void ValidateEditText()
{
this.ParseEditText();
this.UpdateEditText();
}
///
/// Converts the editors current value to a number
///
protected void ParseEditText()
{
try
{
this.Value = this.Constrain(decimal.Parse(this.Control.Text));
}
catch (Exception)
{
return;
}
finally
{
this.UserEdit = false;
}
}
///
/// Ensures that the specified value is between the editors Maximun and
/// Minimum values
///
/// The value to be checked
/// A value is between the editors Maximun and Minimum values
private decimal Constrain(decimal value)
{
if (value < this.minimum)
{
value = this.minimum;
}
if (value > this.maximum)
{
value = this.maximum;
}
return value;
}
///
/// Starts the Timer
///
protected void StartTimer()
{
if (this.timer == null)
{
this.timer = new Timer();
this.timer.Tick += new EventHandler(this.TimerHandler);
}
this.interval = TimerInterval;
this.timer.Interval = this.interval;
this.timer.Start();
}
///
/// Stops the Timer
///
protected void StopTimer()
{
if (this.timer != null)
{
this.timer.Stop();
this.timer.Dispose();
this.timer = null;
}
}
#endregion
#region Properties
///
/// Gets the TextBox used to edit the Cells contents
///
public TextBox TextBox
{
get
{
return this.Control as TextBox;
}
}
///
/// Gets or sets the editors current value
///
protected decimal Value
{
get
{
if (this.UserEdit)
{
this.ValidateEditText();
}
return this.currentValue;
}
set
{
if (value != this.currentValue)
{
if (value < this.minimum)
{
value = this.maximum;
}
if (value > this.maximum)
{
value = this.maximum;
}
this.currentValue = value;
this.UpdateEditText();
}
}
}
///
/// Gets or sets the value to increment or decrement when the up or down
/// buttons are clicked
///
protected decimal Increment
{
get
{
return this.increment;
}
set
{
if (value < new decimal(0))
{
throw new ArgumentException("increment must be greater than zero");
}
this.increment = value;
}
}
///
/// Gets or sets the maximum value for the editor
///
protected decimal Maximum
{
get
{
return this.maximum;
}
set
{
this.maximum = value;
if (this.minimum > this.maximum)
{
this.minimum = this.maximum;
}
}
}
///
/// Gets or sets the minimum value for the editor
///
protected decimal Minimum
{
get
{
return this.minimum;
}
set
{
this.minimum = value;
if (this.minimum > this.maximum)
{
this.maximum = value;
}
}
}
///
/// Gets or sets the string that specifies how the editors contents
/// are formatted
///
protected string Format
{
get
{
return this.format;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
this.format = value;
this.UpdateEditText();
}
}
///
/// Gets or sets whether the editors text is being updated
///
protected bool ChangingText
{
get
{
return this.changingText;
}
set
{
this.changingText = value;
}
}
///
/// Gets or sets whether the arrow keys should be passed to the editor
///
public bool InterceptArrowKeys
{
get
{
return this.interceptArrowKeys;
}
set
{
this.interceptArrowKeys = value;
}
}
///
/// Gets or sets whether the user has changed the editors value
///
protected bool UserEdit
{
get
{
return this.userEdit;
}
set
{
this.userEdit = value;
}
}
#endregion
#region Events
///
/// Handler for the editors TextBox.MouseWheel event
///
/// The object that raised the event
/// A MouseEventArgs that contains the event data
protected internal virtual void OnMouseWheel(object sender, MouseEventArgs e)
{
bool up = true;
this.wheelDelta += e.Delta;
if (Math.Abs(this.wheelDelta) >= 120)
{
if (this.wheelDelta < 0)
{
up = false;
}
if (up)
{
this.UpButton();
}
else
{
this.DownButton();
}
this.wheelDelta = 0;
}
}
///
/// Handler for the editors TextBox.KeyDown event
///
/// The object that raised the event
/// A KeyEventArgs that contains the event data
protected virtual void OnTextBoxKeyDown(object sender, KeyEventArgs e)
{
if (this.interceptArrowKeys)
{
if (e.KeyData == Keys.Up)
{
this.UpButton();
e.Handled = true;
}
else if (e.KeyData == Keys.Down)
{
this.DownButton();
e.Handled = true;
}
}
if (e.KeyCode == Keys.Return)
{
this.ValidateEditText();
}
}
///
/// Handler for the editors TextBox.KeyPress event
///
/// The object that raised the event
/// A KeyPressEventArgs that contains the event data
protected virtual void OnTextBoxKeyPress(object sender, KeyPressEventArgs e)
{
char enter = AsciiChars.CarriageReturn;
char escape = AsciiChars.Escape;
char tab = AsciiChars.HorizontalTab;
NumberFormatInfo info = CultureInfo.CurrentCulture.NumberFormat;
string decimalSeparator = info.NumberDecimalSeparator;
string groupSeparator = info.NumberGroupSeparator;
string negativeSign = info.NegativeSign;
string character = e.KeyChar.ToString();
if ((!char.IsDigit(e.KeyChar) && !character.Equals(decimalSeparator) && !character.Equals(groupSeparator)) &&
!character.Equals(negativeSign) && (e.KeyChar != tab))
{
if ((Control.ModifierKeys & (Keys.Alt | Keys.Control)) == Keys.None)
{
e.Handled = true;
if (e.KeyChar == enter)
{
if (this.EditingTable != null)
{
this.EditingTable.StopEditing();
}
}
else if (e.KeyChar == escape)
{
if (this.EditingTable != null)
{
this.EditingTable.CancelEditing();
}
}
else
{
NativeMethods.MessageBeep(0 /*MB_OK*/);
}
}
}
}
///
/// Handler for the editors TextBox.LostFocus event
///
/// The object that raised the event
/// An EventArgs that contains the event data
protected virtual void OnTextBoxLostFocus(object sender, EventArgs e)
{
if (this.UserEdit)
{
this.ValidateEditText();
}
if (this.EditingTable != null)
{
this.EditingTable.StopEditing();
}
}
///
/// Handler for the editors buttons MouseDown event
///
/// The object that raised the event
/// A CellMouseEventArgs that contains the event data
public void OnEditorButtonMouseDown(object sender, CellMouseEventArgs e)
{
this.ParseEditText();
if (e.Y < this.buttonBounds.Top + (this.buttonBounds.Height / 2))
{
this.buttonID = UpButtonID;
this.UpButton();
}
else
{
this.buttonID = DownButtonID;
this.DownButton();
}
this.StartTimer();
}
///
/// Handler for the editors buttons MouseUp event
///
/// The object that raised the event
/// A CellMouseEventArgs that contains the event data
public void OnEditorButtonMouseUp(object sender, CellMouseEventArgs e)
{
this.StopTimer();
this.buttonID = 0;
}
///
/// Handler for the editors Timer event
///
/// The object that raised the event
/// An EventArgs that contains the event data
private void TimerHandler(object sender, EventArgs e)
{
if (buttonID == 0)
{
this.StopTimer();
return;
}
if (buttonID == UpButtonID)
{
this.UpButton();
}
else
{
this.DownButton();
}
this.interval *= 7;
this.interval /= 10;
if (this.interval < 1)
{
this.interval = 1;
}
this.timer.Interval = this.interval;
}
#endregion
}
}