Question

How to show a Time-picker column in DataGridView?

I don't need to pick Date. I only need time to be selected.

Was it helpful?

Solution

Actually, there is a better way than creating a custom DataGridView column. What I currently do for my application is when the TIMESTAMP column of my DataGridView is entered, I position a DateTimePicker control directly over the cell. When the user has clicked out of the cell (thus confirming his selection), the DateTimePicker Visible is set to False and the DateTimePicker's value is put into the cell. By default, the DateTimePicker control Visibility is set to False until I need it. I also use this for ComboBox controls on regular cells where the user cannot enter a custom value and has to use the list of items from the Setup Screen. This technique is great for faking it. I don't have the code readily available, but it is less code and easier to maintain IMHO.

The above and following technique was taken from Faking alternative controls within a DataGridView control in Win Forms 2.0

Edit: Here is the code -

private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
            if (selectAllToolStripMenuItem.Checked)
                selectAllToolStripMenuItem.Checked = false;

            if (dtPicker.Visible)
                dtPicker.Visible = false;

            if (e.ColumnIndex >= 0)
            {
                if (dataGridView1.Columns[e.ColumnIndex].Name == "Delete")
                {
                    if (adminIsLoggedIn)
                    {
                        removeRow(e);
                    }
                    else
                    {
                        MessageBox.Show("You must be logged in as an Administrator in order to change the facility configuration.", "Delete Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
                else if (dataGridView1.Columns[e.ColumnIndex].Name == "TIMESTAMP")
                {
                    if (adminIsLoggedIn)
                    {
                        setNewCellDate(e);
                    }
                }
                .....
            }
            // ---

}

private void setNewCellDate(DataGridViewCellEventArgs e)
{
            dtPicker.Size = dataGridView1.CurrentCell.Size;
            dtPicker.Top = dataGridView1.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true).Top + dataGridView1.Top;
            dtPicker.Left = dataGridView1.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true).Left + dataGridView1.Left;
            if (!(object.Equals(Convert.ToString(dataGridView1.CurrentCell.Value), "")))
                dtPicker.Value = Convert.ToDateTime(dataGridView1.CurrentCell.Value);
            dtPicker.Visible = true;
}

OTHER TIPS

Looking for this same thing I finally found an MSDN article that shows how to make a custom CalendarColumn so I used that code sample and modified it to create a TimeColumn. It works great - very clean and based on Microsoft Code sample. Not a hack and can be databound efficiently and dependably.

To implement simply add these classes to your project and then select TimeColumn in the ColumnType field of the DataGridView.

public class TimeColumn : DataGridViewColumn
{
    public TimeColumn()
        : base(new TimeCell())
    {
    }

    public override DataGridViewCell CellTemplate
    {
        get
        {
            return base.CellTemplate;
        }
        set
        {
            // Ensure that the cell used for the template is a CalendarCell.
            if (value != null &&
                !value.GetType().IsAssignableFrom(typeof(TimeCell)))
            {
                throw new InvalidCastException("Must be a TimeCell");
            }
            base.CellTemplate = value;
        }
    }
}

public class TimeCell : DataGridViewTextBoxCell
{

    public TimeCell()
        : base()
    {
        // Use the short date format.
        this.Style.Format = "t";
    }

    public override void InitializeEditingControl(int rowIndex, object
        initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
    {
        // Set the value of the editing control to the current cell value.
        base.InitializeEditingControl(rowIndex, initialFormattedValue,
            dataGridViewCellStyle);
        TimeEditingControl ctl =
            DataGridView.EditingControl as TimeEditingControl;
        // Use the default row value when Value property is null.
        if (this.Value == null)
        {
            ctl.Value = (DateTime)this.DefaultNewRowValue;
        }
        else
        {
            ctl.Value = (DateTime)this.Value;
        }
    }

    public override Type EditType
    {
        get
        {
            // Return the type of the editing control that CalendarCell uses.
            return typeof(TimeEditingControl);
        }
    }

    public override Type ValueType
    {
        get
        {
            // Return the type of the value that CalendarCell contains.

            return typeof(DateTime);
        }
    }

    public override object DefaultNewRowValue
    {
        get
        {
            // Use the current date and time as the default value.
            return DateTime.Now;
        }
    }
}

class TimeEditingControl : DateTimePicker, IDataGridViewEditingControl
{
    DataGridView dataGridView;
    private bool valueChanged = false;
    int rowIndex;

