结合Reactive Framework(Rx)查询以提供正确的UI行为的问题

Eni*_*ity 0 .net c# silverlight system.reactive

我正在尝试从Silverlight应用程序中删除更多的传统事件处理程序,转而使用大量Rx查询来提供更好,更易于管理的行为抽象.

我需要解决的问题,但不能按照我想要的方式破解它,是让搜索屏幕的行为正常工作.这是非常标准的东西.这是它应该如何表现:

  • 我有一个文本框,用户可以在其中输入搜索文本.
  • 如果没有文本(或空格只),然后搜索按钮禁用.
  • 当有非空白文本,则搜索按钮激活.
  • 当用户单击搜索时,文本框搜索按钮都被禁用.
  • 结果返回时,文本框搜索按钮都被启用.

我有这些observable(通过标准事件的一些扩展方法创建)来使用:

IObservable<IEvent<TextChangedEventArgs>> textBox.TextChangedObservable()
IObservable<IEvent<RoutedEventArgs>> button.ClickObservable()
IObservable<IEvent<LoadingDataEventArgs>> dataSource.LoadingDataObservable()
IObservable<IEvent<LoadedDataEventArgs>> dataSource.LoadedDataObservable()
Run Code Online (Sandbox Code Playgroud)

我现在有这些查询:

IObservable<bool> dataSourceIsBusy =
    dataSource.LoadingDataObservable().Select(x => true)
    .Merge(dataSource.LoadedDataObservable().Select(x => false));

IObservable<string> textBoxText =
    from x in textBox.TextChangedObservable()
    select textBox.Text.Trim();

IObservable<bool> textBoxTextIsValid =
    from text in textBoxText
    let isValid = !String.IsNullOrEmpty(text)
    select isValid;

IObservable<string> searchTextReady =
    from x in button.ClickObservable()
    select textBox.Text.Trim();
Run Code Online (Sandbox Code Playgroud)

然后这些订阅连接起来:

buttonIsEnabled.Subscribe(x => button.IsEnabled = x);
dataSourceIsBusy.Subscribe(x => textBox.IsEnabled = !x);
searchTextReady.Subscribe(x => this.ExecuteSearch(x));
Run Code Online (Sandbox Code Playgroud)

(我确实保留了对Subscribe方法返回的一次性用法的引用.我已经.ObserveOnDispatcher()添加到每个observable以确保代码在UI线程上运行.)

现在,虽然这有效,但有一件事让我烦恼.searchTextReady调用中的select语句textBox.Text.Trim()获取当前修剪的搜索文本,但我已经在textBoxTextobservable中完成了此操作.我真的不想重复自己,所以我想创建一个结合了这些可观察量的查询,这就是我失败的地方.

当我尝试以下查询时,我会重新调用以执行搜索:

IObservable<string> searchTextReady =
    from text in textBoxText
    from x in button.ClickObservable()
    select text;
Run Code Online (Sandbox Code Playgroud)

以下查询似乎适用于第一个查询,但每次我更改文本框中的文本后,搜索将自动执行而不单击搜索按钮:

IObservable<string> searchTextReady =
    from text in button.ClickObservable()
        .CombineLatest(textBoxText, (c, t) => t)
    select text;
Run Code Online (Sandbox Code Playgroud)

以下查询要求在单击搜索按钮后再进行文本更改,然后再次无法运行:

IObservable<string> searchTextReady =
    from text in textBoxText
      .SkipUntil(button.ClickObservable())
      .TakeUntil(dataSource.LoadingDataObservable())
    select text;
Run Code Online (Sandbox Code Playgroud)

我有什么想法可以使这项工作?

Ana*_*tts 5

这些东西本身很棘手,所以我最终编写了一个MV-VM + Rx库来帮助我 - 事实证明,使用这个库,这项任务非常简单; 这是整个代码,我的博客解释了这些类如何工作的更多信息:

public class TextSearchViewModel
{
    public TextSearchViewModel
    {
        // If there is no text (or whitespace only) then the search button is disabled.
        var isSearchEnabled = this.ObservableForProperty(x => x.SearchText)
            .Select(x => !String.IsNullOrWhitespace(x.Value));

        // Create an ICommand that represents the Search button
        // Setting 1 at a time will make sure the Search button disables while search is running
        DoSearch = new ReactiveAsyncCommand(isSearchEnabled, 1/*at a time*/);

        // When the user clicks search the text box and the search button are both disabled.
        var textBoxEnabled = DoSearch.ItemsInflight
            .Select(x => x == 0);

        // Always update the "TextboxEnabled" property with the latest textBoxEnabled IObservable
        _TextboxEnabled = this.ObservableToProperty(textBoxEnabled, 
            x => x.TextboxEnabled, true);

        // Register our search function to run in a background thread - for each click of the Search
        // button, the searchResults IObservable will produce a new OnNext item
        IObservable<IEnumerable<MyResult>> searchResults = DoSearch.RegisterAsyncFunction(textboxText => {
            var client = new MySearchClient();
            return client.DoSearch((string)textboxText);
        });

        // Always update the SearchResults property with the latest item from the searchResults observable
        _SearchResults = this.ObservableToProperty(searchResults, x => x.SearchResults);
    }

    // Create a standard INotifyPropertyChanged property
    string _SearchText;
    public string SearchText {
        get { return _SearchText; }
        set { this.RaiseAndSetIfChanged(x => x.SearchText, value); }
    }

    // This is a property who will be updated with the results of an observable
    ObservableAsPropertyHelper<bool> _TextboxEnabled;
    public bool TextboxEnabled {
        get { return _TextboxEnabled.Value; }
    }

    // This is an ICommand built to do tasks in the background
    public ReactiveAsyncCommand DoSearch { get; protected set; }

    // This is a property who will be updated with the results of an observable
    ObservableAsPropertyHelper<IEnumerable<MyResult>> _SearchResults;
    public IEnumerable<MyResult> SearchResults {
        get { return _SearchResults.Value; }
    }
} 
Run Code Online (Sandbox Code Playgroud)