how to disable user from exit page, inside OnDisappearing method? - c#

how to disable user from exit page?
here await doesnt work. it just goes back to previous page and than display message
protected async override void OnDisappearing()
{
var answer = await DisplayAlert("Changes un-saved",
"You must tap on 'save' button", "Exit", "Stay on this page");
if (!answer || String.Compare(answer.ToString(), "Exit") == 0)
base.OnDisappearing();
else if(string.Compare(answer.ToString(), "Stay on this page") == 0)
//here i want to stay on same page
}
I tried this but i dont want page to load
var route = $"//{ nameof(EditPage)}";
await Shell.Current.GoToAsync(route);
I also tried this, but onbackbuttonpress only works if user hit back button on mobile. not back button in app
protected override bool OnBackButtonPressed()
{
bool myResult = PromptForExit().Result;
if (myResult)
return true; // dont go back
return false;
}
private async Task<bool> PromptForExit()
{
var answer = await DisplayAlert("Changes un-saved",
"You must tap on 'save' button",
"Exit",
"Stay on this page");
if (string.Compare(answer.ToString(), "Stay on this page") == 0)
return true;
else
return false;
}

Set the attached property Shell.PresentationMode in the page you want to restrict use to stay on it this will show the page as a modal page (navigation bar will be hidden..), and at the same time override the back button like you did, but be sure to allow navigation out of that page somehow if required, thru a button on that page for example that calls Shell.GotoAsync("..")
<ContentPage ...
Shell.PresentationMode="ModalAnimated"
You may also set it value to Modal or ModalNotAnimated.
Docs Shell presentation mode

You can try something like this
protected override bool OnBackButtonPressed()
{
Device.BeginInvokeOnMainThread(async() => {
bool myResult = await PromptForExit();
if (myResult) await this.Navigation.PopAsync();
});
return true;
}
Let me know if it work for you or not.

This is a tricky question since Apple is determined to state that the "back" action must not be interrupted by the application. You can interrupt the android hardware backbutton, you can just yet interrupt the NavigationPage software backbutton, but you cannot in any way interrupt the MasterDetailPage software backbutton.
Used to, if you want to prevent the user from going back, you had to use something like this:
var loginPage = new LoginPage();
var navigationPage = new NavigationPage(loginPage);
NavigationPage.SetHasNavigationBar(loginPage, true);
// Disable the back button
NavigationPage.SetHasBackButton(loginPage, false);
await Navigation.PushModalAsync(navigationPage);
The recommended way of interacting with the "back" action was to use a modal page using Navigation.PushModalAsync(). It's a lot less painful.
Have you already tried using the new Shell. Microsoft introduced it last year just for this reason.
This seems to be an interesting investigation into the Shell: https://theconfuzedsourcecode.wordpress.com/2020/06/09/overriding-back-button-in-xamarin-forms-shell/

Related

Awaiting Pop up in case of Multiple Popup Scenarios in Xamarin Android

I have a popup, let me call it "Warning Popup" with two buttons Yes and No and some event associated to it. Only after executing any one of the event, I want to display another popup (Success PopUp).
But currently, the Success Popup gets displayed over Warning Popup together, which does not allow me to click any of the YES/NO button from Warning popup.
I have tried all possible ways :-
1) awaited the event associated with Popup
2) awaited the popup call
3) awaited the method which calls the popup fragment from it's respective class.
None of them worked.
if(some condition){
//
}
else{
ForceAcceptanceMessageFlag = true;
EventHandler<WarningEventArgs> forceAcceptanceEvent = (sender, e) => PerformForceAcceptanceAsync(tempGoodsItemsList);
string warningMsg = "SOME WARNING";
ShowWarningDialog(AppResources.Error, warningMsg, AppResources.OK, AppResources.Cancel,
forceAcceptanceEvent, new EventHandler<WarningEventArgs>(NoButtonClicked)).Wait();
}
private async void PerformForceAcceptanceAsync(ObservableCollection<FOHItem> looseGoodsItemsList)
{
// some code
}
protected void NoButtonClicked(object sender, WarningEventArgs warningEventArgs)
{
PopupNavigationService.Dismiss(ViewModelLocator.KEY_POPUP_WARNING);
}
Could someone help me with this scenario as I don't want to introduce some flags as there are a lot of other complex flows.

MVC in the controller decide to open a popup or continue to nextpage