    public TimeEditingControl()
    {
        this.Format = DateTimePickerFormat.Time;
        this.ShowUpDown = true;  // replace the timepicker calendar drop down with a up down scroller
    }

    // Implements the IDataGridViewEditingControl.EditingControlFormattedValue 
    // property.
    public object EditingControlFormattedValue
    {
        get
        {
            return this.Value.ToShortTimeString();
        }
        set
        {
            if (value is String)
            {
                try
                {
                    // This will throw an exception of the string is 
                    // null, empty, or not in the format of a date.
                    this.Value = DateTime.Parse((String)value);
                }
                catch
                {
                    // In the case of an exception, just use the 
                    // default value so we're not left with a null
                    // value.
                    this.Value = DateTime.Now;
                }
            }
        }
    }

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

    // Implements the 
    // IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.
    public void ApplyCellStyleToEditingControl(
        DataGridViewCellStyle dataGridViewCellStyle)
    {
        this.Font = dataGridViewCellStyle.Font;
        this.CalendarForeColor = dataGridViewCellStyle.ForeColor;
        this.CalendarMonthBackground = dataGridViewCellStyle.BackColor;
    }

    // Implements the IDataGridViewEditingControl.EditingControlRowIndex 
    // property.
    public int EditingControlRowIndex
    {
        get
        {
            return rowIndex;
        }
        set
        {
            rowIndex = value;
        }
    }

    // Implements the IDataGridViewEditingControl.EditingControlWantsInputKey 
    // method.
    public bool EditingControlWantsInputKey(
        Keys key, bool dataGridViewWantsInputKey)
    {
        // Let the DateTimePicker handle the keys listed.
        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;
        }
    }

    // Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit 
    // method.
    public void PrepareEditingControlForEdit(bool selectAll)
    {
        // No preparation needs to be done.
    }

    // Implements the IDataGridViewEditingControl
    // .RepositionEditingControlOnValueChange property.
    public bool RepositionEditingControlOnValueChange
    {
        get
        {
            return false;
        }
    }

    // Implements the IDataGridViewEditingControl
    // .EditingControlDataGridView property.
    public DataGridView EditingControlDataGridView
    {
        get
        {
            return dataGridView;
        }
        set
        {
            dataGridView = value;
        }
    }

    // Implements the IDataGridViewEditingControl
    // .EditingControlValueChanged property.
    public bool EditingControlValueChanged
    {
        get
        {
            return valueChanged;
        }
        set
        {
            valueChanged = value;
        }
    }

    // Implements the IDataGridViewEditingControl
    // .EditingPanelCursor property.
    public Cursor EditingPanelCursor
    {
        get
        {
            return base.Cursor;
        }
    }

    protected override void OnValueChanged(EventArgs eventargs)
    {
        // Notify the DataGridView that the contents of the cell
        // have changed.
        valueChanged = true;
        this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
        base.OnValueChanged(eventargs);
    }
}

AFAIK, there is a direct way to do this. I think the only way is by creating a Custom DataGridview column. Check this link for creating a custom datagridview column

Sorry for replying to an old thread, but this is the only place I could find where someone has adapted the MS code as I have. In response to RThomas, I have done this but I am finding problems with using the TimeColumn when bound to a datatable. I am using it for employees timesheet entry so the date element is not visible, just the time. When I enter the data it works fine, but when I try editing the data something goes wrong.

The cell value is correct in the OnValueChanged event (using Debug I can see the value of Me.Value) but in the DataGridView.CellEndEditing or CellValidated events the value shows teh current date and the edited time value. Strangely, if the user moves to another cell, then back to the TimeCell, then to another cell again the value is correct, but after the initial edit the date is wrong.

I've looked at all different events but it seems to be something between the value being edited in the DataGridView and committed to the Datatable bound to the DataGridView.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top