Xamarin.Forms: Search and Barcode Scanner

To recap, I’m writing a shopping cart app for Windows Phone, Android, and iOS.  The purpose of the app is primarily to let me use Forms.  Each post will build on top of the previous one.

Last time I worked I added asynchronously loaded images to a list view.  Today I plan on adding a product search that uses a barcode scanner using the ZXing component.

Recap and Code

This is the fifth post in the series, you can find the rest here:

  1. Day 0:  Getting Started (blog / code)
  2. Day 1:  Binding and Navigation (blog / code)
  3. Day 2:  Frames, Event Handlers, and Binding Bugs (blog / code)
  4. Day 3:  Images in Lists (blog / code)
  5. Day 4:  Search and Barcode Scanner (blog / code)

The latest version of the code can always be accessed on the GitHub project page.

Search

Before I implement search with a barcode, it’ll be easier if I add a search box.  For now, I’ll just add it on the categories page.  This should be simple, I’ll throw up a text box and a button.

<StackLayout VerticalOptions="FillAndExpand" Orientation="Horizontal">
  <Entry Text="{Binding SearchTerm}"
         Placeholder="Search"
         HorizontalOptions="FillAndExpand" />
  <Button Text ="Search"
    Command="{Binding SearchCommand}" />
</StackLayout>

Next I wire them into the view model.

private readonly RelayCommand _searchCommand;

public CategoriesListViewModel(IProductService service, INavigationService navi)
{
    _searchCommand = new RelayCommand(async () =>
    {
        var items = (await _service.Search(SearchTerm))
                    .OrderByDescending(i => i.Rating)
                    .ToList();

        if (items != null && items.Any())
        {
            Page page = items.Count == 1
                 ? page = App.GetProductPage(items.First())
                 : page = App.GetProductsListPage(items, SearchTerm);

            await _navi.PushAsync(page);
            SearchTerm = string.Empty;
        }
        else
        {
            await _navi.DisplayAlert("Error", "No results for search " + SearchTerm);
        }
    },
    () => !string.IsNullOrWhiteSpace(SearchTerm));
}

public ICommand SearchCommand { get { return _searchCommand; } }

public string SearchTerm
{
    get { return GetValue<string>(); }
    set
    {
        SetValue(value);
        _searchCommand.RaiseCanExecuteChanged();
    }
}

If our search has exactly one result, then we go directly to that product page; if there are multiple results, we show them in a list.

The ProductService already has a stub for search, but was only half implemented.

public async Task<List<Product>> Search(string searchString)
{
    var items = await _itemsAsync;

    if (string.IsNullOrWhiteSpace(searchString)) return items;

    searchString = searchString.ToLower();
    var filterd = items.Where(i => i.Name.ToLower().Contains(searchString))
                       .ToList();

    return filterd;
}

Adding a Component

The first step in using a component is to add it to your project.  Right click on the “Components” folder in your Droid or iOS project and select “Ge More Components…”

image

I searched for a barcode scanner and got a list of packages.  ZXing sounds familiar and supports all platforms so I go with that.

image

Window’s phone is a little simpler in that I can just download the package from NuGet:

Install-Package ZXing.Net.Mobile

Using a Component

Since each platform references its own implementation of the barcode scanner software, I now need to create a device agnostic interface in the common project.

public interface IScanner
{
    Task<ScanResult> Scan();
}

public class ScanResult
{
    public string Text { get; set; }
}

I then implement these interfaces for each of the platforms.

Android first:

public class DroidScanner : IScanner
{
    public async Task<ScanResult> Scan()
    {
        var scanner = new MobileBarcodeScanner(Forms.Context)
        {
            UseCustomOverlay = false,
            BottomText = "Scanning will happen automatically",
            TopText = "Hold your camera about \n6 inches away from the barcode",
        };

        var result = await scanner.Scan();

        return new ScanResult
        {
            Text = result.Text,
        };
    }
}

The MobileBarcodeScanner takes the current instance of the Android context.  Because this is a common requirement in Android API’s Xamarin exposes the current context in the static property Forms.Context.

WindowsPhone is essentially the same, just a different constructor:

