Take the 2-minute tour ×

I'm trying to remove the more tradition event handlers from a Silverlight application in favour of using a number of Rx queries to provide a better, easier to manage, behavioural abstraction.

The problem that I need to solve, but can't quite crack it the way I want, is getting the behaviour of a search screen working. It's pretty standard stuff. This is how it should behave:

  • I have a text box where the user can enter the search text.
  • If there is no text (or whitespace only) then the search button is disabled.
  • When there is non-whitespace text then the search button is enabled.
  • When the user clicks search the text box and the search button are both disabled.
  • When the results come back the text box and the search button are both enabled.

I have these observables (created via some extension methods over the standard events) to work with:

IObservable<IEvent<TextChangedEventArgs>> textBox.TextChangedObservable()
IObservable<IEvent<RoutedEventArgs>> button.ClickObservable()
IObservable<IEvent<LoadingDataEventArgs>> dataSource.LoadingDataObservable()
IObservable<IEvent<LoadedDataEventArgs>> dataSource.LoadedDataObservable()

I have these queries that work at the moment:

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();

And then these subscriptions to wire it all up:

buttonIsEnabled.Subscribe(x => button.IsEnabled = x);
dataSourceIsBusy.Subscribe(x => textBox.IsEnabled = !x);
searchTextReady.Subscribe(x => this.ExecuteSearch(x));

(I do keep references to the disposables returned by the Subscribe methods. I have .ObserveOnDispatcher() added to each observable to ensure the code runs on the UI thread.)

Now while this works there is one thing that bugs me. The select statement in searchTextReady calls textBox.Text.Trim() to obtain the current trimmed search text, but I already have done this in the textBoxText observable. I really don't want to repeat myself so I would like to create a query that combines these observables and this is where I'm failing.

When I try the following query I get re-entrant calls to execute the search:

IObservable<string> searchTextReady =
    from text in textBoxText
    from x in button.ClickObservable()
    select text;

The following query it seems to work for the first query, but then each time I change the text in the text box thereafter the search is automatically executed without clicking the search button:

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

The following query requires a further text change to occur after the search button is clicked and then fails to run again:

IObservable<string> searchTextReady =
    from text in textBoxText
    select text;

Any ideas how I can make this work?

share|improve this question
add comment

4 Answers

These kind of things are tricky on their own, so I ended up writing an M-V-VM + Rx library to help me out - it turns out that with this library, this task is pretty easy; here's the whole code, my blog explains more about how these classes work:

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; }

    ObservableAsPropertyHelper<IEnumerable<MyResult>> _SearchResults;
    public IEnumerable<MyResult> SearchResults {
        get { return _SearchResults.Value; }
share|improve this answer
I like what you've done here, so I've given you an up-vote, but it doesn't directly answer my question so I'm not ready to accept it as the answer. If nothing else comes along I'll have a look at adapting what you've done for my needs. Right now though this would represent a fairly large code change. Good work though! –  Enigmativity Oct 19 '10 at 23:52
Feel free to steal bits and pieces from the codebase - the important idea though is that you shouldn't be using Rx at the Event/View level, it's far better (and testable!) to use it at the ViewModel level and be making decisions on property change notifications and command invocations. –  Paul Betts Oct 20 '10 at 2:09
Also, would adding a StartWith help you here, in setting the initial state of your observables? –  Paul Betts Oct 20 '10 at 2:12
add comment
up vote 1 down vote accepted

Thanks for everyone's help on this question. I couldn't give anyone the answer because I found that the solution was to use the .Switch() operator and no-one gave that answer.

Since I did find the answer to my problem I thought I would post it here to help anyone else trying to do what I've done.

Part of my original problem what that I didn't want to repeat code. So I introduced a Func<string, bool> textIsValid that could be re-used in my IObservable<bool> textBoxTextIsValid and in my desired IObservable<string> searchTextReady.

Func<string, bool> textIsValid = t => !String.IsNullOrEmpty(t);

Now searchTextReady can be defined as follows:

IObservable<string> searchTextReady =
    (from text in textBoxText
     select (from x in button.ClickObservable().TakeUntil(textBoxText)
             where textIsValid(text)
             select text)

So .Switch() works by taking an IObservable<IObservable<string>> and flattening it out into an IObservable<string>.

It's easy to create an IObservable<IObservable<string>> - just write a query against an observable and select some other observable.

For example:

IObservable<Unit> xs= ...;
IObservable<string> ys= ...;

IObservable<IObservable<string>> zss = xs.Select(x => ys);

IObservable<string> zs = zss.Switch();

But this doesn't do anything particularly useful.

If it's changed around as:

IObservable<Unit> clicks = ...;
IObservable<string> texts = ...;
Func<string, bool> isValid = ...;

IObservable<IObservable<string>> zss =
    texts.Select(t =>
            .Where(x => isValid(t))
            .Select(x => t));

IObservable<string> zs = zss.Switch();

The final observable can be described as:

"Whenever the text changes take only the next click provided that the text doesn't change again and such that the text is valid and then select the value that the text changed to."

I hope this answer helps someone else.

share|improve this answer
add comment

Have you tried ForkJoin

Something like:

    IObservable<string> searchTextReady = Observable.ForkJoin(textBoxText, button.ClickObservable());
    searchTextReady.Subscribe( ....
share|improve this answer
Sorry, but ForkJoin only combines the last values of the observables (which also need to be of the same type). I want the searchTextReady observable to fire every time the search button is clicked and return the latest, not last, textBoxText value. Thanks anyway. –  Enigmativity Oct 19 '10 at 2:25
add comment

DistinctUntilChanged is what you are looking for.

E.g. something like this should work:

// low level observables
var dataSourceLoading = ... // "loading" observable
var dataSourceLoaded = ... // "loaded" observable
var textChange = Observable.FromEvent<TextChangedEventArgs>(MyTextBox, "TextChanged");
var click = Observable.FromEvent<RoutedEventArgs>(MyButton, "Click");

// higher level observables
var text = textChange.Select(_ => MyTextBox.Text.Trim());
var emptyText = text.Select(String.IsNullOrWhiteSpace);
var searchInProgress = dataSourceLoading.Select(_ => true).Merge(dataSourceLoaded.Select(_ => false));

// enable/disable controls
    .Subscribe(v => MyButton.IsEnabled = !v);
    .Subscribe(v => MyTextBox.IsEnabled = !v);

// load data 
    .CombineLatest(text, (c,t) => new {c,t})
    .DistinctUntilChanged(ct => ct.c)
    .Subscribe(ct => LoadData(ct.t));
share|improve this answer
Thanks for your answer, but it doesn't work. The DistinctUntilChanged only allows a single search. When I change the text and try to search again the observable doesn't load the data. –  Enigmativity Oct 22 '10 at 6:18
The only reason why the observable in my example would produce only 1 value is that "c" coming out of "click" is always the same. Did you try the code exactly as shown in my response? –  PL. Oct 22 '10 at 13:51
Had a few minutes to compile the code (see: s3.amazonaws.com/silverlight/DistinctUntilChanged.html) As you can see it works as expected (e.g. loads the source every time user clicks a button) –  PL. Oct 22 '10 at 19:40
add comment

Your Answer


