Responsive xaml properties in Xamarin.Forms

Look at how IMarkupExtension could be used to create dynamic properties based on screen size.

Posted by on

C# Xamarin

I have been working on UI and UX a bit lately and trying to come up with views that are fluid across a whole range of device resolutions.

Now, Xamarin.Forms does do a great job with UI but sometimes there are little things that you want to change on a per device or per resolution level.

I noticed that there is a built in xaml helper called OnPlatform that lets you set xaml properties differently per platform so I dug into that to try and make my own and I found markup extensions which let you do just that, extend markup. So I decided to make a markup extension which allows me to set different xaml properties based on screen size.

Here is the ResponsiveWidthProp, using similar logic we have also created one for height but for now we’ll just work with the width. It’s a pretty simple class it implements the IMarkupExtension interface, it has a public list property for Query which will hold the values and max widths we want, using the ContentProperty attribute here so we can use the elements content to set this property as well as a Default property in case we don’t have a value set for the given width. We then have a ProvideValue function that gets the current screen width then goes through the query list to find a match for the width or falls back to the default.

Markup extension
[ContentProperty("Query")]
public class ResponsiveWidthProp<T> : IMarkupExtension
{
    public IList<ResponsiveSet> Query { get; set; } = new List<ResponsiveSet>();
    
    public T Default { get; set; }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        var size = GetSize();

        foreach (var res in Query.OrderBy(r => r.MaxSize))
        {
            if (size > res.MaxSize)
                continue;
            return res.Value;
        }

        return Default != null ? Default : default(T);
    }
    
    public int GetSize()
    {
        var width = Application.Current.MainPage.Width;

        return (int)width;
    }
}

[ContentProperty("Value")]
public class ResponsiveSet
{
    public int MaxSize { get; set; }
    public object Value { get; set; }
}
Usage
<Image HorizontalOptions="Start" Source="Logo" VerticalOptions="Start">
    <Image.Margin>
        <xaml:ResponsiveHeightProp x:TypeArguments="Thickness" Default="4,24">
            <xaml:ResponsiveSet MaxSize="570">0,16</xaml:ResponsiveSet>
            <xaml:ResponsiveSet MaxSize="1000">2,18</xaml:ResponsiveSet>
        </xaml:ResponsiveHeightProp>
    </Image.Margin>
</Image>

Read the docs on Creating XAML Markup Extensions to find out more about them.