I am currently developing a C# application with a DataGridView and am trying to show a DataGridViewComboBoxColumn containing DateTime objects as ValueType and ValueMember.
I have already applied formatting using the DefaultCellStyle.Format method of the column, however this only seems to apply to a selected value and not all the items in the list.
So when I have something selected, it shows up fine, but when opening the dropdown, all items in there show up as a standard String representation of the DateTime object.
Is this intended behavior or am I missing something?
Thank you,
Tobias Timpe
I am guessing the grid’s data source is a DataTable
and obviously the combo box column is of type DateTime.
If this is the case, then I believe what you are describing is because the grids cell “value” is an actual DateTime
object and the grid is going to maintain the cells value with the DateTime
object’s default ToString()
method… i.e. … MM/DD/YYYY.
You can, as you have stated, format the display, however that does not apply to the drop-down items in the combo box when the drop down is visible. I am guessing above may be the reason why it is displayed this way. If you tried to format it any other way… the grid’s dreaded DataError
will most likely appear.
A possible solution…
Since the combo box is going to contain DateTime
objects and you have a method to get the DateTime
objects, then I recommend you put the dates into a DataTable
structured like below. This will be the DataSource
for the combo box.
private DataTable GetComboTable() {
DataTable dt = new DataTable();
dt.Columns.Add("DateTime", typeof(DateTime));
dt.Columns.Add("StringDateTime", typeof(string));
return dt;
}
The first column (DateTime) will contain the “actual” DateTime
object that will be the ValueMember
for the combo box. This is what will get mapped to the DateTime
column in the grids table and avoid the DataError.
The next (StringDateTime) column will contain the “formatted” date string to display in the drop down. This string will be the combo boxes DisplayMember.
Below is an example of filling this table with some random dates.
private DataTable GetComboDates() {
DataTable dt = GetComboTable();
Random rand = new Random();
int duration = 5 * 365;
DateTime randomDate = DateTime.Today.AddDays(-rand.Next(duration));
for (int i = 0; i < 10; i++) {
dt.Rows.Add(randomDate, String.Format("{0:yyyy/MM/dd - hh:mm:ss tt}", randomDate));
randomDate = DateTime.Today.AddDays(-rand.Next(duration));
}
return dt;
}
Note, that formatting the date string here applies to what the user will see when the drop-down is displayed. As we already know, if the combo columns DefaultCellStyle.Format
(which applies to what the user sees when the combo box is not selected) does not match this format, then they will be different. This means the columns display format must match the one set here to get the desired behavior you describe.
Now that we have a good DataSource
for the combo box column, the function below will use it to return a DataGridViewComboBoxColumn
used for the grid.
private DataGridViewComboBoxColumn GetComboColumn() {
DataGridViewComboBoxColumn comboCol = new DataGridViewComboBoxColumn();
comboCol.DataPropertyName = "Dates";
comboCol.HeaderText = "Dates";
comboCol.DisplayMember = "StringDateTime";
comboCol.ValueMember = "DateTime";
comboCol.Width = 175;
DataTable comboData = GetComboDates();
comboCol.DataSource = comboData;
return comboCol;
}
NOTE: the DataPropertyName
is the name of the column in the grids DataTable
; the DisplayMember
is the name of the column in the combo data table for the formatted date string; the ValueMember
is the name of the column in the combo data table for the actual date.
Below is an example to test this and I feel confident it will maintain the same format for the drop down when the drop down is not displayed. This should work as long as both formats are the same.
DataTable GridTable;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
GridTable = GetTable();
FillTable(GridTable);
dataGridView1.Columns.Add(GetComboColumn());
dataGridView1.DataSource = GridTable;
}
private DataTable GetTable() {
DataTable dt = new DataTable();
dt.Columns.Add("Col0", typeof(string));
dt.Columns.Add("Dates", typeof(DateTime));
return dt;
}
private DataGridViewComboBoxColumn GetComboColumn() {
DataGridViewComboBoxColumn comboCol = new DataGridViewComboBoxColumn();
comboCol.DataPropertyName = "Dates";
comboCol.HeaderText = "Dates";
comboCol.DisplayMember = "StringDateTime";
comboCol.ValueMember = "DateTime";
comboCol.Width = 175;
DataTable comboData = GetComboDates();
comboCol.DataSource = comboData;
return comboCol;
}
private void FillTable(DataTable dt) {
for (int i = 0; i < 10; i++) {
dt.Rows.Add("C0R" + i);
}
}
private DataTable GetComboTable() {
DataTable dt = new DataTable();
dt.Columns.Add("DateTime", typeof(DateTime));
dt.Columns.Add("StringDateTime", typeof(string));
return dt;
}
private DataTable GetComboDates() {
DataTable dt = GetComboTable();
Random rand = new Random();
int duration = 5 * 365;
DateTime randomDate = DateTime.Today.AddDays(-rand.Next(duration));
for (int i = 0; i < 10; i++) {
dt.Rows.Add(randomDate, String.Format("{0:yyyy/MM/dd - hh:mm:ss tt}", randomDate));
randomDate = DateTime.Today.AddDays(-rand.Next(duration));
}
return dt;
}
Hope this helps and makes sense.