James Montemagno
James Montemagno

Live, Love, Bike, and Code.

Tags


Twitter


James Montemagno

Xamarin.Forms Design Time Data Tips & Best Practices

James MontemagnoJames Montemagno

One of my favorite features of building applications with XAML is the ability to use design time data to preview it inside the IDE. Recently in Xamarin.Forms 4.0 the team introduced an official pattern to specify design time properties, controls, or just about anything else. It is a really neat feature that I have been using on my live streams and in my apps. There is some really great docs that you should read through, but I will repeat the core info here.

DesignTime

Why Design Time Data?

Xamarin.Forms XAML Hot Reload is the future ( available today in VS 16.3 and VSM 8.3) for rapid iteration and productivity with XAML, I still sometimes find myself working on new screens or wanting to get a mockup of my UI without having to run my app. Also, sometimes you don't have your ViewModel setup yet and you just want to start building a screen. That is where design time data comes in, allowing you to preview your data without having to run your app. So, let's get started.

Getting Started

The Xamarin.Forms leveraged the basic building blocks of design time data from WPF/UWP and added a few name spaces that you can import into your XAML to start using it.

xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"

If you create a new project or page this will already be included and may even have some design time data already in there. These three lines of code setup the ability to start using design time data with the magical d character. You can of course change this, but d: is a nice and simple. That's it, you are ready to go!

Simple Properties

The previewer will automatically pick up any property that you set, however if you are using data binding then it will not be able to display anything.

<Label Margin="10" 
       Text="{Binding MonkeyCount, StringFormat='{0} Monkeys'}"/>

DesignTime1

To get design time data showing up we can simply add d: to a new Text property and assign the value.

<Label Margin="10" 
       Text="{Binding MonkeyCount, StringFormat='{0} Monkeys'}"
       d:Text="3 Monkeys"/>

DesignTime2

What is really great is that you can actually set ANY property! This is very nifty when you are trying to figure out spacing and sizing. I love to set the BackgroundColor all the time, but now, I can use design time data to see it only in the previewer.

<Label Margin="10" 
       Text="{Binding MonkeyCount, StringFormat='{0} Monkeys'}"
       d:Text="3 Monkeys"
       d:BackgroundColor="Orange"/>

DesignTime3

One of my favorite things to do is to see my ActivityIndicator in the previewer by setting d:IsRunning="True":

<ActivityIndicator 
    Grid.RowSpan="2"
    IsVisible="{Binding IsBusy}"
    IsRunning="{Binding IsBusy}"
    d:IsRunning="True"
    HorizontalOptions="Center"
    VerticalOptions="Center"/>

Activity

Lists of Data

One really cool thing about using design time data is that you can also specify an Array of data. This means that you can easily specify a design time ItemsSource on your ListView, CollectionView, CarouselView, etc. This is really great when we want to work on our cells, but don't have the data yet.

<d:ListView.ItemsSource>
    <x:Array Type="{x:Type x:String}">
        <x:String>Item One</x:String>
        <x:String>Item Two</x:String>
        <x:String>Item Three</x:String>
    </x:Array>
</d:ListView.ItemsSource>

Then you can use d: on any property on the cell, or you can use `{Binding .} to bind to the string that you have specified:

<ListView.ItemTemplate>
    <DataTemplate x:DataType="models:Monkey">
        <ImageCell Text="{Binding .}"
                   Detail="{Binding Location}"
                   d:Detail="Location"
                   ImageSource="{Binding Image}"
                   d:ImageSource="icon.png"/>
    </DataTemplate>
</ListView.ItemTemplate>

DesignTIme4

Lists of Model Data

Lucky for us, we can also use our real data model, which means our bindings will actually work! You just need to make sure that your properties you want to use have a public get and set.

<d:ListView.ItemsSource>
    <x:Array Type="{x:Type models:Monkey}">
        <models:Monkey Name="Baboon" Location="Africa and Asia"/>
        <models:Monkey Name="Capuchin Monkey" Location="Central and South America"/>
        <models:Monkey Name="Blue Monkey" Location="Central and East Africa"/>
    </x:Array>
</d:ListView.ItemsSource>
<ListView.ItemTemplate>
    <DataTemplate x:DataType="models:Monkey">
        <ImageCell Text="{Binding Name}"
                   Detail="{Binding Location}"
                   ImageSource="{Binding Image}"
                   d:ImageSource="icon.png"/>
    </DataTemplate>
</ListView.ItemTemplate>

DesignTIme5

Notice that I am still using d:ImageSource because the previewer wont go and download images from the internet. This icon.png is just in my drawable/resource folder and it will be displayed.

Important: You need to re-compile your libraries/app when you make changes to a model or ViewModel so the previewer can re-load it.

ViewModels, IntelliSense, & XAML Hot Reload

Alright, now for what you have been waiting for... ViewModels! A long time ago, I wrote a blog on how to use design time data ViewModels with the previewer. Many developers may be binding their ViewModel directly in their XAML. If you are using XAML Hot Reload then you may have noticed that everytime you reload that your ViewModel gets re-created! My recommendation would be to set your BindingContext in your page constructor:

public MonkeysPage()
{
    InitializeComponent();
    BindingContext = new MonkeysViewModel();
}

Now, the issue here is that you will no longer get IntelliSense in your XAML because the XAML doesn't know about your BindingContext! Don't worry, design time data to the rescue!

<d:ContentPage.BindingContext>
    <viewmodels:MonkeysViewModel/>
</d:ContentPage.BindingContext>

Now your IntelliSense will work again!

IntelliSense

Running ViewModel Logic

Now, you may be saying.. James, but I already have mock ViewModels that are ready to be consumed in my XAML & the previewer. Well, don't worry because while the previewer no longer runs your page's code behind, it will still execute the constructor of your ViewModel! This means you could set your d:ContentPage.BindingContext to the mock ViewModel, or if you need to pass parameters to the constructor to setup mock data, you can do that to.

So, let's say you have something like this for your ViewModel:

public MonkeysViewModel(bool designData)
{
    if (designData)
    { 
        Monkeys = MonkeyHelper.Monkeys;
        MonkeysGrouped = MonkeyHelper.MonkeysGrouped;
    }
    else
    {
        //Setup web requests and such
    }
}

We can setup the design time ViewModel in XAML and then remove ALL of the d: bindings that we were using:

<d:ContentPage.BindingContext>
    <viewmodels:MonkeysViewModel IsBusy="True">
        <x:Arguments>
            <x:Boolean>true</x:Boolean>
        </x:Arguments>
    </viewmodels:MonkeysViewModel>
</d:ContentPage.BindingContext>

Final

Future of Design Time Data

Design time data today is pretty cool and works well with the updates to the previewer that the team made to make it more stable. My hope for design time data is that the team opens up the API and actually allows you to use design time data at runtime! That is right! At runtime! I think that this would be super cool and I even opened a GitHub issue that you should go +1 on.

Get the code

As always, you can grab the code on GitHub for the monkeys app and give it a go.

This post is part of the Xamarin October Best Practice Challenge. Checkout the GitHub page for more great posts!


Tags



Live, Love, Bike, and Code

Checkout my monthly newsletter that you should subscribe to!

Comments