public class WinPhoneScanner : IScanner
{
    public async Task<ScanResult> Scan()
    {
        var scanner = new MobileBarcodeScanner(MainPage.DispatcherSingleton)
        {
            UseCustomOverlay = false,
            BottomText = "Scanning will happen automatically",
            TopText = "Hold your camera about \n6 inches away from the barcode",
        };

        var result = await scanner.Scan();

        return new ScanResult
        {
            Text = result.Text,
        };
    }
}

Next I register the device specific implementations with the XF DependencyService.  In my Droid project I create a file called ServiceRegistration and add the following lines

using ShoppingCart.Droid.Services;

[assembly: Xamarin.Forms.Dependency(typeof(DroidScanner))]

A similar file is created for WP:

using ShoppingCart.WinPhone.Services;

[assembly: Xamarin.Forms.Dependency(typeof(WinPhoneScanner))]

Now to create an instance of IScanner I resolve it from the DependencyService:

IScanner scanner = DependencyService.Get<IScanner>();

Now all I have to do is wire up a new button to to launch the scanner and use the results.

ScanCommand = new RelayCommand(async () =>
{
    var result = await _scanner.Scan();

    SearchTerm = result.Text;
    Search();
});

The scan command just calls into the search implementation I wrote above.

Adding Permissions

And finally, I just need to add in the permission to access the camera, otherwise the barcode scanner component won’t be able to start up at all.  On Windows phone open up the WMAppManifest, switch to the Capabilities tab, and make sure that ID_CAP_ISV_CAMERA is checked.

imageOr just edit the file by hand and add the permission by hand.

<Capability Name="ID_CAP_ISV_CAMERA" />

Android is similar. Right click the project, select Properties, go to the Android Manifest tab, and select CAMERA in the Required permissions section.

image

Again, you can do this manually by editing the AndroidManifest.xml file directly and adding the CAMERA permission.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
  <uses-sdk />
  <application></application>
  <uses-permission android:name="android.permission.CAMERA" />
</manifest>

Happy coding

Xamarin.Forms: Images in Lists

To recap, I’m writing a shopping cart app for Windows Phone, Android, and iOS.  The purpose of the app is primarily to let me use Forms.  Each post will build on top of the previous one.

Last time I worked through some bugs in XF’s binding and cleaned up the layout a bit.  Today the plan is to add product photos to make the list of products and the product pages look prettier.

Recap and Code

This is the fourth post in the series, you can find the rest here:

  • Day 0:  Getting Started (blog / code)
  • Day 1:  Binding and Navigation (blog / code)
  • Day 2:  Frames, Event Handlers, and Binding Bugs (blog / code)
  • Day 3:  Images in Lists (blog / code)

The latest version of the code can always be accessed on the GitHub project page.

Bugs fixed

Last week I ran into a known bug where XF wasn’t properly responding to IPropertyChaged events.  At the time there was a fix in a beta build of Xamarin.Forms but I had trouble getting the beta dlls to run in my app.  Since then the fix has been officially released.  So, I updated all my nuget packages; lo and behold binding now works as expected.

Long story short:  Xamarin fixed the bug quickly and correctly.  Good for them.

Cleaning the Data

A little nitpicky, but while playing with my app I noticed that the product names don’t look like product names.  They are all lower cases and have odd punctuation.  I wanted to clean this up quickly so I grabbed Humanizer and added some post processing on the client after loading the products.

item.Name = item.Name
                .Titleize()
                .Replace(".", string.Empty);
item.Description = item.Description
                       .Humanize(LetterCasing.Sentence);

Now, if I want to show “product pictures” I’ll need to get those photos from somewhere.  From an unrelated undertaking I had a list of 300+ photos from Imgur.  Imgur also has a nice feature where you can request the image as an icon or full size.  My data lives in a single string of json which is deserialized at startup.  I want to be lazy and not update each item in that list manually so I’m taking a shortcut.  I added the Imgur ids to a queue and then in my post-process of the item, I add the icon and image urls.  Note the “s” at the end of the IconUrl.  That’s Imgur’s way to specify a “small square” icon.

private static void UpdateItem(Product item, string imgurId)
{
    item.ImageUrl = string.Format("http://i.imgur.com/{0}.jpg", imgurId);
    item.IconUrl = string.Format("http://i.imgur.com/{0}s.jpg", imgurId);
}

