Множественные привязки из поиска ReactiveUI с использованием ObservableAsPropertyHelper ‹›

У меня есть рабочая реализация процедуры асинхронного поиска ReactiveUI в приложении WPF MVVM на основе следующего (устаревшего) примера:

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

person syneptody    schedule 10.09.2013    source источник


Ответы (1)


В вашей модели просмотра вместо экземпляра Matches create свойство типа SearchResult скажите MySearchResult. Также реализуйте INotifyPropertyChanged в SearchResult. Обновляйте 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>
person Nitin    schedule 10.09.2013
comment
Спасибо! В соответствии со вкусом ReactiveUI я использовал ObservableAsPropertyHelper ‹SearchResult›. Мое редактирование отражает рабочее решение. - person syneptody; 10.09.2013