Xamarin.Forms is a great technology that let you to develop an application’s UI at once and deploy it on multiple platforms. But often it became cumbersome when you want to do something that doesn’t come out of the box.
One of these things is a custom dialogs. One solution that you might consider is to do it by implementing the dialog on each platform and provide it via service. But we use Xamarin.Forms exactly for the sake of avoiding such things as much as we can. Fortunately, there is a pure Xamarin.Forms solution to the problem.
Let’s focus on Android platform; the solution for iOS is basically the same.
To begin, let’s look at implementation provided by Xamarin itself, Page.DisplayAlert. We can reflect the code written in the assembly using any number of tools, but I’ll use ILSpy. It takes some time to get yourself familiar with structure of code, but it’s worth it.
We can find DisplayAlert code in Page class (Xamarin.Forms.Core assembly). There is implementation of DisplayAlert methods:
As you can see, the method sends a “Xamarin.SendAlert” message with AlertArguments to some code. After a little more exploring, you’ll find that SendAlert leads us to the FormsApplicationActivity.InternalSetPage method in the Xamarin.Forms.Platform.Android assembly. Here is a part of method related to alert:
Ok, nothing really special. We can repeat that on our own. Let’s finally write some code to show simple dialog.
In PCL:
In Droid:
Pretty simple, right?
But this is the implementation of the dialog on each platform. What we really want to achieve is a cross-platform implementation.
Let’s start with creation of simple custom dialog, just to illustrate the process:
We need to change the code to bypass that view to a native part of code.
In PCL:
In Droid:
On the Android side we need something that will always be displayed on top of the current view as a dialog popup. Android’s Dialog class provide this functionality, and would be served like a container to our dialog view. To be able to show dialog with a background shadow, the container should fill the screen. That also makes positioning the popup via HorizontalAlignment and VerticalAlignment properties available.
That looks great, but now we need to convert our Xamarin.Forms view to a native Android ViewGroup somehow. That’s where the Renderer’s magic will come into play. Xamarin.Forms does it via Platform.CreateRenderer (or RendererFactory.GetRenderer before XF 2.0). It’s a bit more complicated in Xamarin.Forms itself, but that API is private, and we don’t need it. We only need CreateRenderer.
If you’re not familiar with renderers, they’re wrappers provided by Xamarin.Forms that accompany each Xamarin.Forms control and create an instance of a native control. Hence we can use that concept to convert Xamarin.Forms controls to native controls.
All renderers implement the IVisualElementRenderer interface and contain a ViewGroup property. That’s exactly what are we looking for, so now we could add those simple two lines to beginning of our Show method and run the app:
Unfortunately, that doesn’t work. Only the fullscreen shadow appears not the dialog. That’s because the popup currently isn’t a part of layout system of either Android or Xamarin.Forms.
Let’s skip layout part for now. That’s fascinating , but too complicated for this article which focuses on creating custom popups. For now I just take code from nice article here:
We need to call it from the Show method and pass the screen resolution as the size of the view. Doing so will setup Xamarin.Forms layout system as a full screen view.
Looks nice, right? But unfortunately this also won’t work yet. We haven’t done with Xamarin.Forms layout configuration. A popup doesn’t have any parents, hence for Xamarin.Forms it doesn’t belong to layout and it tends to skip the layout estimation for performance reasons. So the last thing that we need to do is to set a Parent for the popup.
Let’s set a page which will show the popup as a Parent:
And that’s it! We can see our nice looking button as a popup. But we can’t close it. To solve this, we can introduce a new class PopupBase that will handle some specific popup behaviors like Close. Make the following code changes to support this new class:
Add this to XfPopups.Droid:
That’s it. Now we have the custom popup written in PCL that will be available in both platforms instantly.
iOS code pretty similar and can be found in github repository along with some nice-to-have features like support of await\async.
Hi Eugene Isakov,
ReplyDeleteThank you for sharing code, it's works like Charm.
Could you please share code for Windows(UWP) too.
Thanks,
Niranjan.
This comment has been removed by the author.
ReplyDelete