Now that I have all my data, I’ll start displaying the photos.

Inserting Images

I’m going to start with adding the image to the product page.  it’s just one image on the page so it should be pretty simple.

<ScrollView>
  <StackLayout VerticalOptions="FillAndExpand" Padding="50">

    <Label Text="{Binding Product.Name}"
      HorizontalOptions="Center"
      Font="Bold,Large" />

    <Image Aspect="AspectFill"
            HorizontalOptions="FillAndExpand"
            Source="{Binding Product.ImageUrl}" />

    <Label Text="{Binding Product.Description}"
      HorizontalOptions="Start"
      Font="Medium" />

    <Label Text="{Binding Product.Price, StringFormat='{0:c}'}"
      HorizontalOptions="Start"
      Font="Bold,Medium" />

  </StackLayout>
</ScrollView>

All I needed to do was add an Image element and bind the source to the product’s ImagUrl property.  The image will automatically be downloaded and cached for me.  According to the documentation the cache lasts one day by default but is configurable.  You can even turn the cache off.

This worked pretty well out of the box, everything loaded up very quickly.  The only caveat is that some of my pictures are fairly long so the description and text are pushed off the page.  Wrapping the entire StackLayout in a ScrollView allows me to scroll down and see the rest of my content.

screenshot

Moving to the list view, all I do was bind against the IconUrl in the Product model class. Remember, this is the smaller thumbnail provided to us by Imgur.

<ListView ItemsSource="{Binding Products}" ItemSelected="OnItemSelected">
  <ListView.ItemTemplate>
    <DataTemplate>
      <ViewCell>
        <StackLayout VerticalOptions="FillAndExpand" Orientation="Horizontal" Padding="10">

          <Image Aspect="AspectFill"
                 HeightRequest ="75" WidthRequest="75"
                 Source="{Binding IconUrl}" />

          <Label Text="{Binding Product.Name}" YAlign="Center" Font="Large" />
        </StackLayout>
      </ViewCell>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

I know I want the images to all be the same size so I set the width and height to be 75 pixels. The Aspect property will do its best to show as much of the image as possible, clipping it where needed.  As you can see, I also needed to add a horizontal StackLayout because the data template now has two items in it.  I’ve also upped the text size from before, it was hard to click an item in the list because it was too small.

Here’s the result:

screenshot

It doesn’t look too bad, but there is a problem:  it takes a long time to load.  Since XF synchronously loads the images, the app is completely blank and unresponsive for 2 to 5 seconds.

Researching the Slow

The app hanging when loading a product list is unacceptable.  So the first thing I did to counteract this was to take the network out of the equation.  I added a loading image as a resource to the app.  Since I wanted the same image on all platforms, I added it directly to the PCL project as an EmbeddedResource.

image

Just for testing, I modified my model to expose this image instead of the image from the web and bound my view to that instead.

public ImageSource IconSource
{
    get
    {
        string resource = "ShoppingCart.Resouces.placeholderImageSmall.png";
        return ImageSource.FromResource(resource);
    }
}
<Image Aspect="AspectFill"
       HeightRequest ="75" WidthRequest="75"
       Source="{Binding IconSource}" />

The resource path is separated by dots and includes the assembly name as well as any sub folders.  In my example above, the file “placeholderImageSmall.png” is in the “Resources” folder (which I created) in the “ShoppingCart” project.

With this change, the page loads instantly, but doesn’t look quite as nice since every product is using the same placeholder image.

screenshot

Since XF doesn’t do the load asynchronously, I need to figure out how to display the loading icon while the image is downloaded from the web in the background.

If I was in a pure WPF world, I’d use Priority Binding.  This lets you set a list of locations to get the binding value from, marking some as asynchronous.  Sadly, this is not supported in XF, so that’s right out.

So this means I need to roll it by hand.

Speeding Things Up

I already have a class that manages binding to the result of a task.  I used this last time for my product list which is (theoretically) coming from a slow source.  It is Stephen Cleary’s NotifyTaskCompletion.  This is a good start, but it returns null as the result until the task completes.  In my case I want it to return a default value (my place holder image).  I tweak the constructor and the result property just a touch to get the implementation I want.

