After much research and tinkering, I found the solution. Below is the body of Form1_Load() that works. There are two parts to the solution:
1) Instead of creating the cell/column on the fly, just build it into the design-time view and bind appropriately as shown below the catch() statement
2) The grid will not automatically match up row data with combobox data as I am used to. The for-loop at the end goes through and binds the existing row data selection to the corresponding entry in the combobox.
As an additional note, I found that this solution has additional problems if the DataGridView is located in a tab control. If the grid is on any tab other than the first one, the row/combobox binding will fail. Whether this is a MS bug or not I don't know, but the solution was to put the for-loop in the code below in the SelectedIndexChanged() event for the tab control and run it when changing to the tab in question.
personList.Add(new Person("Tim", 2));
deptList.Add(new Department("A", 1));
deptList.Add(new Department("B", 2));
deptList.Add(new Department("C", 3));
grid1.AutoGenerateColumns = false;
try
{
m_BindSource.DataSource = null;
m_BindSource.DataSource = personList;
grid1.DataSource = m_BindSource;
}
catch (Exception ex)
{
ex.GetType();
MessageBox.Show(ex.Message);
}
((DataGridViewComboBoxColumn)grid1.Columns["Department"]).DataSource = deptList;
((DataGridViewComboBoxColumn)grid1.Columns["Department"]).DisplayMember = "DeptName";
((DataGridViewComboBoxColumn)grid1.Columns["Department"]).ValueMember = "DeptID";
foreach (DataGridViewRow row in grid1.Rows)
{
if (row.DataBoundItem != null)
{
row.Cells["Department"].Value = ((Person)row.DataBoundItem).DepartmentID;
}
}