DisplayAlert causing crash of Xamarin app - xamarin

I am using a display alert for a simple yes or no dialogue but it's freezing my UI and I have no idea why.
private async void BtnDeleteStockTake_Clicked(object sender, EventArgs e)
{
var selectedItem = gridItems.SelectedItem as StockTakeTransArgsSage;
var action = await DisplayAlert("First 1", "Are you sure you wish to delete Stock Take", "Yes", "No");
if (action)
{
StockTakeTransArgsSage _item = new StockTakeTransArgsSage();
_item =database.GetSingleStockTake(selectedItem.StockTakeId).Result;
_item.Quantity = decimal.Parse(txtQty.Text);
int updated = await database.DeleteStockTake(_item);
await DisplayAlert("Second 1", "Stock Take Deleted", "OK");
await RebindData();
}
}
The weird thing, as well as the display alert from the first one, do not dismiss
It's causing the UI to crash out with a fatal error however if I step through my code normally without the display alert it's fine it does display the alert but then freezes.
My Delete stock function
public async Task<int> DeleteStockTake(StockTakeTransArgsSage args)
{
return await database.DeleteAsync(args);
}
My Get Single function.
public async Task<StockTakeTransArgsSage> GetSingleStockTake(int ID)
{
StockTakeTransArgsSage _stocktake = new StockTakeTransArgsSage();
_stocktake = await database.Table<StockTakeTransArgsSage>().Where(w => w.StockTakeId == ID).FirstOrDefaultAsync();
return _stocktake;
}

Most likely from the behavior you are describing (and looking at your code), it sounds like it's because the Display Alert is not being run on the UI thread, and this causes the app to wait for you to dismiss this alert box before being able to do anything. So your app is not crashing or freezing, it's just launching the alert on a different thread.
So change it to the following:
Device.BeginInvokeOnMainThread(() =>
{
DisplayAlert("Second 1", "Stock Take Deleted", "OK");
});
Let me know if that makes sense.

In your code only those lines can cause that behavior:
StockTakeTransArgsSage _item = new StockTakeTransArgsSage();
_item =database.GetSingleStockTake(selectedItem.StockTakeId).Result;
_item.Quantity = decimal.Parse(txtQty.Text);
int updated = await database.DeleteStockTake(_item);
Those lines cannot be executed on UI thread without freezing it for some time. That may cause the crash, you haven't provided any information of what you see in output for the crash so it is the only likely explanation.

Have you tried awaiting like below
StockTakeTransArgsSage _item = new StockTakeTransArgsSage();
_item = await database.GetSingleStockTake(selectedItem.StockTakeId);
_item.Quantity = decimal.Parse(txtQty.Text);
As I can see this will work. And also make sure that until finish the process you are not touching any other UI component in the screen.

Related

nativescript-phone prevents Nativescript-contacts from returning