private readonly TResult _defaultResult;

public NotifyTaskCompletion(Task<TResult> task, TResult defaultResult = default(TResult))
{
    _defaultResult = defaultResult;
    Task = task;

    if (!task.IsCompleted)
    {
        var _ = WatchTaskAsync(task);
    }
}

public TResult Result
{
    get
    {
        return (Task.Status == TaskStatus.RanToCompletion)
          ? Task.Result
          : _defaultResult;
    }
}

The constructor now takes the default value, with a default value of default(TResult) so we don’t break any existing uses of the class.

Next a few helpers to make using this for images simple:

public static class AsyncImageSource
{
    public static NotifyTaskCompletion<ImageSource> FromTask(Task<ImageSource> task, ImageSource defaultSource)
    {
        return new NotifyTaskCompletion<ImageSource>(task, defaultSource);
    }

    public static NotifyTaskCompletion<ImageSource> FromUriAndResource(string uri, string resource)
    {
        var u = new Uri(uri);
        return FromUriAndResource(u, resource);
    }

    public static NotifyTaskCompletion<ImageSource> FromUriAndResource(Uri uri, string resource)
    {
        var t = Task.Run(() => ImageSource.FromUri(uri));
        var defaultResouce = ImageSource.FromResource(resource);

        return FromTask(t, defaultResouce);
    }
}

All this is doing, is creating (and running) a task to download the images in the background and feeding that into the NotifyTaskCompletion.  One caveat here is that even if nothing ever binds to the image, it will still be downloaded.  The ProductViewModel can now expose the IconUrl as an asynchronous wrapper around an ImageSource

public class ProductViewModel : BaseViewModel
{
    private const string _resource = "ShoppingCart.Resources.placeholderImageSmall.png";
    public ProductViewModel(Product product)
    {
        Product = product;
        IconSource = AsyncImageSource.FromUriAndResource(product.IconUrl, _resource);
    }

    public NotifyTaskCompletion<ImageSource> IconSource { get; private set; }

    public Product Product
    {
        get { return GetValue<Product>(); }
        set { SetValue(value); }
    }
}

The ProductsListViewModel will have to expose a list of the ProductViewModel instead of just the raw model which is a shame.  I like binding to the model whenever possible just so I don’t have to worry about passing view models around.  But what can you do?  And now in the view, we bind to the result of the IconSource like so:

<Image Aspect="AspectFill"
       HeightRequest ="75" WidthRequest="75"
       Source="{Binding IconSource.Result}" />

Expectations and Realities

Running up the app my expectation would be to see the loading images when you first go to the product list pages and over the course of 2 to 5 seconds have them change to the correct product images as they are downloaded.  In reality, when I navigated to a page all of the photos were present immediately.  Right away.  No delay.  No loading images.

Happy coding.

Xamarin.Forms: Frames, Event Handlers, and Binding Bugs

To recap, I’m writing a shopping cart app for Windows Phone, Android, and iOS.  The purpose of the app is primarily to let me use Forms.  Each post will build on top of the previous one.

Last time I got some basic binding setup and navigation working.  Today I plan on cleaning up the views a bit so that they look “nice”.  Well nicer.  After that I’d like to show a list of products and let the user drill in and look at details.

Recap and Code

This is the third post in the series, you can find the rest here:

  • Day 0:  Getting Started (blog / code)
  • Day 1:  Binding and Navigation (blog / code)
  • Day 2:  Frames, Event Handlers, and Binding Bugs (blog / code)

The latest version of the code can always be accessed on the GitHub project page.

Cleaning up Login and Main Page

Last time I was focused on functionality.  I was happy to just get the pages to do what I want. Now i want to take a little time and play with the layout a bit.  First on the docket is “MainPage”.  It really isn’t the main page, so I’ll rename that to “WelcomePage”.  This includes the the view model as well.

With that done I want to add some space around all of the text box and button.  Problem is: there’s no “margin” property on any of the controls.  After a little digging, it seems that the only way to add spacing is to wrap each control in its own ContentView and set the Padding property on that.  A slightly simpler approach is to use a Frame instead.  It inherits directly from ContentView and has a default padding of 20.  Despite the fact that this only saves me from setting one property, the fact that it’s in a Frame helps me remember why I’m wrapping the control in the first place.  Let’s wait a few weeks and see if I continue using Frames.

