ReactiveUI 6 instant search (MVVM) - c#

I would like to react to user's typing within a text box.
Actually my question is similar to what has already been posted at Reactive Extensions Instant Search for WPF/MVVM.
Now that we're at the release 6 of ReactiveUI, the previous code is out of date. How can I implement it (with MVVM, i.e. not using events)?

The compelling example # http://reactiveui.net/ should help alot. You also want to make sure your xaml TextBox uses the UpdateSourceTrigger
<TextBox Text="{Binding SearchQuery, UpdateSourceTrigger=PropertyChanged}" Width="50" />
Code from RXUI copied here:
public class SearchViewModel : ReactiveObject, IRoutableViewHost
{
public ReactiveList<SearchResults> SearchResults { get; set; }
private string searchQuery;
public string SearchQuery {
get { return searchQuery; }
set { this.RaiseAndSetIfChanged(ref searchQuery, value); }
}
public ReactiveCommand<List<SearchResults>> Search { get; set; }
public ISearchService SearchService { get; set; }
}
and the constructor code
// Constructor
public SearchViewModel(ISearchService searchService = null)
{
SearchService = searchService ?? Locator.Current.GetService<ISearchService>();
// Here we're describing here, in a *declarative way*, the conditions in
// which the Search command is enabled. Now our Command IsEnabled is
// perfectly efficient, because we're only updating the UI in the scenario
// when it should change.
var canSearch = this.WhenAny(x => x.SearchQuery, x => !String.IsNullOrWhiteSpace(x.Value));
// ReactiveCommand has built-in support for background operations and
// guarantees that this block will only run exactly once at a time, and
// that the CanExecute will auto-disable and that property IsExecuting will
// be set according whilst it is running.
Search = ReactiveCommand.CreateAsyncTask(canSearch, async _ => {
return await searchService.Search(this.SearchQuery);
});
// ReactiveCommands are themselves IObservables, whose value are the results
// from the async method, guaranteed to arrive on the UI thread. We're going
// to take the list of search results that the background operation loaded,
// and them into our SearchResults.
Search.Subscribe(results => {
SearchResults.Clear();
SearchResults.AddRange(results);
});
// ThrownExceptions is any exception thrown from the CreateAsyncTask piped
// to this Observable. Subscribing to this allows you to handle errors on
// the UI thread.
Search.ThrownExceptions
.Subscribe(ex => {
UserError.Throw("Potential Network Connectivity Error", ex);
});
// Whenever the Search query changes, we're going to wait for one second
// of "dead airtime", then automatically invoke the subscribe command.
this.WhenAnyValue(x => x.SearchQuery)
.Throttle(TimeSpan.FromSeconds(1), RxApp.MainThreadScheduler)
.InvokeCommand(this, x => x.Search);
}

Related

Execute Bundles of Parallel Tasks in Series

