WPF异步Task <T>阻止UI

mon*_*str 5 wpf user-interface block task

我已经使用了Task类型.一切都很好而Task没有任何回报.例如:

XAML:

<Button Name="_button"
        Click="ButtonBase_OnClick">
        Click
</Button>  
Run Code Online (Sandbox Code Playgroud)

代码隐藏:

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    _button.IsEnabled = false;

    Task.Factory.StartNew(() =>
    {
        Thread.Sleep(5*1000);
        Dispatcher.Invoke(new Action(() => _button.IsEnabled = true));
    });
}
Run Code Online (Sandbox Code Playgroud)

这很好用.但是我想要Task返回一些值,例如Boolean.所以我需要使用Task<Boolean>:

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    _button.IsEnabled = false;

    var task = Task<Boolean>.Factory.StartNew(() =>
    {
        Thread.Sleep(5*1000);
        return true;
    });

    if (task.Result)
        _button.IsEnabled = true;
}
Run Code Online (Sandbox Code Playgroud)

这里我们遇到了UI阻塞问题.UI线程被锁定,直到任务将返回结果.

_button.IsEnabled = false;
Run Code Online (Sandbox Code Playgroud)

因此,上面的字符串完全被忽略.我在.Net 4.0,所以我不能使用async/await方法.这个问题真的让我感到恶心......有解决方案吗?

Ale*_*ese 8

你的主线程阻塞,因为调用Task.Result等待直到Task完成.相反,你可以使用Task.ContinueWith()访问Task.Result Task已完成.调用TaskScheduler.FromCurrentSynchronizationContext()导致继续在主UI线程上运行(因此您可以_button安全地访问).

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    _button.IsEnabled = false;

    Task<Boolean>.Factory.StartNew(() =>
    {
        Thread.Sleep(5*1000);
        return true;
    }).ContinueWith(t=>
    {
        if (t.Result)
            _button.IsEnabled = true;
    }, TaskScheduler.FromCurrentSynchronizationContext());        
}
Run Code Online (Sandbox Code Playgroud)

更新

如果您使用的是C#5,则可以使用async/await.

private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    _button.IsEnabled = false;

    var result = await Task.Run(() =>
    {
        Thread.Sleep(5*1000);
        return true;
    });

    _button.IsEnabled = result;      
}
Run Code Online (Sandbox Code Playgroud)