The WelcomePage (né MainPage) now looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ShoppingCart.Views.WelcomePage"
             xmlns:local="clr-namespace:ShoppingCart;assembly=ShoppingCart"
             BindingContext="{x:Static local:App.WelcomeViewModel}">

  <StackLayout
    VerticalOptions="Center">
    <Frame>
      <Label Text="Welcome to The Store" Font="Bold, Large" HorizontalOptions="Center" />
    </Frame>

    <Label Text="Login to start shopping" HorizontalOptions="Center" />

    <Frame>
      <Button Text ="Log In" Command="{Binding GoToLoginPageCommand}" HorizontalOptions="Center" />
    </Frame>
  </StackLayout>
</ContentPage>

I also tweaked the welcome text making it bigger and bold as well as adding a call to action to help the user navigate to their next step.  There’s a fair bit you can do with the Font (size, style) property just by providing a comma separated list of values.

The login page got the same spacing treatment, including a nice fat margin around the entire page just so the text boxes don’t sit flush against the right side.  It’s still a little stark so I’ll throw in a touch of color on the title text, just because I can.

<StackLayout VerticalOptions="FillAndExpand" Padding="50">

<Frame Padding="75">
  <Label Text="Login" 
    Font="Bold,Large"
    TextColor="Yellow"
    HorizontalOptions="Center" />
</Frame>

<Label Text="User name" 
  HorizontalOptions="Start" />
<Entry Text ="{Binding Username}" 
  Placeholder ="User name goes here" 
  HorizontalOptions="FillAndExpand" />

<Label Text="Password" 
  HorizontalOptions="Start" />
<Entry Text ="{Binding Password}" 
  Placeholder ="Password goes here" 
  HorizontalOptions="FillAndExpand" />

<Button Text ="Log In" 
  Command="{Binding LoginCommand}" 
  HorizontalOptions="CenterAndExpand" />

</StackLayout>

Now that looks a little bit nicer.

image

Data

Now that the two pages I have look reasonable, I’ll add another.  In order to show some data, I actually need data.  To keep it simple, I start off by creating a list of hard coded C# data.  I have to admit that I got a bit silly here.  At first I tried to hand craft a back log of data.  That got old really fast.  In fact I only got one item defined before I realized I was wasting a lot of time.  Next I decided to grab a list of products (books) from the web and just tweak the data to my needs.  This too proved onerous.  Then I broke down and went to the web to generate all of my data.  I found a great site that even outputs the data in JSON.  It was the first hit on Google.  To process the JSON i nuget and install Newtonsoft’s Json.NET.

I add a ProductLoader and a ProductService class.  The loader simply stores the literal string of JSON and deserializes it on request.  In the future I want to create another implementation that reads the data from disk or the web.  The ProductService doesn’t care where the data comes from, it provides the view models with an interface to query and filter the data.  Because the underlying data will eventually come from a web request, both of these services asynchronously return Tasks. I use Stephen Cleary’s NotifyTaskCompletion in my view model to consume these services.  For a detailed explanation of what’s going on here, take a look at his Patterns for Asynchronous MVVM Applications series.

The data object itself is pretty simple.

public class Item
{
    public string Category { get; set; }
    public string Description { get; set; }
    public string ImageUrl { get; set; }
    public string Name { get; set; }
    public double Price { get; set; }
    public string ProductCode { get; set; }
    public int Rating { get; set; }
    public List<string> Tags { get; set; }
}

The Category property lets us show a short list to the user once they log in.  Once they pick a category they see all of the items in that category and then drill down into a specific item.  To accommodate this flow, I’ll add three more pages with corresponding view models:

  • CategoriesListPage/ViewModel
  • ProductsListPage/ViewModel
  • ProductPage/ViewModel

With a bigger app I’d lean towards single instances of each of these pages and using a message broker to update one from the other.  I..e, when a category is clicked on in the CategoriesList page I’d send an “Update List of Products” message and then navigate to the ProductsPage.  But since I already have the convenient App.cs handling all of my interactions between pages, I’ll just squash it into there.  Not ideal for a larger app that I’d like to keep decoupled, but fine for the five pages I currently have.