I have the following scenario:
When the user click on button next, I have to do some validation that must be done in the controller.
If there is an error, it will show the error messages in a popup with a Yes to continue or No to cancel and close the popup.
But if there are no errors, user will continue to next page without showing a popup.
Here is what I have:
[ActionName(PA.FlowActionName), AcceptPost("nextAanvullen")]
[NoAsyncTimeout]
public ActionResult Pagina_nextAanvullen(OffrerenFlow flow)
{
var medewerkers = ((MedewerkersAanvullenPakket)(flow.CurrentBusinessObject)).Medewerkers;
//validate here
if (true) // for test use true
{
var blok = medewerkers.Deelnemers;
return View("DeelnemersFout", medewerkers); // This doesnt open a popup
}
else
{
// Go to next page
DoWorkEventHandler handler = (o, e) =>
{
AsyncManager.Parameters["navigation"] = Navigate(new NavigationData<OffrerenFlow>((OffrerenFlow)flow), PA.Next,
true);
};
ExecuteAsyncMethod(handler);
}
}
I am not getting an popup, the view just display the view "DeelnemersFout". I need it to be in a popup. Any suggestions are welkom
Assign a value in ViewBag inside IF block
if (true) // for test use true
{
ViewBag.ShowPopup = true;
var blok = medewerkers.Deelnemers;
return View("DeelnemersFout", medewerkers); // This doesnt open a popup
}
and on Cshtml capture this value in a javascript variable and check if there is a value in this variable or not, and show the popup accordingly.

Why is this C# code executing out of sequence? Not ASYNC (at least I don't think it is)

Can anyone help me understand why my call to dialogservice executes after the CanNavigateAway function has returned its value? (My goal is to warn the user they are about to navigate away from a view without saving their changes. If they click OK, the navigation is allowed. I'm using MVVM Light.
When I step through the code, it does reach the dialog service, but then proceeds to the end of CanNavigateAway before creating the dialog. The CanNavigateAway method is called by OnNavigatingFrom.
public bool CanNavigateAway()
{
if (!changesSaved && Model.IsModified && !continueNavigation)
{
dialogService.ShowMessage("Are you sure you want to continue?",
"Confirmation",
buttonConfirmText: "Continue", buttonCancelText: "Discard",
afterHideCallback: (confirmed) =>
{
if (confirmed)
{
// User has pressed the "confirm" button.
// ...
continueNavigation = true;
}
else
{
// User has pressed the "cancel" button
// (or has discared the dialog box).
// ...
continueNavigation = false;
}
});
return continueNavigation;
}
}
Here is the OnNavigatingFrom method from the MVVM Light Bindable Page class:
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
var navigableViewModel = this.DataContext as INavigable;
if (navigableViewModel != null)
{
if (!navigableViewModel.CanNavigateAway())
{
e.Cancel = true;
}
}
}
I tried this a different way to get the dialog service out of the mix, but showConfirmationDialogAsync still does not seem to execute in time:
public bool CanNavigateAway()
{
continueNavigation = false;
if (!changesSaved && Model.IsModified && !continueNavigation)
{
showConfirmationDialogAsync();
return continueNavigation;
}
private async void showConfirmationDialogAsync()
{
continueNavigation = false;
ContentDialog noSaveConfirmation = new ContentDialog
{
Title = "Warning",
Content = "You have unsaved changes. Are you sure you want to leave this page without saving?",
PrimaryButtonText = "Leave without saving",
SecondaryButtonText = "Stay and finish"
};
ContentDialogResult result = await noSaveConfirmation.ShowAsync();
if (result == ContentDialogResult.Primary)
{
continueNavigation = true;
}
else if (result == ContentDialogResult.Secondary)
{
continueNavigation = false;
}
}
None of the solutions will work if you require a response from the user. The problem is that when the code is inside the navigation event handler, it is running on the UI thread and the user prompt runs asynchronously, so that the UI is free to present the dialog to the user. This however means that the event handler finishes before the user has a chance to respond.
However, you can use a workaround solution. Add a flag bool field like forceNavigation. Then inside the OnNavigatingFrom display the dialog to the user and set Cancel to true right away and display the user the confirmation dialog. If the user says yes, then set forceNavigaiton to true and trigger the navigation manually again. Now it will skip the confirmation part and navigate right away.
protected async override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
//if navigation is forced, skip all logic
if ( !forceNavigation )
{
var navigableViewModel = this.DataContext as INavigable;
if (navigableViewModel != null)
{
e.Cancel = true;
//display the dialog to the user, if he says yes, set
//forceNavigation = true; and repeat the navigation (e.g. GoBack, ... )
}
}
}

