问题
I understand how to use delegates to update controls on the main control thread, works like a charm. My problem here is if I'm adding a large DataSet
(say 2000 items) to a bound DataGridView
, it takes 5-8 seconds for the grid to populate and during that 5-8 seconds the whole GUI is locked. How can I update the DataGridView
such that it doesn't lock the user interface?
To be clear, the problem isn't that I'm doing a slow query to a database and the UI is blocking on that, I already have the DataSet object[]
and adding the array of objects to a BindingList<object>
which the DataGrid
is bound to so:
BindingList<object> dataProvider = new BindingList<object>();
DataGridView gridView = new DataGridView();
gridView.DataSource = dataProvider;
// ...stuff happens...
object[] source = dataSet; //of 2000 items
foreach (object item in source) { //this foreach blocks
dataProvider.Add(item);
}
I tried various things (that I knew wouldn't work but figured I'd see) like creating a delegate that did the dataProvider.Add()
, but that didn't matter since it still had to happen on the control thread.
A couple good suggestions revolved around building the BindingList
first and then setting the gridView.DataSource
. While this works (it updates the grid instantly), the only way I see to add more data is to create another new BindingList
, do a gridView.DataSource.copyTo()
(to get the existing data) and add the new data on top of that, then set the gridView.DataSource
to the new BindingList
. This won't work for me since the objects in my list are not static, they are each uploading data to a server asynchronously, and copying them to a new BindingList
would cause problems.
回答1:
You are adding records while the GridView is linked to the DataSource. This means it will update the layout each time.
How about you first fill your DataSource and only then set the DataSource property?
gridView.DataSource = null;
...stuff happens...
object[] source = dataSet; //of 2000 items
foreach (object item in source) { //this foreach blocks
dataProvider.Add(item);
}
gridView.DataSource = dataProvider;
The foreach loop could go to another thread but I don't think you will need that.
回答2:
Like Nick said BackgroundWorker
is probably your best bet.
Here is a very simple example
public partial class Form1 : Form
{
BackgroundWorker b = new BackgroundWorker();
public Form1()
{
InitializeComponent();
b.RunWorkerCompleted += new RunWorkerCompletedEventHandler(b_RunWorkerCompleted);
b.DoWork += new DoWorkEventHandler(b_DoWork);
}
void b_DoWork(object sender, DoWorkEventArgs e)
{
// build dataset here and assigning it to results
e.Result = dataset;
}
void b_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// assign the dataset you built in DoWork in the gridview and update it
dataGridView1.DataSource = e.Result;
dataGridView1.Update();
}
private void button1_Click(object sender, EventArgs e)
{
b.RunWorkerAsync();
}
}
回答3:
Look into using a BackgroundWorker. I don't know much about them myself, but a little searching on Google reveals that as a good possibility.
来源:https://stackoverflow.com/questions/1296340/nonblocking-update-to-a-datagridview