Stepan Loginov
Stepan Loginov

Reputation: 1767

Add rows to DataGridView without freezing GUI

I want to display a table. Data for table must to be precalculated and it is heavy calculation. Also result table is long. I want to provide acces to part of data while another part is calculated in the background.

I wrote background worker for calculation and DataGridView for display. Sometimes worker submit new rows via ProgressChangedEvent. I am tried to add this rows at bottom. My problem starts when I tried add new rows at bottom. My UI is freezes.

Here is how I initialize DataGridView:

private void InitDataGridView() 
{
    BindingList<TableRow> tableRows = new BindingList<TableRow>();
    dataGridView.DataSource = tableRows;
}

and here is how I adding new rows at bottom in ProgressChangedEvent

private void UpdateDataGridView (List<Items> newItems)
{
    BindingList<TableRow> dataSource = (BindingList<TableRow>)this.dataGridView.DataSource;
    foreach (var item in newItems)
        dataSource.Add(new TableRow(item));
}

I think that problem is because adding one row initiate redrawing of the table. But I don't found AddRange for BindingList or mechanism to stop rendering temporary.

Any suggestions?

Upvotes: 1

Views: 1909

Answers (2)

Reza Aghaei
Reza Aghaei

Reputation: 125247

By adding a new item to the list, BindingList raises a ListChanged which leads to updating DataGridView. To make updating of DataGridView more efficient, as an option you can stop raising ListChanged event of the BindingList. This way you can turn it off before adding items and then turn it on and raise the event just after adding a group of items to the list.

Here is an implementation which supports turning ListChanged event on/off:

public class MyBindingList<T>:BindingList<T>
{
    public bool EnableChangeNotifications { get; set; }
    protected override void OnListChanged(ListChangedEventArgs e)
    {
        if(EnableChangeNotifications)
            base.OnListChanged(e);
    }
}

And for example, disable the event when adding items::

list.EnableChangeNotifications = false;
//Add new items to the list using a loop or something else.

Then to showing changes in DataGridView, enable the event and raise it:

list.EnableChangeNotifications = true;
list.ResetBindings();

Upvotes: 1

flyte
flyte

Reputation: 1322

Try setting the AutoSizeColumnsMode property to None or DisplayedCells, as well as set on the AutoSizeMode on each column:

foreach (DataGridViewColumn c in thisGrid.Columns)
{
    c.AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
}

Try toggling this before and after binding, and when updating.

Also, you can try what is suggested on this answer and enable double buffering:

if (!System.Windows.Forms.SystemInformation.TerminalServerSession)
{
  Type dgvType = dataGridView1.GetType();
  PropertyInfo pi = dgvType.GetProperty("DoubleBuffered",
    BindingFlags.Instance | BindingFlags.NonPublic);
  pi.SetValue(dataGridView1, value, null);
}

Upvotes: 0

Related Questions