Doubt: Column DataGridView that only accepts hour format (12h / 24h) in C #?

0

I have a DataGridView that contains several columns and there are two Columns which should only allow the user to type the time , I have made it from the following way:

Event EditingControlShowing to invoke the KeyPress method:

    private void data_grid_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
    {
        TextBox textbox = e.Control as TextBox;
        if (textbox != null)
        {
           textbox.KeyPress -= new KeyPressEventHandler(data_grid_KeyPress);
           textbox.KeyPress += new KeyPressEventHandler(data_grid_KeyPress);
        }
    }

KeyPress Method:

private void data_grid_KeyPress(object sender, KeyPressEventArgs e)
{
    if (data_grid.CurrentCell.ColumnIndex == 4 || data_grid.CurrentCell.ColumnIndex == 5)
    {
        //(char)58 pulsan :
        //(char)65 pulsan A
        //(char)77 pulsan M
        //(char)80 pulsan P
        //(char)97 pulsan a
        //(char)109 pulsan m
        //(char)112 pulsan p
        //(char)46 pulsan .
        //(char)8 pulsan Borrar
        //(char)13 pulsan enter
        //(char)32 pulsan espacio

        //Nota: evento.Handled es false por defecto.

        if (char.IsNumber(e.KeyChar) | e.KeyChar == (char)8 | e.KeyChar == (char)Keys.Escape) // Si es un numero o borrar
        {
            e.Handled = false; // No hacemos nada y dejamos que el sistema controle la pulsación de tecla
            return;
        }

        if (e.KeyChar == (char)46 | e.KeyChar == (char)58) // Si es un punto o dos puntos
        {
            e.Handled = false; // No hacemos nada y dejamos que el sistema controle la pulsación de tecla
            return;
        }

        if (e.KeyChar == (char)65 | e.KeyChar == (char)77 |
            e.KeyChar == (char)80 | e.KeyChar == (char)97 |
            e.KeyChar == (char)109 | e.KeyChar == (char)112 |
            e.KeyChar == (char)32)
        {
            e.Handled = false; // No hacemos nada y dejamos que el sistema controle la pulsación de tecla
            return;
        }

        else if (e.KeyChar == (char)13) // Si es un enter
        {
            e.Handled = true; //Interceptamos la pulsación para que no la permita.
            SendKeys.Send("{TAB}"); //Pulsamos la tecla Tabulador por código
        }
        else //Para el resto de las teclas
        {
            e.Handled = true; // Interceptamos la pulsación para que no tenga lugar
            data_grid.CurrentCell.ErrorText = "Solo se acepta formato de Hora.\n Ejemplo:\n     1:00 \n     2:00 a. m.\n     5:00 P. M.\n     15:00";

        }
    }
} 
  

I created a KeyPress method to control the keys pressed by the user and only accept the ones that I defined in the method. Both columns in the Property: DefaultCellStyle - Format I have them with the format hh:mm:ss tt so that after the user finishes writing the time, I put it in that specified format.

Can I make this type of column without having to intercept each letter that is pressed by the user?

EDIT:

As in this example from @AsierVillanueva , creating a custom column which you can reuse as many times as you need it.

Note: I must NOT use a "DateTimePicker", the user must place it manually, because it will also load data from the database.

    
asked by J. Rodríguez 21.12.2017 в 15:23
source

1 answer

1

Even if you need the value of the cell to be string you can edit the value using a DateTimePicker , you just have to convert the chain to date and vice versa when you pass the value between the edit control and the cell.

First of all you should create the edit control that you will have to implement the IDataGridViewEditingControl interface and it could look like:

class TimeEditingControl : DateTimePicker, IDataGridViewEditingControl
{
    private string _timeFormat = "hh:mm:ss tt";

    public TimeEditingControl()
    {
        Format = DateTimePickerFormat.Custom;
        CustomFormat = _timeFormat;
        ShowUpDown = true;
    }