I have a GUI that allows users to make changes to various objects (which embody some internal rules), then click Save to write the changes to a database using EF6. When they make a change to some objects, I want to first deactivate all the currently active objects in the database (the order is not important), and then when that is completed, I would like to insert new, active objects (again, in any order).
I thought I would be able to achieve this by making two lists of tasks - one for deactivating objects (deactivate_Tasks) and the other for adding new objects (add_Tasks). I thought that I would then be able to await Task.WhenAll(deactivate_Tasks) to make sure that the first set of tasks completed in parallel, but in series with the second set, executed on the next line (await Task.WhenAll(add_Tasks)). Thus the additions would only occur once the deactivations have completed.
However, when I run the code, I get seemingly erratic results, with all the adding and deactivating tasks occurring in an unpredictable order. I don't know why this is and I would like to avoid it - any suggestions would be really welcome.
I have mocked up a sample of my code below to help - hopefully the method names are self-explanatory enough, but please ask if not. In the real case, there are more types of rule to be added and deactivated. I am using C# 4.8 and writing a windows WPF application using the MVVM framework as much as I can.
public class MyViewModel
{
private ICommand _saveCommand;
public ICommand SaveCommand
{
get
{
return _saveCommand ?? (_saveCommand = new RelayCommand(execute => Save(), canExecute => IsDirty));
}
set
{ _saveCommand = value; }
}
private RuleDataService _ruleDataService { get; set; }
public async void Save()
{
var add_Tasks = new List<Task<StatusCode>>();
var deactivate_Tasks = new List<Task<StatusCode>>();
if (CustomerRule_IsDirty)
{
add_Tasks.Add(_rulesDataService.CREATE_Rule(newCustomerRule));
if (existingCustomerRule != null)
{
deactivate_Tasks.Add(_rulesDataService.DEACTIVATE_Rule(existingCustomerRule));
}
}
if (WarehouseRule_IsDirty)
{
add_Tasks.Add(_rulesDataService.CREATE_Rule(newWarehouseRule));
if (existingWarehouseRule != null)
{
deactivate_Tasks.Add(_rulesDataService.DEACTIVATE_Rule(existingWarehouseRule))
}
}
//MY COMMENTS REFLECT WHAT I HOPED TO ACHIEVE, NOT WHAT ACTUALLY HAPPENS
//wait for all the deactivations to be done
await Task.WhenAll(deactivate_Tasks).ConfigureAwait(false);
//once everything is deactivated, add the replacements
await Task.WhenAll(add_Tasks).ConfigureAwait(false);
}
}
/// <summary>
/// Sample only - hopefully the methods names used above are self explanatory, but they all return a Task<StatusCode>
/// </summary>
public class RuleDataService
{
public async Task<StatusCode> DEACTIVATE_Rule(Customer_Rule customer_Rule)
{
using(var context = new DBContext())
{
context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
return await Task.Run(() =>
{
foreach (var existingRule in context.Customer_Rules.Where(r => r.CustomerName == customer_Rule.CustomerName && r.IsActive && r.RuleSetId != customer_Rule.RuleSetId))
{
existingRule.IsActive = false;
}
context.SaveChanges();
return StatusCode.TaskComplete;
}).ConfigureAwait(false);
}
}
public async Task<StatusCode> DEACTIVATE_Rule(Warehouse_Rule warehouse_Rule)
{
//basically the same as the other DEACTIVATE methods, just a different table
}
public async Task<StatusCode> CREATE_Rule(Customer_Rule customer_Rule)
{
//basically the same as the other DB methods, but performs an Add instead of an UPDATE
}
public async Task<StatusCode> CREATE_Rule(Warehouse_Rule warehouse_Rule)
{
//basically the same as the other DB methods, but performs an Add instead of an UPDATE
}
}
I have done a fair amount of googling for answers, but the answers only seem to give advice on how to run a series of tasks in parallel, which I have achieved, not how to bundle up in-series sets of parallel actions.
All async tasks are created "hot". In other words, when your code calls the method that returns a task, that method invocation is what starts the task. The task is in progress by the time it is returned. It's still in progress when it's added to the list.
So, if you want to delay the creation of the second batch, don't call those methods yet:
public async Task Save()
{
var deactivate_Tasks = new List<Task<StatusCode>>();
if (CustomerRule_IsDirty)
{
if (existingCustomerRule != null)
{
deactivate_Tasks.Add(_rulesDataService.DEACTIVATE_Rule(existingCustomerRule));
}
}
if (WarehouseRule_IsDirty)
{
if (existingWarehouseRule != null)
{
deactivate_Tasks.Add(_rulesDataService.DEACTIVATE_Rule(existingWarehouseRule))
}
}
await Task.WhenAll(deactivate_Tasks).ConfigureAwait(false);
var add_Tasks = new List<Task<StatusCode>>();
if (CustomerRule_IsDirty)
{
add_Tasks.Add(_rulesDataService.CREATE_Rule(newCustomerRule));
}
if (WarehouseRule_IsDirty)
{
add_Tasks.Add(_rulesDataService.CREATE_Rule(newWarehouseRule));
}
await Task.WhenAll(add_Tasks).ConfigureAwait(false);
}
await is akin to "continue execution when this task is done". Notably, it does not say anything at all about when the task is started. So your example code both add and deactivate tasks will run in parallel, resulting in the behavior you describe. The solution is to start the add tasks after the deactivate tasks. I.e.
// Create all deactivate tasks
if (CustomerRule_IsDirty && existingCustomerRule != null)
{
deactivate_Tasks.Add(_rulesDataService.DEACTIVATE_Rule(existingCustomerRule));
}
if (WarehouseRule_IsDirty && existingWarehouseRule != null)
{
deactivate_Tasks.Add(_rulesDataService.DEACTIVATE_Rule(existingWarehouseRule))
}
await Task.WhenAll(deactivate_Tasks).ConfigureAwait(false);
// Create all add tasks
if (CustomerRule_IsDirty)
{
add_Tasks.Add(_rulesDataService.CREATE_Rule(newCustomerRule));
}
if (WarehouseRule_IsDirty)
{
add_Tasks.Add(_rulesDataService.CREATE_Rule(newWarehouseRule));
}
await Task.WhenAll(add_Tasks).ConfigureAwait(false);
also, you are creating the DbContext object in another thread-context than where it is used. I would suggest moving the creation and disposal into the Task.Run(...) just to avoid any potential issues.

