Search code examples
c#datagridviewdatagridviewcolumn

DataGridViewComboBoxColumn: Date format doesn't apply to items in list


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


Solution

  • 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.