Awaiting Pop up in case of Multiple Popup Scenarios in Xamarin Android - c#

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.

Related

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

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/

How can i wait an async method to finish and then continue executing an instruction?

I am creating a cross platform app on xamarin.forms.
I'm using a plugin to display a popup window.
However, it seems like the awaitkey is not working since the execution continues before the task is completed.
Besided that, if the button to display a popup is clicked fast many times consecutively, the popup will be displayed this many times clicked instead of displaying once and block all the rest.
I have a command attached to a button. Whenever the button is clicked, the command property is fired corectly but the awaitseems to have no effect.
public ICommand {
get => new Command(async () =>
{
if (ObjectivosDoMes.Count < 3)
await PopupNavigation.Instance.PushAsync(new NovoObjectivoDoMes(ObjectivosDoMes), true);
PodeAdicionarObjMes = ObjectivosDoMes.Count < 3 ? true : false;
});
}
I would like to the code after popup being displayed to be execute just after the popup is closed. This is the library i am using to display popup : https://github.com/rotorgames/Rg.Plugins.Popup
Within your code you are making the assumption that the task returned by PopupNavigation will finish when the popup is closed. Instead, once the popup page has been pushed to the navigation stack this task will finish. So awaiting this task will not be helpful to detect when the popup has been closed. You could hook into the Disappearing event of the popup page. Here is some working example that is self contained and does not depend on other Views/ViewModels.
// in constructor
ButtonTappedCommand = new Command(async () => await OnButtonTappedCommand()) ;
page = new Rg.Plugins.Popup.Pages.PopupPage();
}
private async Task OnButtonTappedCommand()
{
page.Content = new Button()
{
Text="Close",
// close the popup page on tap
Command = new Command(()=>PopupNavigation.Instance.PopAsync())
};
page.Disappearing += Popup_Disappearing;
await PopupNavigation.Instance.PushAsync(page);
}
private void Popup_Disappearing(object sender, EventArgs e)
{
page.Disappearing -= Popup_Disappearing;
Debug.WriteLine("Someone closed the popup");
}
await is working fine. The popup library you're using completes its PushAsync operation when the popup is shown, not when it is closed. See this issue for a workaround that allows you to await the popup being closed.

Windows 10 Development: Showing Message Dialog when phone's hardware button is pressed

I wish to display a message dialog on a page when the back button is pressed on that page.
I have already made the back button works as it should. So I am not including the full code that makes the back button work. What I need to is for the message dialog to show on the specific page (SomePage in this case).
I have this code in App.xaml.cs under the OnBackRequested event:
if (rootFrame.CanGoBack && rootFrame.SourcePageType.Name != "SomePage")
{
MessageDialog errormessage = new MessageDialog("Something");
errormessage.Commands.Add(new UICommand("Some Option", new UICommandInvokedHandler(this.CommandInvokedHandler)));
errormessage.ShowAsync();
}
private void _CommandInvokedHandler(IUICommand command)
{
Frame aFrame = Window.Current.Content as Frame;
if (command.Label == "Some Option")
{
aFrame.GoBack();
}
}
So after debugging line by line, System.ArgumentException in my application is written in the debugging console when the ShowAsync() function is triggered. I think I cannot call ShowAsync() on App.xaml.cs to show on SomePage but maybe I must call it on SomePage. I am not sure how to do it.

Prevent single click on WPF control when double click occured

