69

What is the best way to refresh a DataGridView when you update an underlying data source?

I'm updating the datasource frequently and wanted to display the outcome to the user as it happens.

I've got something like this (and it works), but setting the DataGridView.DataSource to null doesn't seem like the right way.

List<ItemState> itemStates = new List<ItemState>();
dataGridView1.DataSource = itemStates;

for (int i = 0; i < 10; i++) { 
    itemStates.Add(new ItemState { Id = i.ToString() });
    dataGridView1.DataSource = null;
    dataGridView1.DataSource = itemStates;
    System.Threading.Thread.Sleep(500);
}
Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
shaunf
  • 1,063
  • 4
  • 10
  • 11

8 Answers8

57

I ran into this myself. My recommendation: If you have ownership of the datasource, don't use a List. Use a BindingList. The BindingList has events that fire when items are added or changed, and the DataGridView will automatically update itself when these events are fired.

GWLlosa
  • 23,995
  • 17
  • 79
  • 116
  • 4
    This is a good suggestion. After that you just have to call .Refresh() for the datagridview to refresh it's data displayed... – veljkoz Apr 29 '10 at 17:08
  • 3
    can refresh row by row using: bindingList.ResetItem(bindingList.IndexOf(item)); – Adam Butler Nov 23 '11 at 03:25
  • 3
    `.Refresh()` just talks about the control redrawing, nothing to do with bindings unless I am very much mistaken! "Forces the control to invalidate its client area and immediately redraw itself and any child controls." [Control.Refresh Method](http://msdn.microsoft.com/en-us/library/system.windows.forms.control.refresh.aspx) – Paul C Jan 24 '13 at 12:39
  • `BindingList` with `BindingSource` and using `ResetBindings` didn't work for me. – PeterX Jan 31 '19 at 05:21
50

Well, it doesn't get much better than that. Officially, you should use

dataGridView1.DataSource = typeof(List); 
dataGridView1.DataSource = itemStates;

It's still a "clear/reset source" kind of solution, but I have yet to find anything else that would reliably refresh the DGV data source.

Alan
  • 6,501
  • 1
  • 28
  • 24
  • This is the way I have been doing it for a long time and it seems to work the best. It causes all of your data bound controls to check to see if their data has been updated and then refresh if necessary. – John Chuckran Oct 31 '08 at 15:43
  • cheers for validating that. i was hoping you could just change the datasource then perform a refresh of some kind. :P it just seemed like a logical solution. oh well – shaunf Oct 31 '08 at 16:05
  • 1
    What's the significance of using 'typeof(List)' instead of null? – GWLlosa Jul 10 '09 at 19:04
  • 5
    @GWLlosa, if you are using auto generated columns and you set the datasource to NULL it will clear the columns. By using typeof(List) it should maintain the column structure during the refresh. I would personally use AutoGenerateColumns = false; and create the columns on the first pass of the refresh. That way if the user resizes a column it won't toss out their change on refresh. – Chris Porter Jul 13 '09 at 12:01
  • 3
    @Alan Why not use a BindingSource and then you can refresh on that which is so clean and tidy as opposed to re-binding as it were? – Paul C Sep 20 '11 at 21:46
  • 1
    @CodeBlend - Well, if you mean BindingSource.ResetBindings(), it just does not refresh the items for me. The DataSource property needs to be "cleared" and set again to see any changes. A working example with BindingSource would be much appreciated ;) – Alan Sep 30 '11 at 21:47
  • @Alan There isn't anything wrong with 'clearing' as such but to just call one method to 'refresh' makes life so easy. Let me know what you think =) http://stackoverflow.com/questions/7008361/how-can-i-refresh-c-datagridview-after-update/7079829#7079829 – Paul C Oct 04 '11 at 10:44
  • 1
    What's the problem of just setting `dataGridView1.DataSource` to the new data. This update the grid automatically without removing column's structure even when `AutoGenerateColumns` is true. – S.Serpooshan Oct 09 '13 at 08:09
  • when using dgv1.rowEnter event with this solution. , it causes exception DataGridView rowEnter: “Index -1 does not have a value.” at Application.Run . .net internal bug. instead bind dgv .datasource = bindingsource ; bindingsource.datasurce = list . whenever you add remove to list. bindingsource.resetbindings(false); – bh_earth0 Jun 24 '21 at 14:46
  • I have a custom class 'Chains' inherited from SortableBindingList. I was having issues with CurrencyManager errors when refreshing the data in this class. I added the following 2 lines before refreshing and setting BindingSource. This beautifully resolved the issue. dgvChains.DataSource = typeof(BindingSource); dgvChains.Refresh(); Thanks to @Alan for his solution. – Ashok Garg May 15 '22 at 01:54
  • @CodeBlend, please see my answer above. – Ashok Garg May 15 '22 at 01:59
