Confirmation Dialog on back button press event Xamarin.Forms - xamarin

I am stuck at one point in Xamarin.Forms application
On Back button press simply I want to ask user to confirm whether he really wants to exit or not,
"OnBackButtonPressed" I want to show Alert dialog and proceed as user response.
But "OnBackButtonPressed" is not Async I cannot write await DisplayAlert...
Please direct me how should i implement this feature?
I am using ContentPage as my root Page of NavigationPage
public class frmHome : ContentPage
Here is the code :
protected override bool OnBackButtonPressed()
{
var result = await this.DisplayAlert("Alert!", "Do you really want to exit?", "Yes", "No");
if (result)
{
//user wants to exit
//Terminate application
}
else
{
//Dont do anything
}
}

protected override bool OnBackButtonPressed()
{
Device.BeginInvokeOnMainThread(async() => {
var result = await this.DisplayAlert("Alert!", "Do you really want to exit?", "Yes", "No");
if (result) await this.Navigation.PopAsync(); // or anything else
});
return true;
}

Here's the code that worked for me
protected override bool OnBackButtonPressed()
{
Device.BeginInvokeOnMainThread(async () =>
{
var result = await this.DisplayAlert("Alert!", "Do you really want to exit?", "Yes", "No");
if (result)
{
System.Diagnostics.Process.GetCurrentProcess().CloseMainWindow(); // Or anything else
}
});
return true;
}

Easy way to override hardware back button and show a confirmation dialog box to user
protected override bool OnBackButtonPressed()
{
Device.BeginInvokeOnMainThread(async () =>
{
if (await DisplayAlert("Alert", "Are you sure you want to go back ?", "Yes", "No"))
{
base.OnBackButtonPressed();
await Navigation.PopAsync();
}
});
return true;
}

If you are on Mainpage and want to exit your app then this code will help you.
In your UI (PCL)
protected override bool OnBackButtonPressed()
{
Device.BeginInvokeOnMainThread(new Action(async () => {
var result = await this.DisplayAlert("Alert!", "Do you really want to exit?", "Yes", "No");
if (result)
{
if (Device.RuntimePlatform == Device.Android)
DependencyService.Get<IAndroidMethods>().CloseApp();
}
}));
return true;
}
Also create an Interface (in your UI PCL):
public interface IAndroidMethods
{
void CloseApp();
}
Now implement the Android-specific logic in your Android project:
[assembly: Xamarin.Forms.Dependency(typeof(AndroidMethods))]
namespace Your.Namespace
{
public class AndroidMethods : IAndroidMethods
{
public void CloseApp()
{
var activity = (Activity)Forms.Context;
activity.FinishAffinity();
}
}
}

public override void OnBackPressed()
{
RunOnUiThread(
async () =>
{
var isCloseApp = await AlertAsync(this, "NameOfApp", "Do you want to close this app?", "Yes", "No");
if (isCloseApp)
{
var activity = (Activity)Forms.Context;
activity.FinishAffinity();
}
});
}
public Task<bool> AlertAsync(Context context, string title, string message, string positiveButton, string negativeButton)
{
var tcs = new TaskCompletionSource<bool>();
using (var db = new AlertDialog.Builder(context))
{
db.SetTitle(title);
db.SetMessage(message);
db.SetPositiveButton(positiveButton, (sender, args) => { tcs.TrySetResult(true); });
db.SetNegativeButton(negativeButton, (sender, args) => { tcs.TrySetResult(false); });
db.Show();
}
return tcs.Task;
}
Xamarin.Android await AlertDialog.Builder

I am done like this
protected override bool OnBackButtonPressed()
{
var result = await this.DisplayAlert("Alert!", "Do you really want to exit?", "Yes", "No");
if (result)
{
Process.GetCurrentProcess().CloseMainWindow();
Process.GetCurrentProcess().Close();
}
else
{
//Dont do anything
}
}

private async void OnDelete(object sender, EventArgs e)
{
var result = await this.DisplayAlert("Alert!", "Do you really want to exit?", "Yes", "No");
if (result)
{
var menuitem = sender as MenuItem;
string name = menuitem.BindingContext as string;
lielements.Remove(name);
}
else
{
}
}

Related

DisplayAlert Xamarim forms

