反应式扩展即时搜索 WPF/MVVM

3
我想实现一个TextBox,当您输入时,结果会立即出现在另一个ListBox中。我一直在寻找使用Reactive Extensions(Rx)的示例,我找到的所有示例都使用Observable.FromEventPattern()与TextBox的TextChanged事件: 我正在使用WPF和MVVM,因此无法直接访问TextBox或其事件。
我也发现了这个答案,它展示了如何在MVVM设置中使用Observable.FromEventPattern(),但我希望有比捕获每个PropertyChanged事件更好的替代方案。有没有一个很好的替代方案可以与WPF/MVVM良好地配合使用?
3个回答

3

我使用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<ObservableCollection<string>> _searchResults;
        private string _searchText;
        private ICommand _executeSearchCommand;

        public string SearchText
        {
            get
            {
                return _searchText;
            }
            set
            {
                this.RaiseAndSetIfChanged(ref _searchText, value);
            }
        }

        public ObservableCollection<string> 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<MainWindowViewModel, string>("SearchText")
                .Throttle(TimeSpan.FromMilliseconds(800))
                .Select(x => x.Value)
                .DistinctUntilChanged()
                .Where(x => !string.IsNullOrWhiteSpace(x))
                .Subscribe(_executeSearchCommand.Execute);

           _searchResults = new ObservableAsPropertyHelper<ObservableCollection<string>>(results, _ => raisePropertyChanged("SearchResults"));
        }

        private ObservableCollection<string> ExecuteSearch(string searchText)
        {
            var q = from s in _repository where s.ToLower().StartsWith(searchText.ToLower()) select s;
            var results = new ObservableCollection<string>(q);
            return results;
        }
    }
}

2
这是我在这种情况下会采用的方法:
        var query = Observable.FromEventPattern
            <TextChangedEventHandler, TextChangedEventArgs>(
                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<string, IObservable<string>>
这个查询中重要的部分是最后的Switch语句。它通过仅返回最新可观察对象的结果,将一个IObservable<IObservable<string>>转换为一个IObservable<string>
调用.ObserveOnDispatcher().ObserveOn(Scheduler.Default)确保可观察查询的不同部分发生在正确的线程上。

感谢您提供有用的代码示例。然而,我的问题特别要求一个MVVM友好的解决方案,避免使用Observable.FromEventPattern()。您正在使用正是这个,结合不适合MVVM的事件处理程序。 - Gigi
@Gigi - 你可能需要使用混合方法,因为你接受的答案不能确保仅返回最新的查询结果。一对长时间运行的查询可以以任何顺序返回。我的.Switch()的使用可以防止这种情况发生。 - Enigmativity

0

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

<TextBox Text="{Binding SearchParam, Mode=TwoWay}">
    <i:Interaction.Behaviors>
        <catel:UpdateBindingOnTextChanged UpdateDelay="500" />
    </i:Interaction.Behaviors>
</TextBox>

这将会在变更和实际更新之间创建一个500毫秒的延迟。


这听起来很有趣,但从我的理解来看,它并不完全满足要求。即时搜索功能的一部分是,如果在旧搜索的结果尚未到达时发出新的搜索,则会取消旧搜索。 - Gigi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接