需要帮助使WPF应用程序异步更新UI

时间:2019-09-22 15:06:51

标签: c# wpf asynchronous

我无法按预期运行ComputeListener函数。

我希望甚至在 Compute()开始之前就可以在UI上立即更新StatusBlock.Text,但是我无法使其正常工作。

我知道这个问题已经在StackOverflow的其他地方问过了,但是我所见的任何解决方案都不适合我。

在此过程中,我已经处理了几个问题,但是当前的问题是,直到 ComputeListener()完全完成其执行并且所有其他功能都被锁定之后,才可以进行任何更改。 / p>

如果Compute()花费的时间太长,我还希望能够单击关闭按钮来停止应用程序,但这也不会发生。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    public void ComputeListener(object sender, RoutedEventArgs args)
    {
        var t = Task.Run(() => Dispatcher.BeginInvoke(new Action(() => { StatusBlock.Text = "Status: Processing"; })));
        t.Wait();
        Task.Run(() => Dispatcher.BeginInvoke(new Action(() => { Compute(); })));
    }
    private void Compute()
    {
       //code here
    }

2 个答案:

答案 0 :(得分:1)

看起来您已经正确地识别出,在UI线程上执行Compute()将阻止StatusBlock.Text的更改按需要反映在UI中,因此应使用Task。 / p>

但是,在尝试从辅助线程更新UI时,您会遇到跨线程异常,因此您已使用Window.Dispatcher返回了UI线程。

如Icepickle所建议-您想要在这里async / await。我认为以下方法应该有效:

    public async void ComputeListener(object sender, RoutedEventArgs args)
    {
        StatusBlock.Text = "Status: Processing";
        await Task.Run(() => Compute());
    }

    private void Compute()
    {
       //code here
    }

甚至更好:

    public async void ComputeListener(object sender, RoutedEventArgs args)
    {
        StatusBlock.Text = "Status: Processing";
        await ComputeAsync();
    }

    private async Task ComputeAsync()
    {
       //async code here
    }

注意,如果您的Compute()的实现也需要访问UI,您仍然会遇到问题-这是您的业务逻辑(即计算)的代码味道应该独立于WPF前端。

我建议您转向MVVM方法-该Xamarin.Forms documentation很好地解释了一般概念,可以以几乎相同的方式将其应用于WPF

答案 1 :(得分:0)

您应该使用异步调度程序成员Dispatcher.InvokeAsync,而不是将其包装到Task中。

Task.Run始终可以异步执行。 Task.Wait()与强制Task同步执行的想法相矛盾,因为它阻塞线程,直到Task完成,取消或出现故障。阅读Microsoft Docs的注释:Task.Wait。与Task.Run结合使用时,引入死锁的机会很高。因此,如果需要加入多个Task.Wait实例,请始终避免使用Task.WaitAny并使用异步和等待版本Task.WaitAllTask

public partial class MainWindow : Window
{
  public MainWindow()
  {
    InitializeComponent();
  }

  public async void ComputeListener(object sender, RoutedEventArgs args)
  {
    // Don't block while waiting for Dispatcher to execute the action
    await Dispatcher.InvokeAsync(() => StatusBlock.Text = "Status: Processing");

    // Executes asynchronously on a background thread (thread pool thread).
    // Use for CPU bound operations
    await ComputeOnNewBackgroundThreadAsync();

    // Executes asynchronously on the current thread.
    // Use for lightweight async operations 
    Task task = new Task(ComputeOnCurrentUiThread);
    task.Start();
    await task;

    // Waiting for tasks example
    var tasks = new List<Task>();
    Task currentThreadTask = new Task(ComputeOnCurrentUiThread);
    currentThreadTask.Start();
    tasks.Add(currentThreadTask);

    Task backgroundThreadTask = ComputeOnNewBackgroundThreadAsync();
    tasks.Add(backgroundThreadTask);

    // Wait asynchronously for both Task instances to complete
    await Task.WhenAll(tasks);
  }

  private async Task ComputeOnNewBackgroundThreadAsync()
  {
    await Task.Run(
      () =>
      {
        //code here
      });
  }

  private void ComputeOnCurrentUiThread()
  {
    //code here
  }
}