-
Notifications
You must be signed in to change notification settings - Fork 0
Async Commands
An async operation has a state: it is in operation, or it stopped with an error, or a person cancelled it. Usually you write that state manually. Rambla writes it for you.
Put [StateCommand] on an async method. The generator writes four members.
using Rambla;
public partial class SearchViewModel : RamblaState
{
[StateCommand(CancelPrevious = true)]
private async Task SearchAsync(CancellationToken token)
=> Results = await _api.SearchAsync(Query, token);
}The example makes these members:
| Member | Type | Function |
|---|---|---|
SearchCommand |
AsyncStateCommand |
Bind a button to this command. |
IsSearching |
bool |
true while the run is in operation. |
SearchError |
Exception? |
The error of the last run, or null. |
CancelSearchCommand |
ICommand |
Cancels the run. |
IsSearching and SearchError are properties of your view model. They notify
through the same flush as your other properties.
The method must obey these rules:
- It must give a
Task. ATask<T>is not permitted in V1. - It must have no parameters, or one
CancellationTokenparameter. - It must not be static. It must not be generic.
If the method does not obey these rules, the generator makes an error. The errors are RMB006 to RMB009.
By default, the command refuses a second run while a run is in operation.
CanExecute gives false, thus the bound button disables itself.
[StateCommand] // Default: refuse a second run.
private Task SaveAsync() => _repository.SaveAsync();With CancelPrevious, the policy is latest-wins. A new run cancels the run in
operation and replaces it. Use this policy for a search that runs while a person
types.
[StateCommand(CancelPrevious = true)]
private Task SearchAsync(CancellationToken token) => _api.SearchAsync(Query, token);The cancelled run cannot change the state of the new run. Only the last run gives
a value to IsSearching and to SearchError.
An error in the method does not go to the caller. The command puts the error in the error property. A person starts a command from the UI, and no code can catch an error from that operation.
<TextBlock Text="{Binding SearchError.Message}"
Visibility="{Binding SearchError, Converter={StaticResource NullToCollapsed}}" />The command deletes the error when the next run starts.
Cancellation is not an error. If your method makes an OperationCanceledException
from the token of the command, the error property stays null.
<Button Content="Search" Command="{Binding SearchCommand}" />
<Button Content="Cancel" Command="{Binding CancelSearchCommand}" />
<ProgressBar IsIndeterminate="True"
Visibility="{Binding IsSearching, Converter={StaticResource BoolToVisible}}" />The cancel command is enabled only while the run is in operation. Do not write code that enables or disables these buttons.
The generator removes the suffix Async. Then it makes the name of the busy
property with the English -ing form: Save gives IsSaving, and Submit
gives IsSubmitting.
The -ing rule cannot be correct for all verbs. If the name is not correct, give
the name:
[StateCommand(Name = "SignIn", BusyName = "IsSigningIn", ErrorName = "SignInFailure")]
private Task LoginAsync() => _auth.LoginAsync();You can make the command manually:
using Rambla;
var command = new AsyncStateCommand(
execute: token => RefreshAsync(token),
canExecute: () => IsConnected,
cancelPrevious: false,
scheduler: Scheduler);
await command.ExecuteAsync(); // Await the run. It does not throw.
command.Cancel(); // Cancel the run.
command.NotifyCanExecuteChanged(); // Call this if the canExecute data changed.| Member | Function |
|---|---|
IsRunning |
true while the run is in operation. |
Error |
The error of the last run, or null. |
CancelCommand |
Cancels the run. |
CanBeCanceled |
true while there is a run to cancel. |
StateChanged |
Occurs at each change of the run state. |
CanExecuteChanged |
Occurs when CanExecute can be different. |
Start a command from the UI thread. The command then gives the values of
IsRunning, Error and CanExecuteChanged on that same thread, for all
schedulers.
Warning: version 0.5.0 has a defect in this behavior. The run continued on a
thread pool thread. With the ImmediateStateScheduler, the button then stayed
disabled after the first run that was truly asynchronous. Version 0.5.1 corrects
this defect. Use version 0.5.1 or a later version.
Refer to Core Semantics for the full contract.
Rambla — state for real-time .NET desktop applications · MIT · github.com/nicoseijas/Rambla · This wiki follows ASD-STE100.
Use Rambla
Reference