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毫秒的延迟。