我在基于以下(旧版)示例的 WPF MVVM 应用程序中实现了 ReactiveUI 异步搜索例程:
public class TheViewModel : ReactiveObject
{
private string query;
private readonly ObservableAsPropertyHelper<List<string>> matches;
public TheViewModel()
{
var searchEngine = this.ObservableForProperty(input => input.Query)
.Value()
.DistinctUntilChanged()
.Throttle(TimeSpan.FromMilliseconds(800))
.Where(query => !string.IsNullOrWhiteSpace(query) && query.Length > 1);
var search = searchEngine.SelectMany(TheSearchService.DoSearchAsync);
var latestResults =
searchEngine.CombineLatest(search, (latestQuery, latestSearch) => latestSearch.Query != latestQuery ? null : latestSearch.Matches)
.Where(result => result != null);
matches = latestResults.ToProperty(this, result => result.Matches);
}
public string Query
{
get
{
return query;
}
set
{
this.RaiseAndSetIfChanged(ref query, value);
}
}
public List<string> Matches
{
get
{
return matches.Value;
}
}
}
ReactiveXAML 按预期工作,我可以像这样轻松绑定到 Matches 属性
<ListBox Grid.Row="1" Margin="6" ItemsSource="{Binding Matches}" />
但是我想重构 TheSearchService.DoSearchAsync() 以返回更复杂的结果结构,如下所示:
public class SearchResult
{
public string Query { get; set; }
public List<string> Matches { get; set; }
public int MatchCount { get; set; }
public double SearchTime { get; set; }
}
匹配项仍将表示为List<string>
将绑定到同一个 ListBox 但我还想在每次搜索时绑定到字符串元数据属性,该属性以某种格式返回匹配计数和搜索时间,例如:
string.Format("Found {0} matches in {1}s", x.MatchCount, x.SearchTime)
如何更改 ViewModel 实现以允许每次搜索有多个绑定?
基于公认答案的工作实施
public class TheViewModel : ReactiveObject
{
...
private readonly ObservableAsPropertyHelper<SearchResult> results;
public TheViewModel()
{
...
var latestResults = searchEngine.CombineLatest(search, (latestQuery, latestSearch) => latestSearch.Query != latestQuery ? null : latestSearch)
.Where(result => result != null);
results = latestResults.ToProperty(this, result => result.Result);
}
...
public SearchResult Result
{
get
{
return results.Value;
}
}
}
这是视图
<StackPanel>
<TextBox Text="{Binding Query, UpdateSourceTrigger=PropertyChanged}"
Margin="6"
FontSize="26" />
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="Found {0} matches in {1}s">
<Binding Path="Result.MatchCount" />
<Binding Path="Result.SearchTime" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
<ListBox Grid.Row="1"
Margin="6"
ItemsSource="{Binding Result.Matches}" />