VB version here.

I learned something new today so I felt the need to share it. It has always been the case that complex data-binding in Windows Forms requires an object that implements the IList or IListSource interface. Arrays implement IList, as does the List<T> class. The DataTable class implements IListSource and DataView implements IList. The BindingList<T> and BindingSource classes both implement IList. These are the most common types used as data sources for ComboBoxes, DataGridViews and the like.

I generally recommend that people use a BindingSource when binding, so bind their data to the BindingSource and bind that to the control(s). In some cases it makes little difference but in others it can help and I have found such a way that it can help that I wasn't previously aware of. When binding read-only data from a database, I have generally recommended creating a data reader, loading the data into a DataTable and then binding that to a BindingSource. That's because I assumed that a BindingSource also required an IList or IListSource, but that turns out not to be the case. In various cases, the BindingSource will generate its own IBindingList<T> and binding a data reader is one such case.

If you bind a data reader (SqlDataReader, OleDbDataReader, etc) to a BindingSource then it will automatically generate an IBindingList<System.Data.Common.DataRecordInternal>. That list is similar to the DataRowCollection from the Rows property of a DataTable or the DataView from the DefaultView property. You can index a DataRecordInteral by column name or ordinal in the same way you can a DataRow or DataRowView but, unlike those other two, it only stores one version of the data, which makes it more efficient for read-only data. It also supports all the same PropertyDescriptor functionality that a DataGridView needs to automatically generate columns.

Here's an example of how I may have bound read-only data from a database before:
csharp Code:
  1. // Create a new DataTable
  2. var table = new DataTable();
  3.  
  4. using (var connection = new SqlConnection("connection string here"))
  5. using (var command = new SqlCommand("SELECT * FROM MyTable", connection))
  6. {
  7.     connection.Open();
  8.  
  9.     using (var reader = command.ExecuteReader())
  10.     {
  11.         // Populate the DataTable from the data reader
  12.         table.Load(reader);
  13.     }
  14. }
  15.  
  16. // Bind the DataTable
  17. bindingSource1.DataSource = table;
  18.  
  19. comboBox1.DisplayMember = "Name";
  20. comboBox1.ValueMember = "Id";
  21. comboBox1.DataSource = bindingSource1;
  22.  
  23. textBox1.DataBindings.Add("Text", bindingSource1, "Description");
  24.  
  25. dataGridView1.DataSource = bindingSource1;
and here's how I might do it now:
csharp Code:
  1. using (var connection = new SqlConnection("connection string here"))
  2. using (var command = new SqlCommand("SELECT * FROM MyTable", connection))
  3. {
  4.     connection.Open();
  5.  
  6.     using (var reader = command.ExecuteReader())
  7.     {
  8.         // Bind the data reader and generate an IBindingList<DataRecordInternal>
  9.         bindingSource1.DataSource = reader;
  10.     }
  11. }
  12.  
  13. comboBox1.DisplayMember = "Name";
  14. comboBox1.ValueMember = "Id";
  15. comboBox1.DataSource = bindingSource1;
  16.  
  17. textBox1.DataBindings.Add("Text", bindingSource1, "Description");
  18.  
  19. dataGridView1.DataSource = bindingSource1;
As you can see, the code changes very little but there is slightly less code and the data structures underneath will be less resource-intensive. In the first case, each item is a DataRowView from the DefaultView of the DataTable while, in the second case, each item is DataRecordInternal from an IBindingList<T>. The user will see no difference and you see little difference as the developer but your app is a little bit more efficient.