await for a PushModalAsync form to closed in xamarin forms
Solution 1
Use the Disappearing
event on your modal page.
Example:
var modalPage = new ContentPage();
modalPage.Disappearing += (sender2, e2) =>
{
System.Diagnostics.Debug.WriteLine("The modal page is dismissed, do something now");
};
await content.Navigation.PushModalAsync(modalPage);
System.Diagnostics.Debug.WriteLine("The modal page is now on screen, hit back button");
Or use a EventWaitHandle
:
var waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
var modalPage = new ContentPage();
modalPage.Disappearing += (sender2, e2) =>
{
waitHandle.Set();
};
await content.Navigation.PushModalAsync(modalPage);
System.Diagnostics.Debug.WriteLine("The modal page is now on screen, hit back button");
await Task.Run(() => waitHandle.WaitOne());
System.Diagnostics.Debug.WriteLine("The modal page is dismissed, do something now");
Solution 2
A bit late on the answer here, but it might be best to listen to the Application's OnModalPagePopping
event handler.
First, create the modal page. Give it a property to store the data you want to later retrieve:
public class MyModalPage : ContentPage
{
public string Data { get; set; }
public MyModalPage()
{
InitializeComponent();
// ... set up the page ...
}
private async void PopThisPage()
{
// When you want to pop the page, just call this method
// Perhaps you have a text view with x:Name="PhoneNumber", for example
Data = PhoneNumber.Text; // store the "return value" before popping
await MyProject.App.Current.MainPage.Navigation.PopModalAsync();
}
}
In the parent page, you can create the modal page, and set up the event handler to listen for when the modal page pops:
public class MyPage : ContentPage
{
MyModalPage _myModalPage;
public MyPage()
{
InitializeComponent();
// ... set up the page ...
}
private async void ShowModalPage()
{
// When you want to show the modal page, just call this method
// add the event handler for to listen for the modal popping event:
MyProject.App.Current.ModalPopping += HandleModalPopping;
_myModalPage = new MyModalPage();
await MyProject.App.Current.MainPage.Navigation.PushModalAsync(_myModalPage());
}
private void HandleModalPopping(object sender, ModalPoppingEventArgs e)
{
if (e.Modal == _myModalPage)
{
// now we can retrieve that phone number:
var phoneNumber = _myModalPage.Data;
_myModalPage = null;
// remember to remove the event handler:
MyProject.App.Current.ModalPopping -= HandleModalPopping;
}
}
}
This is better than using the OnDisappearing
method, as others have already stated that can be called when the app is backgrounded, etc. And its behavior is not consistent across platforms.
There is also another event OnModalPopped
, which is called after the modal is completely popped from the navigation stack. If using that, it should work similarly.
Solution 3
You can try to create an event, call when pop close.
public partial class AddSchool : ContentPage
{
public delegate void PopupClosedDelegate();
public event PopupClosedDelegate PopupClosed;
public AddSchool()
{
InitializeComponent();
}
private async void Button_OK_Clicked(object sender, EventArgs e)
{
//doing some operations like entry to db etc and close page
await Navigation.PopModalAsync();
if (PopupClosed!=null)
{
PopupClosed();
}
}
private async void cancelClicked(object sender, EventArgs e)
{
await Navigation.PopModalAsync();
if (PopupClosed != null)
{
PopupClosed();
}
}
}
I put it on the button click event, maybe you can put on close or dispose event. Then here is implement
public partial class SelectSchool : ContentPage
{
public SelectSchool()
{
InitializeComponent();
#region toolbar
ToolbarItem tbi = null;
if (Device.OS == TargetPlatform.Android)
{
tbi = new ToolbarItem("+", "plus", async () =>
{
var target_page = new AddSchool();
target_page.PopupClosed += () => { /*Do something here*/ };
Navigation.PushModalAsync(target_page);
}, 0, 0);
}
ToolbarItems.Add(tbi);
#endregion
this.Title = "Select School";
}
}
Hope this help.
Solution 4
I create an extension method for this:
public static class DialogUtils
{
public static async Task ShowPageAsDialog(this INavigation navigation, Page page)
{
int pagesOnStack = navigation.NavigationStack.Count + 1;
var waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
page.Disappearing += (s, e) =>
{
if (navigation.NavigationStack.Count <= pagesOnStack)
waitHandle.Set();
};
await navigation.PushAsync(page);
await Task.Run(() => waitHandle.WaitOne());
}
}
And I can use it:
private async void bShowDialogPage_Clicked(object sender, EventArgs e)
{
var page = new DialogPage();
await page.LoadData();
await Navigation.ShowPageAsDialog(page);
var result = page.PageResult;
}
It supports situations when dialog page show another page. I prefer NavigationStack instead ModalStack due to NavigationPage and BackButton.
Solution 5
Another way of accomplishing this is by calling an event from the page's OnDisapearing method, this event can then be subscribed by the navigation service which you create, and you can then use the "TaskCompletionSource"
to wati until your page finishes its work and then complete the task.
For more details about accomplishing this, you can check this blog post.
Here is the base page's implementation, every page in this demo app inherit this page:
public class BasePage<T> : ContentPage
{
public event Action<T> PageDisapearing;
protected T _navigationResut;
public BasePage()
{
}
protected override void OnDisappearing()
{
PageDisapearing?.Invoke(_navigationResut);
if (PageDisapearing != null)
{
foreach (var @delegate in PageDisapearing.GetInvocationList())
{
PageDisapearing -= @delegate as Action<T>;
}
}
base.OnDisappearing();
}
}
Here is an overview of the navigation service you should use:
public async Task<T> NavigateToModal<T>(string modalName)
{
var source = new TaskCompletionSource<T>();
if (modalName == nameof(NewItemPage))
{
var page = new NewItemPage();
page.PageDisapearing += (result) =>
{
var res = (T)Convert.ChangeType(result, typeof(T));
source.SetResult(res);
};
await App.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(page));
}
return await source.Task;
}
To call this page with the navigation service, you can use the following code:
var item = await new SimpleNavigationService().NavigateToModal<Item>(nameof(NewItemPage));
Items.Add(item);
Sebastian
working on .net technologies to create web , desktop and mobile apps
Updated on September 05, 2020Comments
-
Sebastian over 3 years
I have a page and on clicking a plus button on toolbar i am calling a popup page
from popup page user can add a new entry or cancel / close window without doing anything
Everything is working fine and code is like this
public partial class SelectSchool : ContentPage { public SelectSchool() { InitializeComponent(); #region toolbar ToolbarItem tbi = null; if (Device.OS == TargetPlatform.Android) { tbi = new ToolbarItem("+", "plus", async () => { var target_page = new AddSchool(); Navigation.PushModalAsync(target_page); }, 0,0); } ToolbarItems.Add(tbi); #endregion this.Title = "Select School"; } }
And my popup page is like
public partial class AddSchool : ContentPage { public AddSchool() { InitializeComponent(); } private async void Button_OK_Clicked(object sender, EventArgs e) { //doing some operations like entry to db etc and close page Navigation.PopModalAsync(); } private void cancelClicked(object sender, EventArgs e) { Navigation.PopModalAsync(); } }
But now i want to wait for the Popup to get closed to do some additional coding and i tried below code
if (Device.OS == TargetPlatform.Android) { tbi = new ToolbarItem("+", "plus", async () => { var target_page = new AddSchool(); await Navigation.PushModalAsync(target_page); //await till target_page is closed and once its closed call my next function here }, 0,0); }
But await is not working . How can i await on this area till the popup getting closed ? Any idea??