使用ObservableAsPropertyHelper<>来自ReactiveUI搜索的多个绑定

时间:2013-09-10 16:34:37

标签: c# wpf mvvm system.reactive reactiveui

我在WPF MVVM应用程序中有一个基于以下(遗留)示例的ReactiveUI异步搜索例程的工作实现:

http://blog.paulbetts.org/index.php/2010/07/05/reactivexaml-series-implementing-search-with-observableaspropertyhelper/

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}" />

1 个答案:

答案 0 :(得分:1)

在您的viewmodel中,不是让Matches的实例创建类型为SearchResult的属性,而是说MySearchResult。还在SearchResult上实现INotifyPropertyChanged。每次搜索操作后更新SearchResult

现在,您的列表框绑定将类似于<ListBox Grid.Row="1" Margin="6" ItemsSource="{Binding MySearchResult.Matches}" />

要显示搜索结果,您可以使用如下文本块:

 <TextBlock>
    <TextBlock.Text>
        <MultiBinding StringFormat="Found {0} matches in {1}s">
            <Binding Path="MySearchResult.MatchCount"/>
             <Binding Path="MySearchResult.SearchTime"/>
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>