29

The cleanest, most efficient and paradigm-friendly solution in this case is to use a System.Windows.Forms.BindingSource as a proxy between your list of items (datasource) and your DataGridView:

var itemStates = new List<ItemState>();
var bindingSource1 = new System.Windows.Forms.BindingSource { DataSource = itemStates };
dataGridView1.DataSource = bindingSource1;

Then, when adding items, use Add() method of BindingSource instead of your list's Add() method:

for (var i = 0; i < 10; i++)
{
    bindingSource1.Add(new ItemState { Id = i.ToString() });
    System.Threading.Thread.Sleep(500);
}

This way you adding items to your list and notifying DataGridView about those additions with the same line of code. No need to reset DataGridView's DataSource every time you make a change to the list.

It also worth mentioning that you can drop a BindingSource onto your form directly in Visual Studio's Forms Designer and attach it as a data source to your DataGridView there as well, which saves you a line of code in the above example where I'm doing it manually.

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
3

Observablecollection :Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed. You can enumerate over any collection that implements the IEnumerable interface. However, to set up dynamic bindings so that insertions or deletions in the collection update the UI automatically, the collection must implement the INotifyCollectionChanged interface. This interface exposes the CollectionChanged event, an event that should be raised whenever the underlying collection changes.

Observablecollection<ItemState> itemStates = new Observablecollection<ItemState>();

for (int i = 0; i < 10; i++) { 
    itemStates.Add(new ItemState { Id = i.ToString() });
  }
 dataGridView1.DataSource = itemStates;
Kashif
  • 2,926
  • 4
  • 20
  • 20
0

This is copy my answer from THIS place.

Only need to fill datagrid again like this:

this.XXXTableAdapter.Fill(this.DataSet.XXX);

If you use automaticlly connect from dataGridView this code create automaticlly in Form_Load()

Community
  • 1
  • 1
starko
  • 1,150
  • 11
  • 26
0

You are setting the datasource inside of the loop and sleeping 500 after each add. Why not just add to itemstates and then set your datasource AFTER you have added everything. If you want the thread sleep after that fine. The first block of code here is yours the second block I modified.

for (int i = 0; i < 10; i++) { 
    itemStates.Add(new ItemState { Id = i.ToString() });
    dataGridView1.DataSource = null;
    dataGridView1.DataSource = itemStates;
    System.Threading.Thread.Sleep(500);
}

Change your Code As follows: this is much faster.

for (int i = 0; i < 10; i++) { 
    itemStates.Add(new ItemState { Id = i.ToString() });

}
    dataGridView1.DataSource = typeof(List); 
    dataGridView1.DataSource = itemStates;
    System.Threading.Thread.Sleep(500);
Stix
  • 485
  • 4
  • 13
0

Just delete all Rows and fill it after deleting:

BindingList<ItemState> itemStates = new BindingList<ItemState>();
datagridview1.Rows.Clear();

for(int i = 0; i < 10; i++)
{
    itemStates.Add(new ItemState { id = i.ToString() });
}

datagridview1.DataSource = itemStates;
Thread.Sleep(500);
David
  • 1
-5

Try this Code

List itemStates = new List();

for (int i = 0; i < 10; i++)
{ 
    itemStates.Add(new ItemState { Id = i.ToString() });
    dataGridView1.DataSource = itemStates;
    dataGridView1.DataBind();
    System.Threading.Thread.Sleep(500);
}
Georg
  • 90
  • 2