Logout Display alert xamarin.forms - xamarin

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

Related

Xamarin.Forms android check whether user clicked Deny and Don't ask again

In my xamarin.forms app. I am using xamarin.Essentials to check and request permissions. I can check whether a permission is granted or not in android. The problem I am facing is when user clicks "Deny and Don't ask again" the status still shows Denied. How can we know whether user selected Deny and Dont ask so that I can show a warning message according to that. Any help is appreciated.
For check and request permission in Xamarin.Forms you could also use the plugin Permission.Plugin from nuget .
The option Deny and Don't ask again will only appear when you deny the request the second time . However , there is no such a specific status to tag it .
As a workaround , we could define a custom property to save the time that we deny .
int DenyCount = 0;
private async void FabOnClick(object sender, EventArgs eventArgs)
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync<LocationPermission>();
if (status != PermissionStatus.Granted)
{
if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Location))
{
//await DisplayAlert("Need location", "Gunna need that location", "OK");
DenyCount++;
if(DenyCount>=2)
{
//...
}
}
status = await CrossPermissions.Current.RequestPermissionAsync<LocationPermission>();
}
if (status == PermissionStatus.Granted)
{
//Query permission
DenyCount = 0; // reset the value of DenyCount
}
else if (status == PermissionStatus.Restricted)
{
//location denied
}
}
Xamarin.Essentials provides a method for detecting whether the user has clicked to "don't ask again". You can check if a dialog should be shown by calling ShouldShowRationale:
var canAskAgain = Xamarin.Essentials.Permissions.ShouldShowRationale<Permission_Type>();
var didCheckDontAskAgain = !canAskAgain;
If this is true, that means the user did not click "Deny and Don't Ask Again".
Also, note that on Android 12 (and maybe 11, not sure), if you click "Deny" mulitple times, Android assumes that you mean "Don't ask again". The user may never explicitly check that option, but it may be inferred by Android.

DisplayAlert causing crash of Xamarin app

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.

What is the best solution for "reseting" the Authentication stack?

Currently in my application I have two Navigation stacks.
Authentication
Main
My Authentication stack looks like this:
Splash Page
Choose Create or Login Page
Login Page
After that I call:
CoreMethods.SwitchOutRootNavigation(NavigationContext.Main);
This all works fine.
When I call Logout from within the Main stack like this:
CoreMethods.SwitchOutRootNavigation(NavigationContext.Authentication);
I will currently be on "Login Page", but I really want it to be the first page "Splash Page".
Having the Navigation stacks remember the stack history is perfect for all other cases.
Question: What is the best solution for "reseting" the Authentication stack?
What I normally do in my apps is following.
I have IAuthenticationService which has a State property, which can be LoggedIn or LoggedOut. When session state changed due to explicit login, or for instance token expires, I set the State to LoggedOut. Also I fire a broadcast message SessionStateChanged through Messenger, so I can catch this message all around the app, and react correspondingly in UI level, like change screen states and so on.
If need to completely log the user, I mean show login page when State is LoggedOut, which is your case, I do the following. I use Xamarin.Forms, but the approach would be similar if you use native iOS or Android.
In my main App class (the one which derives from Xamarin.Forms.Application) I create a method call UpdateMainPage, something like this
private async void UpdateMainPage()
{
if (_authService.State == SessionState.LoggedIn)
MainPage = new NavigationPage(new RequestPage());
else
MainPage = new NavigationPage(new SignInPage());
}
What happens I just change the root page of the application to SignIn flow or Main flow depending on SessionState. Then in my constructor I do the following.
public FormsApp()
{
InitializeComponent();
_authService = Mvx.Resolve<IAuthenticationService>();
UpdateMainPage();
var messenger = Mvx.Resolve<IMvxMessenger>();
_sessionStateChangedToken = messenger.Subscribe<SessionStateChangedMessage>(HandleSessionStateChanged);
}
What I need to do, I need to setup main page beforehand, then I subscribe to SessionStateChanged event, where I trigger UpdateMainPage
private void HandleSessionStateChanged(SessionStateChangedMessage sessionStateChangedMessage)
{
UpdateMainPage();
}
I used this approach for several apps, and it work perfect for me. Hope this helps
I had the very same problem recently and this is what I did:
Navigation stacks:
public enum NavigationStacks {Authentication, Main}
In the App.xaml.cs:
//Navigation stack when user is authenticated.
var mainPage = FreshPageModelResolver.ResolvePageModel<MainPageModel>();
var mainNavigation = new FreshNavigationContainer(MainPage, NavigationStacks.Main.ToString());
//Navigation stack for when user is not authenticated.
var splashScreenPage= FreshPageModelResolver.ResolvePageModel<SplashScreenPageModel>();
var authenticationNavigation = new FreshNavigationContainer(splashScreenPage, NavigationStacks.Authentication.ToString());
here you can leverage James Montemagno's Settings Plugin
if (Settings.IsUserLoggedIn)
{
MainPage = mainNavigation;
}
else
{
MainPage = authenticationNavigation;
}
So far you had already done the code above. But the idea for the problem is to clear the authentication stack except the root page i.e splash Screen:
public static void PopToStackRoot(NavigationStacks navigationStack)
{
switch (navigationStack)
{
case NavigationStacks.Authentication:
{
var mainPage = FreshPageModelResolver.ResolvePageModel<MainPageModel>();
var mainNavigation = new FreshNavigationContainer(MainPage, NavigationStacks.Main.ToString());
break;
}
case NavigationStacks.Main:
{
var splashScreenPage= FreshPageModelResolver.ResolvePageModel<SplashScreenPageModel>();
var authenticationNavigation = new FreshNavigationContainer(splashScreenPage, NavigationStacks.Authentication.ToString());
break;
}
}
}
And finally here is the code inside Logout command:
private void Logout()
{
Settings.IsUserLoggedIn = false;
NavigationService.PopToStackRoot(NavigationStacks.Authentication);
CoreMethods.SwitchOutRootNavigation(NavigationStacks.Authentication.ToString());
}
I know there may be better and more efficient approaches. But that worked for me.

