Reactive Extensions即时搜索WPF / MVVM

我想实现一个TextBox,当你输入时,结果立即出现在另一个ListBox中。 我一直在寻找Reactive Extensions(Rx)的例子,我找到的所有例子都使用Observable.FromEventPattern()和TextBox的TextChanged事件:

  • 使用Rx(Reactive Extensions)实现简单的即时搜索 (这实际上是WinForms)
  • 启用Rx的WPF自动完成文本框 – 第1/2部分
  • 使用Reactive Extensions搜索TextChanged

我正在使用WPF和MVVM,因此我无法直接访问TextBox或其事件。

我也偶然发现了这个答案 ,它显示了如何在MVVM设置中使用Observable.FromEventPattern() ,但我希望能找到比捕获每个PropertyChanged事件更好的东西。

什么是Observable.FromEventPattern()一个很好的替代品,可以很好地与WPF / MVVM一起使用?

这与ReactiveUI一起工作。

该解决方案基于ReactiveUI上的博客文章 ,但代码有点过时 。 我在BitBucket上托管解决方案 ,以方便访问。 它使用ReactiveUI 5.5.1。

这是该解决方案的ViewModel。 SearchText绑定到用户键入其查询的View中的TextBox ,而SearchResults绑定到显示结果的ListBox

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections.ObjectModel; using System.Threading.Tasks; using System.ComponentModel; using System.Reactive.Linq; using System.Windows.Input; using ReactiveUI; namespace ReactiveExtensionsSearch { public class MainWindowViewModel : ReactiveObject { private string[] _repository = new string[] { "Mario", "Maria", "Gigi", "Jack", "James", "Jeremy" }; private ObservableAsPropertyHelper> _searchResults; private string _searchText; private ICommand _executeSearchCommand; public string SearchText { get { return _searchText; } set { this.RaiseAndSetIfChanged(ref _searchText, value); } } public ObservableCollection SearchResults { get { return _searchResults.Value; } } public MainWindowViewModel() { var executeSearchCommand = new ReactiveCommand(); var results = executeSearchCommand.RegisterAsyncFunction(s => { return ExecuteSearch(s as string); }); _executeSearchCommand = executeSearchCommand; this.ObservableForProperty("SearchText") .Throttle(TimeSpan.FromMilliseconds(800)) .Select(x => x.Value) .DistinctUntilChanged() .Where(x => !string.IsNullOrWhiteSpace(x)) .Subscribe(_executeSearchCommand.Execute); _searchResults = new ObservableAsPropertyHelper>(results, _ => raisePropertyChanged("SearchResults")); } private ObservableCollection ExecuteSearch(string searchText) { var q = from s in _repository where s.ToLower().StartsWith(searchText.ToLower()) select s; var results = new ObservableCollection(q); return results; } } } 

这是我在这个实例中使用的方法:

  var query = Observable.FromEventPattern ( h => textBox1.TextChanged += h, h => textBox1.TextChanged -= h) .Throttle(TimeSpan.FromMilliseconds(100)) .ObserveOnDispatcher() .Select(x => textBox1.Text) .DistinctUntilChanged() .Do(x => listBox1.Items.Clear()) .ObserveOn(Scheduler.Default) .Select(x => executeSearch(x)) .Switch() .ObserveOnDispatcher(); query.Subscribe(x => listBox1.Items.Add(x)); 

executeSearch代码具有以下签名: Func>

此查询的重要部分是最终的Switch语句。 它通过仅返回最新的observable的结果将IObservable>转换为IObservable

.ObserveOnDispatcher() &。 .ObserveOn(Scheduler.Default)的调用确保可观察查询的不同部分发生在正确的线程上。

您可以使用行为来实现此function。 一个很好的例子是Catel的UpdateBindingOnTextChanged行为 。 它可以像这样使用:

      

这将在更改和实际更新之间创建500毫秒的延迟。