I have Image control with PreviewMouseLeftButtonDown event handled.
The logic is to change image content when single click occured and to activate other visual style when double click occured.
I know about ClickCount property like some answers said (e.g. this) and successfully distinguish between single/double clicks, but problem is that single click occures always, wherether double click follow or not follow the next moment (which is fair enought, anyway). So, with double click both actions processed - for single click and the next moment for double click.
The question: is there any method to prevent single click before occuring right after that double click, other than handling this situation with some kind of timers magic?
Edit:
I found question with good comment, which makes an analogy with windows explorer - how it wait after single click on selected file, and start renaming just ensured that no other click occured after first click.
Delay will definitely exist in purpose to solve this problem, but does it mean that windows explorer using exactly timer, or maybe it have some other option (some property or event that can be awaited) to hold single click in case double click occured?
Finally there were no suggestions received with timer-unrelated solution (and I didn't find any either), so here is simple example how to prevent single click when double click occurred.
Xaml:
<Window x:Class="StackOverflow.DoubleClickExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="100" Width="150"
MouseDown="RootElement_OnMouseDown">
</Window>
Code-behind:
namespace StackOverflow.DoubleClickExample
{
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
public partial class MainWindow : Window
{
[DllImport("user32.dll")]
public static extern uint GetDoubleClickTime();
public MainWindow()
{
this.InitializeComponent();
}
private Guid lastGuid = Guid.Empty;
private void RootElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 1)
{
// Create new unique id and save it into field.
var guid = Guid.NewGuid();
this.lastGuid = guid;
// Run task asynchronously for ensuring that there is no another click
// happened in time interval when double-click can occure.
Task.Run(async () =>
{
// Wait system double-click time interval.
await Task.Delay((int)GetDoubleClickTime());
// If no double-click occured in awaited time interval, then
// last saved id (saved when first click occured) will be unchanged.
if (guid == this.lastGuid)
{
// Here is any logic for single-click handling.
Trace.WriteLine("Single-click occured");
}
});
return;
}
// Can be here only when e.ClickCount > 1, so must change last saved unique id.
// After that, asynchronously running task (for single-click) will detect
// that id was changed and so will NOT run single-click logic.
this.lastGuid = Guid.NewGuid();
// Here is any logic for double-click handling.
Trace.WriteLine("Double-click occured");
}
}
}
For testing, make clicks in window area and track messages writing into output window in visual studio (menu View -> Output).
Another way is using CancellationTokenSource and trigger its Cancel method when double-click occured. Just replace lastGuid field and RootElement_OnMouseDown method:
private CancellationTokenSource cancellationTokenSource;
private void RootElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 1)
{
try
{
this.cancellationTokenSource = new CancellationTokenSource();
var token = this.cancellationTokenSource.Token;
// Run task asynchronously for ensuring that there is no another click
// happened in time interval when double-click can occure.
Task.Run(async () =>
{
// Wait system double-click time interval.
await Task.Delay((int)GetDoubleClickTime(), token);
// Here is any logic for single-click handling.
Trace.WriteLine("Single-click occured");
}, token);
}
catch (OperationCanceledException)
{
// This exception always occure when task is cancelled.
// It happening by design, just ignore it.
}
return;
}
// Cancel single-click task.
if (this.cancellationTokenSource != null)
{
this.cancellationTokenSource.Cancel();
}
// Here is any logic for double-click handling.
Trace.WriteLine("Double-click occured");
}
I guess you need to use a timer. For getting the max time that is valid for a double click still to occur you could use following function (tested; output is 500 ms):
[DllImport("user32.dll")]
static extern uint GetDoubleClickTime();
(source: how to get the double-click-time in WPF)
Usually when you have several values you want to bind to one WPF control you use something like ItemsSource and bind it to a List in view model. But I guess that doesn't work for Image control. So you should go with a timer and use the value of the function above for your timer.

Display notification (e.g. messagebox) while application is being closed

In my application, I am overriding the OnClose event the way shown below. Since the application can take some time to perform SynchronizeLotsaStuff, I want to notify the user that application will soon close.
I tried with a MessageBox, but that blocks the continuation of the program, and also displays an "OK" button that is not desired.
I guess I would prefer something more characteristic, such as fading/graying the window, or even a "splash screen" for closing, but a regular messagebox would be fine too.
// MainWindow.xaml.cs:
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
MessageBox.Show("Wait while application is closed...");
if (this.DataContext != null) {
var vm = this.DataContext as ShellViewModel;
// possibly long-running method
vm.SynchronizeLotsaStuff();
}
base.OnClosing(e);
}
UPDATE: Following Stijn Bernards advice, I put the MessageBox stuff inside a thread, but I haven't found (yes I googled) a proper way to terminate it. Even if Abort, the MessageBox keeps displaying after MainWindow has closed, until I click "OK" button.
// MainWindow.xaml.cs:
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
var messagethread = new Thread(new ThreadStart(() => {
MessageBox.Show("Aguarde enquanto o aplicativo é encerrado...");
}));
messagethread.Start();
if (this.DataContext != null) {
var vm = this.DataContext as ShellViewModel;
// possibly long-running method
vm.SynchronizeLotsaStuff();
}
// UGLY UGLY UGLY (and doesn't work either)
messagethread.Abort();
base.OnClosing(e);
}
I have tried using the abort thread and to my suprises it isn't working.
And well I can say it doesn't look easy...
So I advice to look into this: click me
It's a good tutorial about how to create your own message box, which would then also help you with your problem of removing the ok button.
Cheers.

Categories