One possible solution as suggested in the comments, is to “create” the DataGridViewComboBoxColumn
the way you describe, then add it to the grid. Do this before you set the grids data source. Mapping this combo box column to the “WorkStatus” property of the Work
list is done using the combo columns DataPropertyName
. Setting the columns DataPropertyName
to “WorkStatus” should do the trick.
One important problem you may run into is if AFTER setting up the combo box column with the 1 – Complete, 2 – In Progress… etc. The int
values 1-4 are the values in the “Status” field of the list of Work
items… HOPEFULLY! This may always be the case, however if the value of one of the Status
fields is NOT 1, 2, 3 or 4… then the grid is going to throw a DataError
when this value is mapped to the combo box column. In most cases, you won’t know this until the data is read and boom crash.
The point is that if you are going to set up a combo box column and then “map” a column from the data source to this column… you better be damn sure it doesn’t have any invalid “status” values. Otherwise a crash (data error) is guaranteed. It would be prudent to wire up the DataError
anyway.
Given this, it appears necessary to at least “check” to make sure something like this doesn’t happen when the data is bound to the grid. One simple solution is to loop through all the data (before binding it to the grid) and “check the “status” values of each Work
item. What is unknown is what to do if a value greater than 4 or less than 1 is found in the data. You can’t just let it go as explained above. This solution is to simply “add” the value to the combo box with an “unknown” string value. This will at least “guarantee” that you will avoid the data error from invalid values.
Putting this together may go something like this…
Below is the Work class as posted however I did change some variable names. This is the class that is bound to the grid.
public class Work {
public int WorkID { get; set; }
public string WorkName { get; set; }
public int WorkStatus { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
}
Next, is the Status
class that is used to define the Status
objects. This is used for the combo box column. A constructor is added along with the overridden Equals
methods. This will facilitate “checking” to see if an existing “Status” value is already in the list using the list’s Contains
property.
public class Status : IEquatable<Status> {
public int StatusID { get; set; }
public string StatusString { get; set; }
public Status(int statusID, string statusString) {
StatusID = statusID;
StatusString = statusString;
}
public override bool Equals(object obj) {
if (obj.GetType() != GetType()) return false;
return Equals(obj as Status);
}
public bool Equals(Status that) {
return that != null && this.StatusID == that.StatusID;
}
public override int GetHashCode() {
var hashCode = -1280899892;
hashCode = hashCode * -1521134295 + StatusID.GetHashCode();
return hashCode;
}
}
Setting up the ListOfStatus
will require looping through the list of Work
objects and “checking” each of the status values making sure the values are between 1 and 4 inclusive. A method that returns this list could come in handy and may look something like below… First the “default” Status
values are created and added to the list (1, 2, 3, 4), then, a loop through the Work
items to add any values that are not already in the status list.
private List<Status> GetStatusList(List<Work> workItems) {
List<Status> listOfStatus = new List<Status>();
// add the default values for the combo boxes
Status curStatus = new Status(1, "Complete");
listOfStatus.Add(curStatus);
curStatus = new Status(2, "In Progress");
listOfStatus.Add(curStatus);
curStatus = new Status(3, "Errored");
listOfStatus.Add(curStatus);
curStatus = new Status(4, "On Hold");
listOfStatus.Add(curStatus);
// check to make sure the data (workItems) does NOT have any values in the Status field...
// that are NOT one of the values above... (specifically 1, 2, 3 or 4)
// if the value is NOT one of the values above... we will simply add it to the list
Status unknownStatus;
foreach (Work work in workItems) {
if (work.WorkStatus < 1 || work.WorkStatus > 4) {
unknownStatus = new Status(work.WorkStatus, "Unknown_" + work.WorkStatus);
if (!listOfStatus.Contains(unknownStatus)) {
listOfStatus.Add(unknownStatus);
}
}
}
return listOfStatus;
}
Now that we have a good list of Status
objects, we can proceed to creating the combo box column and use this list as a data source for the combo column. Given a list of Work
objects is all we need to create this column and a method that returns this DataGridViewComboBoxColumn
may look something like below…. First, we get the status list from the above method, then set the columns properties making sure to set the columns DataPropertyName
to match the Work
objects WorkStatus
property. If this does not match, the column will not map properly. The added “status” column will end up as the first column in the grid so you may want to change its ordinal value.
private DataGridViewComboBoxColumn GetComboColumn(List<Work> listOfWork) {
List<Status> ListOfStatus = GetStatusList(listOfWork);
DataGridViewComboBoxColumn comboCol = new DataGridViewComboBoxColumn();
comboCol.Name = "Status";
comboCol.DataPropertyName = "WorkStatus";
comboCol.DisplayMember = "StatusString";
comboCol.ValueMember = "StatusID";
comboCol.DataSource = ListOfStatus;
return comboCol;
}
An example of using the above methods is below.
private void Form1_Load(object sender, EventArgs e) {
List<Work> ListOfWork = new List<Work>();
FillListOfWork(ListOfWork);
dataGridView1.Columns.Add(GetComboColumn(ListOfWork));
dataGridView1.DataSource = ListOfWork;
}
private void FillListOfWork(List<Work> ListOfWork) {
Work newWork;
Random rand = new Random();
for (int i = 0; i < 100; i++) {
newWork = new Work();
newWork.WorkID = i;
newWork.WorkName = "Work Name " + i;
newWork.WorkStatus = rand.Next(1, 5);
newWork.DateCreated = DateTime.Now;
newWork.DateModified = DateTime.Now;
ListOfWork.Add(newWork);
}
}
private void dataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e) {
MessageBox.Show("DataError: " + e.Exception.Message);
}
I hope this helps.