如何在 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或其他属性来忽略这个异常。