GUI froze while Dispatcher.BeginInvoke or Task.Sta

2019-07-26 14:27发布

NOTE: Depends on this quetion

Hi. I have a view-model like this:

public class ViewModel {
    private readonly IPersonService _personService;
    private readonly ObservableCollection<SearchPersonModel> _foundedList;
    private readonly DispatcherTimer _timer;
    private readonly Dispatcher _dispatcher;
    private CancellationTokenSource _tokenSource;

    public SearchPatientViewModel(IPersonService personService) {
        _personService = personService;
        _foundedList = new ObservableCollection<SearchPersonModel>();
        _dispatcher = (/*CurrentApplication*/).Dispatcher;
        _timer = new DispatcherTimer(
            TimeSpan.FromMilliseconds(1000),
            DispatcherPriority.Normal,
            TimerCallBack,
            _dispatcher);
        _tokenSource = new CancellationTokenSource();
    }

    public string Term {
        get { return _term; }
        set {
            // implementing INotifyPropertyChanged
            if(_term== value)
                return;
            _term= value;
            OnPropertyChanged(() => Term);
            tokenSource.Cancel(); // canceling prev search query
            _timer.Stop(); // stop the timer to reset it
            // start it again to do a search query if user change not the term for 1000ms
            _timer.Start(); 
        }
    }

    private void TimerCallBack(object sender, EventArgs e) {
        _timer.Stop();
        _tokenSource = new CancellationTokenSource();
        var task = Task<IEnumerable<SearchPersonModel>>.Factory
            .StartNew(Search, _tokenSource.Token);
        _dispatcher.BeginInvoke((Action)(() => {
            _foundedList.Clear();
            foreach(var item in task.Result)
                _foundedList.Add(item);
        }), DispatcherPriority.Background);
    }

    private IEnumerable<SearchPersonModel> Search() {
        return _personService.DoSearch(this.Term);
    }

}

and in the IPersonService implementation I do this:

public class PersonService : IPersonService {
    public IEnumerable<SearchPersonModel> DoSearch(string term){
        System.Threading.Thread.Sleep(10000);
        return some-search-result;
    }
}

However, I expect that while search query is executing, GUI be free. But it froze! Have you any idea where is my mistake? can you help me please? Thanks in advanced!

1条回答
我命由我不由天
2楼-- · 2019-07-26 14:59

The problem is that evaluating task.Result will block until the query has completed.

The simplest option is probably to make the Search method perform the _dispatcher.BeginInvoke call at the end instead.

Another option - which will become easier with C# 5 - would be to add a continuation to the task, so that when it's completed you can update the UI. At the moment you'd use Task.ContinueWith; with C# 5 you'd use async and await.

查看更多
登录 后发表回答