Keyboard freezes on Xamarin.Forms Searchbar

I have a custom implemented control which is very memory intensive (it basically displays a group header and an accordionControl for each group).
The CustomControls AccordionControlGrouping and AccordionControl both inherit from StackLayout, so I have implemented my own bindable ItemsSource. I actively change the views of the controls when the ItemsSource changes.
I have optimized this change now (using LINQ and BinarySearch and that stuff) to get from 5sec. per search to 0.8sec.
This wouldn't be a problem per se, but each time a user enters a key the keyboard freezes and is therefore very unconvinient to search.
My SearchBar is bound to the command of a ViewModel like this
<SearchBar x:Name="EventSearchBar"
Text="{Binding SearchText}"
SearchCommand="{Binding SearchCommand}"
Placeholder="Suche"/>
SearchCommand
private Xamarin.Forms.Command _searchCommand;
public System.Windows.Input.ICommand SearchCommand
{
get
{
_searchCommand = _searchCommand ?? new Xamarin.Forms.Command(UpdateAccordionControlGrouping, CanExecuteSearchCommand);
return _searchCommand;
}
}
private bool CanExecuteSearchCommand()
{
return true;
}
SearchText Property
private string _searchText = string.Empty;
public string SearchText
{
get { return _searchText; }
set
{
if (_searchText != value)
{
_searchText = value ?? string.Empty;
RaisePropertyChanged(nameof(SearchText));
// Perform the search
if (SearchCommand.CanExecute(null))
SearchCommand.Execute(null);
}
}
}
UpdateAccordionControlGrouping
private void UpdateAccordionControlGrouping()
{
// Do some LINQ stuff to group and filter a global collection of data.
// And raise the #OnPropertyChanged event for the ItemsSource of my
// custom control.
}
Now how do I make this whole process async?
I have already looked into this article and it didn't work for me.
To run your method 'UpdateAccordionControlGrouping()' async you can do this:
private async Task UpdateAccordionControlGrouping()
{
var newgrouping = await Task.Run(() =>
{
//do CPU bound work here
return newgrouping;
});
// assign newgrouping to the correct control
}
Be aware that if you need to change any data from the view (Bindings etc) you might get a CrossThreadException. You need to use a Dispatcher then as seen here: Dispatcher.Dispatch on the UI thread
If you work with MvvmLight you get to use the DispatchHelper Simple example of DispatcherHelper

ReactiveCommand return values and View feedback loops

