WPF 异步任务<T>阻止 UI



我已经使用过Task类型。当Task没有返回任何结果时,一切都很好。例如:

XAML:

<Button Name="_button"
        Click="ButtonBase_OnClick">
        Click
</Button>  
后台代码:

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));
    });
}

这很好。但是我想让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;
}

这里我们有一个UI阻塞的问题。UI线程被锁定,直到任务返回结果。

_button.IsEnabled = false;

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

你的主线程阻塞了,因为对Task.Result的调用要等到Task完成。相反,您可以在完成Task之后使用Task.ContinueWith()访问Task.Result 。对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());        
}

如果你正在使用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;      
}

最新更新