App crashing if I hide a few controls when a page loads without using MessageDialog

I have an app in which I have a setting which allows the user to stop the app's access to their location. This is stored in Windows.Storage.ApplicationData.Current.RoamingSettings.Values["location"]. If the location service + this setting allows access then I load a page with the map open. If the setting allows access and the location services are Off a message is displayed and I hide a few controls when the page loads. If the setting is off then I just want to hide the controls without any message.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
.....
// MUST ENABLE THE LOCATION CAPABILITY!!!
var locator = new Geolocator();
locator.DesiredAccuracyInMeters = 50;
locator.ReportInterval = (uint)TimeSpan.FromSeconds(15).TotalMilliseconds;
setloc(locator);
this.navigationHelper.OnNavigatedTo(e);
}
public async void setloc(Geolocator locator)
{
if (locator.LocationStatus != PositionStatus.Disabled && (bool)Windows.Storage.ApplicationData.Current.RoamingSettings.Values["location"]==true)
{
var position = await locator.GetGeopositionAsync();
await MyMap.TrySetViewAsync(position.Coordinate.Point, 16D);
....
return;
}
else if (locator.LocationStatus == PositionStatus.Disabled && (bool)Windows.Storage.ApplicationData.Current.RoamingSettings.Values["location"] == true)
{
MessageDialog msgbox = new MessageDialog("Location Services are turned off. Please turn them on to save Location while saving a Tip", "Location Unavailable");
await msgbox.ShowAsync();
savebutton.Visibility = Visibility.Collapsed;
myMapBlock.Visibility = Visibility.Collapsed;
return;
}
***// MessageDialog msgbox1 = new MessageDialog("Location Services are turned off. Please turn them on to save Location while saving a Tip", "Location Unavailable");
// await msgbox1.ShowAsync();***
savebutton.Visibility = Visibility.Collapsed;
myMapBlock.Visibility = Visibility.Collapsed;
}
Everything is fine when that setting is on(true) but when it's off(false), something strange happens.
The code above doesn't work. It causes the app to crash but when I uncomment the part which is within *** in the code, the message is displayed and the page is loaded properly. If I just try to hide the myMapBlock and the savebutton without using the MessageDialog, it crashes.
I want to hide the controls without using the MessageDialog. How can I do that?
Can you change following line:
setloc(locator);
to:
await Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => { await setloc(locator); });
(change signature of void setloc method to Task)
In my opinion it looks like page is not loaded yet, MessageDialog can not be displayed. Dispatcher.RunAsync should enqueue this action and it should be proccessed after correct page initialization.
Also the base .OnNavigatedTo(..) call should be made before your location-messagedialog code.
That's my guess - can you provide a crash stacktrace?

Triggering method dependent on action on second window

Please can someone tell me the technique that would be used, for the following scenario.
I would like to authenticate users, before I allow my code to perform another action.
I have a method that opens a new window that contains my authentication form (username and password).
private bool userLogin()
{
Window loginInterface = new Window()
{
Title = "Please Login",
Content = new login(),
Height = 282,
Width = 300,
ResizeMode = ResizeMode.NoResize,
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
loginInterface.Owner = this;
loginInterface.ShowDialog();
return true;
}
I'm calling this method like so, on button click:
private void perform_action(object sender, RoutedEventArgs e)
{
if (!userLogin())
{
// Failed login, do nothing
}
else
{
// Authentication successful, perform action
delete_item();
}
}
The window opens fine, but how can I now make my method return true or false based on the what the user does on the opened form?
So when the user clicks the login button named login_button, my code already validates the credentials, but I need the 'bool' value sent back.
Can I make my first window almost wait for an action to be performed on another window and get the response back?
The Window.ShowDialog() method actually already returns a bool?. This can be set at any point from within the Window by setting (for example) this.DialogResult = true. You can then close the window and access the value from the calling code.
To close the window with a result:
this.DialogResult = true;
...and then to use that result in the calling code:
var myWindow = /*create window*/;
var result = myWindow.ShowDialog();
if (result == true)
{
//...
}
To close the login screen you can set DialogResult to true or false, and ShowDialog returns this value. For other things you can create events on the second window and subscribe to them on the first.
userLogin should return something other than true.
I would do something like this (based on the code shown):
return loginInterface.WasSuccessful; // you'd have to add this property

Categories