How can I call an async method when a button is clicked and avoid warning messages for message not returning void? - xamarin

Here's the code that I have.
The IDE is giving me a warning line under the "async" in the 3rd line, that says "Asynchronous method must not return void"
public QHPageViewModel()
{
DeleteQuizCmd = new Command<string>(async (x) => await DeleteQuizAsync(x));
}
private async Task DeleteQuizAsync(string quizId)
{
var canContinue = await Shell.Current.DisplayAlert("Delete Quiz", "Do you want to delete the results for Quiz " + quizId, "OK", "Cancel");
if (canContinue == false)
return;
App.DB.DeleteQuizHistory(System.Convert.ToInt32(quizId));
UpdateQuizDetails();
}
Is there an issue with the way that this is coded, is there a better way to code this or should I just ignore this message? What are the consequences of ignoring the message?

It yells at you cause basic XF Commands don't have async capabilities.
If you want warnings to go away, you can write your own AsyncCommand implementing ICommand, or just use the AsyncCommand from stephen cleary: https://github.com/StephenCleary/Mvvm.Async/tree/master/src/Nito.Mvvm.Async

To avoid any warning, and keep the same logic you can also write:
if (await Shell.Current.DisplayAlert("Delete Quiz", "Do you want to delete the results for Quiz " + quizId, "OK", "Cancel"))
{
App.DB.DeleteQuizHistory(System.Convert.ToInt32(quizId));
UpdateQuizDetails();
}

Related

Xamarin Plugin.BluetoothLE await characteristic notification

I would like to write one nice, tidy "login" function that I use to log in to a bluetooth peripheral by sending it a password.
public async Task<bool> LogIn()
{
bool result = false;
//First log in
var connectHook = Device.ConnectHook(BleService.Control, new Guid[] { BleCharacteristic.PasswordResult });
connectHook.Subscribe(ScanResult =>
{
ScanResult.Characteristic.DisableNotifications();
//Succesful Login
if (ScanResult.Data[0] == 1)
{
result = true;
}
//Failed Login
else
{
result = false;
}
});
await Device.WriteCharacteristic(BleService.Control, BleCharacteristic.Password, PasswordBytes);
await connectHook.FirstAsync();
return result;
}
I basically want to subscribe to the password result characteristic, then write the password, and then wait for the password result before returning from the function.
Is this the place to use a promise? I have read about them but do not understand their use.
Thanks
EDIT:
Did some digging and think I've found a good answer. I've updated the code above to reflect it, let me know what you think.
EDIT 2:
So, the code above doesn't work because nothing is returned from
await connectHook.FirstAsync();
If I comment that line out it all works....but it seems like it's working because I have a race condition and the notification gets back before the other code completes.
Does the function magically await for that notification? I thought the observable implemented IEnumerable and would return the first item as long as it had come back? I'm new to this reactive stuff so please be kind.
In you code:
The method connectHook.Subscribe is a asynchronous method. Before you added the two await methods, the function login will directly return false without wait the callback in connectHook.Subscribe,so the return value in login will not be changed whether you login successfully or failed to login.
After you add the two await methods,I guess the callback in connectHook.Subscribe has been triggered after these two methods finished running. So the return value will be changed with the return value in the callback.

How to remove tooManyAttempts message in Prompt.Choice? How to accept text in Prompt.Choice that is not in list of options? C#