Lists and DataTemplates

The first thing to tackle is to show the list of categories.  This is similar to traditional two step process in Windows XAML.  Step one:  bind the ItemsSource property to the list.  A quick reminder that the Categories property is a NotifyTaskCompletion<List<string>> which means I need to bind against Categories.Result.  Step two:  define a data template to define how each row looks.  Strictly speaking, since my underlying data is a string, I can skip this step.  Since this is my first time defining a data template in XF, I’ll define one anyway just to make sure it works how I expect.  In my example I’m making the background color red just so I can verify that it’s using my DataTemplate.

<ListView ItemsSource="{Binding Categories.Result}" ItemSelected="OnItemSelected">
    <ListView.ItemTemplate>
      <DataTemplate>
        <ViewCell>
          <Label Text="{Binding .}" BackgroundColor = "Red" YAlign="Center" Font="Medium" />
        </ViewCell>
      </DataTemplate>
    </ListView.ItemTemplate>
  </ListView>

Handling Events

You’ll notice in the above definition of my ListView that I’m setting the ItemsSelected event handler.  I’m not binding against the view model here, I’m calling into code behind which then calls into my view model.

public partial class CategoriesListPage
{
    public CategoriesListPage()
    {
        InitializeComponent();
    }

    private void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
    {
        var param = e.SelectedItem as string;
        var command = ((CategoriesListViewModel)BindingContext).NavigateToCategory;

        if (command.CanExecute(param))
        {
            command.Execute(param);
        }
    }
}

This is a lot of boilerplate code for handling a click.  The problem is that the ListView doesn’t expose a command I can bind against when an item is selected.  In a traditional Xaml app I’d use the EventToCommand pattern except that it is built on Blend behaviors which aren’t PCL compatible and therefore not Xamarin compatible.

Another option was to subclass the ListView class and expose the command logic that I need.  I might eventually go this route, but I’ll probably need more than a few list boxes in my app to justify it.

Binding Bug in XF

The command for navigating to a category’s page gets a list of all the items for that category and calls into the static App class to get the page to navigate to.

var page = App.GetProductsListPage(items, categoryName);
await _navi.PushAsync(page);

Originally App.cs just updated the properties on the view model and returned the single instance of the ProductsListPage.

public static Page ProductsListPage { get; private set; }

public static Page GetProductsListPage(List<Item> items, string title)
{
    if (string.IsNullOrWhiteSpace(title)) title = "Products";

    ProductsListViewModel.Products = items;
    ProductsListViewModel.Title = title;
    return ProductsListPage;
}

This relies on the binding to update the view when something has changed and INotifyProperty.PropertyChanged is raised.  The first time this is called it works just fine.  It fails on all subsequent calls.  After a lot of debugging and assuming that I was wrong I found a recent post on Xamarin’s forums explaining that there is a bug where the UI is not updated when a bound value is changed.  Note that this only effects values that are updated in the view model updating on the view; the other way works just fine.  Updating a value in the view (like a text entry) correctly updates the bound value in the view model.  This is why my login page worked just fine.

Xamarin has released a fix for this bug, but as of writing this it is in a pre build of XF.  I tried to use it but kept getting runtime DLL errors.  I tried several times before having to give up on this as an immediate solution.  I will say that this may have just been an issue with user error since it was close to 1 AM at this point.

In my specific case, there was no reason that I had to reuse the same view.  Simply recreating the view every time I wanted to display it was simple enough.

public static Page GetProductsListPage(List<Item> items, string title)
{
    if (string.IsNullOrWhiteSpace(title)) title = "Products";

    ProductsListViewModel.Products = items;
    ProductsListViewModel.Title = title;
}

Summary

Today I was able to clean up some of the views using the Frame control to provide margins.  I had to resort to two workarounds, one for routing events to commands in code behind the other for updating the view when a bound value has changed.

I’m not sure what I want to tackle next week.  Perhaps reading from the file stream?  Maybe fleshing out the products view a bit so I display an image to go with the product?

Until then, happy coding.

Xamarin.Forms: Binding and Navigation

To recap, I’m writing a shopping cart app for Windows Phone, Android, and iOS.  The purpose of the app is primarily to let me use Forms.  Each post will build on top of the previous one.