I have an app where I want to select a person from contacts and then send a text to that person. It works as expected for the first user, but after that the app never receives control after the contact is selected. I've isolated the problem to the Nativescript-phone plugin. If you simply call phone.sms() to send a text, and then call contacts.getContact(), the problem occurs. I see this on both Android and iOS.
I've created a sample app that demos the problem at https://github.com/dlcole/contactTester. The sample app is Android only. I've spent a couple days on this and welcome any insights.
Edit 4/21/2020:
I've spent more time on this and can see what's happening. Both plugins have the same event handler and same request codes:
nativescript-phone:
var SEND_SMS = 1001;
activity.onActivityResult = function(requestCode, resultCode, data) {
nativescript-contacts:
var PICK_CONTACT = 1001;
appModule.android.on("activityResult", function(eventData) {
What happens is that after invoking phone.sms, calling contacts.getContact causes control to return to the phone plugin, and NOT the contacts plugin. I tried changing phone's request code to 1002 but had the same results.
So, the next step is to determine how to avoid the collision of the event handlers.
Instead of using activityResult event, nativescript-phone plugin overwrites the default activity result callback.
A workaround is to set the callback to it's original value after you are done with nativescript-phone.
exports.sendText = function (args) {
console.log("entering sendText");
const activity = appModule.android.foregroundActivity || appModule.android.startActivity;
const onActivityResult = activity.onActivityResult;
permissions.requestPermissions([android.Manifest.permission.CALL_PHONE],
"Permission needed to send text")
.then(() => {
console.log("permission granted");
phone.sms()
.then((result) => {
console.log(JSON.stringify(result, null, 4));
activity.onActivityResult = onActivityResult;
})
})
}

Using ZXingScannerPage with XF, my content page has weird behavior

I am making an app in xamarin forms of which I will have a login similar to that of whatapp web, an on-screen qr that will be scanned by the phone, in the emulator with visual studio 2017 I have no problems, but when I export the app to an apk and the I install on a mobile device, the app reads the qr and returns to the previous login screen, not showing any reaction, which should be to go to the next screen where I have a dashboard.
What can be? I enclose my code used.
btnScanQRCode.IsEnabled = false;
var scan = new ZXingScannerPage();
scan.OnScanResult += (result) =>
{
scan.IsScanning = false;
Device.BeginInvokeOnMainThread(async () =>
{
await Application.Current.MainPage.Navigation.PopAsync();
var resultado = JsonConvert.DeserializeObject<QrCode>(result.Text);
JObject qrObject = JObject.Parse(JsonConvert.SerializeObject(resultado));
JsonSchema schema = JsonSchema.Parse(SettingHelper.SchemaJson);
bool valid = qrObject.IsValid(schema);
if (valid == true)
{
App.Database.InsertQrCode(resultado);
QrCode qr = App.Database.GetQrCode();
await _viewModel.Login();
await Navigation.PushAsync(new Organization());
}
else
{
await DisplayAlert("False", JsonConvert.SerializeObject(resultado), "ok");
}
});
};
await Application.Current.MainPage.Navigation.PushAsync(scan);
btnScanQRCode.IsEnabled = true;
This was originally a comment, but through the writing i realized this is the answer.
You need to debug your code. Attach a device and deploy the app in Debug config. Step through your code and see where it fails.
It sounds like it's crashing silently and probably on the line where you Deserialize result.Text in a QrCode. result.Text is just a string and will never deserialize into an object. You probably need a constructor that takes a string like QrCode(result.Text).
First scan then use the result to do other things in your app.
var scanner = new ZXing.Mobile.MobileBarcodeScanner();
var result = await scanner.Scan();
Check for proper camera permissions. I bet your problem is there.

Swapping the context between the flows

I am try to implement Swapping context between dialog flows .Assume i am in middle of one dialog flow and i wants to move to another functionality or dialog flow with new utterance..Here bot should prompt Do you want to move to another flow ?...However we have implemented Global Message Scorables here.... Please help me any one. Thanks in advance
i am trying to get the result, i am created one more method in core bot and try to check the luis score again and redirect to new dialog flow based on luis score
enter code here
var msg = stepContext.Context.Activity.Text;
var recognizerResult=await
_services.BasicBotLuisApplication.RecognizeAsync(stepContext.Context, cancellationToken);
var topScoreIntent = recognizerResult?.GetTopScoringIntent();
elseif(topScoreIntent.Value.score>double.Parse(appSettings.Value.LuisScore))
{
var luisRes = recognizerResult.Properties["luisResult"] as LuisResult;
return await stepContext.BeginDialogAsync(nameof(CreateDialog), luisRes,
cancellationToken);
}
it is working normal flows or type any other keywords like help, cancel, stop. but i give it any dialog flow at that time it's not working.
I'm not sure why this isn't working, as the logic seems sound. But you say that the other interrupts (which I think in latest core-bot sample are specific utterances) are working. Have you tried checking intent in the interrupt function? Here is what I have done with my nodejs bot, hopefully this will help in your case. Instead of checking for utterances, I'm checking intent. Cancel and Help just provide text, but Expedite and Escalate start new dialogs.
async isTurnInterrupted(dc, luisResults) {
const topIntent = LuisRecognizer.topIntent(luisResults);
const topIntentScore = luisResults.intents[topIntent].score;
// see if there are any conversation interrupts we need to handle
if (topIntent === CANCEL_INTENT & topIntentScore > 0.6) {
if (dc.activeDialog) {
// cancel all active dialog (clean the stack)
await dc.cancelAllDialogs();
await dc.context.sendActivity('Ok. I\'ve cancelled our last activity.');
} else {
await dc.context.sendActivity('I don\'t have anything to cancel. If you\'re not trying to cancel something, please ask your question again.');
}
return true; // this is an interruption
}
if (topIntent === HELP_INTENT & topIntentScore > 0.5) {
await dc.context.sendActivity('Let me try to provide some help.');
await dc.context.sendActivity('Right now I am trained to help you with order status and tracking. If you are stuck in a conversation, type "Cancel" to start over.');
return true; // this is an interruption
}
if (topIntent === EXPEDITE_INTENT & topIntentScore > 0.5) {
await dc.beginDialog(INTERRUPT_DIALOG, topIntent);
return false; // pushing new dialog so not an interruption
}
if (topIntent === ESCALATE_INTENT & topIntentScore > 0.5) {
await dc.beginDialog(INTERRUPT_DIALOG, topIntent);
return false; // pushing new dialog so not an interruption
}
return false; // this is not an interruption
}

Logout Display alert xamarin.forms

I've been trying to allow a user to confirm logout by using DisplayAlert. If they click "No" it should remain in their profile page else they should be redirected back to the login page. I haven't managed to get this done, if I click Yes or No, both options remain in the profile page
public async void LogoutBtn(object sender, EventArgs e)
{
var answer = await DisplayAlert("Exit", "Do you wan't to exit the App?", "Yes", "No");
if(answer.Equals("Yes"))
{
Settings.FirstName = string.Empty;
Settings.LastName = string.Empty;
Settings.Email = string.Empty;
await Navigation.PushAsync(new MainPage());
}
else
{
App.Current.MainPage = new Profile();
}
}
DisplayAlert returns a boolean (true / false):
var answer = await DisplayAlert("Exit", "Do you wan't to exit the App?", "Yes", "No");
if (answer)
{
// User choose Yes
}
else
{
// User choose No
}
As SushiHangover point out DisplayAlert returns a bool. You should replace it with DisplayActionSheet or stay with it and correct the if.
Your else clause seems to be incorrect.
If the user choose No you should do nothing. Why do you assign a new Profile to the MainPage?
Beside that, it seems that you have problems with navigation in general. Looking at your code, logout will push a page on a top of the executing page. Seems a bit weird. I would recommend to get familiar wit the Navigation topic firs of all:
Official doc
There is a free course on Xamarin University on Navigation

SmtpClient.SendAsync blocking my ASP.NET MVC Request

I have a Action that sends a simple email:
[HttpPost, ActionName("Index")]
public ActionResult IndexPost(ContactForm contactForm)
{
if (ModelState.IsValid)
{
new EmailService().SendAsync(contactForm.Email, contactForm.Name, contactForm.Subject, contactForm.Body, true);
return RedirectToAction(MVC.Contact.Success());
}
return View(contactForm);
}
And a email service:
public void SendAsync(string fromEmail, string fromName, string subject, string body, bool isBodyHtml)
{
MailMessage mailMessage....
....
SmtpClient client = new SmtpClient(settingRepository.SmtpAddress, settingRepository.SmtpPort);
client.EnableSsl = settingRepository.SmtpSsl;
client.Credentials = new NetworkCredential(settingRepository.SmtpUserName, settingRepository.SmtpPassword);
client.SendCompleted += client_SendCompleted;
client.SendAsync(mailMessage, Tuple.Create(client, mailMessage));
}
private void client_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
Tuple<SmtpClient, MailMessage> data = (Tuple<SmtpClient, MailMessage>)e.UserState;
data.Item1.Dispose();
data.Item2.Dispose();
if (e.Error != null)
{
}
}
When I send a email, I am using Async method, then my method SendAsync return immediately, then RedirectToAction is called. But the response(in this case a redirect) isnĀ“t sent by ASP.NET until client_SendCompleted is completed.
Here's what I'm trying to understand:
When watching the execution in Visual Studio debugger, the SendAsync returns immediately (and RedirectToAction is called), but nothing happens in the browser until email is sent?
If i put a breakpoint inside client_SendCompleted, the client stay at loading.... until I hit F5 at debugger.
This is by design. ASP.NET will automatically wait for any outstanding async work to finish before finishing the request if the async work was kicked off in a way that calls into the underlying SynchronizationContext. This is to ensure that if your async operation tries to interact with the HttpContext, HttpResponse, etc. it will still be around.
If you want to do true fire & forget, you need to wrap your call in ThreadPool.QueueUserWorkItem. This will force it to run on a new thread pool thread without going through the SynchronizationContext, so the request will then happily return.
Note however, that if for any reason the app domain were to go down while your send was still in progress (e.g. if you changed the web.config file, dropped a new file into bin, the app pool recycled, etc.) your async send would be abruptly interrupted. If you care about that, take a look at Phil Haacks WebBackgrounder for ASP.NET, which let's you queue and run background work (like sending an email) in such a way that will ensure it gracefully finishes in the case the app domain shuts down.
This is an interesting one. I've reproduced the unexpected behaviour, but I can't explain it. I'll keep digging.
Anyway the solution seems to be to queue a background thread, which kind of defeats the purpose in using SendAsync. You end up with this:
MailMessage mailMessage = new MailMessage(...);
SmtpClient client = new SmtpClient(...);
client.SendCompleted += (s, e) =>
{
client.Dispose();
mailMessage.Dispose();
};
ThreadPool.QueueUserWorkItem(o =>
client.SendAsync(mailMessage, Tuple.Create(client, mailMessage)));
Which may as well become:
ThreadPool.QueueUserWorkItem(o => {
using (SmtpClient client = new SmtpClient(...))
{
using (MailMessage mailMessage = new MailMessage(...))
{
client.Send(mailMessage, Tuple.Create(client, mailMessage));
}
}
});
With .Net 4.5.2, you can do this with ActionMailer.Net:
var mailer = new MailController();
var msg = mailer.SomeMailAction(recipient);
var tcs = new TaskCompletionSource<MailMessage>();
mailer.OnMailSentCallback = tcs.SetResult;
HostingEnvironment.QueueBackgroundWorkItem(async ct =>
{
msg.DeliverAsync();
await tcs.Task;
Trace.TraceInformation("Mail sent to " + recipient);
});
Please read this first: http://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx
I sent the bug to Microsoft Connect https://connect.microsoft.com/VisualStudio/feedback/details/688210/smtpclient-sendasync-blocking-my-asp-net-mvc-request

Resources