Binding a DataGridView to a Collection in C#

By FoxLearn 1/16/2025 6:22:09 AM   67
In this guide, I will show you how to bind a DataGridView to a regular .NET collection.

Before we get started with the collection, we need an object to hold the data.

For this example, I'll use a Book class, which contains basic information about a book, such as title, author, and publication year.

public class Book
{
  private string _title;
  private string _author;
  private int _year;

  public Book(string title, string author, int year)
  {
    _title = title;
    _author = author;
    _year = year;
  }

  public string Title
  {
    get { return _title; }
    set { _title = value; }
  }

  public string Author
  {
    get { return _author; }
    set { _author = value; }
  }

  public int Year
  {
    get { return _year; }
    set { _year = value; }
  }
}

Now that we have the Book class, we need a collection to store multiple Book objects. I'll use a List<Book> from the System.Collections.Generic namespace:

List<Book> books = new List<Book>();
books.Add(new Book("The Catcher in the Rye", "J.D. Salinger", 1951));
books.Add(new Book("1984", "George Orwell", 1949));
books.Add(new Book("To Kill a Mockingbird", "Harper Lee", 1960));

Binding this List<Book> to a DataGridView is simple. All you need to do is set the DataSource property of the DataGridView to the List:

_dgBooks.DataSource = books;

Once this is done, the DataGridView will automatically create columns for each property of the Book object, and a row will be generated for each Book in the list. This creates one-way binding, where any changes made in the DataGridView will reflect in the List<Book>.

However, if you want changes in the Book objects to be reflected in the DataGridView, you'll need to implement the INotifyPropertyChanged interface.

public class Book : INotifyPropertyChanged
{
  private string _title;
  private string _author;
  private int _year;

  public event PropertyChangedEventHandler PropertyChanged;

  public Book(string title, string author, int year)
  {
    _title = title;
    _author = author;
    _year = year;
  }

  public string Title
  {
    get { return _title; }
    set
    {
      _title = value;
      this.NotifyPropertyChanged("Title");
    }
  }

  public string Author
  {
    get { return _author; }
    set
    {
      _author = value;
      this.NotifyPropertyChanged("Author");
    }
  }

  public int Year
  {
    get { return _year; }
    set
    {
      _year = value;
      this.NotifyPropertyChanged("Year");
    }
  }

  private void NotifyPropertyChanged(string name)
  {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
  }
}

However, there's a catch: the List<T> class doesn't notify changes made to its contained objects. To resolve this, we can use the BindingList<T>, which does support notifications when objects inside the collection change:

BindingList<Book> books = new BindingList<Book>();

books.Add(new Book("The Catcher in the Rye", "J.D. Salinger", 1951));
books.Add(new Book("1984", "George Orwell", 1949));
books.Add(new Book("To Kill a Mockingbird", "Harper Lee", 1960));

_dgBooks.DataSource = books;

Now, if any changes are made to a Book object (even from an external thread or process), the DataGridView will automatically reflect those changes.

Next, you may want to customize the column headers and bindings.

By default, the DataGridView will automatically generate column names based on the property names, but you can disable that by setting AutoGenerateColumns to false and manually defining the columns.

_dgBooks.AutoGenerateColumns = false;

DataGridViewTextBoxColumn titleColumn = new DataGridViewTextBoxColumn();
titleColumn.DataPropertyName = "Title";
titleColumn.HeaderText = "Book Title";

DataGridViewTextBoxColumn authorColumn = new DataGridViewTextBoxColumn();
authorColumn.DataPropertyName = "Author";
authorColumn.HeaderText = "Book Author";

DataGridViewTextBoxColumn yearColumn = new DataGridViewTextBoxColumn();
yearColumn.DataPropertyName = "Year";
yearColumn.HeaderText = "Publication Year";

_dgBooks.Columns.Add(titleColumn);
_dgBooks.Columns.Add(authorColumn);
_dgBooks.Columns.Add(yearColumn);

BindingList<Book> books = new BindingList<Book>();

books.Add(new Book("The Catcher in the Rye", "J.D. Salinger", 1951));
books.Add(new Book("1984", "George Orwell", 1949));
books.Add(new Book("To Kill a Mockingbird", "Harper Lee", 1960));

_dgBooks.DataSource = books;

This will allow you to control the column headers and data property names.

The DataPropertyName is bound to the property of the object (like Title, Author, or Year), and the HeaderText is what the user will see in the DataGridView's column header.