How can I call an async command with a view model? - xamarin

I have this code and I am wanting to move it into a view model:
resetButton.Clicked += async (sender, e) =>
{
if (App.totalPhrasePoints < 100 || await App.phrasesPage.DisplayAlert(
"Reset score",
"You have " + App.totalPhrasePoints.ToString() + " points. Reset to 0 ? ", "Yes", "No"))
App.DB.ResetPointsForSelectedPhrase(App.cfs);
};
I realize I will need to set up something like this:
In my XAML code;
<Button x:Name="resetButton" Text="Reset All Points to Zero" Command="{Binding ResetButtonClickedCommand}"/>
And in my C# code:
private ICommand resetButtonClickedCommand;
public ICommand ResetButtonClickedCommand
{
get
{
return resetButtonClickedCommand ??
(resetButtonClickedCommand = new Command(() =>
{
}));
}
But how can I fit the async action into a command?

You can try something like this:
(resetButtonClickedCommand = new Command(async () => await SomeMethod()));
async Task SomeMethod()
{
// do stuff
}

And to expand on already provided answer, if you need to pass a parameter to the command, you can use something like
(resetButtonClickedCommand = new Command<object>(async (o) => await SomeMethod(o)));
async Task SomeMethod(object o)
{
// do stuff with received object
}
You could replace object above by anything you want as well.

On further testing, this class may be overkill for most uses.
Despite the downvotes, chawala's answer works fine in my tests.
Importantly, the presence of async on the method declaration is sufficient to avoid blocking UI thread. Therefore, chawala's answer is "not broken"; does not merit those downvotes, imho.
To be clear: the explicit async => await answers are of course perfectly fine, without any issue. Use them if that gives you more confidence.
My answer was intended to make the call site cleaner. HOWEVER, maxc's first comment is correct: what I have done is no longer "identical" to the explicit async => await. So far, I haven't found any situation where that matters. Whether with or without async/await inside new Command, if you click a button several times quickly, all the clicks get queued. I've even tested with SomeMethod switching to a new page. I have yet to find any difference whatsoever to the explicit async/await. All answers on this page have identical results, in my tests.
async void works just as well as async Task, if you aren't using the Task result anyway, and you aren't adding any code to do something useful with any exceptions that occur during this method.
In this class code, see my comment "TBD: Consider adding exception-handling logic here.".
Or to put it another way: most devs are writing code where it makes no difference. If that's a problem, then its going to equally be a problem in their new Command(await () => async SomeMethod()); version.
Below is a convenience class. Using it simplifies combining commands with async.
If you have an async method like this (copied from accepted answer):
async Task SomeMethod()
{
// do stuff
}
Without this class, using that async method in a Command looks like this (copied from accepted answer):
resetButtonClickedCommand = new Command(async () => await SomeMethod());
With the class, usage is streamlined:
resetButtonClickedCommand = new AsyncCommand(SomeMethod);
The result is equivalent to the slightly longer code line shown without using this class. Not a huge benefit, but its nice to have code that hides clutter, and gives a name to an often-used concept.
The benefit becomes more noticeable given a method that takes a parameter:
async Task SomeMethod(object param)
{
// do stuff
}
Without class:
yourCommand = new Command(async (param) => await SomeMethod(param));
With class (same as the no-parameter case; compiler calls the appropriate constructor):
yourCommand = new AsyncCommand(SomeMethod);
Definition of class AsyncCommand:
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Input;
namespace MyUtilities
{
/// <summary>
/// Simplifies using an "async" method as the implementor of a Command.
/// Given "async Task SomeMethod() { ... }", replaces "yourCommand = new Command(async () => await SomeMethod());"
/// with "yourCommand = new AsyncCommand(SomeMethod);".
/// Also works for methods that take a parameter: Given "async Task SomeMethod(object param) { ... }",
/// Usage: "yourCommand = new Command(async (param) => await SomeMethod(param));" again becomes "yourCommand = new AsyncCommand(SomeMethod);".
/// </summary>
public class AsyncCommand : ICommand
{
Func<object, Task> _execute;
Func<object, bool> _canExecute;
/// <summary>
/// Use this constructor for commands that have a command parameter.
/// </summary>
/// <param name="execute"></param>
/// <param name="canExecute"></param>
/// <param name="notificationSource"></param>
public AsyncCommand(Func<object,Task> execute, Func<object, bool> canExecute = null, INotifyPropertyChanged notificationSource = null)
{
_execute = execute;
_canExecute = canExecute ?? (_ => true);
if (notificationSource != null)
{
notificationSource.PropertyChanged += (s, e) => RaiseCanExecuteChanged();
}
}
/// <summary>
/// Use this constructor for commands that don't have a command parameter.
/// </summary>
public AsyncCommand(Func<Task> execute, Func<bool> canExecute = null, INotifyPropertyChanged notificationSource = null)
:this(_ => execute.Invoke(), _ => (canExecute ?? (() => true)).Invoke(), notificationSource)
{
}
public bool CanExecute(object param = null) => _canExecute.Invoke(param);
public Task ExecuteAsync(object param = null) => _execute.Invoke(param);
public async void Execute(object param = null)
{
// TBD: Consider adding exception-handling logic here.
// Without such logic, quoting https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming
// "With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started."
await ExecuteAsync(param);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}
Re comments below about async void Execute. Both class Command and interface ICommand have method void Execute. Being compatible with those, means having the same method signature - so the usually recommended async Task MethodName() is not an option here. See links in my comments for discussion of the implications of having void here.

To instance AsyncCommand with a parameter, is correct this approach:
this.SaveCommand = new AsyncCommand((o) => SaveCommandHandlerAsync (o));
or is need

you can write this also:-
(resetButtonClickedCommand = new Command(DoSomething));
async void DoSomething()
{
// do something
}
Note :- It shows warning at SomeMethod.

Related

Xamarin Forms Commands with Async Methods without lambda

I am trying to pass an asynchronous method to a command in xamarin forms. In microsoft docs, the sample codes are provided with lambda expressions. As I am pretty new at c#, I want to see the explicit form of it to understand the concept clearly:
The code with lambda:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
NavigateCommand = new Command<Type>(
async (Type pageType) =>
{
Page page = (Page)Activator.CreateInstance(pageType);
await Navigation.PushAsync(page);
});
BindingContext = this;
}
public ICommand NavigateCommand { private set; get; }
}
So, my question is how to retype NavigationCommand without lambda. I think It would be more beneficial to the beginners.
Thanks a lot for any respond.
You could check the following code
NavigateCommand = new Command<Type>((pageType) => TestCommand(pageType));
async void TestCommand(Type pageType)
{
Page page = (Page)Activator.CreateInstance(pageType);
await Navigation.PushAsync(page);
}
If your method has no argument, you could init it like
NavigateCommand = new Command(TestCommand);
async void TestCommand()
{
//...
}

ReactiveUI ReactiveCommand and Xamarin 4.0 Shell Navigation

Using Xamarin Forms 4.0 with the new Shell feature, I have a button on a page .Let's call this page the Start Page.
When I press the button the user is navigated to a Second Page using Shell.Current.GoToAsync.
When the user later navigates back to the Start Page the button is now deactiviated. But why?
Here is the code in the view model:
public class SomeViewModel : ReactiveObject
{
private ReactiveCommand<Unit, Unit> _someCommand;
public ReactiveCommand<Unit, Unit> SomeCommand
{
get => _someCommand;
set => this.RaiseAndSetIfChanged(ref _someCommand, value);
}
public SomeViewModel
{
SomeCommand = ReactiveCommand.CreateFromTask<Unit>(async x =>
{
await Shell.Current.GoToAsync("//Root/SomePage");
}).Subscribe();
}
By trial and error, I found a solution.
Specifically, by making the following change to the definition of SomeCommand, the button will no longer be disabled when the user returns to the Start Page:
[...]
SomeCommand = ReactiveCommand.CreateFromTask<Unit>(async x =>
{
await Task.CompletedTask;
})
.Select(_ => Observable.FromAsync(() => Shell.Current.GoToAsync("//Root/SomePage")}")))
.Concat()
.Subscribe();
[...]
Can anyone expain what is going on here? Why does the first approach not work, and why does the second approach work? To me the two approaches look more or less identical.
I'm using ReactiveUI version v9.17.4. So far I've only tested this on iOS.
UPDATE: Turned out to be a threading issue.
Use Interactions
public static class Interactions
{
public static readonly Interaction<string, Unit> Navigate = new Interaction<string, Unit>();
}
then, in AppShell constructor:
Interactions.Navigate.RegisterHandler(async context =>
{
await Current.GoToAsync(context.Input);
context.SetOutput(Unit.Default);
}
and in your ViewModel constructor:
ReactiveCommand<Unit, Unit> NavigateToSettingsCommand = ReactiveCommand.CreateFromObservable(() => Interactions.Navigate.Handle("settings"))

Closing an async Response

I'm trying to close the current response but nothing happens when I try HttpContext.Response.Body.Close() and Response.End() does not exist.
The reason I'm trying to achieve this is because of legacy validator functions that write an error and close the response, or at least stopping the parent WebAPI method.
Example:
private async Task Register_v2()
{
//Read JSON to object
UserRegisterRequest userRegisterRequest = Request.ReadBody().FromJson<UserRegisterRequest>();
//Validate object (legacy static method with a lot of logic)
//Validate() should end the response if object not validated
userRegisterRequest.Validate(isJson: true, isThrowHttpError: true);
//Code still reaches here and request does not close
string test = "hey I'm alive";
}
Can I workaround this with middleware somehow?
Thanks
There are two ways to terminate the Request pipeline.
Use app.Run in Startup.Configure
Do not invoke _next(context) in Middleware.InvokeAsync
For your scenario, you could try second option by determining whether to invoke _next(context).
public class FirstMiddleware
{
private readonly RequestDelegate _next;
public FirstMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
await context.Response.WriteAsync($"This is { GetType().Name }");
//decide whether to invoke line below based on your business logic
//await _next(context);
bool isValid = userRegisterRequest.Validate(isJson: true, isThrowHttpError: true);
//change userRegisterRequest.Validate to reutrn whether the model is valid
if(!isValid)
{
await context.Response.WriteAsync($"Model is not valid");
}
else
{
await _next(context);
}
}
}

Use Bing Spell Check before Microsoft Translate which is called before call to LUIS

So...
I'm trying to use the Bot Framwork with LUIS in Swedish.
Using the samples I implemented translation of the input from Swedish to English and then called the LUIS functionality.
It worked perfect until we got some very strange intent hits from LUIS.
What we found out was that a very small spelling error (in Swedish) caused the translation to create a message that triggered wrong intent.
We can solve the problem by checking the score of the received intent, but the message back to the user "I didn't understand that" isn't especially helpful.
Running the same message through Bing Spell Check and replace the faulty text with the correct one will produce a correct behaviour (mostly).
What I would like to do is to use the result from the Spell Check to ask the user if the text he/she should be replace with the result from Bing.
Now the problem: I can't find a proper way to implement the dialog to the user. (If possible, I would like to avoid using the PromptDialog.Confirm since it is tricky to localize)
What I have now (that doesn't work) is roughly:
if (activity.Type == ActivityTypes.Message)
{
correctedText = await sc.BingSpellCheck(activity.Text, spellValues);
if (spellValues.Count > 0)
{
// Ask the client if the correction is ok
await Conversation.SendAsync(activity, () => new CorrectSpellingDialog());
}
Translate.Current.ToEnglish(activity.Text, "en");
await Conversation.SendAsync(activity, () => new MeBotLuisDialog());
}
What I would like here is to create a CorrectSpellingDialog() that just returns true or false, nad if it is true I will call the ...MeBotLuisDialog().
Sorry for all the text but it's a long problem :-)
Any takers on this?
(The other solution I had was to create an Intent "SpellCheckError" that is trigged from the Bing Spell Check and the in the intent send a message with the corrected message back to the bot (even though I don't know I that is doable in a proper way))
// Tommy
To enable Bing Spell Check API in your bot, you will first get the key from Bing service, then in the Web.config file add the BingSpellCheckApiKey and together enable IsSpellCorrectionEnabled for example:
<appSettings>
<!-- update these with your BotId, Microsoft App Id and your Microsoft App Password-->
<add key="BotId" value="YourBotId" />
<add key="MicrosoftAppId" value="" />
<add key="MicrosoftAppPassword" value="" />
<add key="BingSpellCheckApiKey" value="YourBingSpellApiKey" />
<add key="IsSpellCorrectionEnabled" value="true" />
</appSettings>
Then you can create a component for your bot to use Bing Spell Api, for more information, you can refer to Quickstart for Bing Spell Check API with C#. Here you can a service in your app for example like this Service.
Then in MessagesController, check the spell first before sending message to dialog. and since your want to show a confirm dialog to user if the text should be replace with the result from Bing, you can send a FormFlow first to let user to have a choice. For example:
Code of MessagesController:
private static readonly bool IsSpellCorrectionEnabled = bool.Parse(WebConfigurationManager.AppSettings["IsSpellCorrectionEnabled"]);
private BingSpellCheckService spellService = new BingSpellCheckService();
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
if (IsSpellCorrectionEnabled)
{
try
{
var text = await this.spellService.GetCorrectedTextAsync(activity.Text);
if(text != activity.Text)
{
//if spelling is wrong, go to rootdialog
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog(activity.Text, text));
}
else
{
//if right, direct go to luisdialog
await Conversation.SendAsync(activity, () => new Dialogs.MyLuisDialog());
}
}
catch(Exception ex)
{
Trace.TraceError(ex.ToString());
}
}
else
{
await Conversation.SendAsync(activity, () => new Dialogs.MyLuisDialog());
}
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
Code of my RootDialog:
[Serializable]
public class RootDialog : IDialog<object>
{
private string otext;
private string ntext;
public RootDialog(string oldtext, string newtext)
{
otext = oldtext;
ntext = newtext;
}
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
var form = new FormDialog<Confirmation>(new Confirmation(otext, ntext),
Confirmation.BuildForm, FormOptions.PromptInStart, null);
context.Call(form, this.GetResultAsync);
}
private async Task GetResultAsync(IDialogContext context, IAwaitable<Confirmation> result)
{
var state = await result;
await context.Forward(new MyLuisDialog(), null, context.Activity, System.Threading.CancellationToken.None);
}
}
[Serializable]
public class Confirmation
{
public string Text { get; set; }
private static string otext;
private static string ntext;
public Confirmation(string oldtext, string newtext)
{
otext = oldtext;
ntext = newtext;
}
public static IForm<Confirmation> BuildForm()
{
return new FormBuilder<Confirmation>()
.Field(new FieldReflector<Confirmation>(nameof(Text))
.SetType(null)
.SetDefine(async(state, field) =>
{
field
.AddDescription(otext, otext)
.AddTerms(otext, otext)
.AddDescription(ntext,ntext)
.AddTerms(ntext, ntext);
return true;
}))
.Build();
}
}

Errors using a LINQ query in a Silverlight application

I am trying to consume the WCF services using Silverlight application in Sharepoint.
It's going to display all the data from a list in a grid. Somehow it is throwing a error.
Cannot convert lambda expression to type 'system.Delegate' because it is not a delegate type.
using the generic type 'system.collections.generic.ienumerable' requires 1 type arguments
SLprojectsCRUD2010WCF.sharepointservice.list1item' is a type but is used like a variable.
How can this be solved?
private SharePointService.SkinnyBlondeDataContext _context;
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(LayoutRoot_Loaded);
}
private void ShowProjects()
{
// Declare the LINQ query
var projectsQuery = (from p in _context.Tasks
select p) as DataServiceQuery<SharePointService.TasksItem>;
// Execute the LINQ query
projectsQuery.BeginExecute((IAsyncResult asyncResult) => Dispatcher.BeginInvoke(() =>
{ // Runs in the UI thread
// EndExecute returns
IEnumerable < TasksItem > this.dataGridProjects.ItemsSource = projectsQuery.EndExecute(asyncResult).ToList();
}), projectsQuery);
}
private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
// Get the context
_context = new SharePointService.SkinnyBlondeDataContext(
new Uri("http://vanir0269/_vti_bin/listdata.svc", UriKind.Absolute));
ShowProjects();
}
Until your source code is formatted properly it'll be a pain to see what the LINQ problem is, but the lambda expression problem is easy: Dispatcher.BeginInvoke takes a Delegate, and lambda expressions can only be converted into specific delegate types. This is easy to fix:
projectsQuery.BeginExecute((IAsyncResult asyncResult) => {
Action action = () => {
// Code in here
};
Dispatcher.BeginInvoke(action, null);
});

Resources