I Have this:
var accept = await DisplayAlert("Title", "ask", "yes", "not");
I would like this displayalert to be visible for 5 seconds if the user does not choose any option the displayalert disappears
how can i do this?
Welcome to SO !
Unfortunately , Xamarin Forms not provides some like Dismiss method for alert windown . However , we can use other ways to achieve that . Such as using Xamarin.Forms DependencyService .
First , we can create a IShowAlertService interface in xamarin forms :
public interface IShowAlertService
{
Task<bool> ShowAlert(string title,string message,string ok, string cancel);
}
Next in iOS , Create its implement class :
[assembly: Dependency(typeof(ShowAlertService))]
namespace XamarinTableView.iOS
{
class ShowAlertService : IShowAlertService
{
TaskCompletionSource<bool> taskCompletionSource;
public Task<bool> ShowAlert(string title, string message, string ok, string cancel)
{
taskCompletionSource = new TaskCompletionSource<bool>();
var okCancelAlertController = UIAlertController.Create(title, message, UIAlertControllerStyle.Alert);
//Add Actions
okCancelAlertController.AddAction(UIAlertAction.Create(ok, UIAlertActionStyle.Default, alert => {
Console.WriteLine("Okay was clicked");
taskCompletionSource.SetResult(true);
}));
okCancelAlertController.AddAction(UIAlertAction.Create(cancel, UIAlertActionStyle.Cancel, alert => {
Console.WriteLine("Cancel was clicked");
taskCompletionSource.SetResult(true);
}));
UIWindow window = UIApplication.SharedApplication.KeyWindow;
var viewController = window.RootViewController;
//Present Alert
viewController.PresentViewController(okCancelAlertController, true, null);
Device.StartTimer(new TimeSpan(0, 0, 3), () =>
{
taskCompletionSource.SetResult(false);
Device.BeginInvokeOnMainThread(() =>
{
okCancelAlertController.DismissViewController(true,null);
// interact with UI elements
Console.WriteLine("Auto Dismiss");
});
return false; // runs again, or false to stop
});
return taskCompletionSource.Task;
}
}
And in Android , also do that :
[assembly: Dependency(typeof(ShowAlertService))]
namespace XamarinTableView.Droid
{
class ShowAlertService : IShowAlertService
{
TaskCompletionSource<bool> taskCompletionSource;
public Task<bool> ShowAlert(string title, string message, string ok, string cancel)
{
taskCompletionSource = new TaskCompletionSource<bool>();
Android.App.AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.Instance);
AlertDialog alert = dialog.Create();
alert.SetTitle("Title");
alert.SetMessage("Complex Alert");
alert.SetButton("OK", (c, ev) =>
{
// Ok button click task
Console.WriteLine("Okay was clicked");
taskCompletionSource.SetResult(true);
});
alert.SetButton2("CANCEL", (c, ev) => {
Console.WriteLine("Cancel was clicked");
taskCompletionSource.SetResult(false);
});
alert.Show();
Device.StartTimer(new TimeSpan(0, 0, 3), () =>
{
if (taskCompletionSource.Equals(null))
taskCompletionSource.SetResult(false);
Device.BeginInvokeOnMainThread(() =>
{
alert.Dismiss();
// interact with UI elements
Console.WriteLine("Auto Dismiss");
});
return false; // runs again, or false to stop
});
return taskCompletionSource.Task;
}
}
}
Here in Android , need to create a static Instance in MainActivity . Becasue we need to use it in ShowAlertService :
public class MainActivity : FormsAppCompatActivity
{
internal static MainActivity Instance { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
// ...
Instance = this;
}
//...
}
Now in Xamarin Forms , we can involke it as follow :
private async void Button_Clicked(object sender, EventArgs e)
{
bool value = await DependencyService.Get<IShowAlertService>().ShowAlert("Alert", "You have been alerted", "OK", "Cancel");
Console.WriteLine("Value is : "+value);
}
The effect as follow :

Can't open modal on new main page