I'm familiar with the concepts of MVVM and have used MvvmCross, but am trying out ReactiveUI and trying to wrap my head around some concepts.
I'm writing a tool in WPF (maybe branching to other frameworks) for designers to create and edit data files that are then used by another end-user program. I have a ViewModel that represents a DataModel document and want to perform validation on the data to inform the designers of any potentially breaking behavior. The underlying classes look like this:
public class DataModel
{
// member data here
public void Validate(Validator validator)
{
// perform specific complex validation here and add errors to validator
}
}
// aggregator for validation errors
public class Validator
{
public IList<Error> Errors { get; }
}
The ViewModel should have a ReactiveCommand Validate that the View can bind to a button, but once it's done, I want to display a dialog to the user showing the validation errors or that none were found. Is there a direct way to pass Validator.Errors back to the View, or would I have to create an IObservable or ReactiveList property for the View to subscribe?
Begin Edit
Thanks to help in the comments, I can figure out how to save on user confirmation using UserErrors. Still trying to figure out return values for validation. Here's what I have so far in my ViewModel:
public class ViewModel
{
public DataModel Model { get; }
public ReactiveCommand<List<Error>> { get; protected set; }
public ViewModel(DataModel model)
{
Model = model;
Validate = ReactiveCommand.CreateAsyncObservable<List<Error>>(_ =>
{
Validator validator = new Validator();
Model.Validate(validator);
// not sure how to turn validator.Errors into the IObservable<List<Error>> that ReactiveUI wants me to return.
});
}
}
Would it be better to make Validate a ReactiveCommand<Error> and just call validator.Errors.ToObservable()? Can I still iterate through the errors in my View?
End Edit
Similarly, I would like to have a save function that first performs validation. If no validation errors are found, it saves the DataModel to file. If errors are found, the View should inform the user and get confirmation before saving. What's the ReactiveUI way of handling this feedback loop of:
Execute Save Command -> validation (possibly call Validate command?) -> if errors then request confirmation from View -> save on confirmation or do nothing
As mentioned in comments there are some examples here:
https://github.com/reactiveui/ReactiveUI/blob/master/docs/basics/errors.md
and:
https://github.com/reactiveui/ReactiveUI.Samples/tree/master/ReactiveUI.Samples.Routing
In regards to the different dialogs for different error types, one approach is you could base it off the RecoveryCommand. Eg show the different options based on what you provide, when you fire the UserError you can provide the RecoveryCommands and then do the custom logic based on it.
Then in the view model of where you handle the errors you can do something like:
// The show command will return the decision from the user how to proceed with a error.
// The UserError will have a number of recovery options associated with it, which the dialog
// will present to the user. In testing mode this will likely be the test triggering the recovery command.
// We will wait until one of those recovery commands is executed, then get the result from it.
ShowCommand = ReactiveCommand.CreateAsyncObservable(x =>
{
var userError = x as UserError;
// We must always have a valid user error.
if (userError == null)
{
return Observable.Throw<RecoveryOptionResult>(new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Expected a UserError but got {0}", x)));
}
Error = userError;
Message = Error.ErrorMessage;
// This fancy statement says go through all the recovery options we are presenting to the user
// subscribe to their is executing event, if the event fires, get the return result and pass that back
// as our return value for this command.
return (Error.RecoveryOptions.Select(cmd => cmd.IsExecuting.SelectMany(_ => !cmd.RecoveryResult.HasValue ? Observable.Empty<RecoveryOptionResult>() : Observable.Return(cmd.RecoveryResult.Value)))).Merge().Take(1);
});
I assume as you said that only one command will be executed. I basically will combine the different recovery commands IsExecuting into one, and when the first one is clicked I assume that is the recovery option I want.
Then where you throw the recovery command you can handle it how you need:
var retryCommand = new RecoveryCommand("Retry") { IsDefault = true };
retryCommand.Subscribe(_ => retryCommand.RecoveryResult = RecoveryOptionResult.RetryOperation);
var userError = new UserError(errorMessage, errorResolution, new[] { retryCommand, RecoveryCommand.Cancel });
switch (await UserError.Throw(userError))
{
case RecoveryOptionResult.RetryOperation:
await Setup();
break;
case RecoveryOptionResult.FailOperation:
case RecoveryOptionResult.CancelOperation:
if (HostScreen.Router.NavigateBack.CanExecute(null))
{
HostScreen.Router.NavigateBack.Execute(null);
};
break;
default:
throw new ArgumentOutOfRangeException();
}
Another approach may be to derive off UserError class, and show the different dialogs based on which class type comes up. Eg keep a dictionary of controls that you want to display based on the class type. When you register your handler for displaying the error dialog just show the appropriate dialog.
Below example shows the usage of Commands to do validation. I used an inbuilt do-nothing command called NavigationCommands.Search. Button:Click calls DataModel:Validate which if finds errors, populates the passed in Validator.
MainWindow.xaml
<Window ...>
<Window.CommandBindings>
<CommandBinding Command="NavigationCommands.Search" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed"/>
</Window.CommandBindings>
<Grid>
...
<Button Content="Button" Command="NavigationCommands.Search" HorizontalAlignment="Left" Margin="229,28,0,0" Grid.Row="1" VerticalAlignment="Top" Width="75"/>
</Grid>
</Window>
Mainwindow.xaml.cs
namespace WpfCommands
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
DataModel dm = new DataModel();
public MainWindow()
{
InitializeComponent();
}
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
Validator myValidator = new Validator();
dm.Validate(myValidator);
if (myValidator.Errors.Count > 0)
{
MessageBox.Show("Errors !");
// do something with errors
}
}
}
}
DataModel.cs
namespace WpfCommands
{
public class DataModel
{
public void Validate(Validator v)
{
// do some validation
v.Errors.Add(new Error() { Message = "Error1" });
}
}
// aggregator for validation errors
public class Validator
{
IList<Error> _errors = new List<Error>();
public IList<Error> Errors { get { return _errors; } }
}
public class Error
{
public string Message { get; set; }
}
}

Subscribing To ThrownExceptions in ReactiveUi

