如何在 WPF 中停止方法(通过单击按钮触发)
How to stop a method (triggered by button click) in WPF
我的 WPF 中有一个 private async void Button_Click
方法,它 运行 是一个非常复杂的 SQL 查询,可以 运行 几分钟。
我希望用户可以通过单击另一个按钮来停止此方法。
我的代码是这样的:
public partial class MainWindow : Window
{
private async void Button_Click(object sender, RoutedEventArgs e)
{
string SQL_Query= " a very long and complicated SQL query ... "
SqlCommand SQL_Query_cmd = new SqlCommand(SQL_Query, conn);
DataTable dt = new DataTable();
await Task.Run(() => {
using (SqlDataAdapter a = new SqlDataAdapter(SQL_Query_cmd))
{ a.Fill(dt);}
});
}
}
我在这篇 link How to use WPF Background Worker 中读到了 BackgroundWorker
。
但是不明白如何将它集成到我的代码中。我想,我的“填充数据表”代码已经是异步的,但我不知道如何停止它。假设将要结束此方法的按钮称为 stop_btn
,其 Click 方法称为 cancelButton_Click
.
请将您的答案写在post,而不是评论中。我将不胜感激。
以下是如何使用 IDbCommand.Cancel
method and a CancellationTokenSource
在服务器端和客户端执行取消。
private IDbCommand _activeSqlCommand;
private CancellationTokenSource _cts;
private async void btnExecute_Click(object sender, RoutedEventArgs e)
{
// The _activeSqlCommand and _cts should be null here.
// Otherwise, you may end up with multiple concurrent executions.
Debug.Assert(_activeSqlCommand == null);
Debug.Assert(_cts == null);
var sqlQuery = "A very long and complicated SQL query...";
var localSqlCommand = new SqlCommand(sqlQuery, _connection);
var localCts = new CancellationTokenSource();
_activeSqlCommand = localSqlCommand;
_cts = localCts;
btnExecute.IsEnabled = false;
btnCancel.IsEnabled = true;
try
{
DataTable dataTable = await AsCancelable(Task.Run(() =>
{
var dt = new DataTable();
using (SqlDataAdapter a = new SqlDataAdapter(localSqlCommand))
a.Fill(dt);
return dt;
}, localCts.Token), localCts.Token);
// Here use the dataTable to update the UI
}
catch (OperationCanceledException) { } // Ignore
catch (SqlException ex) when (ex.ErrorCode == CANCEL_ERROR_CODE) { } // Ignore
finally
{
btnCancel.IsEnabled = false;
btnExecute.IsEnabled = true;
// The _activeSqlCommand and _cts should still have the local values here.
Debug.Assert(_activeSqlCommand == localSqlCommand);
Debug.Assert(_cts == localCts);
_activeSqlCommand = null;
_cts = null;
localCts.Dispose();
}
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
_activeSqlCommand?.Cancel();
_cts?.Cancel();
}
private static Task<T> AsCancelable<T>(Task<T> task,
CancellationToken cancellationToken)
{
var cancelable = new Task<T>(() => default, cancellationToken);
return Task.WhenAny(task, cancelable).Unwrap();
}
你得弄清楚数据库服务器在取消执行时抛出什么样的异常,并根据它的ErrorCode
或其他属性来忽略这个异常。
我的 WPF 中有一个 private async void Button_Click
方法,它 运行 是一个非常复杂的 SQL 查询,可以 运行 几分钟。
我希望用户可以通过单击另一个按钮来停止此方法。
我的代码是这样的:
public partial class MainWindow : Window
{
private async void Button_Click(object sender, RoutedEventArgs e)
{
string SQL_Query= " a very long and complicated SQL query ... "
SqlCommand SQL_Query_cmd = new SqlCommand(SQL_Query, conn);
DataTable dt = new DataTable();
await Task.Run(() => {
using (SqlDataAdapter a = new SqlDataAdapter(SQL_Query_cmd))
{ a.Fill(dt);}
});
}
}
我在这篇 link How to use WPF Background Worker 中读到了 BackgroundWorker
。
但是不明白如何将它集成到我的代码中。我想,我的“填充数据表”代码已经是异步的,但我不知道如何停止它。假设将要结束此方法的按钮称为 stop_btn
,其 Click 方法称为 cancelButton_Click
.
请将您的答案写在post,而不是评论中。我将不胜感激。
以下是如何使用 IDbCommand.Cancel
method and a CancellationTokenSource
在服务器端和客户端执行取消。
private IDbCommand _activeSqlCommand;
private CancellationTokenSource _cts;
private async void btnExecute_Click(object sender, RoutedEventArgs e)
{
// The _activeSqlCommand and _cts should be null here.
// Otherwise, you may end up with multiple concurrent executions.
Debug.Assert(_activeSqlCommand == null);
Debug.Assert(_cts == null);
var sqlQuery = "A very long and complicated SQL query...";
var localSqlCommand = new SqlCommand(sqlQuery, _connection);
var localCts = new CancellationTokenSource();
_activeSqlCommand = localSqlCommand;
_cts = localCts;
btnExecute.IsEnabled = false;
btnCancel.IsEnabled = true;
try
{
DataTable dataTable = await AsCancelable(Task.Run(() =>
{
var dt = new DataTable();
using (SqlDataAdapter a = new SqlDataAdapter(localSqlCommand))
a.Fill(dt);
return dt;
}, localCts.Token), localCts.Token);
// Here use the dataTable to update the UI
}
catch (OperationCanceledException) { } // Ignore
catch (SqlException ex) when (ex.ErrorCode == CANCEL_ERROR_CODE) { } // Ignore
finally
{
btnCancel.IsEnabled = false;
btnExecute.IsEnabled = true;
// The _activeSqlCommand and _cts should still have the local values here.
Debug.Assert(_activeSqlCommand == localSqlCommand);
Debug.Assert(_cts == localCts);
_activeSqlCommand = null;
_cts = null;
localCts.Dispose();
}
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
_activeSqlCommand?.Cancel();
_cts?.Cancel();
}
private static Task<T> AsCancelable<T>(Task<T> task,
CancellationToken cancellationToken)
{
var cancelable = new Task<T>(() => default, cancellationToken);
return Task.WhenAny(task, cancelable).Unwrap();
}
你得弄清楚数据库服务器在取消执行时抛出什么样的异常,并根据它的ErrorCode
或其他属性来忽略这个异常。