public AppViewModel(ReactiveAsyncCommand reactiveAsyncCommand = null, IObservable <List <FlickrPhoto> > searchResults = null) { ExecuteSearch = reactiveAsyncCommand ?? new ReactiveAsyncCommand(); ReactiveCommandMixins.InvokeCommand(this.ObservableForProperty(model => model.SearchTerm) .Throttle(TimeSpan.FromMilliseconds(800), RxApp.TaskpoolScheduler) .Select(x => x.Value) .DistinctUntilChanged() .Where(x => !string.IsNullOrWhiteSpace(x)), ExecuteSearch); _spinnerVisibility = ExecuteSearch.ItemsInflight.Select(x => x > 0 ? Visibility.Visible : Visibility.Collapsed) .ToProperty(this, model => model.SpinnerVisibility, Visibility.Hidden); var results = searchResults ?? ExecuteSearch.RegisterAsyncFunction(term => GetSearchResultFromFlickr((string)term)); _searchResults = results.ToProperty(this, model => model.SearchResults, new List <FlickrPhoto>()); }
public AppViewModel(ReactiveAsyncCommand testExecuteSearchCommand = null, IObservable <List <FlickrPhoto> > testSearchResults = null) { ExecuteSearch = testExecuteSearchCommand ?? new ReactiveAsyncCommand(); /* Creating our UI declaratively * * The Properties in this ViewModel are related to each other in different * ways - with other frameworks, it is difficult to describe each relation * succinctly; the code to implement "The UI spinner spins while the search * is live" usually ends up spread out over several event handlers. * * However, with RxUI, we can describe how properties are related in a very * organized clear way. Let's describe the workflow of what the user does in * this application, in the order they do it. */ // We're going to take a Property and turn it into an Observable here - this // Observable will yield a value every time the Search term changes (which in // the XAML, is connected to the TextBox). // // We're going to use the Throttle operator to ignore changes that // happen too quickly, since we don't want to issue a search for each // key pressed! We then pull the Value of the change, then filter // out changes that are identical, as well as strings that are empty. // // Finally, we use RxUI's InvokeCommand operator, which takes the String // and calls the Execute method on the ExecuteSearch Command, after // making sure the Command can be executed via calling CanExecute. this.ObservableForProperty(x => x.SearchTerm) .Throttle(TimeSpan.FromMilliseconds(800), RxApp.DeferredScheduler) .Select(x => x.Value) .DistinctUntilChanged() .Where(x => !String.IsNullOrWhiteSpace(x)) .InvokeCommand(ExecuteSearch); // How would we describe when to show the spinner in English? We // might say something like, "The spinner's visibility is whether // the search is running". RxUI lets us write these kinds of // statements in code. // // ExecuteSearch has an IObservable<int> called ItemsInFlight that // fires every time a new item starts or stops. We Select() that into // a Visibility (0 = Collapsed, > 0 = Visible), then we will use RxUI's // ToProperty operator, which is a helper to create an // ObservableAsPropertyHelper object. // // Essentially, we're saying here, "The value of SpinnerVisibility is // the in-flight items Selected into a Visibility" _SpinnerVisibility = ExecuteSearch.ItemsInflight .Select(x => x > 0 ? Visibility.Visible : Visibility.Collapsed) .ToProperty(this, x => x.SpinnerVisibility, Visibility.Hidden); // Here, we're going to actually describe what happens when the Command // gets invoked - we're going to run the GetSearchResultsFromFlickr every // time the Command is executed. // // The important bit here is the return value - an Observable. We're going // to end up here with a Stream of FlickrPhoto Lists: every time someone // calls Execute, we eventually end up with a new list. IObservable <List <FlickrPhoto> > results; if (testSearchResults != null) { results = testSearchResults; } else { results = ExecuteSearch.RegisterAsyncFunction(term => GetSearchResultsFromFlickr((string)term)); } // ...which we then immediately put into the SearchResults Property. _SearchResults = results.ToProperty(this, x => x.SearchResults, new List <FlickrPhoto>()); }