score:1

Firstly you need to create an IObservable<string> to abstract the changing the value on your control. The "easiest" way to do this would be with a Subject<string>, but most likely its the wrong way to do it.

Below is the code you should put into your ViewModel.

IDisposable _searchSubscriber =
    _searchString
         .Buffer(TimeSpan.FromMillisecond(300))
         .Select(searchString => 
                Observable.StartAsync(cancelToken => 
                      Search(searchString, cancelToken)
                ).Switch()
         .ObserveOn(new DispatcherScheduler())
         .Subscribe(results => Channels = results);

public Task<List<Channel>> Search(string searchTerm, CancellationToken cancel)
{
    var query = dbContext.Channels.Where(x => x.Name.StartsWith(searchTerm));
    return query.ToListAsync(cancel);
}

private BehaviorSubject<string> _searchString = new BehaviorSubject<string>("");
public string SearchString
{
    get { return _searchString.Value; }
    set { _searchString.OnNext(value); OnPropertyChanged("SearchString"); }
}

Rx.net is an extremely powerful library, which of course means it does have a bit of a learning curve (although the fact is this is complex because your problem is complex).

Let me lay it out...

.Buffer(TimeSpan.FromMilliseconds(300)) debounces your query, so it only runs the query once every 300 milliseconds.

Observable.StartAsync(cancelToken => Search(searchString, cancelToken)) creates an Observable for the search task, which will be cancelled when it is disposed.

Select(x => ...).Switch() takes only the latest query results, and disposes the last query.

ObserveOn(...) run the following on the scheduler used, make sure you use either DispatchScheduler if you are using WPF, or WinformsScheduler if you use Winforms.

Subscribe(results => ...) do something with the results.


Related Articles