El
DataGridView es un control con funcionalidad básica que permite una gran extensibilidad. En este artículo mostraré como crear una columna para datos de tipo fecha que utilice un
DateTimePicker como editor.
Para crear cualquier columna personalizada en un
DataGridView básicamente deberemos trabajar con 3 clases.
Una clase que implemente la interfaz
IDataGridViewEditingControl que define el control que utilizaremos para editar los datos.
Una clase que herede de
DataGridViewCell que define la plantilla de las celdas de la columna y que se encargará de inicializar el control de edición y de obtener, devolver y formatear los valores.
Una clase que herede de
DataGridViewColumn que representará la columna y utilizará la clase de celda como plantilla.
Creando el proyecto de ejemplo
Voy a crear un nuevo proyecto GridExtension del tipo Biblioteca de Clases.
Al proyecto le agregaré una referencia al ensamblado System.Windows.Forms y System.Drawing.
Al proyecto le voy a añadir un único archivo DateTimeGridColumn.cs/DateTimeGridColumn.vb en la que incluiré las tres clases necesarias para definir la columna.
Creando el editor de fechas
Para generar el control de edición he creado una clase
DateTimeEditingControl que hereda de la clase
DateTimePicker (control que utilizaré para editar los valores de las celdas) y que implementa la interfaz
IDataGridViewEditingControl.
class DateTimeEditingControl : DateTimePicker, IDataGridViewEditingControl
{
private DataGridView grid;
private bool valueChanged;
public DateTimeEditingControl()
{
this.Format = DateTimePickerFormat.Custom;
}
protected override void OnValueChanged(EventArgs eventargs)
{
base.OnValueChanged(eventargs);
SendToGridValueChanged();
}
private void SendToGridValueChanged()
{
valueChanged = true;
if (grid != null)
grid.NotifyCurrentCellDirty(true);
}
#region Miembros de IDataGridViewEditingControl
public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)
{
this.Font = dataGridViewCellStyle.Font;
this.CalendarForeColor = dataGridViewCellStyle.ForeColor;
this.CalendarMonthBackground = dataGridViewCellStyle.BackColor;
}
public DataGridView EditingControlDataGridView
{
get { return grid; }
set { grid = value; }
}
public object EditingControlFormattedValue
{
get
{
return this.Value.ToString(this.CustomFormat);
}
set
{
try { this.Value = DateTime.Parse((string)value); }
catch { this.Value = DateTime.Now; }
SendToGridValueChanged();
}
}
public int EditingControlRowIndex { get; set; }
public bool EditingControlValueChanged
{
get { return valueChanged; }
set { valueChanged = value; }
}
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 Cursor EditingPanelCursor
{
get { return base.Cursor; }
}
public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context)
{
return EditingControlFormattedValue;
}
public void PrepareEditingControlForEdit(bool selectAll)
{
}
public bool RepositionEditingControlOnValueChange
{
get { return false; }
}
#endregion
}
Class DateTimeEditingControl
Inherits DateTimePicker
Implements IDataGridViewEditingControl
Private grid As DataGridView
Private valChanged As Boolean
Public Sub New()
Me.Format = DateTimePickerFormat.Custom
End Sub
Protected Overrides Sub OnValueChanged(eventargs As EventArgs)
MyBase.OnValueChanged(eventargs)
SendToGridValueChanged()
End Sub
Private Sub SendToGridValueChanged()
valChanged = True
If grid IsNot Nothing Then
grid.NotifyCurrentCellDirty(True)
End If
End Sub
#Region "Miembros de IDataGridViewEditingControl"
Public Sub ApplyCellStyleToEditingControl(dataGridViewCellStyle As DataGridViewCellStyle) _
Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
Me.Font = dataGridViewCellStyle.Font
Me.CalendarForeColor = dataGridViewCellStyle.ForeColor
Me.CalendarMonthBackground = dataGridViewCellStyle.BackColor
End Sub
Public Property EditingControlDataGridView As DataGridView _
Implements IDataGridViewEditingControl.EditingControlDataGridView
Get
Return grid
End Get
Set(value As DataGridView)
grid = value
End Set
End Property
Public Property EditingControlFormattedValue As Object _
Implements IDataGridViewEditingControl.EditingControlFormattedValue
Get
Return Me.Value.ToString(Me.CustomFormat)
End Get
Set(value As Object)
Try
Me.Value = DateTime.Parse(CType(value, String))
Catch ex As Exception
Me.Value = DateTime.Now
End Try
SendToGridValueChanged()
End Set
End Property
Public Property EditingControlRowIndex As Integer _
Implements IDataGridViewEditingControl.EditingControlRowIndex
Public Property EditingControlValueChanged As Boolean _
Implements IDataGridViewEditingControl.EditingControlValueChanged
Get
Return valChanged
End Get
Set(value As Boolean)
valChanged = value
End Set
End Property
Public Function EditingControlWantsInputKey(keyData As Keys, dataGridViewWantsInputKey As Boolean) As Boolean _
Implements IDataGridViewEditingControl.EditingControlWantsInputKey
Select Case keyData And Keys.KeyCode
Case Keys.Left, Keys.Up, Keys.Down, Keys.Right, _
Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp
Return True
Case Else
Return Not dataGridViewWantsInputKey
End Select
End Function
Public ReadOnly Property EditingPanelCursor As Cursor _
Implements IDataGridViewEditingControl.EditingPanelCursor
Get
Return MyBase.Cursor
End Get
End Property
Public Function GetEditingControlFormattedValue(context As DataGridViewDataErrorContexts) As Object _
Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
Return EditingControlFormattedValue
End Function
Public Sub PrepareEditingControlForEdit(selectAll As Boolean) _
Implements IDataGridViewEditingControl.PrepareEditingControlForEdit
End Sub
Public ReadOnly Property RepositionEditingControlOnValueChange As Boolean _
Implements IDataGridViewEditingControl.RepositionEditingControlOnValueChange
Get
Return False
End Get
End Property
#End Region
End Class
Esta clase contiene la mayor parte del código del ejemplo ya que implementa la lógica de edición. Así que, como diría Jack el Destripador: vamos por partes.
En el constructor establecemos la propiedad
Format del
DateTimePicker a
DateTimePickerFormat.Custom ya que estableceremos el formato de la fecha a partir del formato definido en la columna.
Sobrescribimos el método
OnValueChanged para notificar al
DataGridView que se ha modificado el contenido de la celda a través del método
NotifyCurrentCellDirty.
A continuación implementamos la interfaz
IDataGridViewEditingControl.
En el método
ApplyCellStyleToEditingControl trasladamos el estilo definido para la celda a las propiedades del
DateTimePicker.
En la propiedad
EditingControlFormattedValue se realiza la conversión entre el valor tipo
DateTime de la celda y el
string a visualizar.
En el método
EditingControlWantsInputKey se establecen las teclas que necesita gestionar el control
DateTimePicker durante la edición del valor.
Definiendo el tipo de celda
A continuación he definido la clase
DateTimeCell que hereda de
DataGridViewTextBoxCell para utilizar como plantilla de celda en la columna:
public class DateTimeCell: DataGridViewTextBoxCell
{
public DateTimeCell() : base() { }
public override Type EditType {
get { return typeof(DateTimeEditingControl); }
}
public override Type ValueType
{
get { return typeof(DateTime); }
}
public override object DefaultNewRowValue
{
get
{
object defaultValue = base.DefaultNewRowValue;
if (defaultValue is DateTime)
return defaultValue;
else
return DateTime.Now;
}
}
public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
{
base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
DateTimeEditingControl ctl = DataGridView.EditingControl as DateTimeEditingControl;
try
{
if (this.Value == null)
ctl.Value = (DateTime)this.DefaultNewRowValue;
else
ctl.Value = (DateTime)this.Value;
}
catch (Exception)
{
ctl.Value = (DateTime)this.DefaultNewRowValue;
}
if (dataGridViewCellStyle.Format.Length == 1)
{
string[] patterns = DateTimeFormatInfo.CurrentInfo.GetAllDateTimePatterns(dataGridViewCellStyle.Format.ToCharArray()[0]);
if (patterns.Length > 0)
ctl.CustomFormat = patterns[0].ToString();
else
ctl.CustomFormat = dataGridViewCellStyle.Format;
}
else
ctl.CustomFormat = dataGridViewCellStyle.Format;
}
}
Public Class DateTimeCell
Inherits DataGridViewTextBoxCell
Public Sub New()
MyBase.New()
End Sub
Public Overrides ReadOnly Property EditType As Type
Get
Return GetType(DateTimeEditingControl)
End Get
End Property
Public Overrides ReadOnly Property ValueType As Type
Get
Return GetType(DateTime)
End Get
End Property
Public Overrides ReadOnly Property DefaultNewRowValue As Object
Get
Dim defaultValue As Object = MyBase.DefaultNewRowValue
If TypeOf defaultValue Is DateTime Then
Return defaultValue
Else
Return DateTime.Now
End If
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 DateTimeEditingControl = CType(DataGridView.EditingControl, DateTimeEditingControl)
Try
If Me.Value = Nothing Then
ctl.Value = CType(Me.DefaultNewRowValue, DateTime)
Else
ctl.Value = CType(Me.Value, DateTime)
End If
Catch ex As Exception
ctl.Value = CType(Me.DefaultNewRowValue, DateTime)
End Try
If dataGridViewCellStyle.Format.Length = 1 Then
Dim patterns As String() = DateTimeFormatInfo.CurrentInfo.GetAllDateTimePatterns( _
dataGridViewCellStyle.Format.ToCharArray()(0))
If patterns.Length > 0 Then
ctl.CustomFormat = patterns(0).ToString()
Else
ctl.CustomFormat = dataGridViewCellStyle.Format
End If
Else
ctl.CustomFormat = dataGridViewCellStyle.Format
End If
End Sub
End Class
Las propiedades
EditType y
ValueType definen el tipo del control a utilizar para la edición de la celda y el tipo del valor de la celda respectivamente.
He sobrescrito el método
InitializeEditingControl para establecer el valor y el formato del control
DateTimePicker.
Definiendo el tipo de columna
Finalmente defino la clase
DateTimeGridColumn que hereda de
DataGridViewColumn y que define el tipo de columna que asignaremos a las columnas de tipo fecha del
DataGridView.
public class DateTimeGridColumn: DataGridViewColumn
{
public DateTimeGridColumn() : base(new DateTimeCell()) { }
public override DataGridViewCell CellTemplate
{
get
{
return base.CellTemplate;
}
set
{
if (value != null &&
!value.GetType().IsAssignableFrom(typeof(DateTimeCell)))
throw new InvalidCastException("Debe especificar una instancia de DateTimeCell");
base.CellTemplate = value;
}
}
}
Public Class DateTimeGridColumn
Inherits DataGridViewColumn
Public Sub New()
MyBase.New(New DateTimeCell())
End Sub
Public Overrides Property CellTemplate As DataGridViewCell
Get
Return MyBase.CellTemplate
End Get
Set(value As DataGridViewCell)
If value IsNot Nothing AndAlso _
Not value.GetType().IsAssignableFrom(GetType(DateTimeCell)) Then
Throw New InvalidCastException("Debe especificar una instancia de DateTimeCell")
End If
MyBase.CellTemplate = value
End Set
End Property
End Class
En el constructor de la columna llamo al constructor de la clase base
DataGridViewColumn pasándole una nueva instancia de
DateTimeCell para usar como plantilla de la celda.
Por otra lado simplemente he sobrescrito la propiedad
CellTemplate para asegurarme de que la plantilla de celda asignada a la columna sea del tipo
DateTimeCell.
Probando la columna de fechas
Con esto ya estaría creado el nuevo tipo de columna. Para probarlo voy a añadir a la solución un nuevo proyecto GridTest del tipo Aplicación de Windows Forms.
Al proyecto GridTest le he añadido una referencia al proyecto GridExtension.
En el formulario que Visual Studio crea por defecto le he añadido un control DataGridView al que le he agregado dos columnas: una columna Texto del tipo DataGridViewTextBoxColumn y una columna Fecha del tipo DateTimeGridColumn.
Para seleccionar el formato de fecha a utilizar en la columna he editado la columna Fecha y, en la ventana de edición de la propiedad DefaultCellStyle he establecido el valor de Format a g para establecer el formato de fecha general: fecha corta y hora corta.
Ahora ya podemos arrancar la aplicación para comprobar el funcionamiento del nuevo tipo de columna.
Muchas gracias por tu aporte, me ha servido bastante :)
ResponderEliminarhola que tal he estado viendo tus articulos y la verdad es que son muy buenos solo queria preguntarte si se le pude poner como un simbolo predeterminado y cualquier dato que se ponga salgacon ese simolo
ResponderEliminaragradeceria tu ayuda
Buenas noches.
ResponderEliminarGracias Ingeniero , acabo de acomplar las librerias de classes a mi datagridview de mi programa
y esta funcionando de manera espectacular.
me despliega el calendario de fechas de forma automatica y selecciono la fecha que deseo sin ningun problema .
gracias y quedamos a la orden
Ing. Silvestre Govea Rdz. (Sistemas Monterrey NL)
Excelente aporte muchas gracias por su compañerismo.
ResponderEliminarEspero seguir aprendiendo de usted.
Jorge Aguilar
Buenas esta bien tu clase para la fecha en la grilla, pero una consulta como hago cuando seleccione una fecha ejecute un evento, para completar se le agradecería
ResponderEliminarSaludos,
Error 2 'GridExtension2.DateTimeEditingControl' is not accessible in this context because it is 'Friend'. C:\Users\usuario\Desktop\Sistemas\EVSAMOD\EVSAMOD\SUBS\frmcargaextra.Designer.vb 207 23 EVSAMOD como lo resuelvo?
ResponderEliminary donde encuntro la referencia
ResponderEliminarBasta con que elabores las tres clases y las guardes dentro de tu proyecto.
EliminarUna vez guardadas agregas el datagridview y al agregar una columna de manera automatica encontraras el tipo CalendarColum.
Saludos!
Excelente aportación me sirvió mucho.
ResponderEliminarSaludos!