如何在 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 中停止方法(通过按钮单击触发)的主要内容,如果未能解决你的问题,请参考以下文章

WPF中想要让一个按钮单击事件结束后自动再单击这个按钮,也就是让单机事件连续反复执行,应该如何实现?

如何停止 WPF 中的后台工作人员?

如何实现按钮单击事件 C#

如何以编程方式单击 WPF 中的按钮?

WPF中如何实现重叠的按钮点击事件同时触发

如何捕获在Visual Studio 2015中单击“保存工作项”按钮时触发的事件?