Mimicking a modal in Xamarin.Forms

Control Templates are a versatile element in Xamarin Forms. A good description can be found in the Microsoft documentation. Recently I wanted to try and save some screen space on an app where I have some help text that, especially on smaller phones, takes up more room than I would like. Using a Control Template to add functionality that replicates a modal window on a website was a nice way to ensure that the help text was still available, but only if the user wanted/needed to see it.

Here’s how we can do this.

We start by creating a new ContentView that will be our modal. Ideally, we want this to be reusable, so convert the <ContentView.Content> to <ContentView.ControlTemplate>. Doing this allows us to define a control that will display dynamic data.

Set up the layout for our Modal. In my example, I have a close button to hide the modal, a Header Label that is set when the control is used on a screen and a ContentPresenter. It is the ContentPresenter that will take the dynamic content we want to pass through.

<ContentView.ControlTemplate>
<ControlTemplate>
<StackLayout
BackgroundColor="#4f000000"
HorizontalOptions="Fill"
VerticalOptions="Fill">
<Frame
Margin="15,25"
Padding="5"
Style="{StaticResource ModalStyle}"
VerticalOptions="Center">
<StackLayout>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Label
x:Name="ModalHeaderLabel"
Grid.ColumnSpan="2"
HorizontalOptions="Center"
HorizontalTextAlignment="Center"
Style="{StaticResource HeaderLabel}"
Text="{Binding Source={x:Reference ModalControl}, Path=ModalHeader}"
VerticalOptions="Start" />
<Button
x:Name="CloseModalBtn"
Grid.Column="1"
Clicked="CloseModalBtn_OnClicked"
HorizontalOptions="End"
Text="X" />
</Grid>
<ContentPresenter />
</StackLayout>
</Frame>
</StackLayout>
</ControlTemplate>
</ContentView.ControlTemplate>
view raw Modal.xaml hosted with ❤ by GitHub

In the code behind, we need to specify the bindable properties. At the very least, we will need a property to track if the modal is visible or not. For my example, I also have a bindable property defined for the header I have set up.

namespace XamarinModalExample.Controls
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Modal : ContentView
{
public static readonly BindableProperty ShowModalProperty
= BindableProperty.Create(nameof(ShowModal), typeof(bool), typeof(Modal), defaultValue: false, defaultBindingMode: BindingMode.TwoWay, propertyChanged: ShowModalPropertyChanged);
public static readonly BindableProperty ModalHeaderProperty
= BindableProperty.Create(nameof(ModalHeader), typeof(string), typeof(Modal), defaultValue: string.Empty, defaultBindingMode: BindingMode.TwoWay);
public string ModalHeader
{
get => GetValue(ModalHeaderProperty).ToString();
set => SetValue(ModalHeaderProperty, value);
}
public bool ShowModal
{
get => (bool)GetValue(ShowModalProperty);
set => SetValue(ShowModalProperty, value);
}
public Modal()
{
InitializeComponent();
IsVisible = false;
}
private static void ShowModalPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
{
((Modal)bindable).IsVisible = (bool)newvalue;
}
private void CloseModalBtn_OnClicked(object sender, EventArgs e)
{
ShowModal = false;
}
}
view raw Modal.xaml.cs hosted with ❤ by GitHub

Now, to use the control, we declare a new XML namespace to allow us to reference it within our ContentPage.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="XamarinModalExample.MainView"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:XamarinModalExample.Controls;assembly=XamarinModalExample">
view raw MainView.xaml hosted with ❤ by GitHub

In order to allow us to display the modal over our actual screen content, we set our ContentPage root element to be a Grid. Our main content for the page is set to appear in Grid.Row=”0″. And then we set our modal control to also display in Grid.Row=”0″.

<Grid>
<ScrollView Grid.Row="0">
<StackLayout>
<!– main content here –>
</StackLayout>
</ScrollView>
<controls:Modal
Grid.Row="0"
ModalHeader="Help Text"
ShowModal="{Binding ShowHelpModal}"
VerticalOptions="Fill">
<StackLayout>
<!– modal content here –>
</StackLayout>
</controls:Modal>
</Grid>
view raw MainView.xaml hosted with ❤ by GitHub

The modal visibility is controlled by a binding to ShowHelpModal, a boolean property in our ViewModel, so on the first rendering of the page, it is hidden. Because I have set up a header property for my example, I have also set the ModalHeader value.

In the main layout of the page, we add a button that will display the modal for us. And that’s it!

The full code for this example can be found here.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s