How to show different pages when app launches time in windows phone 7?

When app launches time need to show the registration page.once user registered it shouldn't goes to registration page need to go log in page.
How to achieve this?
You can navigate to the start page of a Windows Phone app from code.
Remove the "DefaultTask" entry from the WMAppManifest
Remove the NavigationPage attribute from the "DefaultTask" in WMAppManifest, and in the Launching event of your app use the something like the example below to navigate to the page of choice upon launch.
private void Application_Launching(object sender, LaunchingEventArgs e)
{
if (registered)
{
((App)Application.Current).RootFrame.Navigate(new Uri("/<your start page>.xaml", UriKind.Relative));
}
else
{
((App)Application.Current).RootFrame.Navigate(new Uri("/<your registration page>.xaml", UriKind.Relative));
}
}
You just have to decide how you want to determine that someone already registered.
I guess you haven't put a lot of thought to this, the setup is pretty easy! When a user registers you could set a variable in the settings defining that a user already has registered. When the application starts, evaluate this setting and if the user registered you show the register-page, otherwise the login-page. Example:
//After (succesful) registration
Properties.Settings.Default.HasRegistered = true;
Properties.Settings.Default.Save();
//Check the value
var hasRegistered = Properties.Settings.Default.HasRegistered;
if(hasRegistered)
//show Login
else
//show Registration
You can also use the IsolatedStorageSettings.ApplcationSettings to do this. The code below is just sample code, you'll have to provide validation if the settings already exist on the first startup of the app and set a default value 'false' for the setting if no registration has occured yet.
//After registration
var settings = IsolatedStorageSettings.ApplicationSettings;
if (settings.Contains("HasRegistered"))
settings["HasRegistered"] = true;
settings.Save();
//Check value
var settings = IsolatedStorageSettings.ApplicationSettings;
if (settings.Contains("HasRegistered"))
{
var registered = bool.Parse(settings["HasRegistered"]);
if(registered)
//show login
else
//show registration
}
Hope this helps!

Pass value from user control (popup) to current page windows phone 7

From Mainpage, I use this to open the user control Login.xaml :
myPopup = new Popup() { IsOpen = true, Child = new Login() };
I want to enter information into the Login user control and pass that value back to Mainpage to process, how can I do this? I know how to use OnNavigateTo and From to pass value to other page but it won't work on this case because the Popup actually lays on top the MainPage, so it does not navigate anywhere.
You need to use events for that:
var login = new Login();
login.Completed += (a, b) =>
{
// retrieve login.Data
// close popup
};
myPopup = new Popup() { IsOpen = true, Child = login };
and login user control:
{
...
public event EventHandler Completed;
void OnCompleted
{
var h = Completed;
if (h!= null) h(this, new EventArgs());
}
}
remember to call OnCompleted() in the login whereever it suits (e.g. OK button handler)
Well, you could just use some global variable. However using globals is very bad design. Better option is using for example Event Aggregator pattern. Read about it here, here or here.

Resources