【问题标题】:Disable button on a long operation长时间操作时禁用按钮
【发布时间】:2016-10-03 15:44:41
【问题描述】:

在长时间操作期间,我想禁用一个按钮并清除一个文本块。

按钮点击事件在绘制屏幕之前完全处理,因此按钮永远不会被明显禁用

private bool btnEnabled = true;
public bool BtnEnabled
{
    get { return btnEnabled; }
    set
    {
        if (value == btnEnabled)
            return;
        btnEnabled = value;
        NotifyPropertyChanged("BtnEnabled");
    }
}
private string results = "start";
public string Results
{
    get { return results; }
    set
    {
        if (value == results)
            return;
        results = value;
        NotifyPropertyChanged("Results");
    }
}

private void btn_Click(object sender, RoutedEventArgs e)
{
    BtnEnabled = false;
    Results = string.Empty;
    // whould like to displany wait spinner
    LongProcess();
    BtnEnabled = true;
}
private void LongProcess()
{
    //database query
    System.Threading.Thread.Sleep(10000);
    Results = "this is the results";
}

如何让按钮在长事件期间禁用?

我遇到的问题是用户感到沮丧并开始点击,而这些点击都在队列中并得到处理。

【问题讨论】:

  • 使用后台处理运行长代码。这将允许 UI 线程更新并显示更改以禁用按钮。
  • 我认为你可以尝试 async & await 来实现这一点。
  • @Rennie yes await 似乎可以解决问题
  • @Paparazzi 我正在发布答案。

标签: c# .net wpf


【解决方案1】:

要回答您的具体问题,您可以使用类似这样的方法让 UI 处理消息(在您将按钮设置为禁用后):

Application.Current.Dispatcher.Invoke(
             DispatcherPriority.Background, 
             new Action(delegate { })
           );

或者更完整的:

public void DoEvents()
{
    DispatcherFrame frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(ExitFrame), frame);
    Dispatcher.PushFrame(frame);
}

public object ExitFrame(object f)
{
    ((DispatcherFrame)f).Continue = false;
    return null;
}

再说一遍,不建议这样做(这相当于在 Winforms 上使用 Application.DoEvents(),它有很多警告,除非你知道自己在做什么,否则应该像瘟疫一样避免使用)。长时间密集型操作不应该在 UI 线程上执行,或者如果它们不是 CPU 密集型,您可以使用 async/await

【讨论】:

  • 长操作是等待数据库查询
  • 您可以使用 async/await :但话又说回来,这将是一个不同的答案,与您提出的问题不对应
  • 最好禁用按钮(或事件整个窗口),显示光标或其他加载 UI 元素,然后在单独的线程中执行缓慢的过程。当任务结束时,使用 Dispatcher.Invoke(... 在 UI 线程上重新启用您的窗口
【解决方案2】:

这就是我的结局

private async void btn_Click(object sender, RoutedEventArgs e)
{

    BtnEnabled = false;
    Results = string.Empty;
    // whould like to displany wait spinner
    //LongProcess();
    cts = new CancellationTokenSource();
    try
    {
        //cts.Cancel(); just for test
        string result = await WaitAsynchronouslyAsync(cts.Token);
        //string result = await WaitSynchronously();

        BtnEnabled = true;
        Results = result;
    }
    catch (OperationCanceledException)
    {
        Results = "Operation canceled";  // this is not called
    }
    cts = null;
}
// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync(CancellationToken ct)
{
    //DataBaseQuery();  // this just runs async
    //SqlCommand cmd = new SqlCommand();
    //await cmd.ExecuteScalarAsync(ct);
    await Task.Delay(10000);
    return "Finished";
}

【讨论】:

    猜你喜欢
    • 2015-10-08
    • 2018-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多