Rg.Plugins.Popup: PushPopupAsync() returns before popup is actually displayed

await Navigation.PushPopupAsync(popup, false);

You can await this but… The call returns before the popup is actually displayed. So more like when the popup system receives the request for the popup to be displayed.

I’ve had a few situations were I await the push, do some work, dismiss the popup BEFORE IT ACTUALLY APPEARS… then the popup appears… and is on screen forever now because my code already dismissed it.

I shouldn’t be running into race conditions on something I await.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 57 (22 by maintainers)

Most upvoted comments

@tlhintoq my code can await until popup is closed, or not await, depends on the logic 😃

public async Task ShowMyPopupAsync(object context, bool waitUntilClosed = false)
{
     var myPopup = new MyPopup { BindingContext = context };
     if (waitUntilClosed)
          await myPopup.PopupClosedTask;
}

and your Page (which you showing as popup) will contain next:

public partial class MyPopup : Rg.Plugins.Popup.Pages.PopupPage
{
     private TaskCompletionSource<bool> taskCompletionSource;
     public Task PopupClosedTask { get { return taskCompletionSource.Task; } }

     public MyPopup()
     {
          InitializeComponent();
     }

     protected override void OnAppearing()
     {
          base.OnAppearing();
          taskCompletionSource = new TaskCompletionSource<bool>();
     }

     protected override void OnDisappearing()
     {
          base.OnDisappearing();
          taskCompletionSource.SetResult(true);
     }
}

You can also make PopupClosedTask as interface and implement for any Page

This is just what I was looking for. With the new tuple support in c#, I can easily return multiple objects without having to create a new class. In my popup, I am accepting a string for a prompt which is stored in a UserText property. I also have an Accept and Cancel button. The Accept button assigns ‘true’ to an Accepted property. Then your OnDisappearing method becomes.

private TaskCompletionSource<(bool isAccepted , string userText)> _taskCompletionSource;
public Task<(bool isAccepted, string userText)> PopupClosedTask => _taskCompletionSource.Task;

protected override void OnDisappearing()
{
     base.OnDisappearing();
     taskCompletionSource.SetResult((Accepted, UserText));
}

My calling code looks like…

await PopupNavigation.Instance.PushAsync(inputPopup,true);
var ret = await inputPopup.PopupClosedTask;

Debug.WriteLine($"{ret.isAccepted} - {ret.userText}");

Thanks.

@tlhintoq my code can await until popup is closed, or not await, depends on the logic 😃

public async Task ShowMyPopupAsync(object context, bool waitUntilClosed = false)
{
     var myPopup = new MyPopup { BindingContext = context };
     if (waitUntilClosed)
          await myPopup.PopupClosedTask;
}

and your Page (which you showing as popup) will contain next:

public partial class MyPopup : Rg.Plugins.Popup.Pages.PopupPage
{
     private TaskCompletionSource<bool> taskCompletionSource;
     public Task PopupClosedTask { get { return taskCompletionSource.Task; } }

     public MyPopup()
     {
          InitializeComponent();
     }

     protected override void OnAppearing()
     {
          base.OnAppearing();
          taskCompletionSource = new TaskCompletionSource<bool>();
     }

     protected override void OnDisappearing()
     {
          base.OnDisappearing();
          taskCompletionSource.SetResult(true);
     }
}

You can also make PopupClosedTask as interface and implement for any Page