This code fully demonstrates what looks like a bug. Sequence is...
App opens
Default main page created.
Login box popped up.
Login box closed. (Click on page to simulate)
New main page created.
Login box popped up.(Click on page to simulate)
Exception... Cannot access disposed object.
Any ideas?
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace test333
{
public class Main1 : ContentPage
{
public Main1()
{
var btn = new Button();
btn.Clicked += (sender, e) =>
{
MessagingCenter.Send<object>(this, "LoginLogout");
Navigation.PopModalAsync();
};
Content = btn;
}
}
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new Main1 { BackgroundColor = Color.Red };
MessagingCenter.Subscribe<object>(this, "LoginLogout", s =>
Device.BeginInvokeOnMainThread(async () => await HandleLogOut()));
HandleLogOut();
}
bool login = true;
async Task HandleLogOut()
{
if (login)
{
await MainPage.Navigation.PushModalAsync(new Main1 { BackgroundColor = Color.Orange });
}
else
{
MainPage = new Main1 { BackgroundColor = Color.Green };
}
login = !login;
}
}
}
I think the problem happens in step 6:Login box popped up.(Click on page to simulate).
Have a look at below codes in btn.clicked:
btn.Clicked += (sender, e) =>
{
MessagingCenter.Send<object>(this, "LoginLogout");
Console.WriteLine("popAction");
Navigation.PopModalAsync();
};
And handle logout:
async Task HandleLogOut()
{
if (login)
{
await MainPage.Navigation.PushModalAsync(new Main1 { BackgroundColor = Color.Orange });
Console.WriteLine("PushModalAction");
}
else
{
MainPage = new Main1 { BackgroundColor = Color.Green };
}
login = !login;
}
I print the popAction and PushAction in your code, you will find that the popAction is executed before PushAction.
Solution:
You should perform popAction after pushModalAction is completed.
I moved the Navigation.PopModalAsync(); from the btn.clicked to HandleLogOut and then it works well.
btn.Clicked += async (sender, e) =>
{
MessagingCenter.Send<object>(this, "LoginLogout");
};
In handle logout:
async Task HandleLogOut()
{
if (login)
{
Console.WriteLine("PushModalAction");
await MainPage.Navigation.PushModalAsync(new Main1 { BackgroundColor = Color.Orange });
Console.WriteLine("popAction");
MainPage.Navigation.PopModalAsync();
}
else
{
MainPage = new Main1 { BackgroundColor = Color.Green };
}
login = !login;
}

while deny the camera permission in android it wont ask again

