using System; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; namespace GradeCalc { /// /// Defines the editing control for the DataGridViewNumericUpDownCell custom cell type. /// internal class DataGridViewNumericUpDownEditingControl : NumericUpDown, IDataGridViewEditingControl { // Needed to forward keyboard messages to the child TextBox control. [DllImport("USER32.DLL", CharSet = CharSet.Auto)] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); // The grid that owns this editing control private DataGridView dataGridView; // Stores whether the editing control's value has changed or not private bool valueChanged; // Stores the row index in which the editing control resides private int rowIndex; /// /// Constructor of the editing control class /// public DataGridViewNumericUpDownEditingControl() { // The editing control must not be part of the tabbing loop TabStop = false; } // Beginning of the IDataGridViewEditingControl interface implementation /// /// Property which caches the grid that uses this editing control /// public virtual DataGridView EditingControlDataGridView { get { return dataGridView; } set { dataGridView = value; } } /// /// Property which represents the current formatted value of the editing control /// public virtual object EditingControlFormattedValue { get { return GetEditingControlFormattedValue(DataGridViewDataErrorContexts.Formatting); } set { Text = (string)value; } } /// /// Property which represents the row in which the editing control resides /// public virtual int EditingControlRowIndex { get { return rowIndex; } set { rowIndex = value; } } /// /// Property which indicates whether the value of the editing control has changed or not /// public virtual bool EditingControlValueChanged { get { return valueChanged; } set { valueChanged = value; } } /// /// Property which determines which cursor must be used for the editing panel, /// i.e. the parent of the editing control. /// public virtual Cursor EditingPanelCursor { get { return Cursors.Default; } } /// /// Property which indicates whether the editing control needs to be repositioned /// when its value changes. /// public virtual bool RepositionEditingControlOnValueChange { get { return false; } } /// /// Method called by the grid before the editing control is shown so it can adapt to the /// provided cell style. /// public virtual void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle) { Font = dataGridViewCellStyle.Font; if (dataGridViewCellStyle.BackColor.A < 255) { // The NumericUpDown control does not support transparent back colors Color opaqueBackColor = Color.FromArgb(255, dataGridViewCellStyle.BackColor); BackColor = opaqueBackColor; dataGridView.EditingPanel.BackColor = opaqueBackColor; } else { BackColor = dataGridViewCellStyle.BackColor; } ForeColor = dataGridViewCellStyle.ForeColor; TextAlign = DataGridViewNumericUpDownCell.TranslateAlignment(dataGridViewCellStyle.Alignment); } /// /// Method called by the grid on keystrokes to determine if the editing control is /// interested in the key or not. /// public virtual bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey) { switch (keyData & Keys.KeyCode) { case Keys.Right: { TextBox textBox = Controls[1] as TextBox; if (textBox != null) { // If the end of the selection is at the end of the string, // let the DataGridView treat the key message if (RightToLeft == RightToLeft.No && !(textBox.SelectionLength == 0 && textBox.SelectionStart == textBox.Text.Length) || RightToLeft == RightToLeft.Yes && !(textBox.SelectionLength == 0 && textBox.SelectionStart == 0)) { return true; } } break; } case Keys.Left: { TextBox textBox = Controls[1] as TextBox; if (textBox != null) { // If the end of the selection is at the begining of the string // or if the entire text is selected and we did not start editing, // send this character to the dataGridView, else process the key message if (RightToLeft == RightToLeft.No && !(textBox.SelectionLength == 0 && textBox.SelectionStart == 0) || RightToLeft == RightToLeft.Yes && !(textBox.SelectionLength == 0 && textBox.SelectionStart == textBox.Text.Length)) { return true; } } break; } case Keys.Down: // If the current value hasn't reached its minimum yet, handle the key. Otherwise let // the grid handle it. if (Value > Minimum) { return true; } break; case Keys.Up: // If the current value hasn't reached its maximum yet, handle the key. Otherwise let // the grid handle it. if (Value < Maximum) { return true; } break; case Keys.Home: case Keys.End: { // Let the grid handle the key if the entire text is selected. TextBox textBox = Controls[1] as TextBox; if (textBox != null) { if (textBox.SelectionLength != textBox.Text.Length) { return true; } } break; } case Keys.Delete: { // Let the grid handle the key if the carret is at the end of the text. TextBox textBox = Controls[1] as TextBox; if (textBox != null) { if (textBox.SelectionLength > 0 || textBox.SelectionStart < textBox.Text.Length) { return true; } } break; } } return !dataGridViewWantsInputKey; } /// /// Returns the current value of the editing control. /// public virtual object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context) { bool userEdit = UserEdit; try { // Prevent the Value from being set to Maximum or Minimum when the cell is being painted. UserEdit = (context & DataGridViewDataErrorContexts.Display) == 0; return Value.ToString((ThousandsSeparator ? "N" : "F") + DecimalPlaces.ToString()); } finally { UserEdit = userEdit; } } /// /// Called by the grid to give the editing control a chance to prepare itself for /// the editing session. /// public virtual void PrepareEditingControlForEdit(bool selectAll) { TextBox textBox = Controls[1] as TextBox; if (textBox != null) { if (selectAll) { textBox.SelectAll(); } else { // Do not select all the text, but // position the caret at the end of the text textBox.SelectionStart = textBox.Text.Length; } } } // End of the IDataGridViewEditingControl interface implementation /// /// Small utility function that updates the local dirty state and /// notifies the grid of the value change. /// private void NotifyDataGridViewOfValueChange() { if (!valueChanged) { valueChanged = true; dataGridView.NotifyCurrentCellDirty(true); } } /// /// Listen to the KeyPress notification to know when the value changed, and /// notify the grid of the change. /// protected override void OnKeyPress(KeyPressEventArgs e) { base.OnKeyPress(e); // The value changes when a digit, the decimal separator, the group separator or // the negative sign is pressed. bool notifyValueChange = false; if (char.IsDigit(e.KeyChar)) { notifyValueChange = true; } else { System.Globalization.NumberFormatInfo numberFormatInfo = System.Globalization.CultureInfo.CurrentCulture.NumberFormat; string decimalSeparatorStr = numberFormatInfo.NumberDecimalSeparator; string groupSeparatorStr = numberFormatInfo.NumberGroupSeparator; string negativeSignStr = numberFormatInfo.NegativeSign; if (!string.IsNullOrEmpty(decimalSeparatorStr) && decimalSeparatorStr.Length == 1) { notifyValueChange = decimalSeparatorStr[0] == e.KeyChar; } if (!notifyValueChange && !string.IsNullOrEmpty(groupSeparatorStr) && groupSeparatorStr.Length == 1) { notifyValueChange = groupSeparatorStr[0] == e.KeyChar; } if (!notifyValueChange && !string.IsNullOrEmpty(negativeSignStr) && negativeSignStr.Length == 1) { notifyValueChange = negativeSignStr[0] == e.KeyChar; } } if (notifyValueChange) { // Let the DataGridView know about the value change NotifyDataGridViewOfValueChange(); } } /// /// Listen to the ValueChanged notification to forward the change to the grid. /// protected override void OnValueChanged(EventArgs e) { base.OnValueChanged(e); if (Focused) { // Let the DataGridView know about the value change NotifyDataGridViewOfValueChange(); } } /// /// A few keyboard messages need to be forwarded to the inner textbox of the /// NumericUpDown control so that the first character pressed appears in it. /// protected override bool ProcessKeyEventArgs(ref Message m) { TextBox textBox = Controls[1] as TextBox; if (textBox != null) { SendMessage(textBox.Handle, m.Msg, m.WParam, m.LParam); return true; } else { return base.ProcessKeyEventArgs(ref m); } } } }