Last time we did little more than get our feet wet.  The plan for today is to create a page with data binding  At this stage of the app, that sounds like a login page to me.

An aside.  It got rather tiring saying “Xamarin.Forms” all the time and I’m not quite comfortable just calling it “Forms” because I keep thinking back to WinForms whenever I do.  So I’m just going to go the abbreviation route and call it XF.  The abbreviation has the benefit of starting with an “X” and those are just cool.

Recap and Code

This is the second post in the series, you can find the rest here:

  • Day 0:  Getting Started (blog / code)
  • Day 1:  Binding and Navigation (blog / code)

The latest version of the code can always be accessed on the GitHub project page.

Binding

Before I’m able to do anything fancy, I need to figure out how to do some basic binding.  To test this out, I create a basic view model with one property that returns some constant text.

public class MainViewModel
{
    public string Message {get { return "Hello World";}}
}

I update the view to simply bind to this message now instead of hard coding the text in the XAML.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ShoppingCart.Views.MainPage">
    <Label Text="{Binding Message}" HorizontalOptions="Center" />
</ContentPage>

For now, I’ll just set the BindingContext in the page’s constructor.

public partial class MainPage : ContentPage
{
    public MainPage(MainViewModel vm)
    {
        this.BindingContext = vm;
        InitializeComponent();
    }
}

All that’s left is to pass in a new view model when creating the page.

new MainPage(new MainViewModel())

Firing up the emulator, this works out of the gate.  Nice.  Time to move on to something a little more difficult.

The Main Page

I don’t want the login page to be the first page of the app.  I think some sort of splash screen or welcome message would make more sense.  For now It will simply just have some text and a button that’ll take you to the login page.  I’ll add the standard Command=”{Binding GoToLoginPageCommand}” now and wire it up next.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ShoppingCart.Views.MainPage"
             xmlns:local="clr-namespace:ShoppingCart;assembly=ShoppingCart"
             BindingContext="{x:Static local:App.MainViewModel}">

  <StackLayout
    VerticalOptions="Center">
    <Label Text="Welcome to the main page." HorizontalOptions="Center" />

    <Button Text ="Log In" Command="{Binding GoToLoginPageCommand}" HorizontalOptions="Center" />
  </StackLayout>
</ContentPage>

Now that I know I need to bind to a command, I add the MainViewModel.  The command adds some complexity for two reasons.  First it’s a command so I need to either implement ICommand or find an already ready implementation.  There’s nothing that I could find in XF that implements ICommand for me and I don’t want to bring in an outside framework like MvvmLight just yet so I go ahead and add add a simple implementation of ICommand.  It’s so simple that I even named it SimpleCommand.cs.  I’m not really sure if this will stay long term, but it’ll do the job for now.

The second bit of complexity isn’t as easy.  It’s the fact that I don’t know how to do navigation in XF.  So it’s time for some research.  To the LIBRARY!

Figuring Out Navigation

According to the doco, all you need to do is wrap your ContentPage in a Navigation page.  This is as simple as

new NavigationPage(new MainPage())

Then in your page you can access the Navigation property.  After playing with this property a bit I learned that how it works depends on your platform.  On Windows Phone it was non null and worked regardless of whether or not you created a NavigationPage.  Android was less forgiving.  One nice touch was that as soon as you wrap your droid page you get the ActivityBar back button.

Playing around with the Navigation property on the page on both Windows and Android, I found it to be buggy.  I set up a simple pair of pages with one button each.  The first page would merely navigate to the second page.  The second page would just pop the stack and return to the first.  With some experimenting it looked to me that the XF infrastructure worked best if you always used the same exact instance of INavigation.

In my opinion, there’s a problem with navigation living in the page.  Navigation is important business logic and business logic should not live in the view layer.  I don’t want to fight the infrastructure too much but if at all possible, I’m going to want to see what can be done to move navigation to the view model layer.

With this knowledge and my desire to abstract the navigation property out to the view model, I created my own wrapper navigation service.

public class NavigationService : Xamarin.Forms.INavigation
{
    public INavigation Navi { get; internal set; }

    public Task<Page> PopAsync()
    {
        return Navi.PopAsync();
    }