first time application asking permission for the camera, if we deny the permission it won't ask again if we allow its working fine ??
var scanPage = new ZXingScannerPage();
var cameraStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera);
if (cameraStatus != PermissionStatus.Granted)
{
var results = await CrossPermissions.Current.RequestPermissionsAsync(new[] { Permission.Camera });
cameraStatus = results[Permission.Camera];
if (cameraStatus == PermissionStatus.Granted)
{
// Navigate to our scanner page
await Navigation.PushAsync(scanPage);
scanPage.OnScanResult += (result) =>
{
Device.BeginInvokeOnMainThread(async () =>
{
await Navigation.PopAsync();
txtbarcode.Text = result.Text;
});
};
}
else if (cameraStatus == PermissionStatus.Unknown)
{
await Navigation.PushAsync(scanPage);
scanPage.OnScanResult += (result) =>
{
Device.BeginInvokeOnMainThread(async () =>
{
await Navigation.PopAsync();
txtbarcode.Text = result.Text;
});
};
}
If we deny the camera permission, again its asks while opening camera until we allow the permission.
I wrote a demo about this.
https://github.com/851265601/CheckPermissionDemo
This a GIF of this demo.
Did you check the permission every time when you use the camera function? In this demo, when i click the button ,it will check the permission, then give the result to users, if not give the permission, it will still ask the persmission like the following code.
async void ButtonPermission_OnClicked(object sender, EventArgs e)
{
if (busy)
return;
busy = true;
((Button)sender).IsEnabled = false;
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera);
await DisplayAlert("Pre - Results", status.ToString(), "OK");
if (status != PermissionStatus.Granted)
{
status = await Utils.CheckPermissions(Permission.Camera);
await DisplayAlert("Results", status.ToString(), "OK");
}
busy = false;
((Button)sender).IsEnabled = true;
}
}
When DisplayAlert was appear, it are used MainApplication for different life cycle
namespace CheckPermissionDemo.Droid
{
//You can specify additional application information in this attribute
[Application]
public class MainApplication : Application, Application.IActivityLifecycleCallbacks
{
public MainApplication(IntPtr handle, JniHandleOwnership transer)
: base(handle, transer)
{
}
public override void OnCreate()
{
base.OnCreate();
RegisterActivityLifecycleCallbacks(this);
//A great place to initialize Xamarin.Insights and Dependency Services!
}
public override void OnTerminate()
{
base.OnTerminate();
UnregisterActivityLifecycleCallbacks(this);
}
public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityDestroyed(Activity activity)
{
}
public void OnActivityPaused(Activity activity)
{
}
public void OnActivityResumed(Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
{
}
public void OnActivityStarted(Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityStopped(Activity activity)
{
}
}
}

Why the resume function don't work for context.Forward in bot framework?

I'm new in Bot framework and I face this issue:
I want to move from dialog to another dialog, the callback function is working for context.Call but not for context.Forward?
I try multiple solutions such as put this.CallbackFunctionName but it dosen't work.
Here is my code for call new dialog:
switch (submitType)
{
case "alarm":
context.Call(new AlarmDialog(), ResumeAfterAlarmDialog);
context.Done(true);
return;
case "game":
await context.Forward(new AlarmDialog(), ResumeAfterAlarmDialog, value, CancellationToken.None);
return;
}
And here the method that I call:
private async Task ResumeAfterAlarmDialog(IDialogContext context, IAwaitable<object> result)
{
await context.PostAsync($"You are finish the alarm dialog");
context.Wait(this.MessageReceivedAsync);
}
And this is the error that I have for the context.Forward:
cannot use a method group as an argument to a dynamically dispatched operation.Did you intend to invoke the method?
Here the full implementation of the class:
namespace CardEx.Dialogs
{
[Serializable]
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;
if (activity.Value != null)
{
// Got an Action Submit
dynamic value = activity.Value;
string submitType = value.Type.ToString();
switch (submitType)
{
case "alarm":
context.Call(new AlarmDialog(), ResumeAfterAlarmDialog);
return;
case "alarm2":
await context.Forward(new AlarmDialog(), ResumeAfterAlarmDialog, value, CancellationToken.None);
return;
}
}
AdaptiveCard aCard = new AdaptiveCard()
{
Body = new List<AdaptiveElement>()
{
new AdaptiveTextBlock()
{
Text = "Welcome!",
Weight = AdaptiveTextWeight.Bolder,
Size = AdaptiveTextSize.Large
},
new AdaptiveTextBlock() { Text = "Please choose one of the following:" },
},
Actions = new List<AdaptiveAction>()
{
new AdaptiveSubmitAction()
{
Title = "Set an alarm",
DataJson = "{ \"Type\": \"alarm\" }"
},
new AdaptiveSubmitAction()
{
Title = "Play a game",
DataJson = "{ \"Type\": \"game\" }"
}
}
};
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = aCard
};
var reply = context.MakeMessage();
reply.Attachments.Add(attachment);
await context.PostAsync(reply, CancellationToken.None);
context.Wait(MessageReceivedAsync);
}
private async Task ResumeAfterAlarmDialog(IDialogContext context, IAwaitable<object> result)
{
await context.PostAsync($"You are finish the alarm dialog");
context.Wait(this.MessageReceivedAsync);
}
}
}
Thank you.
cannot use a method group as an argument to a dynamically dispatched operation.Did you intend to invoke the method?
the parameter value used in context.Forward is a dynamic type, which raised this issue.
Please try following code:
await context.Forward(new TestDialog(), ResumeAfterAlarmDialog, (object)value, CancellationToken.None);

Xamarin Forms Error Handling - Messaging Center Alert not displaying from model

I'm trying to implement error trapping functionality from the view model to the view controler, so if there is an error, a message is displayed.
Here is the code i'm using:
ItemsPage View Model
async Task ExecuteCommand()
{
...
try
{
}
catch (Exception ex)
{
Debug.WriteLine(ex);
MessagingCenter.Send(new MessagingCenterAlert
{
Title = "Error",
Message = "Unable to load services.",
Cancel = "OK"
}, "message");
}
finally
{
IsBusy = false;
}
ItemsPage.XAML.cs File
In the constructor for the page..
...
public ItemsPage()
{
InitializeComponent();
MessagingCenter.Subscribe<ItemsViewModel, MessagingCenterAlert>
(this, "message", (sender, arg) => {
var argu = arg;
DisplayAlert("Problem","message body" + argu.Message, "OK");
});
Not sure if this is the correct way to do this, but it is not displaying the alert if there is an error.
Try wrapping it in a call to Device.BeginInvokeOnMainThread like:
MessagingCenter.Subscribe<ItemsViewModel, MessagingCenterAlert>
(this, "message", (sender, arg) => {
var argu = arg;
Device.BeginInvokeOnMainThread (() => {
DisplayAlert("Problem","message body" + argu.Message, "OK");
});
});

Resources