如何在不冻结 UI 的情况下删除大量行?
How can I delete a huge number of rows without freeze the UI?
我想弄清楚如何从 DataGridView
, by delete I mean run a MySql delete query and remove the row itself from the DataGridView
. The issue is do all that without freeze the UI. I'm trying make it work with a BackgroundWorker
. I know it still need do a Invoke()
中删除大量记录,但它仍然冻结 UI,但这些调用发生得相对较快,而其他所有调用稍后会继续在后台进行,例如删除数据库,这是最耗时的任务。如果有更好的办法欢迎。
我当前的实现显然不起作用,row.Index
始终是 -1
,如下所示:
点击按钮:
var rows = dataGridView1.SelectedRows;
backgroundWorker.RunWorkerAsync(rows);
然后是backgroundWork的doWork事件:
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
using (MySqlConnection con = Database.GetConnection())
{
using (MySqlCommand cmd = con.CreateCommand())
{
cmd.Connection = con;
con.Open();
var SelectedRows = e.Argument as DataGridViewSelectedRowCollection;
int rowIndex = 0;
int total = SelectedRows.Count;
foreach (DataGridViewRow row in SelectedRows)
{
int id = getIdByRow_safe(row.Index); //row.Index is -1
cmd.CommandText = string.Format("delete from xxx where id = {1}", id);
bool deleted = cmd.ExecuteNonQuery() == 1;
if (!deleted)
{
/* error handling ... */
}
int progress = (int)(100.0 / total * ++rowIndex);
BackgroundWorker.ReportProgress(progress, row);
}
}
}
}
和 getId() 函数及系列:
delegate int getIDEventHandler(int i);
int getIdByRow(int index)
{
return (int)dataGridView1.Rows[index].Cells["id"].Value;
}
int getIdByRow_safe(int index)
{
if (dataGridView1.InvokeRequired)
return (int) dataGridView1.Invoke(new getIDEventHandler(getIdByRow), new object[] { index });
return getIdByRow(index);
}
我该如何解决这个问题?
我建议过渡到 async
/await
而不是 Background/Worker
。 Here's the MSDN 关于 async
/await
。这是 Microsoft 的用法示例:
// Three things to note in the signature:
// - The method has an async modifier.
// - The return type is Task or Task<T>. (See "Return Types" section.)
// Here, it is Task<int> because the return statement returns an integer.
// - The method name ends in "Async."
async Task<int> AccessTheWebAsync()
{
// You need to add a reference to System.Net.Http to declare client.
HttpClient client = new HttpClient();
// GetStringAsync returns a Task<string>. That means that when you await the
// task you'll get a string (urlContents).
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();
// The await operator suspends AccessTheWebAsync.
// - AccessTheWebAsync can't continue until getStringTask is complete.
// - Meanwhile, control returns to the caller of AccessTheWebAsync.
// - Control resumes here when getStringTask is complete.
// - The await operator then retrieves the string result from getStringTask.
string urlContents = await getStringTask;
// The return statement specifies an integer result.
// Any methods that are awaiting AccessTheWebAsync retrieve the length value.
return urlContents.Length;
}
我想弄清楚如何从 DataGridView
, by delete I mean run a MySql delete query and remove the row itself from the DataGridView
. The issue is do all that without freeze the UI. I'm trying make it work with a BackgroundWorker
. I know it still need do a Invoke()
中删除大量记录,但它仍然冻结 UI,但这些调用发生得相对较快,而其他所有调用稍后会继续在后台进行,例如删除数据库,这是最耗时的任务。如果有更好的办法欢迎。
我当前的实现显然不起作用,row.Index
始终是 -1
,如下所示:
点击按钮:
var rows = dataGridView1.SelectedRows;
backgroundWorker.RunWorkerAsync(rows);
然后是backgroundWork的doWork事件:
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
using (MySqlConnection con = Database.GetConnection())
{
using (MySqlCommand cmd = con.CreateCommand())
{
cmd.Connection = con;
con.Open();
var SelectedRows = e.Argument as DataGridViewSelectedRowCollection;
int rowIndex = 0;
int total = SelectedRows.Count;
foreach (DataGridViewRow row in SelectedRows)
{
int id = getIdByRow_safe(row.Index); //row.Index is -1
cmd.CommandText = string.Format("delete from xxx where id = {1}", id);
bool deleted = cmd.ExecuteNonQuery() == 1;
if (!deleted)
{
/* error handling ... */
}
int progress = (int)(100.0 / total * ++rowIndex);
BackgroundWorker.ReportProgress(progress, row);
}
}
}
}
和 getId() 函数及系列:
delegate int getIDEventHandler(int i);
int getIdByRow(int index)
{
return (int)dataGridView1.Rows[index].Cells["id"].Value;
}
int getIdByRow_safe(int index)
{
if (dataGridView1.InvokeRequired)
return (int) dataGridView1.Invoke(new getIDEventHandler(getIdByRow), new object[] { index });
return getIdByRow(index);
}
我该如何解决这个问题?
我建议过渡到 async
/await
而不是 Background/Worker
。 Here's the MSDN 关于 async
/await
。这是 Microsoft 的用法示例:
// Three things to note in the signature:
// - The method has an async modifier.
// - The return type is Task or Task<T>. (See "Return Types" section.)
// Here, it is Task<int> because the return statement returns an integer.
// - The method name ends in "Async."
async Task<int> AccessTheWebAsync()
{
// You need to add a reference to System.Net.Http to declare client.
HttpClient client = new HttpClient();
// GetStringAsync returns a Task<string>. That means that when you await the
// task you'll get a string (urlContents).
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();
// The await operator suspends AccessTheWebAsync.
// - AccessTheWebAsync can't continue until getStringTask is complete.
// - Meanwhile, control returns to the caller of AccessTheWebAsync.
// - Control resumes here when getStringTask is complete.
// - The await operator then retrieves the string result from getStringTask.
string urlContents = await getStringTask;
// The return statement specifies an integer result.
// Any methods that are awaiting AccessTheWebAsync retrieve the length value.
return urlContents.Length;
}