    // Value nullable
    public new DateTime? Value
    {
        get { return base.Value == MinDate ? (DateTime?) null : base.Value; }
        set
        {
            base.Value = (DateTime) (value == null ? MinDate : value);
            CustomFormat = value == null ? " " : _timeFormat;
        }
    }

    protected override void OnKeyDown(KeyEventArgs e)
    {
        base.OnKeyDown(e);
        if (e.KeyCode == Keys.Delete)
        {
            Value = null;
        }
        else
        {
            if (CustomFormat == " ")
            {
                CustomFormat = _timeFormat;
                Value = DateTime.Now;
            }
        }
    }

    protected override void OnValueChanged(EventArgs eventargs)
    {
        base.OnValueChanged(eventargs);
        SendToGridValueChanged();
    }

    private void SendToGridValueChanged()
    {
        EditingControlValueChanged = true;
        EditingControlDataGridView?.NotifyCurrentCellDirty(true);
    }

    #region Miembros de IDataGridViewEditingControl

    public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)
    {
        Font = dataGridViewCellStyle.Font;
    }

    public DataGridView EditingControlDataGridView { get; set; }

    public object EditingControlFormattedValue
    {
        get { return Value == null ? string.Empty : Value.Value.ToString(CustomFormat, CultureInfo.CurrentUICulture); }
        set
        {
            try
            {
                Value = string.IsNullOrEmpty((string) value) ? (DateTime?) null : DateTime.ParseExact((string)value, CustomFormat, CultureInfo.CurrentUICulture);
            }
            catch { Value = DateTime.Now; }
            SendToGridValueChanged();
        }
    }

    public int EditingControlRowIndex { get; set; }

    public bool EditingControlValueChanged { get; set; }

    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 => Cursor;

    public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context)
    {
        return EditingControlFormattedValue;
    }

    public void PrepareEditingControlForEdit(bool selectAll) { }

    public bool RepositionEditingControlOnValueChange => false;

    #endregion

}

As you can see the control inherits from DateTimePicker and implements the interface IDataGridViewEditingControl to be able to use it as a cell editor in a DataGridView .

The desired time format is set in the constructor and the ShowUpDown to true property is set to show the "spin buttons" instead of the calendar.

The most important thing about the control code is in the EditingControlFormattedValue property. In the methods get and set of this property is where the string value received from the cell is converted to a date value to be assigned to DateTimePicker and vice versa.

Be careful with the time format you have chosen because the DateTimePicker control sometimes does not correctly display the AM and PM suffixes depending on the user's regional settings.

To be able to maintain null or empty values I have overwritten the property Value of DateTimePicker to support these values. When a null value is set, the value of the base control is set to MinDate . So that in these cases the date is not displayed the property CustomFormat is set to a space so that the control appears empty.

The "Delete" key allows you to set the empty or null value, while any other key resets the format to enter a new time.

Next we should create the class for the cell that will inherit from DataGridViewTextBoxCell and that will simply have code to indicate that the cell editor must be an instance of TimeEditingControl and to set the value of the editor through the property EditingControlFormattedValue :

public class TimeCell : DataGridViewTextBoxCell
{

    public override Type EditType => typeof(TimeEditingControl);

    public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
    {
        base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
        var editingControl = (TimeEditingControl)DataGridView.EditingControl;
        editingControl.EditingControlFormattedValue = Value;
    }

}

In the same way we will create the class TimeColumn for the column, in which we will simply indicate that the class TimeCell should be used to generate the cells:

public class TimeGridColumn : DataGridViewColumn
{

    public TimeGridColumn() : base(new TimeCell()) { }

    public override DataGridViewCell CellTemplate
    {
        get
        {
            return base.CellTemplate;
        }
        set
        {
            if (value != null &&
                    !value.GetType().IsAssignableFrom(typeof(TimeCell)))
                throw new InvalidCastException("Debe especificar una instancia de TimeCell");
            base.CellTemplate = value;
        }
    }
}

From here you could create for example a property in the column to specify the time format or take advantage of the property Format of DefaultCellStyle .

    
answered by 02.01.2018 / 16:00
source