I see that you have made a custom DataGridComboBoxColumn
and have implemented a version of SetAlternateChoicesUsingDataSource
that seems to be modeled after the method of the same name in the Microsoft code example for DataGridViewComboBoxColumn.
The purpose of SetAlternateChoicesUsingDataSource
in that example is to provide ComboBox
drop down options that are tailored and specified for each user using the AllowedCustomerTypes
property that you show in your CreditUser
class. Something like this:

But based on your comment, the choices are the same for each row. More like this:

This means that your code can be simplified.
DataSources for DataGridViewComboBoxColumn and DataGridView
I believe what might be causing the confusion is that the data sources for DataGridView
and for DataGridViewComboBoxColumn
are completely unrelated in this case. Since there is no need to provide each user with individualized options, the source of drop down items only needs to be set one time for the entire column.
The DataSource
for DataGridViewComboBoxColumn
is an array of strings named ResponsibleList
that will not change.
private readonly string[] ResponsibleList = new []
{
"Production",
"Distribution",
"Customer Service",
"Sales",
String.Empty
};
The DataSource
for dataGridViewCreditUser
is a binding list named CreditUsers
.
readonly BindingList<CreditUser> CreditUsers = new BindingList<CreditUser>();
Initialize
Assigning these data sources is done in the override of OnLoad
(there's no need to have the form subscribe to its own Load
event). Allow me to explain what I've done and you can modify this flow to your specific requirements.
protected override void OnLoad(EventArgs e)
{
dataGridViewCreditUser.DataSource = CreditUsers;
Adding one or more items will autogenerate the columns.
// Calls a mock method that returns a simulated response of three CreditUsers.
foreach (var creditUser in mockMainForm_srv_GetCreditUser("", ""))
{
CreditUsers.Add(creditUser);
}
Create a ComboBox
column that will be swapped out for the autogenerated one. This is where ResponsibleList
becomes the DataSource
for the ComboBox
.
var colCB = new DataGridViewComboBoxColumn
{
Name = nameof(CreditUser.Responsible),
// Connects the column value to the Responsible property of CreditUser
DataPropertyName = nameof(CreditUser.Responsible),
AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells,
// Connects the four drop-down options in ResponsibleList to the ComboBox
DataSource = ResponsibleList,
};
Perform the swap. Remove the autogenerated column and replace it with the custom version.
var index = dataGridViewCreditUser.Columns[nameof(CreditUser.Responsible)].Index;
dataGridViewCreditUser.Columns.RemoveAt(index);
dataGridViewCreditUser.Columns.Insert(index, colCB);
Make sure the cell is NOT left in an editing state after change of ComboBox or CheckBox.
dataGridViewCreditUser.CurrentCellDirtyStateChanged += (sender, e) =>
{
switch (dataGridViewCreditUser.Columns[dataGridViewCreditUser.CurrentCell.ColumnIndex].Name)
{
case nameof(CreditUser.Responsible):
case nameof(CreditUser.Restricted):
dataGridViewCreditUser.CommitEdit(DataGridViewDataErrorContexts.Commit);
break;
}
};
To monitor ongoing changes, update the Title bar whenever the source list is modified.
CreditUsers.ListChanged += (sender, e) =>
{
if ((dataGridViewCreditUser.CurrentCell != null) && (dataGridViewCreditUser.CurrentCell.RowIndex < CreditUsers.Count))
{
var creditUser = CreditUsers[dataGridViewCreditUser.CurrentCell.RowIndex];
Text = creditUser.ToString();
}
};
Now that the DataGridView
is all set up the columns can be formatted.
foreach (DataGridViewColumn col in dataGridViewCreditUser.Columns)
{
if (col.Name == nameof(CreditUser.UserName))
{
col.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
else
{
col.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
}
}
}
MOCK QUERY FOR TESTING
// MOCK for minimal example
private List<CreditUser> mockMainForm_srv_GetCreditUser(string v1, string v2)
{
return new List<CreditUser>
{
new CreditUser
{
UserName = "Tom",
CreditLimit=10000m,
},
new CreditUser
{
UserName = "Richard",
CreditLimit=1250m,
Restricted = true
},
new CreditUser
{
UserName = "Harry",
CreditLimit=10000m,
},
};
}
CreditUser
class
// REDUCED for minimal example
public class CreditUser : INotifyPropertyChanged
{
string _UserName = string.Empty;
public string UserName
{
get => _UserName;
set
{
if (!Equals(_UserName, value))
{
_UserName = value;
OnPropertyChanged();
}
}
}
string _Responsible = String.Empty;
public string Responsible
{
get => _Responsible;
set
{
if (!Equals(_Responsible, value))
{
_Responsible = value;
OnPropertyChanged();
}
}
}
decimal _CreditLimit = 0;
public decimal CreditLimit
{
get => _CreditLimit;
set
{
if (!Equals(_CreditLimit, value))
{
_CreditLimit = value;
OnPropertyChanged();
}
}
}
bool _Restricted = false;
public bool Restricted
{
get => _Restricted;
set
{
if (!Equals(_Restricted, value))
{
_Restricted = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public override string ToString()
{
var ctype = Responsible == string.Empty ? "Not Specified" : $"{Responsible}";
return Restricted ?
$"{UserName} ({ctype}) Restricted" :
$"{UserName} ({ctype}) {CreditLimit}";
}
}