Artículos anteriores:
Windows Forms. DataGridView. Columna para números con decimales (I)
Una vez definida la clase para la definición de la columna aún nos quedaría implementar la lógica de la clase
NumericGridCell que se encargará de controlar los caracteres introducidos por el usuario y el formato en el que se mostrarán los números en la celda.
En primer lugar voy a crear el constructor, que simplemente llamará al constructor de la clase base, y una propiedad privada de sólo lectura que devolverá el formato de número definido en la columna.
public class NumericGridCell: DataGridViewTextBoxCell
{
public NumericGridCell() : base() { }
private NumberFormatInfo NumberFormat
{
get
{
return ((NumericGridColumn)OwningColumn).NumberFormat;
}
}
}
Public Class NumericGridCell
Inherits DataGridViewTextBoxCell
Public Sub New()
MyBase.New()
End Sub
Private ReadOnly Property NumberFormat As NumberFormatInfo
Get
Return CType(OwningColumn, NumericGridColumn).NumberFormat
End Get
End Property
End Class
En la clase
NumericGridCell voy a sobreescribir la propiedad
ValueType para especificar que el tipo de los valores de la celda es
decimal? (el que sea un tipo de datos que acepte nulos nos permitirá diferenciar entre un valor 0 y un valor vacío), y el método
InitializeEditingControl para añadir un controlador de evento al evento
KeyPress del control de edición de la celda.
El controlador de este evento (el método
NumericCell_KeyPress) va a ser el responsable de controlar que los caracteres introducidos son válidos. Si se trata de un carácter numérico se comprueba que el número de decimales no exceda del indicado en la propiedad
DecimalDigits de la columna. Si se trata del carácter definido como separador decimal en la propiedad
DecimalSeparator de la columna, se comprueba que no se haya introducido anteriormente. En caso contrario establece la propiedad
Handled del objeto
KeyPressEventArgs a
true para anular el tratamiento de la tecla pulsada.
public override Type ValueType
{
get { return typeof(decimal?); }
}
public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
{
base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
Control ctl = DataGridView.EditingControl;
ctl.KeyPress += new KeyPressEventHandler(NumericCell_KeyPress);
}
private void NumericCell_KeyPress(object sender, KeyPressEventArgs e)
{
DataGridViewCell currentCell = ((IDataGridViewEditingControl)sender).EditingControlDataGridView.CurrentCell;
if (!(currentCell is NumericGridCell)) return;
DataGridViewTextBoxEditingControl ctl = (DataGridViewTextBoxEditingControl)sender;
if (char.IsDigit(e.KeyChar))
{
int separatorPosition = ctl.Text.IndexOf(NumberFormat.NumberDecimalSeparator);
if (separatorPosition >= 0 && ctl.SelectionStart>separatorPosition)
{
int currentDecimals = ctl.Text.Length - separatorPosition
- NumberFormat.NumberDecimalSeparator.Length - ctl.SelectionLength;
if (currentDecimals >= NumberFormat.NumberDecimalDigits)
e.Handled = true;
}
}
else if (e.KeyChar.ToString().Equals(NumberFormat.NumberDecimalSeparator))
{
e.Handled = ctl.Text.IndexOf(NumberFormat.NumberDecimalSeparator) >= 0;
}
else
{
e.Handled = !char.IsControl(e.KeyChar);
}
}
Public Overrides ReadOnly Property ValueType As Type
Get
Return GetType(Decimal?)
End Get
End Property
Public Overrides Sub InitializeEditingControl(rowIndex As Integer, initialFormattedValue As Object, dataGridViewCellStyle As DataGridViewCellStyle)
MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)
Dim ctl As Control = DataGridView.EditingControl
AddHandler ctl.KeyPress, AddressOf NumericCell_KeyPress
End Sub
Private Sub NumericCell_KeyPress(sender As Object, e As KeyPressEventArgs)
Dim currentCell As DataGridViewCell =
CType(sender, IDataGridViewEditingControl).EditingControlDataGridView.CurrentCell
If TypeOf currentCell IsNot NumericGridCell Then Return
Dim ctl As DataGridViewTextBoxEditingControl = CType(sender, DataGridViewTextBoxEditingControl)
If Char.IsDigit(e.KeyChar) Then
Dim separatorPosition As Integer = ctl.Text.IndexOf(NumberFormat.NumberDecimalSeparator)
If separatorPosition >= 0 AndAlso ctl.SelectionStart > separatorPosition Then
Dim currentDecimals = ctl.Text.Length - separatorPosition _
- NumberFormat.NumberDecimalSeparator.Length - ctl.SelectionLength
If currentDecimals >= NumberFormat.NumberDecimalDigits Then
e.Handled = True
End If
End If
ElseIf e.KeyChar.ToString().Equals(NumberFormat.NumberDecimalSeparator) Then
e.Handled = ctl.Text.IndexOf(NumberFormat.NumberDecimalSeparator) >= 0
Else
e.Handled = Not Char.IsControl(e.KeyChar)
End If
End Sub
A continuación sobrescribiré la propiedad
DefaultNewRowValue para asegurarme de que el valor devuelto es un valor válido. La propiedad devolverá
null si no se ha definido un valor decimal válido como valor por defecto.
public override object DefaultNewRowValue
{
get
{
object defaultValue = base.DefaultNewRowValue;
if (defaultValue is decimal)
return defaultValue;
else
return null;
}
}
Public Overrides ReadOnly Property DefaultNewRowValue As Object
Get
Dim defaultValue As Object = MyBase.DefaultNewRowValue
If TypeOf defaultValue Is Decimal Then
Return defaultValue
Else
Return Nothing
End If
End Get
End Property
Por último, y para que el valor de la celda se represente de acuerdo al formato indicado en las propiedades de la columna, sobrescribiré
GetInheritedStyle para establecer la propiedad
FormatProvider del objeto
DataGridViewCellSytle devuelto al formato definido en la propiedad privada
NumberFormat. Y el método
GetFormattedValue para que devuelva el valor de la celda correctamente formateado:
public override DataGridViewCellStyle GetInheritedStyle(DataGridViewCellStyle inheritedCellStyle, int rowIndex, bool includeColors)
{
DataGridViewCellStyle style= base.GetInheritedStyle(inheritedCellStyle, rowIndex, includeColors);
style.FormatProvider = NumberFormat;
return style;
}
protected override object GetFormattedValue(object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)
{
if (value == null)
return null;
else
return ((decimal)value).ToString(cellStyle.FormatProvider);
}
Public Overrides Function GetInheritedStyle(inheritedCellStyle As DataGridViewCellStyle, rowIndex As Integer, includeColors As Boolean) As DataGridViewCellStyle
Dim style As DataGridViewCellStyle = MyBase.GetInheritedStyle(inheritedCellStyle, rowIndex, includeColors)
style.FormatProvider = NumberFormat
Return style
End Function
Protected Overrides Function GetFormattedValue(value As Object, rowIndex As Integer, ByRef cellStyle As DataGridViewCellStyle, valueTypeConverter As TypeConverter, formattedValueTypeConverter As TypeConverter, context As DataGridViewDataErrorContexts) As Object
If value Is Nothing Then
Return Nothing
Else
Return CType(value, Decimal).ToString(cellStyle.FormatProvider)
End If
End Function
Para probar la nueva columna simplemente deberemos añadir una nueva columna a un control
DataGridView seleccionando
NumericGridColumn como tipo de columna.
Dentro de las propiedades de la columna encontraremos las dos nuevas propiedades:
Al ejecutar comprobaremos que la columna nos permite introducir valores decimales con el separador decimal y el número de decimales indicados:
El código completo de este artículo, tanto en C# como en Visual Basic, está disponible en:
Códigos de muestra - Ejemplos MSDN
Este comentario ha sido eliminado por el autor.
ResponderEliminarEste comentario ha sido eliminado por el autor.
ResponderEliminarHola Assier, soy yo de nuevo despues de mucho tiempo, este ejemplo era lo que estaba esperando de hace mucho tiempo, se ve realmente bueno, lo único que no me sirvió, me muestra el grid, pro al introducir los números, no me pone el punto decimal, estoy en VB 2015, proyecto de inicio es Grid test.
ResponderEliminarMe puedes ayudar ?
Gracias
excelente aporte, tenia tiempo tratando de resolver una situacion con estas caracteristicas.
ResponderEliminarHola como podria hacer una columna para direccion IP ejemplo 253.255.255.255
ResponderEliminar