I am migrating to version 6 of Reactive UI, and am trying to more completely use the tools it provides, namely ThrownExceptions. Nothing happens when I subscribe to the thrown exceptions property. I'm sure I'm missing something just not sure what it is right now.
In my simplified example, there is a button with a command bound it it.
public ReactiveCommand<object> Delete { get; private set; }
public MainWindowViewModel()
{
Delete = ReactiveCommand.Create();
Delete.Subscribe(e => CommandExec());
Delete.ThrownExceptions.Subscribe(ex => HandleException(ex));
}
private object HandleException(Exception ex)
{
MessageBox.Show("Exception Handled");
return null;
}
public IObservable<object> CommandExec()
{
throw new Exception("throwing");
}
My assumption is that I would see an "Exception Handled" MessageBox when the exception was thrown. I'm sure i'm subscribing to something, it's just not clear what it is right now.
ThrownExceptions only applies to the background operation declared with CreateAsyncXYZ:
var someCmd = ReactiveCommand.CreateAsyncObservable(_ =>
Observable.Throw<Unit>(new Exception("Oh Noes!"));
someCmd.ThrownExceptions.Subscribe(ex => Console.WriteLine(ex.Message));
await someCmd.ExecuteAsync();
>>> Oh Noes!
In ReactiveUI, you should never put Interesting™ code inside the Subscribe block - Subscribe is solely to log the result of operations, or to wire up properties to other properties.

What is the best way to use an asynchronous API with ReactiveUI to populate a collection?

Let us say I have this ReactiveUI view model structure, with Model being some arbitrary model type.
class ViewModel : ReactiveObject
{
private readonly ReactiveList<Model> _models;
public IReactiveList<Model> Models { get { return _models; } }
public IReactiveCommand LoadModels { get; private set; }
public bool LoadingModels { get; private set; } // Notifies;
}
And these models come from a task-based asynchronous API modeled by this interface:
interface ITaskApi
{
Task<IEnumerable<Model>> GetModelsAsync();
}
After taking a look at how Octokit.net's reactive library was written, I wrote the following class to adapt the API to a reactive world:
class ObservableApi
{
private readonly ITaskApi _taskApi;
public IObservable<Model> GetModels() {
return _taskApi.GetModelsAsync().ToObservable().SelectMany(c => c);
}
}
And now, I have written the following ways to implement loading of models inside of the the LoadModels command, in the ViewModel() constructor:
// In both cases, we want the command to be disabled when loading:
LoadModels = new ReactiveCommand(this.WhenAny(x => x.LoadingModels, x => !x.Value));
// First method, with the Observable API;
LoadModels.Subscribe(_ =>
{
LoadingModels = true;
_models.Clear();
observableClient.GetModels().ObserveOnDispatcher()
.Subscribe(
m => _models.Add(m),
onCompleted: () => { LoadingModels = false; });
});
// Second method, with the task API;
LoadModels.Subscribe(async _ =>
{
LoadingModels = true;
try {
var loadedModels = await taskClient.GetModelsAsync();
_models.Clear();
_models.AddRange(loadedModels);
} catch (Exception ex) {
RxApp.DefaultExceptionHandler.OnNext(ex);
} finally {
LoadingModels = false;
}
});
While I think that both ways will do the job, I have the following misgivings:
In the first sample, should I dispose the inner subscription or will that be done when the inner observable completes or errors?
In the second sample, I know that exceptions raised in the GetModelsAsync method will be swallowed.
What is the "best", most idiomatic way to populate a ReactiveList<T> from an asynchronous enumeration (either IObservable<T> or Task<IEnumerable<T>> (or would IObservable<IEnumerable<T>> be better?))?
After taking a look at how Octokit.net's reactive library was written, I wrote the following class to adapt the API to a reactive world:
While you sometimes want to do this (i.e. flatten the collection), it's usually more convenient to just leave it as IEnumerable<T> unless you then plan to invoke an async method on each item in the list. Since we just want to stuff everything in a List, we don't want to do this. Just leave it as Task<T>
In the first sample, should I dispose the inner subscription or will that be done when the inner observable completes or errors?
Any time you have a Subscribe inside another Subscribe, you probably instead want the SelectMany operator. However, there is a better way to do what you're trying to do, you should check out this docs article for more info.
So, here's how I would write your code:
// In both cases, we want the command to be disabled when loading:
LoadModels = new ReactiveCommand();
LoadModels.RegisterAsyncTask(_ => taskClient.GetModelsAsync())
.Subscribe(items =>
{
// This Using makes it so the UI only looks at the collection
// once we're totally done updating it, since we're basically
// changing it completely.
using (_models.SuppressChangeNotifications())
{
_models.Clear();
_models.AddRange(items);
}
});
LoadModels.ThrownExceptions
.Subscribe(ex => Console.WriteLine("GetModelsAsync blew up: " + ex.ToString());
// NB: _loadingModels is an ObservableAsPropertyHelper<bool>
LoadModels.IsExecuting
.ToProperty(this, x => x.LoadingModels, out _loadingModels);

Categories