Forum Xamarin.Forms

NullReferenceException in PopModalAsync()

MichaelRumplerMichaelRumpler ATMember ✭✭✭✭✭
edited July 2015 in Xamarin.Forms

I'm trying to write an async method which should be called from a background thread. The method should open a modal page, read some input fields, close the modal page and return the read values.

This is my method:

    public static async Task<PasswordDialogValues> OpenPasswordDialog()
    {
        var vm = new PasswordFormViewModel();

        Device.BeginInvokeOnMainThread(async () =>
        {
            var pwPage = new ModalContentPage()
            {
                WidthRequest = 380,
                HeightRequest = 310,
                BindingContext = vm,
                Content = new PasswordFormView(),
            };
            await MainPage.Navigation.PushModalAsync(pwPage);
        });

        var values = await vm.GetValues();      //.ConfigureAwait(false);

        Device.BeginInvokeOnMainThread(async () =>
            {
                Debug.WriteLine("OpenPasswordDialog: starting PopModalAsync()");
                await MainPage.Navigation.PopModalAsync();
                Debug.WriteLine("OpenPasswordDialog: PopModalAsync() finished");
            }
        );

        return values;
    }

It works on the iPad. But on the iPhone simulator the app crashes and leaves this in my output window:

[0:] RoyalTSi[7894:562070] OpenPasswordDialog: starting PopModalAsync()
[0:] 
[0:] RoyalTSi[7894:562070] System.NullReferenceException: Object reference not set to an instance of an object
  at Xamarin.Forms.Platform.iOS.Platform.DidAppear () [0x0000d] in <filename unknown>:0 
  at Xamarin.Forms.Platform.iOS.PlatformRenderer.ViewDidAppear (Boolean animated) [0x00006] in <filename unknown>:0 
  at (wrapper managed-to-native) UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
  at UIKit.UIApplication.Main (System.String[] args, IntPtr principal, IntPtr delegate) [0x00005] in /Users/builder/data/lanes/1962/8b265d64/source/maccore/src/UIKit/UIApplication.cs:63 
  at UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x00038] in /Users/builder/data/lanes/1962/8b265d64/source/maccore/src/UIKit/UIApplication.cs:47 
  at RoyalMobileApps.XF.iOS.Application.Main (System.String[] args) [0x00002] in d:\RoyalFamily\RoyalMobileApps.XF\iOS\RoyalTSi\Main.cs:19 

As the text "starting PopModalAsync()" is there and "PopModalAsync() finished" is not, I do know, which line produced the exception even though the stack trace does not list it.

It looks like the method works, when I call it from the UI thread. But when I call it from a background thread, then it crashes. I tried with and without the ConfigureAwait on the GetValues call - no difference.

How do I have to write that method?

Best Answer

