I have a few pages that I'd like to call like functions. As in, I'd like to show the page modally, then do something with a result.
Because awaiting PushModalAsync doesn't actually block until popped, i'm using messagecenter to subscript for the result. It seems kinda clunky. Is there a more elegant way to do this?
// what I think I have to do: async Task TakePicture() { MessagingCenter.Subscribe<CameraPageViewModel, ImageSource>(this, "CameraImage", async (sender, imageSource) => { ImageSource = imageSource; }); await ViewModelNavigation.PushModalAsync(new CameraPageViewModel()); // This returns right away } // what I wish I could do: async Task TakePicture() { var cameraPage = new CameraPageViewModel(); await ViewModelNavigation.PushModalAsync(cameraPage); // In my imagination, this blocks until Popped ImageSource = cameraPage.Image; }
Posts
Try something like this (in your CameraPageViewModel):
Disclaimer: I just typed that code in the browser so obviously I haven't run it or even compiled it. But I think the idea is sound.
That's much simpler, thanks
Not trying to nitpick, @adamkemp - Especially 18 month old threads with code typed right into the browser (which is a game I can't play).
But I think it should be
(no async)
With the async, I get a 'Since this is async method, the return expression must be of type ImageSource'. Other than that, this method worked great for me. Left this note here in case it helps someone else.
I swear I use more of your forum responses than all others combined. So thanks.
You're right. I edited the comment to avoid confusing more people.
Hi,
I'm trying to do something similar, using SignaturePad within a modal page, I would like to return the image/imagesource back when the modal page is popped.
I have a xaml page like this:
I tried to replicate the @adamkemp's code but I have not idea of what I'm doing
Can someone, please, give me a more completely example?
thanks
Well, I can share what I'm using this method with. Not a signature pad, but maybe you can adapt it. I've got a page that has nothing but a list of Cart objects in it. Looks like this:
Code behind looks like this
When I call it, all I want back is a cart object back. So calling it looks like this:
Hope that helps. The syntax is a bit convoluted, calling a public static method inside the page that fires off a version of itself, blah blah... But doesn't involve message handlers or dixie cups with strings. Has worked for me so far.
@BrianLagunas
I'm curious how you'd implement this with Prism.
I want to implement a
Task<Credentials> GetCredentials()
method that when called, replaces the main view (or blocks with a modal) that returns when the user clicks OK or cancel (returning null) on the login page.I know this is an old thread but I was looking for a similar solution. I have tried this and it works great. I have one question though. If the user presses the backbutton on his device the page that was opened by PushModalAsyc gets popped. But the TakePicture task does not return. It keeps waiting for an answer. Isn't that a memory leak? What happens with that task if the answer never comes because the request was "cancelled"?
This approach can easily cause a memory leak so run such solutions through a profiler to see if the modal is ever released.
Is there a better alternative to this solution? Because whatever solution I seem to read of are all vulnerable to memory leaks. All because there is no simple way of knowing that the pushed page has been closed by the user.
I usually use Prism and hook into the navigation event back, that doesn't always match all scenarios but it's a nicer approach without the leaks.
Can you use PushAsync instead? If so, you can use the Popped event of NavigationPage to handle cleanup of any resources, including event handlers, subscriptions etc. I use a common base class for my ContentPages, and have a virtual OnPopped method in there that each subclass can override. I have OnPopped called from the Popped event handler.
I can use PushAsync if I must. I have seen NMackay's example over here and I will give that a try. I understand that PushModalAsync won't run the Popped event?
I understand that PushModalAsync won't run the Popped event?
Not the last time I checked.
This bug suggests the Popped event is raised by modals now although it never used to be like John says.
Watch out for this bug is your using iOS and Forms 4.3.0
https://github.com/xamarin/Xamarin.Forms/issues/7878
I'm running 4.3.0.947036. Just did a quick test on UWP, and Popped was not fired when popping a modal page.
I checked and
PushModalAsync
did not raise thePopped
event.I also found that
PopToRootAsync
does not raisePopped
events (for non-modal pushes) and that is a little strange to me because I would have expected that the pages would raisePopped
events from top to bottom for each page. Any reason for that? Because this would mean I need to pop the pages "manually" until I hit root.PopToRootAsync raises PoppedToRoot. You can handle that to call cleanup code for all of the popped pages. This is my handler:
Thank you so much for the code. I'm learning by the minute thanks to you and @NMackay.
I see that
poppedToRootEventArgs.PoppedPages
contains the pages in an order from bottom to top. That is confirmed by the Microsoft docs. So if I loop through thePoppedPages
to clean them up the most upper page in the stack would be cleaned last. I think i'll revert the loop to avoid potential leaks with events created between the pages that are being popped.Prism does this automatically if your page/PartialView/viewmodel inherits IDestructible. My suggestion
That link above was to work with MVVM Light but as John suggests, will work well with a non framework approach too.
Lots causes leaks, a few obvious ones are:
etc
That's fair, although I would recommend decoupling your pages so that there are no dependencies between them to worry about.