[WinForms - How To] Agregar controles en una grilla (Opción 1)

agosto 10, 2016 Christian Amado 0 Comentarios

.NET En ocasiones, dentro de un formulario Windows, necesitamos introducir controles especiales que cumplan con una determinada función. Por ejemplo, necesitamos un selector de fechas para poder trabajar con este tipo de datos. Es ahí donde necesitamos personalizar la celda en cuestión.

Una opción válida para esto es crear una clase que herede de la clase DataGridViewColumn, con esto lograremos reemplazar la celda por el control de nuestra preferencia.

En este caso crearemos una clase llamada CalendarColumn que hereda de DataGridViewColumn:
public class CalendarColumn : DataGridViewColumn
{
    public CalendarColumn() : base(new CalendarCell())
    {
    }

    public override DataGridViewCell CellTemplate
    {
        get
        {
            return base.CellTemplate;
        }
        set
        {
            // Nos aseguramos que la elda utilizada para esta plantilla sea del tipo CalendarCell.
            if (value != null && 
                !value.GetType().IsAssignableFrom(typeof(CalendarCell)))
            {
                throw new InvalidCastException("Debe ser del tipo CalendarCell");
            }
            base.CellTemplate = value;
        }
    }
}
Ahora que tenemos lista esta sección procedemos a la creación de nuestra celda que reemplazará a la celda en cuestión. Para ello debemos heredar de la clase DataGridViewTextBoxCell que permitirá lograr el objetivo:
public class CalendarCell : DataGridViewTextBoxCell
{

    public CalendarCell()
        : base()
    {
        // Especificar el formato de fecha a mostrarse en la celda.
        this.Style.Format = "d";
    }

    public override void InitializeEditingControl(int rowIndex, object 
        initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
    {
        // Establecer el valor de edición del control de la celda actual seleccionada.
        base.InitializeEditingControl(rowIndex, initialFormattedValue, 
            dataGridViewCellStyle);
        CalendarEditingControl ctl = 
            DataGridView.EditingControl as CalendarEditingControl;
        // Utilizar el valor de fila predeterminado cuando la propiedad Value es nulo.
        if (this.Value == null)
        {
            ctl.Value = (DateTime)this.DefaultNewRowValue;
        }
        else
        {
            ctl.Value = (DateTime)this.Value;
        }
    }

    public override Type EditType
    {
        get
        {
            // Retornar el tipo de control editable que la clase CalendarCell utiliará.
            return typeof(CalendarEditingControl);
        }
    }

    public override Type ValueType
    {
        get
        {
            // Retorna el tipo de datos que contiene la clase CalendarCell.

            return typeof(DateTime);
        }
    }

    public override object DefaultNewRowValue
    {
        get
        {
            // Utiliza la fecha y hora actuales como valor predeterminado.
            return DateTime.Now;
        }
    }
}
Esta última parte lo utilizamos para crear el control en sí que se encontrará alojado en la celda con el tipo de dato necesario para que podamos visualizar el selector de fechas:
public class CalendarEditingControl : DateTimePicker, IDataGridViewEditingControl
{
    DataGridView dataGridView;
    private bool valueChanged = false;
    int rowIndex;

    public CalendarEditingControl()
    {
        this.Format = DateTimePickerFormat.Short;
    }

    // Implementa la propiedad IDataGridViewEditingControl.EditingControlFormattedValue.
    public object EditingControlFormattedValue
    {
        get
        {
            return this.Value.ToShortDateString();
        }
        set
        {            
            if (value is String)
            {
                try
                {
                    // Esto lanzará una excepción si el string es nulo 
                    // vacío o no posee el formato de fecha.
                    this.Value = DateTime.Parse((String)value);
                }
                catch
                {
                    // En caso de ocurrir alguna excepción
                    // se toma el valor predeterminado de manera a no dejar la propiedad
                    // sin valor.
                    this.Value = DateTime.Now;
                }
            }
        }
    }

    // Implements the 
    // IDataGridViewEditingControl.GetEditingControlFormattedValue method.
    public object GetEditingControlFormattedValue(
        DataGridViewDataErrorContexts context)
    {
        return EditingControlFormattedValue;
    }

    // Implementamos el método 
    // IDataGridViewEditingControl.ApplyCellStyleToEditingControl.
    public void ApplyCellStyleToEditingControl(
        DataGridViewCellStyle dataGridViewCellStyle)
    {
        this.Font = dataGridViewCellStyle.Font;
        this.CalendarForeColor = dataGridViewCellStyle.ForeColor;
        this.CalendarMonthBackground = dataGridViewCellStyle.BackColor;
    }

    // Implementamos la propiedad IDataGridViewEditingControl.EditingControlRowIndex.
    public int EditingControlRowIndex
    {
        get
        {
            return rowIndex;
        }
        set
        {
            rowIndex = value;
        }
    }

    // Implementamos el método IDataGridViewEditingControl.EditingControlWantsInputKey.
    public bool EditingControlWantsInputKey(
        Keys key, bool dataGridViewWantsInputKey)
    {
        // Dejamos que DateTimePicker administre las teclas.
        switch (key & 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;
        }
    }

    // Implementamos el método IDataGridViewEditingControl.PrepareEditingControlForEdit.
    public void PrepareEditingControlForEdit(bool selectAll)
    {

    }

    // Implementamos la propiedad IDataGridViewEditingControl
    // .RepositionEditingControlOnValueChange.
    public bool RepositionEditingControlOnValueChange
    {
        get
        {
            return false;
        }
    }

    // Implementamos la propiedad IDataGridViewEditingControl
    // .EditingControlDataGridView.
    public DataGridView EditingControlDataGridView
    {
        get
        {
            return dataGridView;
        }
        set
        {
            dataGridView = value;
        }
    }

    // Implementa a propiedad IDataGridViewEditingControl
    // .EditingControlValueChanged.
    public bool EditingControlValueChanged
    {
        get
        {
            return valueChanged;
        }
        set
        {
            valueChanged = value;
        }
    }

    // Implenta la propiedad IDataGridViewEditingControl
    // .EditingPanelCursor.
    public Cursor EditingPanelCursor
    {
        get
        {
            return base.Cursor;
        }
    }

    protected override void OnValueChanged(EventArgs eventargs)
    {
        // Notifica al control DataGridView que el contenido de la celda
        // ha cambiado.
        valueChanged = true;
        this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
        base.OnValueChanged(eventargs);
    }
}
De esta manera hemos creado un nuevo control que se incrustrará dentro de la celda que necesitemos mostrar como fecha.

En este caso, dentro del formulario se podría utilizar esta forma para utilizar el selector de fecha dentro de la celda del control DataGridView:
public class Form1 : Form
{
    public Form1()
    {
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        //Llamamos al control que hemos creado
        CalendarColumn col = new CalendarColumn();

        //Agregamos dicha columna a la grilla
        this.dataGridView1.Columns.Add(col);
        this.dataGridView1.RowCount = 5;

        //Asignamos la fecha a todas las filas de la columna que corresponda al selector de fecha
        foreach (DataGridViewRow row in this.dataGridView1.Rows)
        {
            row.Cells[3].Value = DateTime.Now;
        }
    }
}
De esta manera, hemos logrado colocar el control de selector de fechas dentro de la celda de una grilla.