如何在 WPF 中停止方法(通过按钮单击触发)
Posted
技术标签:
【中文标题】如何在 WPF 中停止方法(通过按钮单击触发)【英文标题】:How to stop a method (triggered by button click) in WPF 【发布时间】:2021-06-10 09:58:03 【问题描述】:我的 WPF 中有一个私有 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);
);
我在这个链接How to use WPF Background Worker 中读到了BackgroundWorker
。
但不明白如何将它集成到我的代码中。我认为,我的“填充数据表”代码已经是异步的,但我不知道如何停止它。假设要结束这个方法的按钮叫stop_btn
,它的Click方法叫cancelButton_Click
。
请在帖子中写下您的答案,而不是 cmets。我将不胜感激。
【问题讨论】:
您可以为此使用ExecuteReaderAsync_ 和CancellationTokenSource
。
@KlausGütter 谢谢克劳斯。能否请您在帖子中写下您的答案。我不知道如何实施你所说的。另外我没有阅读器,我在数据表中显示结果。我修改了帖子。
这能回答你的问题吗? Fill DataTable asynchronously?
SqlCommand
有一个Cancel()
方法。见docs.microsoft.com/en-us/dotnet/api/…
这能回答你的问题吗? Canceling DataAdapter.Fill()
【参考方案1】:
您可以使用IDbCommand.Cancel
方法和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
或其他一些属性忽略此异常。
【讨论】:
非常感谢。我正在努力理解。到目前为止,我收到了一些关于我的正常 SQL 查询执行的错误。我是否可以要求您将与取消数据库执行相关的部分分开并注释掉其余部分。我希望我至少可以实现那部分。 @Iraj 您可以注释掉_cts?.Cancel();
行,这样CancellationTokenSource
就不会被取消。这样,btnCancel_Click
将不会响应,并且如果数据库服务器决定由于某种原因无法取消查询,则可能根本没有响应。
我现在遇到的问题是应该在数据网格中显示信息的命令不起作用。即:dataGrid1.ItemsSource = dt.DefaultView;
@Iraj dt
现在是 Task.Run
委托中的内部变量。要更新 UI,您可以使用 dataTable
变量。
非常感谢以上是关于如何在 WPF 中停止方法(通过按钮单击触发)的主要内容,如果未能解决你的问题,请参考以下文章