我想实现一个TextBox,当你输入时,结果立即出现在另一个ListBox中。我一直在寻找Reactive Extensions(Rx)的示例,我发现的所有示例都使用Observable.FromEventPattern()
以及TextBox的TextChanged
事件:
我在WPVM中使用WPF,因此我无法直接访问TextBox或其事件。
我也偶然发现了this answer,它显示了如何在MVVM设置中使用Observable.FromEventPattern()
,但我希望能找到比捕获每一个{{1}更好的东西} event。
什么是PropertyChanged
可以很好地替代WPF / MVVM的替代方案?
答案 0 :(得分:3)
使用ReactiveUI。
该解决方案基于blog post ReactiveUI,但代码有点out of date。我在BitBucket上托管solution以便于访问。它使用ReactiveUI 5.5.1。
这是该解决方案的ViewModel。 SearchText
绑定到视图中的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;
}
}
}
答案 1 :(得分: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
语句。它只会返回最新的observable的结果,将IObservable<IObservable<string>>
转换为IObservable<string>
。
拨打.ObserveOnDispatcher()
&amp; .ObserveOn(Scheduler.Default)
确保可观察查询的不同部分发生在正确的线程上。
答案 2 :(得分:0)
您可以使用行为来实现此功能。一个很好的例子是卡特尔的UpdateBindingOnTextChanged behavior。它可以像这样使用:
<TextBox Text="{Binding SearchParam, Mode=TwoWay}">
<i:Interaction.Behaviors>
<catel:UpdateBindingOnTextChanged UpdateDelay="500" />
</i:Interaction.Behaviors>
</TextBox>
这将在更改和实际更新之间产生500毫秒的延迟。