Answers

  • Xami3Xami3 PKMember, University ✭✭✭
    edited July 2015

    @MichaelRumpler

    How about something like this?

    public static async Task<PasswordDialogValues> OpenPasswordDialog()
    {
        var vm = new PasswordFormViewModel();
        var tcs = new TaskCompletionSource<PasswordDialogValues>(); 
    
        Device.BeginInvokeOnMainThread(async () =>
        {
            var pwPage = new ModalContentPage()
            {
                WidthRequest = 380,
                HeightRequest = 310,
                BindingContext = vm,
                Content = new PasswordFormView(),
            };
            await MainPage.Navigation.PushModalAsync(pwPage);
    
            MessagingCenter.Unsubscribe<MainPage> (this, "Hi");
            MessagingCenter.Subscribe<MainPage> (this, "Hi", (sender) => 
            {
                MessagingCenter.Unsubscribe<MainPage> (this, "Hi");
                var values = await vm.GetValues();
                tcs.SetResult(result);
            });
        });
        return tcs.Task;
    }
    

    in your PasswordFormView

    on ok button

        MessagingCenter.Send<MainPage, string> (this, "Hi", "Ok Pressed");
        await Navigation.PopModalAsync();
    
  • ylemsoulylemsoul RUMember ✭✭✭
    edited July 2015

    I would submit this as a bug. NRE is something that should not happen. The only thing I'm not sure is modal navigation via MainPage only is correct.

  • NMackayNMackay GBInsider, University admin
    edited July 2015

    I had something similar recently and used a message listener of type ModalCloseMsg in the main navigationpage constuctor to take care of closing modal dialogs. Seems to do the trick.

    `
    public partial class StartPage
    {
    public StartPage()
    {
    InitializeComponent();

            // Register login dialog listeners
            Messenger.Default.Register<ModalCloseMsg>(this, action =>
            {
                if (!Equals(action.DialogName, ViewModelLocator.PageKeyLogin)) { return; }
    
                var nav = ServiceLocator.Current.GetInstance<INavService>();
                nav.PopModal();
            });
        }
    }
    

    `

  • MichaelRumplerMichaelRumpler ATMember ✭✭✭✭✭

    @Xami3

    I changed it now to this code:

        public static Task<PasswordDialogValues> OpenPasswordDialog()
        {
            var vm = new PasswordFormViewModel();
            var tcs = new TaskCompletionSource<PasswordDialogValues>();
    
            MessagingCenter.Subscribe<PasswordFormViewModel>(vm, "Close", async _ =>
            {
                MessagingCenter.Unsubscribe<PasswordFormViewModel>(vm, "Close");
    
                var values = await vm.GetValues();      //.ConfigureAwait(false);
                tcs.SetResult(values);
            });
    
            Device.BeginInvokeOnMainThread(async () =>
            {
                var pwPage = new ModalContentPage()
                {
                    WidthRequest = 380,
                    HeightRequest = 310,
                    BindingContext = vm,
                    Content = new PasswordFormView(),
                };
                await MainPage.Navigation.PushModalAsync(pwPage);
            });
    
            return tcs.Task;
        }
    
        public static async void ClosePasswordDialog()
        {
            Debug.WriteLine("ClosePasswordDialog: starting PopModalAsync()");
            await MainPage.Navigation.PopModalAsync();
            Debug.WriteLine("ClosePasswordDialog: PopModalAsync() finished");
        }
    

    And the PasswordFormViewModel looks like this:

        private TaskCompletionSource<PasswordDialogValues> PasswordTCS;
    
        public PasswordFormViewModel()
        {
            PasswordOKCommand = new Command(OnPasswordOK);
            PasswordCancelCommand = new Command(OnPasswordCancel);
    
            PasswordTCS = new TaskCompletionSource<PasswordDialogValues>();
        }
    
        public Task<PasswordDialogValues> GetValues()
        {
            return PasswordTCS.Task;
        }
    
        public void OnPasswordOK()
        {
            PasswordTCS.SetResult(new PasswordDialogValues { Password = Password, RememberPassword = RememberPassword });
    
            MessagingCenter.Send<PasswordFormViewModel>(this, "Close");
            RoyalMobileApps.XF.Helpers.UI.ClosePasswordDialog();
        }
    
        public void OnPasswordCancel()
        {
            PasswordTCS.SetResult(null);
    
            MessagingCenter.Send<PasswordFormViewModel>(this, "Close");
            RoyalMobileApps.XF.Helpers.UI.ClosePasswordDialog();
        }
    

    I also tried various variants between the two, however, I still get that NRE.

    Yes, @ylemsoul I will file a bug for this. It works on the iPad simulator and crashes on the iPhone 4s simulator. This cannot be intended. However, I do need a workaround.

    @NMackay your solution also involves the MessagingCenter, or did I miss anything which distinguishes it from the first answer?

  • NMackayNMackay GBInsider, University admin

    @MichaelRumpler

    Not really, I was just reinforcing the point that I have tried various solutions and the only reliable way I could close modals was by having a message listener in the main navigation page to handle it. It works reliably both on iOS simulator and hardware (well iPhone4s, iPhone5s and 6 tested).

  • Xami3Xami3 PKMember, University ✭✭✭

    @MichaelRumpler

    I also tried various variants between the two, however, I still get that NRE.>

    hmmm interesting, do you have a test project ( Either containing just two pages and a vm with one property or something similar )?

  • Xami3Xami3 PKMember, University ✭✭✭

    @MichaelRumpler

    That's good news :)

  • DavidStrickland0DavidStrickland0 USMember ✭✭✭

    For anyone else that stumbles on this. It sounds like a case of
    https://bugzilla.xamarin.com/show_bug.cgi?id=40911 and or
    https://bugzilla.xamarin.com/show_bug.cgi?id=42214

    These both run into this issue though they couch it around Facebook.
    Personnally got this multiple times using different custom renderers in iOS, Modal Dialogs etc. and thought I'd post up the bug numbers in case anyone wants to track it. 40911 contains a work around which is what we eventually did as well to get around it.

    Looks like they think its fixed in 2.3.4

Sign In or Register to comment.