I'm using bot-Framework SDK3 C#.
I want to allow user input anything which is not in "PromptDialog.Choice"'s options. Any better ways to recommend?
This is my code.
private async Task SelectCategory(IDialogContext context)
{
List<string> options = new List<string>();
options = category.Keys.ToList();
options.Add("Category1");
options.Add("Category2");
options.Add("Category3");
PromptOptions<string> promptOptions = new PromptOptions<string>(
prompt: "which one do you prefer?",
tooManyAttempts: "",
options: options,
attempts: 0);
PromptDialog.Choice(context: context, resume: ResumeAfterSelectCategory, promptOptions: promptOptions);
await Task.FromResult<object>(null);
}
private async Task ResumeAfterSelectCategory(IDialogContext context, IAwaitable<string> result)
{
try
{
selected = await result;
}
catch (Exception)
{
// if the user's input is not in the select options, it will come here
}
}
But the problem is it always send the message "tooManyAttempts". If I set it to empty, I will send me "0".
I suppose you are using NodeJS. You can use the simple builder.Prompts.choice with maxRetries set to 0. Here is a sample snippet. It asks user to choose some option from a list, or they can enter something which is not in the list.
If you are using C# SDK, you can find some similar option for the list.
bot.dialog("optionalList", [
function(session){
builder.Prompts.choice(
session,
"Click any button or type something",
["option1", "option2", "option3"],
{maxRetries: 0} // setting maxRetries to zero causes no implicit checking
)
},
function(session, result){
// something from the list has been clicked
if(result.response && result.response.entity){
console.log(result.response.entity); // use the clicked button
} else {
console.log(session.message.text) // if user entered something which is not in the list
}
}
]);
EDIT 1:
Hi, Saw that you are using C# SDK. I am not that proficient with that but I can give you some suggestion.
The list which you generate in the async task SelectCategory you can generate in some other place, which is also accessible to the second async task ResumeAfterSelectCategory, (like making it a class variable or getting from database).
Now that the list is accessible in the 2nd task, you can compare what user has typed against the list to determine if the message is from the list or not.
If message is something from the list, then take action accordingly, otherwise user has entered something which is not in the list, and then take action accordingly.
Your 2nd problem is
And if user typed, it will show a message "you tried to many times"
What is meant by that? Does bot sends "you tried to many times" to the bot visitor. In which case it could be the behavior of library. You will be able to control that only if library provides some option. Else I don't know. Hope, that helps
EDIT 2:
I came across this SO question Can I add custom logic to a Bot Framework PromptDialog for handling invalid answers?
You can use that questions answer. Basically extending PromptDialog.PromptChoice<T>.Here is an example.
Override TryParse method like this
protected override bool TryParse(IMessageActivity message, out T result)
{
bool fromList = base.TryParse(message, out result);
if (fromList)
{
return true;
} else {
// do something here
return true; // signal that parsing was correct
}
}
I used node.js and to get message which user entered. use this code snippet.
(session, args) => {
builder.Prompts.text(session, "Please Enter your name.");
},
(session, args) => {
session.dialogData.username = args.response;
session.send(`Your user name is `${session.dialogData.username}`);
}

What's the difference between these two nservicebus statements

In one documentation they say IHandleMessages handler hast to be written this way (signature is automatically generated when I choose to "Implement interface" option in Visual Studio):
public class PlaceOrderHandler : IHandleMessages<PlaceOrder>
{
public Task Handle(PlaceOrder message, IMessageHandlerContext context)
{
var orderPlaced = new OrderPlaced { OrderId = message.OrderId };
return context.Publish(orderPlaced);
}
}
While another documentation says it has to be written this way:
public class PlaceOrderHandler : IHandleMessages<PlaceOrder>
{
public async Task Handle(PlaceOrder message, IMessageHandlerContext context)
{
var orderPlaced = new OrderPlaced { OrderId = message.OrderId };
await context.Publish<OrderPlaced>(e => { e.OrderId = message.OrderId; });
}
}
I wonder what is the difference between these two statements, can someone explain in simple language?
Which option is the right one?
Both are correct options. The difference between the two is how a single asynchronous operation is handles in the Handle method.
In the first case, a Task is returned as-is. In the second case, publishing is awaited within the Handle method. The difference? In the first case no async state machine is created by the compiler as the task of publishing returned back. In the second scenario, a state machine is created.
Which option is the right one to use? They are both correct options. If a method is called frequently and you care for the unnecessary allocations not to take place, returnng a single task without awaiting is more efficient.

google.picker.DocsUploadView().setParent('XXXX') issue

Trying to get some help with this code block.
My script first looks for a specific folder and if it exists the pass the id of the folder to the google.picker.DocsUploadView(). When I hard-code the value of setParent to 'gdfid', everything works well. On the other hand, I need the code to be parameterized.
thanks in advance for any assistance
Pete
here's my code:
var gdfid;
function createPicker() {
if (pickerApiLoaded && oauthToken) {
gapi.client.drive.files.list({
"corpora": "user",
"spaces": "drive",
"fields": "files(id,name)",
"q": "name = 'myUploads"
}).then(function(response) {
console.log( response.result.files.length );
if (response.result.files.length > 0) {
console.log( response.result );
gdfid = response.result.files[0].id;
}
//alert('Folder ID: ' + gdfid);
});
var picker = new google.picker.PickerBuilder().
setTitle('Upload to myPratt Folder').
enableFeature(google.picker.Feature.MULTISELECT_ENABLED).
enableFeature(google.picker.Feature.NAV_HIDDEN).
addView(new google.picker.DocsUploadView().
setIncludeFolders(false).
setParent('gdfid')). //tried with and without quotes
setOAuthToken(oauthToken).
setDeveloperKey(developerKey).
setCallback(pickerCallback).
build();
picker.setVisible(true);
}
}
It's probably the .then{} promise code block. I've had lots of trouble with them. The problem is that the .then{} code block has a closed scope.
When you assign gdfid = response.result.files[0].id; it's assumed that it is changing the global variable. But it isn't. It's only creating a local version of gdfid.
I ran around in circles myself for ages trying to figure out how to save external state information from within a .then{} block. Any possible solutions I came up with, were invariably no better than the callback hell that promises were supposed to solve in the first place. I even had problems returning objects out from it. I think the problem is that a .then{} block needs to run from a returned promise. Promises are actually functions earmarked to run in the future. They are subject to scoping restrictions, because they cannot make assumptions about the state of code outside the function. And they only pass object/variables a certain way. Trying to assign globals or returning data the regular way, from inside the .then{} block, is fraught with problems. It will often leave you tearing your hair out.
Try refactoring your code into a function with async/await, and use a try-catch statement to capture promise fails (Note: The try-catch statement still suffers from the global variable isolation problem, but at least it seems to be solely within the catch block. This is only an issue when an error occurs). I find async await much cleaner and easier to understand, and the scoping of variables works more intuitively.
In your case you could rewrite the code thus:
function async createPicker() {
var gdfid;
if (pickerApiLoaded && oauthToken) {
try {
var response = await gapi.client.drive.files.list({
"corpora": "user",
"spaces": "drive",
"fields": "files(id,name)",
"q": "name = 'myUploads"
});
console.log( response.result.files.length );
if (response.result.files.length > 0) {
console.log( response.result );
gdfid = response.result.files[0].id;
}
//alert('Folder ID: ' + gdfid);
var picker = new google.picker.PickerBuilder()
.setTitle('Upload to myPratt Folder')
.enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
.enableFeature(google.picker.Feature.NAV_HIDDEN)
.addView(new google.picker.DocsUploadView()
.setIncludeFolders(false)
.setParent(gdfid)) //tried with and without quotes
.setOAuthToken(oauthToken)
.setDeveloperKey(developerKey)
.setCallback(pickerCallback)
.build();
picker.setVisible(true);
} catch (e) {
console.log("Error displaying file list");
}
};
}
The only real difference here, is the await in front of the gapi.client.drive.files
function forces the code to wait for a callback to assign the response variable. This is not too much of a slowdown issue when running single popup UI elements that the user interacts with.
The gdfid variable is no longer global. In fact you don't even need it. You could setParent directly from the response variable.

how to get access to current MessageActivity from PromptDialog choice in BotFramework

I'm presenting the user with 4 choices via the PromptDialog.choice method. In my Resume method, I want to forward the user to a dialog to handle their choices. I no longer have access to the current MessageActivity object
and wonder what my options are? I'd like to pass the original Message if at all possible. Passing an empty one seems like a hack. And the dialog PrintGraphicDialog will just display a graphic image and return back to the 4 choices. using Context.Call hits the PrintGraphicDialog's StartAsync method and has the context.wait() call which requires the user to type something. Then it prints the graphic. Not quite what is wanted either.
private void ShowOptions(IDialogContext context)
{
PromptDialog.Choice(context, this.OnOptionSelected, new List<string>() { OptionOne, OptionTwo, OptionThree, OptionFour }, "Please select from the following options:", "Not a valid option", 3);
}
private async Task OnOptionSelected(IDialogContext context, IAwaitable<string> result)
{
try
{
string optionSelected = await result;
switch (optionSelected)
{
case OptionOne:
await context.Forward(new PrintGraphicDialog(), this.ResumeAfterOptionDialog, context.MakeMessage(), CancellationToken.None);
break;
case OptionTwo:
break;
case OptionThree:
break;
case OptionFour:
break;
}
}
catch (TooManyAttemptsException ex)
{
}
}
You can store the message in a variable right before calling your PromptDialog.Choice and then use it in the Resume method.
Since the dialog I was forwarding control to really only needs a few properties from the original message, I just created a class, stored them in there and before forwarding, did context.MakeMessage() and populated that with the stored off properties. Seems like there should be a better way.

Resources