    public Task<Page> PopModalAsync()
    {
        return Navi.PopModalAsync();
    }

    public Task PopToRootAsync()
    {
        return Navi.PopToRootAsync();
    }

    public Task PushAsync(Page page)
    {
        return Navi.PushAsync(page);
    }

    public Task PushModalAsync(Page page)
    {
        return Navi.PushModalAsync(page);
    }
}

Very simply, it directs all calls to an internal implementation.  Again, the static App class is responsible for coordinating the creation of these classes

NavigationService navi = new NavigationService();
MainViewModel = new MainViewModel(navi);

MainPage = new NavigationPage(new MainPage());

navi.Navi = MainPage.Navigation;

Back to the MainViewModel

Now that we’ve solved the problems of commands and navigation, it’s pretty simple to finish off our MainViewModel.

public class MainViewModel
{
    private readonly INavigation _navi;

    public MainViewModel(INavigation navi)
    {
        _navi = navi;
    }

    public ICommand GoToLoginPageCommand
    {
        get { return new SimpleCommand(() => _navi.PushAsync(App.LoginPage)); }
    }
}

I left the creation of the login page in the App.  This way all my page creation can live in one place.

The Login Page

The login page and view model model are pretty simple.  Fields for username and password, and a button to submit.  Upon “successfully” logging in we’ll just return back to the  the main page.  If there’s a failure then we display a message box.

Like navigation, displaying a message box is tightly coupled to the page.  I wound up wrapping it up in my Navigation class in a similar fashion as navigation.  It’s not really the best spot for it, but it cut down on the number of classes and interfaces i was creating.  Also, it was a fast addition.  Given the size of the project I don’t really have a problem with it now, but I foresee it being moved out at some point.

Here’s what the navigation interface and implementation look like now:

public interface INavigationService : INavigation
{
    Task<bool> DisplayAlert(string title, string message, string accept, string cancel = null);
}

public class NavigationService : INavigationService
{
    public INavigation Navi { get; internal set; }
    public Page myPage { get; set; }

    public Task<Page> PopAsync()
    {
        return Navi.PopAsync();
    }

    public Task<Page> PopModalAsync()
    {
        return Navi.PopModalAsync();
    }

    public Task PopToRootAsync()
    {
        return Navi.PopToRootAsync();
    }

    public Task PushAsync(Page page)
    {
        return Navi.PushAsync(page);
    }

    public Task PushModalAsync(Page page)
    {
        return Navi.PushModalAsync(page);
    }

    public Task<bool> DisplayAlert(string title, string message, string accept, string cancel = null)
    {
        return myPage.DisplayAlert(title, message, accept, cancel);
    }
}

Wiring up ViewModels

A pattern I have always liked from the MvvmLight toolkit is setting up the binding to the view model directly in the view’s XAML.  I had to tweak the default MvvmLight template a bit to get this to work, but in the end it wasn’t pretty simple.  In the view, create a static binding to a property exposed by the App class.  This could be any class, but I went with the App class since it is already being used as a static resource for all of the views anyway.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:ShoppingCart;assembly=ShoppingCart"
             BindingContext="{x:Static local:App.LoginViewModel}"
             x:Class="ShoppingCart.Views.LoginPage">
  <!-- Code removed for brevity-->

</ContentPage>

This lets us simplify App.cs just a little bit, now that we don’t have to pass in the view models to the views.

public static class App
{
    public static MainViewModel MainViewModel { get; set; }
    public static LoginViewModel LoginViewModel { get; set; }

    static App()
    {
        ILoginService login = new LoginService();
        NavigationService navi = new NavigationService();

        MainViewModel = new MainViewModel(navi);
        LoginViewModel  = new LoginViewModel(login, navi);

        MainPage = new NavigationPage(new MainPage());
        LoginPage = new NavigationPage(new LoginPage());

        navi.Navi = MainPage.Navigation;
        navi.myPage = MainPage;
    }

    public static Page LoginPage { get; private set; }
    public static Page MainPage { get; private set; }
}

Summary

This is pretty much it for this week.  I’ve added binding, commands, and navigation as well as wired up the view models.  Next week I’ll flesh out the products pages and try to actually add the shopping cart.  Until then, happy coding.