Last active
June 18, 2017 20:24
-
-
Save meitinger/5071951a653af5714ee1 to your computer and use it in GitHub Desktop.
[abandoned] Draft for data grid controls for all input types.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Data; | |
using System.Drawing; | |
using System.Drawing.Design; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
using System.Windows.Forms; | |
namespace WindowsFormsApplication1 | |
{ | |
public abstract class DataGridViewTypedColumn<T> : DataGridViewColumn where T : Control, IDataGridViewEditingControl, new() | |
{ | |
private static readonly List<CellProperty> Properties = new List<CellProperty>(); | |
protected abstract class CellProperty | |
{ | |
internal CellProperty() { } | |
internal abstract void Clone(T source, T dest); | |
} | |
/// <summary> | |
/// Wrapper for a property to compensate for missing <c>ref</c>-functionality. | |
/// </summary> | |
/// <typeparam name="U">The property's type.</typeparam> | |
protected sealed class CellProperty<U> : CellProperty | |
{ | |
private readonly PropertyInfo prop; | |
/// <summary> | |
/// Creates a new wrapper around a given property. | |
/// </summary> | |
/// <param name="prop">An expression in the form of <c>control => control.Property</c>.</param> | |
public CellProperty(Expression<Func<T, U>> prop) | |
{ | |
// check that we got a non-indexed property with the proper declaring type | |
if (prop == null) | |
throw new ArgumentNullException("prop"); | |
if | |
( | |
prop.Body.NodeType != ExpressionType.MemberAccess || | |
((MemberExpression)prop.Body).Member.MemberType != MemberTypes.Property || | |
((PropertyInfo)((MemberExpression)prop.Body).Member).GetIndexParameters().Length > 0 || | |
!((PropertyInfo)((MemberExpression)prop.Body).Member).DeclaringType.IsAssignableFrom(typeof(T)) | |
) | |
throw new ArgumentOutOfRangeException("prop"); | |
// store the property info | |
this.prop = (PropertyInfo)((MemberExpression)prop.Body).Member; | |
Properties.Add(this); | |
} | |
internal override void Clone(T source, T dest) | |
{ | |
this.prop.SetValue(dest, this.prop.GetValue(source, null), null); | |
} | |
private void SetValue(Cell cell, U value) | |
{ | |
// set the value | |
this.prop.SetValue(cell.FormattingControl, value, null); | |
// propagate the changes if this isn't a shared row | |
if (cell.RowIndex != -1) | |
{ | |
if (cell.EditingControl != null && cell.RowIndex == cell.EditingControl.EditingControlRowIndex) | |
this.prop.SetValue(cell.EditingControl, value, null); | |
cell.DataGridView.InvalidateCell(cell); | |
} | |
} | |
/// <summary> | |
/// Gets or sets the property of a given column's cell(s). | |
/// </summary> | |
/// <param name="column">The column whose property was queried or stored.</param> | |
/// <returns>The current template value.</returns> | |
/// <exception cref="System.InvalidOperationException">No template was set.</exception> | |
public U this[DataGridViewTypedColumn<T> column] | |
{ | |
get | |
{ | |
// ensure there's a template and get its value | |
var template = (Cell)column.CellTemplate; | |
if (template == null) | |
throw new InvalidOperationException(); | |
return (U)this.prop.GetValue(template.FormattingControl, null); | |
} | |
set | |
{ | |
// check if the template's value differs | |
var template = (Cell)column.CellTemplate; | |
if (template == null) | |
throw new InvalidOperationException(); | |
if (!object.Equals((U)this.prop.GetValue(template.FormattingControl, null), value)) | |
{ | |
// update the template value | |
this.SetValue(template, value); | |
var gridView = column.DataGridView; | |
if (gridView != null) | |
{ | |
// update all other cells and invalidate the column | |
var rows = gridView.Rows; | |
var count = rows.Count; | |
for (int i = 0; i < count; i++) | |
{ | |
var cell = rows.SharedRow(i).Cells[column.Index] as Cell; | |
if (cell != null) | |
this.SetValue(cell, value); | |
} | |
gridView.InvalidateColumn(column.Index); | |
} | |
} | |
} | |
} | |
} | |
private class Cell : DataGridViewTextBoxCell | |
{ | |
private bool formatToString = false; | |
public Cell() | |
{ | |
// create a hidden formatting control | |
this.FormattingControl = new T(); | |
this.FormattingControl.Visible = false; | |
} | |
public override object Clone() | |
{ | |
// clone all properties | |
var clone = (Cell)base.Clone(); | |
foreach (var prop in Properties) | |
prop.Clone(this.FormattingControl, clone.FormattingControl); | |
return clone; | |
} | |
internal T FormattingControl | |
{ | |
get; | |
private set; | |
} | |
internal T EditingControl | |
{ | |
get; | |
private set; | |
} | |
public override Type EditType | |
{ | |
get { return typeof(T); } | |
} | |
public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) | |
{ | |
// initialize and store the control | |
base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle); | |
var editingControl = base.DataGridView.EditingControl as T; | |
foreach (var prop in Properties) | |
prop.Clone(this.FormattingControl, editingControl); | |
editingControl.EditingControlFormattedValue = initialFormattedValue; | |
this.EditingControl = editingControl; | |
} | |
public override void DetachEditingControl() | |
{ | |
// detach and clear the control | |
base.DetachEditingControl(); | |
this.EditingControl = null; | |
} | |
protected override Rectangle GetContentBounds(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex) | |
{ | |
// call the base method but use string formatting while doing so | |
if (this.formatToString) | |
return base.GetContentBounds(graphics, cellStyle, rowIndex); | |
this.formatToString = true; | |
try { return base.GetContentBounds(graphics, cellStyle, rowIndex); } | |
finally { this.formatToString = false; } | |
} | |
protected override Size GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, System.Drawing.Size constraintSize) | |
{ | |
// call the base method but use string formatting while doing so | |
if (this.formatToString) | |
return base.GetPreferredSize(graphics, cellStyle, rowIndex, constraintSize); | |
this.formatToString = true; | |
try { return base.GetPreferredSize(graphics, cellStyle, rowIndex, constraintSize); } | |
finally { this.formatToString = false; } | |
} | |
protected override object GetFormattedValue(object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context) | |
{ | |
// convert the DBNull to null and optionally the value to string | |
var formattedValue = value == DBNull.Value ? null : value; | |
if (this.formatToString) | |
{ | |
this.FormattingControl.EditingControlFormattedValue = formattedValue; | |
formattedValue = this.FormattingControl.Text; | |
} | |
return formattedValue; | |
} | |
public override object ParseFormattedValue(object formattedValue, DataGridViewCellStyle cellStyle, TypeConverter formattedValueTypeConverter, TypeConverter valueTypeConverter) | |
{ | |
// convert null to DBNull | |
return formattedValue ?? DBNull.Value; | |
} | |
protected override void OnDataGridViewChanged() | |
{ | |
// set the formatting control's parent and ensure its handle gets created | |
base.OnDataGridViewChanged(); | |
this.FormattingControl.Parent = base.DataGridView; | |
this.FormattingControl.Visible = true; | |
this.FormattingControl.Visible = false; | |
} | |
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts) | |
{ | |
// call the base method with a string value | |
this.FormattingControl.EditingControlFormattedValue = formattedValue; | |
base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, this.FormattingControl.Text, errorText, cellStyle, advancedBorderStyle, paintParts); | |
} | |
} | |
protected DataGridViewTypedColumn() : base(new Cell()) { } | |
public override DataGridViewCell CellTemplate | |
{ | |
get { return base.CellTemplate; } | |
set | |
{ | |
// ensure a proper template class | |
if (value != null && !(value is Cell)) | |
throw new InvalidCastException(); | |
base.CellTemplate = value; | |
} | |
} | |
} | |
[ToolboxBitmap(typeof(DateTimePicker), "DateTimePicker.bmp")] | |
public class DataGridViewDateTimePickerColumn : DataGridViewTypedColumn<DataGridViewDataTimePickerEditingControl> | |
{ | |
private static readonly CellProperty<DateTimePickerFormat> FormatProperty = new CellProperty<DateTimePickerFormat>(t => t.Format); | |
private static readonly CellProperty<string> CustomFormatProperty = new CellProperty<string>(t => t.CustomFormat); | |
/// <summary> | |
/// Gets or sets the format of the date and time displayed in the cell. | |
/// </summary> | |
[DefaultValue(DateTimePickerFormat.Short)] | |
public DateTimePickerFormat Format | |
{ | |
get { return FormatProperty[this]; } | |
set { FormatProperty[this] = value; } | |
} | |
/// <summary> | |
/// Gets or sets the custom date/time format string. | |
/// </summary> | |
[DefaultValue((string)null), Localizable(true)] | |
public string CustomFormat | |
{ | |
get { return CustomFormatProperty[this]; } | |
set { CustomFormatProperty[this] = value; } | |
} | |
} | |
public class DataGridViewDataTimePickerEditingControl : DateTimePicker, IDataGridViewEditingControl | |
{ | |
private static readonly DateTime SqlMinDate = new DateTime(1753, 1, 1); | |
private static readonly DateTime SqlMaxDate = new DateTime(9998, 12, 31); // 9999 is not supported by the control | |
private static readonly DateTime SqlNullDate = new DateTime(1900, 1, 1); | |
public DataGridViewDataTimePickerEditingControl() | |
{ | |
// activate the check box and set the range | |
base.ShowCheckBox = true; | |
base.MinDate = SqlMinDate; | |
base.MaxDate = SqlMaxDate; | |
} | |
#region common to all | |
public DataGridView EditingControlDataGridView { get; set; } | |
public int EditingControlRowIndex { get; set; } | |
public object EditingControlFormattedValue { get { return this.Value; } set { this.Value = value == null ? (DateTime?)null : Convert.ToDateTime(value); } } | |
public bool EditingControlValueChanged { get; set; } | |
public Cursor EditingPanelCursor { get { return Cursors.Default; } } | |
public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context) { return this.EditingControlFormattedValue; } | |
public bool RepositionEditingControlOnValueChange { get { return false; } } | |
#endregion | |
public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle) | |
{ | |
// set the calender font and colors | |
base.CalendarFont = dataGridViewCellStyle.Font; | |
base.CalendarForeColor = dataGridViewCellStyle.ForeColor; | |
base.CalendarMonthBackground = dataGridViewCellStyle.BackColor; | |
} | |
public bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey) | |
{ | |
switch (keyData & Keys.KeyCode) | |
{ | |
case Keys.Left: | |
case Keys.Up: | |
case Keys.Down: | |
case Keys.Right: | |
case Keys.Home: | |
case Keys.End: | |
case Keys.PageDown: | |
case Keys.PageUp: | |
return true; | |
default: | |
return !dataGridViewWantsInputKey; | |
} | |
} | |
public void PrepareEditingControlForEdit(bool selectAll) | |
{ | |
} | |
public override string Text | |
{ | |
get | |
{ | |
// return an empty string if the date/time is not set | |
return base.Checked ? base.Text : string.Empty; | |
} | |
set | |
{ | |
base.Text = value; | |
} | |
} | |
public new DateTimePickerFormat Format | |
{ | |
get { return base.Format; } | |
set | |
{ | |
base.Format = value; | |
base.ShowUpDown = value == DateTimePickerFormat.Time; | |
} | |
} | |
[Bindable(true)] | |
[RefreshProperties(RefreshProperties.All)] | |
public new DateTime? Value | |
{ | |
get | |
{ // get the appropriate return value | |
if (!base.Checked) | |
return null; | |
switch (base.Format) | |
{ | |
case DateTimePickerFormat.Long: | |
case DateTimePickerFormat.Short: | |
return base.Value.Date; | |
case DateTimePickerFormat.Time: | |
return SqlNullDate + TimeSpan.FromTicks(base.Value.TimeOfDay.Ticks); | |
default: | |
return base.Value; | |
} | |
} | |
set | |
{ | |
// set the (default) value and the check box | |
base.Value = value.HasValue ? value.Value : DateTime.Now; | |
base.Checked = value.HasValue; | |
} | |
} | |
protected override void OnValueChanged(EventArgs eventargs) | |
{ | |
// forward the event and mark the cell as dirty | |
base.OnValueChanged(eventargs); | |
if (this.EditingControlDataGridView != null) | |
{ | |
this.EditingControlValueChanged = true; | |
this.EditingControlDataGridView.NotifyCurrentCellDirty(true); | |
} | |
} | |
} | |
[ToolboxBitmap(typeof(ComboBox), "ComboBox.bmp")] | |
public class DataGridViewComboBoxExColumn : DataGridViewTypedColumn<DataGridViewComboBoxExEditingControl> | |
{ | |
private static readonly CellProperty<object> DataSourceProperty = new CellProperty<object>(t => t.DataSource); | |
private static readonly CellProperty<string> DisplayMemberProperty = new CellProperty<string>(t => t.DisplayMember); | |
private static readonly CellProperty<string> ValueMemberProperty = new CellProperty<string>(t => t.ValueMember); | |
private static readonly CellProperty<string> ExpressionProperty = new CellProperty<string>(t => t.Expression); | |
/// <summary> | |
/// Gets or sets the data source that populates the selections for the combo boxes. | |
/// </summary> | |
[DefaultValue((string)null), AttributeProvider(typeof(IListSource)), RefreshProperties(RefreshProperties.Repaint)] | |
public object DataSource | |
{ | |
get { return DataSourceProperty[this]; } | |
set { DataSourceProperty[this] = value; } | |
} | |
/// <summary> | |
/// Gets or sets a string that specifies the property or column from which to retrieve strings for display in the combo boxes. | |
/// </summary> | |
[DefaultValue(""), Editor("System.Windows.Forms.Design.DataMemberFieldEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor)), TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] | |
public string DisplayMember | |
{ | |
get { return DisplayMemberProperty[this]; } | |
set { DisplayMemberProperty[this] = value; } | |
} | |
/// <summary> | |
/// Gets or sets a string that specifies the property or column from which to get values that correspond to the selections in the drop-down list. | |
/// </summary> | |
[DefaultValue(""), Editor("System.Windows.Forms.Design.DataMemberFieldEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor)), TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] | |
public string ValueMember | |
{ | |
get { return ValueMemberProperty[this]; } | |
set { ValueMemberProperty[this] = value; } | |
} | |
/// <summary> | |
/// Gets or sets the expression used to filter rows, calculate the values in a column, or create an aggregate column. | |
/// </summary> | |
[DefaultValue("")] | |
public string Expression | |
{ | |
get { return ExpressionProperty[this]; } | |
set { ExpressionProperty[this] = value; } | |
} | |
} | |
public class DataGridViewComboBoxExEditingControl : ComboBox, IDataGridViewEditingControl | |
{ | |
private class DataExpression | |
{ | |
private static readonly Type Type; | |
private static readonly ConstructorInfo Constructor; | |
private static readonly MethodInfo EvaluateMethod; | |
private static readonly MethodInfo BindMethod; | |
private static readonly PropertyInfo ExpressionProperty; | |
static DataExpression() | |
{ | |
// reflect the underlying DataExpression type | |
Type = Assembly.GetAssembly(typeof(DataTable)).GetType("System.Data.DataExpression"); | |
Constructor = Type.GetConstructor(BindingFlags.CreateInstance | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(DataTable), typeof(string), typeof(Type) }, null); | |
BindMethod = Type.GetMethod("Bind", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(DataTable) }, null); | |
EvaluateMethod = Type.GetMethod("Evaluate", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(DataRow), typeof(DataRowVersion) }, null); | |
ExpressionProperty = Type.GetProperty("Expression", BindingFlags.Instance | BindingFlags.NonPublic, null, typeof(string), Type.EmptyTypes, null); | |
} | |
private object expression; | |
private DataTable boundTo; | |
public DataExpression(string expression) | |
{ | |
if (expression == null) | |
throw new ArgumentNullException("expression"); | |
try { this.expression = Constructor.Invoke(new object[] { null, expression, typeof(string) }); } | |
catch (TargetInvocationException e) { throw e.InnerException; } | |
} | |
public string Evaluate(DataRowView dataRowView) | |
{ | |
// check the input, bind the expression if necessary and evaluate it | |
if (dataRowView == null) | |
throw new ArgumentNullException("dataRow"); | |
if (this.boundTo != dataRowView.Row.Table) | |
{ | |
BindMethod.Invoke(this.expression, new object[] { dataRowView.Row.Table }); | |
this.boundTo = dataRowView.Row.Table; | |
} | |
return dataRowView.Row.HasVersion(dataRowView.RowVersion) ? (string)EvaluateMethod.Invoke(this.expression, new object[] { dataRowView.Row, dataRowView.RowVersion }) : null; | |
} | |
public string Value | |
{ | |
get { return (string)ExpressionProperty.GetValue(this.expression, null); } | |
} | |
} | |
private DataExpression expression = null; | |
public DataGridViewComboBoxExEditingControl() | |
{ | |
// set the proper defaults | |
base.AutoCompleteMode = AutoCompleteMode.Suggest; | |
base.AutoCompleteSource = AutoCompleteSource.ListItems; | |
base.DropDownStyle = ComboBoxStyle.DropDownList; | |
base.FormattingEnabled = false; | |
} | |
#region common to all | |
public DataGridView EditingControlDataGridView { get; set; } | |
public int EditingControlRowIndex { get; set; } | |
public object EditingControlFormattedValue { get { return this.SelectedIndex == -1 ? null : this.SelectedValue; } set { if (value == null) this.SelectedIndex = -1; else this.SelectedValue = value; } } | |
public bool EditingControlValueChanged { get; set; } | |
public Cursor EditingPanelCursor { get { return Cursors.Default; } } | |
public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context) { return this.EditingControlFormattedValue; } | |
public bool RepositionEditingControlOnValueChange { get { return false; } } | |
#endregion | |
public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle) | |
{ | |
// set the font, colors and format provider | |
this.Font = dataGridViewCellStyle.Font; | |
this.ForeColor = dataGridViewCellStyle.ForeColor; | |
this.BackColor = dataGridViewCellStyle.BackColor; | |
} | |
public bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey) | |
{ | |
switch (keyData & Keys.KeyCode) | |
{ | |
case Keys.Up: | |
case Keys.Down: | |
case Keys.Delete: | |
// always handle UP, DOWN and DEL | |
return true; | |
case Keys.Enter: | |
case Keys.Escape: | |
// only handle ENTER and ESC if the list is shown | |
return base.DroppedDown; | |
default: | |
return !dataGridViewWantsInputKey; | |
} | |
} | |
public void PrepareEditingControlForEdit(bool selectAll) | |
{ | |
// drop down the list for convenience | |
if (selectAll) | |
base.DroppedDown = true; | |
} | |
protected override void OnVisibleChanged(EventArgs e) | |
{ | |
// hide the list if the box is hidden | |
if (!base.Visible && base.DroppedDown) | |
base.DroppedDown = false; | |
} | |
protected override void OnKeyUp(KeyEventArgs e) | |
{ | |
// clear the selection if DEL is pressed | |
base.OnKeyUp(e); | |
if (e.KeyCode == Keys.Delete && e.Modifiers == 0) | |
{ | |
// close the dropped down list if necessary | |
if (base.DroppedDown) | |
base.DroppedDown = false; | |
this.SelectedIndex = -1; | |
} | |
} | |
protected override bool AllowSelection | |
{ | |
get { return false; } | |
} | |
protected override void OnFormat(ListControlConvertEventArgs e) | |
{ | |
base.OnFormat(e); | |
e.Value = this.expression.Evaluate((DataRowView)e.ListItem); | |
} | |
protected override void OnDataSourceChanged(EventArgs e) | |
{ | |
base.OnDataSourceChanged(e); | |
} | |
public string Expression | |
{ | |
get { return this.expression == null ? string.Empty : this.expression.Value; } | |
set | |
{ | |
var isEmpty = string.IsNullOrEmpty(value); | |
this.expression = isEmpty ? null : new DataExpression(value); | |
this.FormattingEnabled = !isEmpty; | |
} | |
} | |
protected override void OnSelectedValueChanged(EventArgs eventargs) | |
{ | |
// forward the event and mark the cell as dirty | |
base.OnSelectedValueChanged(eventargs); | |
if (this.EditingControlDataGridView != null) | |
{ | |
this.EditingControlValueChanged = true; | |
this.EditingControlDataGridView.NotifyCurrentCellDirty(true); | |
} | |
} | |
} | |
[ToolboxBitmap(typeof(NumericUpDown), "NumericUpDown.bmp")] | |
public class DataGridViewNumericUpDownColumn : DataGridViewTypedColumn<DataGridViewNumericUpDownEditingControl> | |
{ | |
private static readonly CellProperty<int> PrecisionProperty = new CellProperty<int>(t => t.Precision); | |
private static readonly CellProperty<int> ScaleProperty = new CellProperty<int>(t => t.Scale); | |
private static readonly CellProperty<bool> ThousandsSeparatorProperty = new CellProperty<bool>(t => t.ThousandsSeparator); | |
/// <summary> | |
/// Gets or sets the maximum total number of decimal digits that will be stored, both to the left and to the right of the decimal point. | |
/// </summary> | |
/// <exception cref="System.ArgumentOutOfRangeException">The value is smaller than 1 or <see cref="Scale"/> or larger than 38.</exception> | |
[DefaultValue(18)] | |
public int Precision | |
{ | |
get { return PrecisionProperty[this]; } | |
set { PrecisionProperty[this] = value; } | |
} | |
/// <summary> | |
/// Gets or sets the number of decimal digits that will be stored to the right of the decimal point. | |
/// </summary> | |
/// <exception cref="System.ArgumentOutOfRangeException">The value is smaller than 0 or larger than <see cref="Precision"/>.</exception> | |
[DefaultValue(0)] | |
public int Scale | |
{ | |
get { return ScaleProperty[this]; } | |
set { ScaleProperty[this] = value; } | |
} | |
/// <summary> | |
/// Gets or sets a value indicating whether a thousands separator is displayed in the spin box (also known as an up-down control) when appropriate. | |
/// </summary> | |
[DefaultValue(false)] | |
public bool ThousandsSeparator | |
{ | |
get { return ThousandsSeparatorProperty[this]; } | |
set { ThousandsSeparatorProperty[this] = value; } | |
} | |
} | |
public class DataGridViewNumericUpDownEditingControl : NumericUpDown, IDataGridViewEditingControl | |
{ | |
private bool isNull = true; | |
private bool ignoreTextBoxKeyDown = false; | |
private bool ignoreKeyDown = false; | |
private int precision = 18; | |
#region common to all | |
public DataGridView EditingControlDataGridView { get; set; } | |
public int EditingControlRowIndex { get; set; } | |
public object EditingControlFormattedValue { get { return this.Value; } set { this.Value = value == null ? (decimal?)null : Convert.ToDecimal(value); } } | |
public bool EditingControlValueChanged { get; set; } | |
public Cursor EditingPanelCursor { get { return Cursors.Default; } } | |
public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context) { return this.EditingControlFormattedValue; } | |
public bool RepositionEditingControlOnValueChange { get { return false; } } | |
#endregion | |
public DataGridViewNumericUpDownEditingControl() | |
{ | |
// set the initial limits | |
this.UpdateLimits(); | |
} | |
public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle) | |
{ | |
// set the font, colors and format provider | |
this.Font = dataGridViewCellStyle.Font; | |
this.ForeColor = dataGridViewCellStyle.ForeColor; | |
this.BackColor = dataGridViewCellStyle.BackColor; | |
} | |
public bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey) | |
{ | |
switch (keyData & Keys.KeyCode) | |
{ | |
case Keys.Left: | |
case Keys.Up: | |
case Keys.Down: | |
case Keys.Right: | |
return true; | |
default: | |
return !dataGridViewWantsInputKey; | |
} | |
} | |
public void PrepareEditingControlForEdit(bool selectAll) | |
{ | |
// select all if necessary | |
if (selectAll) | |
base.Select(0, int.MaxValue); | |
} | |
protected override void UpdateEditText() | |
{ | |
// end any pending editing and set the text | |
if (base.UserEdit) | |
this.EndUserEdit(); | |
base.ChangingText = true; | |
this.Text = this.isNull ? string.Empty : base.Value.ToString((base.ThousandsSeparator ? "N" : "F") + base.DecimalPlaces); | |
} | |
private void EndUserEdit() | |
{ | |
// parse the value the user has entered | |
try | |
{ | |
if (!string.IsNullOrEmpty(this.Text)) | |
{ | |
var v = default(decimal); | |
if (decimal.TryParse(this.Text, out v)) | |
{ | |
// round, constrain and set the value | |
v = decimal.Round(v, base.DecimalPlaces); | |
if (v < base.Minimum) | |
v = base.Minimum; | |
if (v > base.Maximum) | |
v = base.Maximum; | |
this.Value = v; | |
} | |
} | |
else | |
this.Value = null; | |
} | |
finally | |
{ | |
base.UserEdit = false; | |
} | |
} | |
protected override void ValidateEditText() | |
{ | |
// change the behavior to include the possibility for null values | |
this.EndUserEdit(); | |
this.UpdateEditText(); | |
} | |
public override void DownButton() | |
{ | |
// always end the user edit before forwarding the call | |
if (base.UserEdit) | |
this.EndUserEdit(); | |
base.DownButton(); | |
} | |
public override void UpButton() | |
{ | |
// always end the user edit before forwarding the call | |
if (base.UserEdit) | |
this.EndUserEdit(); | |
base.UpButton(); | |
} | |
protected override void OnKeyPress(KeyPressEventArgs e) | |
{ | |
// avoid recursion from this method | |
if (this.ignoreKeyDown) | |
return; | |
base.OnKeyPress(e); | |
// only handle events originating from outside the textbox | |
if (!this.ignoreTextBoxKeyDown) | |
{ | |
// check if the pressed key is valid | |
this.ignoreKeyDown = true; | |
try { base.OnTextBoxKeyPress(this, e); } | |
finally { this.ignoreKeyDown = false; } | |
// if so, set the textbox value | |
if (!e.Handled) | |
{ | |
// prevent parsing if the value isn't yet parsable | |
var text = e.KeyChar.ToString(); | |
var dummy = default(decimal); | |
this.ChangingText = !decimal.TryParse(text, out dummy); | |
this.Text = text; | |
this.Select(1, 0); | |
} | |
} | |
} | |
protected override void OnTextBoxKeyPress(object source, KeyPressEventArgs e) | |
{ | |
// ignore any nested OnKeyPress | |
if (this.ignoreTextBoxKeyDown) | |
base.OnTextBoxKeyPress(source, e); | |
this.ignoreTextBoxKeyDown = true; | |
try { base.OnTextBoxKeyPress(source, e); } | |
finally { this.ignoreTextBoxKeyDown = false; } | |
} | |
private void UpdateLimits() | |
{ | |
// set the limits according to precision and scale | |
var limit = this.Precision == this.Scale ? decimal.One : (new decimal(Math.Pow(10, this.Precision - this.Scale)) - decimal.One); | |
base.Minimum = -limit; | |
base.Maximum = limit; | |
} | |
public int Precision | |
{ | |
get { return this.precision; } | |
set | |
{ | |
if (value < 1 || value > 38 || value < this.Scale) | |
throw new ArgumentOutOfRangeException("Precision"); | |
this.precision = value; | |
this.UpdateLimits(); | |
} | |
} | |
public new int Scale | |
{ | |
get { return base.DecimalPlaces; } | |
set | |
{ | |
if (value < 0 || value > this.Precision) | |
throw new ArgumentOutOfRangeException("Scale"); | |
base.DecimalPlaces = value; | |
this.UpdateLimits(); | |
} | |
} | |
[Bindable(true)] | |
public new decimal? Value | |
{ | |
get | |
{ | |
// finish any editing | |
if (base.UserEdit) | |
this.ValidateEditText(); | |
// either return null or the value | |
if (this.isNull) | |
return null; | |
return base.Value; | |
} | |
set | |
{ | |
// cancel any editing | |
if (base.UserEdit) | |
base.UserEdit = false; | |
// set the value and the null flag | |
base.Value = value == null ? decimal.Zero : (decimal)value; | |
if (this.isNull != (value == null)) | |
{ | |
this.isNull = value == null; | |
this.OnValueChanged(EventArgs.Empty); | |
this.UpdateEditText(); | |
} | |
} | |
} | |
protected override void OnValueChanged(EventArgs eventargs) | |
{ | |
// forward the event and mark the cell as dirty | |
base.OnValueChanged(eventargs); | |
if (this.EditingControlDataGridView != null) | |
{ | |
this.EditingControlValueChanged = true; | |
this.EditingControlDataGridView.NotifyCurrentCellDirty(true); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment