Creating a Xamarin Forms App Part 11 : Updating to Xamarin Forms 1.3

  • Part 1 : Introduction
  • Part 2 : Getting Started
  • Part 3 : How to use Xamarin Forms with Visual Studio without the Business Edition
  • Part 4 : Application Resources
  • Part 5 : Dependency Injection
  • Part 6 : View Model First Navigation
  • Part 7 : Unit Testing
  • Part 8 : Consuming a RESTful Web Service
  • Part 9 : Working with Alerts and Dialogs
  • Part 10 : Designing and Developing the User Interface
  • Part 11 : Updating to Xamarin Forms 1.3
  • Part 12 : Extending the User Interface

Xamarin.Forms 1.3 is currently available as a Community Preview. To update to this release follow these steps on the Xamarin developer site. This also provides details on updating to the Unified API for iOS.

This update includes the following much needed features that are available in WPF.

  • Styles
  • Triggers
  • Behaviors
  • Application

Jesse Liberty provides a really nice blog post on Xamarin Forms Styles here and Triggers here. And you can find out more about Behaviors in Xamarin Forms here.

Your App class should now inherit from Application. Interestingly this introduces everything I had previously provided in my own App base class, which I covered in part 4 of this series. i.e.

public static Application Current { get; } 
public Page MainPage{ get; set; }
public ResourceDictionary Resources{  get; set; }

And also introduces the OnStart, OnSleep and OnResume overridable methods for handling application lifecycle.

I have therefore removed my App base class and replaced it with the Xamarin Forms Application class. The MountainWeatherApp therefore now inherits from this but still provides the Application Resources in the xaml file in the same way as before.

I thought I’d take the Styles for a spin so I’ve created styles for each of the Label styles and placed these in the MountainWeatherApp xam, which now looks like this.

<?xml version="1.0" encoding="UTF-8"?>
<Application 
    xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
      xmlns:converters="clr-namespace:Silkweb.Mobile.MountainWeather.Converters;assembly=Silkweb.Mobile.MountainWeather"
    x:Class="Silkweb.Mobile.MountainWeather.MountainWeatherApp">
    <Application.Resources>
        <ResourceDictionary>
              <converters:ImageSourceConverter x:Key="imageSourceConverter" />
              <Color x:Key="backgroundColor">#3d78a2</Color>
              <Color x:Key="highlightColor">#1AFFFFFF</Color>
              <Color x:Key="textColor">White</Color>
              <Color x:Key="accentColor">White</Color>
                          
              <Style x:Key="headerStyle" TargetType="Label">
                <Setter Property="Font" Value="Bold, Medium" />
                <Setter Property="TextColor" Value="{StaticResource accentColor}" />
            </Style>

            <Style x:Key="largeHeaderStyle" TargetType="Label" BasedOn="{StaticResource headerStyle}">
                <Setter Property="Font" Value="Bold, Large" />
            </Style>

            <Style x:Key="smallHeaderStyle" TargetType="Label" BasedOn="{StaticResource headerStyle}">
                <Setter Property="Font" Value="Bold, Small" />
            </Style>
                        
            <Style x:Key="textStyle" TargetType="Label">
                <Setter Property="Font" Value="Small" />
                <Setter Property="TextColor" Value="{StaticResource textColor}" />
            </Style>

            <Style x:Key="microTextStyle" TargetType="Label" BasedOn="{StaticResource textStyle}">
                <Setter Property="Font" Value="Micro" />
            </Style>

        </ResourceDictionary>
    </Application.Resources>
</Application>

I can then use these styles in my ForecastView like this.

<StackLayout Spacing="0">
    <Label Text="Summary" Style="{StaticResource headerStyle}" />
    <Label Text="{Binding Weather}" Style="{StaticResource textStyle}" />
</StackLayout>

Interestingly I no longer need the ApplicationResources markup extension to lookup the resource from the Application.Resources. I can just use StaticResource like in WPF and it still works.

A great place to try out Triggers is in the DataTemplate for the Selected Tab. I was using a ValueConverter to convert the IsSelected property to a color for Selected and unselected. A much better way of doing this is to use a DataTrigger. I have created the following style in the ForecastReportView, which contains a DataTrigger for when the IsSelected value is True.

            <Style x:Key="tabGridStyle" TargetType="Grid">
                <Setter Property="BackgroundColor" Value="{StaticResource highlightColor}" />
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsSelected}" TargetType="Grid" Value="True">
                        <Setter Property="BackgroundColor" Value="Transparent" />
                    </DataTrigger>
                </Style.Triggers>
            </Style> 

and then I use this in the DataTemplate like this.

            <DataTemplate x:Key="itemTemplate">
                <Grid Padding="1">
                    <Grid WidthRequest="100" HeightRequest="100" Style="{StaticResource tabGridStyle}">
                        <StackLayout Padding="5" Spacing="0">
                            <Label Text="{Binding Date, StringFormat='{0:ddd}'}" Font="Mirco" VerticalOptions="Start" HorizontalOptions="Start" TextColor="{ex:ApplicationResource textColor}" />
                            <Label Text="{Binding Date, Converter={StaticResource dateTimeConverter}, ConverterParameter='d{0} MMM'}" Font="Mirco" VerticalOptions="Start" HorizontalOptions="Start" TextColor="{ex:ApplicationResource textColor}" />
                            <Image Source="{Binding Icon}" VerticalOptions="Start" HorizontalOptions="Center" WidthRequest="48" HeightRequest="48" />
                        </StackLayout>
                    </Grid>
                </Grid>
            </DataTemplate>

I have also created an ItemsSourceBehavior using the new Behavior class. This allows you to attach this behavior to a StackLayout and bind the ItemsSource property to a list and ItemTemplate. The StackLayout will then be populated with items based on these properties.

namespace Silkweb.Mobile.Core.Behaviors
{
    public class ItemsSourceBehavior : Behavior<StackLayout>
    {
        private StackLayout _associatedObject;

        public static readonly BindableProperty ItemsSourceProperty = 
            BindableProperty.Create<ItemsSourceBehavior, IList>(p => p.ItemsSource, default(IList), BindingMode.Default, null, ItemsSourceChanged);

        public IList ItemsSource
        { 
            get { return (IList)GetValue(ItemsSourceProperty); } 
            set { SetValue(ItemsSourceProperty, value); } 
        }

        public static readonly BindableProperty ItemTemplateProperty = 
            BindableProperty.Create<ItemsSourceBehavior, DataTemplate>(p => p.ItemTemplate, default(DataTemplate));

        public DataTemplate ItemTemplate
        { 
            get { return (DataTemplate)GetValue(ItemTemplateProperty); } 
            set { SetValue(ItemTemplateProperty, value); } 
        }

        static void ItemsSourceChanged(BindableObject bindable, IList oldValue, IList newValue)
        {
            var behavior = bindable as ItemsSourceBehavior;
            behavior.SetItems();
        }

        private void SetItems()
        {
            _associatedObject.Children.Clear();

            if (ItemsSource == null)
                return;

            foreach (var item in ItemsSource)
                _associatedObject.Children.Add(GetItemView(item));
        }

        private View GetItemView(object item)
        {
            var content = ItemTemplate.CreateContent();
            var view = content as View;
            view.BindingContext = item;
            return view;
        }

        protected override void OnAttachedTo(StackLayout bindable)
        {
            _associatedObject = bindable;
            base.OnAttachedTo(bindable);
        }
    }
}

I could therefore replace the ItemsView in the ForecastView with a StackLayout and attach this behaviour like this:

                <StackLayout>
                    <StackLayout.Behaviors>
                        <behaviors:ItemsSourceBehavior ItemsSource="{Binding WeatherPeriods}" ItemTemplate="{StaticResource itemItemplate}" />
                    </StackLayout.Behaviors>
                </StackLayout>

Xamarin Forms 1.3 is therefore a significant update and one that contains many features that will make developing in Xamarin Forms a lot easier.

12 thoughts on “Creating a Xamarin Forms App Part 11 : Updating to Xamarin Forms 1.3

  1. Jonathan,

    I have really enjoyed reading this series of posts, thanks for devoting time and effort to do this. As I was looking through the code I was wondering why you chose to parse the response yourself, rather than having .NET do it for you.

    As an example, the call to the sitelist endpoint in your code is (or was)…

    var locationToken = JObject.Parse(result)[“Locations”][“Location”];

    locations = locationToken.Select(location => new Location()
    {
    Id = (int)location[“@id”],
    Name = (string)location[“@name”]
    }).ToArray();

    With a bit of furtling you can get Json.NET to parse this all for you. Step one (and this is the magic bit), call the service and retrieve the JSON and pop it onto the clipboard. Then use the magical “Paste JSON as classes” – this menu option lives at Edit -> Paste Special, and I believe it’s been there since VS 2010. That gets you a set of classes that parse the response.

    Then, due to the nature of the location data (having @id and @name) you need to alter the location class a little…

    public class Location
    {
    [JsonProperty(“@id”)]
    public string Id { get; set; }

    [JsonProperty(“@name”)]
    public string Name { get; set; }
    }

    Here the JsonProperty attributes provide an alias to the serializer – and I can also have nicely capitalised .NET property names too. With that done I can call the service as follows…

    var client = new HttpClient { BaseAddress =
    new Uri(“http://datapoint.metoffice.gov.uk/public/data/txt/wxfcs/mountainarea/json/”) };
    var response = await client.GetAsync(“sitelist?key=xxxxx”);

    return await response.Content.ReadAsAsync();

    I much prefer having some other code parse JSON than mine, hope this helps!

    Once again thanks for this series, I’m looking forward to the next installment!

    Like

    1. Morgan,

      I’m really glad you’re enjoying my series. It’s a very good point you’ve raised about the JSON deserialisation. The reason I have opted to parse it manually is that I didn’t like the structure of the classes that gets generated from the JSON structure. In fact I had a bit of trouble trying to work out what the hierarchy should be and I didn’t like what I was ending up with. Having said that I didn’t know about the ‘Paste JSON As Classes’ feature in Visual Studio. Thanks for pointing that out. I wish I’d known about it cos I doubt I would have gone down the route I did. But it does still generate an unnatural hierarchy due to the nature in which the Met Office have structured the data.

      For example, let’s take the locations feed. The JSON typically looks like this:

      {
      “Locations”: {
      “Location”: [{
      “@id”: “100”,
      “@name”: “Brecon Beacons”
      }, {
      “@id”: “101”,
      “@name”: “East Highland”
      }, {
      “@id”: “102”,
      “@name”: “Lake District”
      }, {
      “@id”: “103”,
      “@name”: “Peak District”
      }, {
      “@id”: “104”,
      “@name”: “Snowdonia”
      }, {
      “@id”: “105”,
      “@name”: “West Highland”
      }, {
      “@id”: “106”,
      “@name”: “Yorkshire Dales”
      }]
      }
      }

      Using ‘Paste JSON As Classes’ I end up with the following classes:

      public class Rootobject
      {
      public Locations Locations { get; set; }
      }

      public class Locations
      {
      public Location[] Location { get; set; }
      }

      public class Location
      {
      public string id { get; set; }
      public string name { get; set; }
      }

      Notice that the Array of Locations is Wrapped in a Locations class that is then wrapped inside the Rootobject class. So to access a location you’d say rootObject.Locations.Location[0].name. I really didn’t like this. My Model simply consists of a Location class like this:

      public class Location
      {
      public int Id { get; set; }
      public string Name { get; set; }
      }

      and then I just return IEnumerable, that’s it. It get’s even worse when working with the Forecast feed which is quite complex. I end up with a whole bunch of classes that I don’t really want and the hierarchy is more complex than it needs to be. In addition to that I then have to decorate a lot of the properties with the JsonPropertyAttribute which I don’t like either.

      Having said that it might have saved me a whole load of work and I suppose I’d just live with the class structure that gets generated. I will be extending the app to include further feeds from the Met Office DataPoint service so I think I’ll give it a go and see how it works out.

      Like

  2. Hi, thanks for these blog posts – very informative!

    I’ve just started playing around with the xamarin style changes in 1.3 and have tried putting them into the application.resources area like you have.

    If I reference a named style it is applied successfully, but if I try to create a global style, there is nothing applied.

    Have you come across this?

    Thanks,
    Donal

    Like

  3. Hi Jonathan,

    First of all, I want to thank you for your work. I am a (french) beginner in MVVM / WPF / Xamarin applications, and I really like your ViewModel first / navigation pattern. I understood a lot of things with your tutorials.

    I have one problem, let’s say that I have a BasketViewModel with products (count) property and a total price property.

    I would like to display thoses properties on a product page, or home page. Do I really need to inject BasketViewModel inside the ProductViewModel or HomeViewModel ? I would like to reduce coupling.

    The only way I found for now is to use a ViewModelLocator so that the views directly access to the BasketViewModel. But I am not comfortable with it, it doesn’t seems very “ViewModel first”.

    Like

    1. I would not recommend using any sort of service/viewModel locator pattern. You should use a dependency injection framework like Autofac and inject any dependencies into your constructors. I would recommend that you introduce an interface called something like IShoppingBasket, with properties for Count and Total Price, that your BasketViewModel should implement and register your BasketViewModel with your DI container against this interface. You can then inject IShoppingBasket into the constructor of your Product/Home ViewModels. This still keeps them nicely decoupled from your BasketViewModel and is easily testable.

      Like

  4. It appears that the source code is missing all of the images in the iOS/Resources folder. Causes the build to fail. Are these intentionally omitted and loaded dynamically from a remote source at runtime? Or maybe you just overlooked them when you packaged up the source? It would be really great to find this source on GitHub, but I was unable to track down your GitHub account. Thanks!

    Like

    1. Hi, They are ommitted for licensing reasons. Please check the readme file in the same directory. The source code is no longer available in GitHub but you can click the links at the bottom of any of the series posts to download a zip of the source.

      Like

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 )

Google+ photo

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

Connecting to %s