Xamarin.Forms: Native Views

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 fiddled with async loading and added an application level menu.  This week I’m going to add native views on Windows Phone and Android using PageRenders.

Recap and Code

This is the tenth 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)
  • Day 4:  Search and Barcode Scanner (blog / code)
  • Day 5:  Dependency Injection (blog / code)
  • Day 6:  Styling (blog / code)
  • Day 7:  Attached Behaviors (blog / code)
  • Day 8:  Writing to Disk (blog / code)
  • Day 9:  App and Action Bars (blog / code)
  • Day 10:  Native Views (blog / code)

For a full index of posts, including future posts, go to the GitHub project page.

About Page

I want to add a quick about page to the app.  I’ll be honest here, I couldn’t think of a great example where the views would be drastically different depending on the platform.  They will probably look almost exactly the same.  Specifically, they will contain two buttons that will take the user to this blog, (specifically this post), or to the GitHub project page.  The WinPhone version will contain two extra labels.  Not overly fancy, but educational enough.

First things first, I’ll add a simple view model:

public class AboutViewModel : BaseViewModel
{
    public AboutViewModel()
    {
        OpenUrlCommand = new Command<string>(s => Device.OpenUri(new Uri(s)));
    }

    public string BlogUrl { get { return @"http://blog.masterdevs.com/xf-day-10/"; } }

    public string CodeUrl { get { return @"https://github.com/jquintus/spikes/tree/master/XamarinSpikes/ShoppingCart"; } }

    public ICommand OpenUrlCommand { get; private set; }
}

 

The only thing remotely interesting here is the Device.OpenUri(…) call.  It does pretty much what you expect it to, namely opens the URI in the native browser.  This view model is so simple that I don’t even really need to inherit from BaseViewModel.  I do anyway just to future proof it and for consistency.

Next thing I need to do is add the AboutPage stub in in the core project (ShoppingCart.csproj).  For reasons I’ll go into a bit later, this can’t be defined in Xaml.

namespace ShoppingCart.Views
{
    public class AboutPage : ContentPage
    {
        public AboutPage()
        {
            Title = "About";
            Content = new Label { Text = "This page is not available for your platform", }; 
        }
    }
}

Nice and simple.  Just set the title and get out of there.

Now all I need to do is wire up a button somewhere to navigate me to this page.  I already have an action bar and app bar on the main CategoriesListPage, so I’ll just add another button there.

<ContentPage.ToolbarItems>
  <ToolbarItem Name="Log Out" Command="{Binding LogOut}"  Order="Primary" Priority="0">
    <ToolbarItem.Icon>
      <OnPlatform x:TypeArguments="FileImageSource"
                  WinPhone="Assets/Logout.png"
                  Android="ic_action_logout.png" />
    </ToolbarItem.Icon>
  </ToolbarItem>

  <ToolbarItem Name="About" 
               Command="{Binding AboutCommand}"  
               Order="Secondary" 
               Priority="0"/>
</ContentPage.ToolbarItems>

I don’t bother with an icon, so I put it in the “Secondary” order.  On WinPhone and Droid this means it will only show up win you hit the three dots to expand the menu.  It’s bound to the “AboutCommand” which just uses the existing navigation system to take you to the AboutPage.

WinPhone

The first step to getting a native page shown is to define a native page.  So here’s the Xaml for my WinPhoneAboutPage.

<phone:PhoneApplicationPage
    x:Class="ShoppingCart.WinPhone.Views.WinPhoneAboutPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    mc:Ignorable="d"
    shell:SystemTray.IsVisible="True">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0" Margin="12,17,0,28">
            <TextBlock Text="Shopping Cart"
                       Style="{StaticResource PhoneTextNormalStyle}"
                       Foreground="{StaticResource PhoneAccentBrush}" />
            <TextBlock Text="about" Margin="9,-7,0,0"
                       Style="{StaticResource PhoneTextTitle1Style}"
                       Foreground="{StaticResource PhoneAccentBrush}" />
        </StackPanel>

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="auto" />
                <RowDefinition Height="*" />
                <RowDefinition Height="auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>

            <Button Grid.Row="1" Content="Browse Source Code"
                    Command="{Binding OpenUrlCommand}"
                    CommandParameter="{Binding CodeUrl}" />

            <Button Grid.Row="3" Content="Read Blog"
                    Command="{Binding OpenUrlCommand}"
                    CommandParameter="{Binding BlogUrl}" />
        </Grid>
    </Grid>
</phone:PhoneApplicationPage>

 

A very standard view.  The next thing I need to do is to set the DataContext of the page so my bindings actually work.  I’m inclined to follow the MvvmLight model with the ServiceLocator, but in all honesty that seems like a lot of ceremony for what I know will be one instance of a native view in this app.  So, I cheat a little a bit and just manually set the context in the code behind:

public partial class WinPhoneAboutPage : PhoneApplicationPage
{
    public WinPhoneAboutPage()
    {
        this.DataContext = ShoppingCart.App.AboutViewModel;
        InitializeComponent();
    }
}

Now to wire it up I’ll add a PageRenderer:

public class WinPhoneAboutPageRenderer :  Xamarin.Forms.Platform.WinPhone.PageRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
    {
        base.OnElementChanged(e);
        this.Children.Add(new AboutPage());
    }
}

And now that we have the PageRenderer defined, we need to tell the system to actually use it:

[assembly: ExportRenderer(typeof(ShoppingCart.Views.AboutPage), 
                          typeof(ShoppingCart.WinPhone.Views.WinPhoneAboutPageRenderer))]

 

This line can go anywhere in the assembly (just not within a namespace).  A lot of the examples place it in the same file as the renderer.  This has the benefit of keeping it close to where we’re using it.  I’ve elected to add this line at the beginning of the WinPhoneSetup file.  If we wind up with several definitions for renderers, it would be nice to have them all in one place.  I could be wrong about this.

Firing up the emulator and this looks… more than a little wrong.

WP Bad Renderer

So, on my fist pass of the ShoppingCart.AboutPage, I had added a label and two buttons.  When the WinPhoneAboutPageRenderer created the WinPhoneAboutPage, it just overlaid it on top of the existing controls.  Ok, so what if we add a call to Children.Clear()?  This still doesn’t look right, and to show exactly what’s wrong, I’ve added a splash of color to the page.

 

WP Bad Sizing

 

I set the background color of the entire page to red, and of the grid with my buttons to a light green.  As you can see, it’s not exactly taking up the entire page.

Children.Add doesn’t seem to be working for me at all, so I’ll try calling SetNativeControl.  The problem here is that since I’ve inherited from PageRenderer it expects a Xamarin.Forms.Page and I have a Microsoft.Phone.Controls.PhoneApplicationPage.  So I need to change what I’m inheriting from.

public class WinPhoneAboutPageRenderer 
  : VisualElementRenderer<Xamarin.Forms.Page, 
                          Microsoft.Phone.Controls.PhoneApplicationPage>
{
    protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
    {
        base.OnElementChanged(e);
        SetNativeControl(new WinPhoneAboutPage());
    }
}

 

Now that I’m inheriting from the VisualElementRenderer (the base class for the PageRenderer), I can specify that the object I’ll specify to replace the Xamarin.Forms.Page will be a WinPhone page.  Now it’s a simple matter of passing SetNativeControl a new instance of my WinPhoneAboutPage. This winds up looking like what I want.

WP Good

 

Droid About Page

Moving on to Droid, I create an xml file defining my layout.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <View
        android:layout_height="0dp"
        android:layout_width="fill_parent"
        android:layout_weight="1" />
    <Button
        android:id="@+id/button_blog"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Read Blog" />
    <View
        android:layout_height="0dp"
        android:layout_width="fill_parent"
        android:layout_weight="1" />
    <Button
        android:id="@+id/button_code"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Browse Code" />
    <View
        android:layout_height="0dp"
        android:layout_width="fill_parent"
        android:layout_weight="1" />
</LinearLayout>

Again, simple two buttons.  The views are just there as spacers.

And pretty much straight from the samples, here’s my renderer:

public class DroidAboutPageRenderer : PageRenderer
{
    private Android.Views.View _view;

    protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Page> e)
    {
        base.OnElementChanged(e);

        AboutViewModel viewModel = App.AboutViewModel;
        var activity = this.Context as Activity;
        _view = activity.LayoutInflater.Inflate(Resource.Layout.AboutLayout, this, false);

        var blogButton = _view.FindViewById<Button>(Resource.Id.button_blog);
        var codeButton = _view.FindViewById<Button>(Resource.Id.button_code);

        blogButton.Click += (sender, ev) => viewModel.OpenUrlCommand.Execute(viewModel.BlogUrl);
        codeButton.Click += (sender, ev) => viewModel.OpenUrlCommand.Execute(viewModel.CodeUrl);
        AddView(_view);
    }

    protected override void OnLayout(bool changed, int l, int t, int r, int b)
    {
        base.OnLayout(changed, l, t, r, b);
        var msw = MeasureSpec.MakeMeasureSpec(r - l, MeasureSpecMode.Exactly);
        var msh = MeasureSpec.MakeMeasureSpec(b - t, MeasureSpecMode.Exactly);
        _view.Measure(msw, msh);
        _view.Layout(0, 0, r - l, b - t);
    }
}

First things first, I grab the view model from my static cache.  Then I just inflate my view, and start grabbing my buttons so I can add click handlers.  Android doesn’t have a concept of data binding, so adding click handlers is a tad manual.  Once everything is wired up, I add my view to the renderer.  And now I have some errors.

I/MonoDroid( 1596): UNHANDLED EXCEPTION: System.InvalidOperationException: SetElement did not create the correct number of children
I/MonoDroid( 1596):   at Xamarin.Forms.Platform.Android.VisualElementPackager.SetElement (Xamarin.Forms.VisualElement oldElement, Xamarin.Forms.VisualElement newElement) [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596):   at Xamarin.Forms.Platform.Android.VisualElementPackager.Load () [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596):   at Xamarin.Forms.Platform.Android.VisualElementRenderer`1[Xamarin.Forms.Page].SetPackager (Xamarin.Forms.Platform.Android.VisualElementPackager packager) [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596):   at Xamarin.Forms.Platform.Android.VisualElementRenderer`1[Xamarin.Forms.Page].SetElement (Xamarin.Forms.Page element) [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596):   at Xamarin.Forms.Platform.Android.VisualElementRenderer`1[Xamarin.Forms.Page].Xamarin.Forms.Platform.Android.IVisualElementRenderer.SetElement (Xamarin.Forms.VisualElement element) [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596):   at Xamarin.Forms.Platform.Android.RendererFactory.GetRenderer (Xamarin.Forms.VisualElement view) [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596):   at Xamarin.Forms.Platform.Android.NavigationRenderer.SwitchContentAsync (Xamarin.Forms.Page view, Boolean animated, Boolean removed) [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596):   at Xamarin.Forms.Platform.Android.NavigationRenderer.OnPushAsync (Xamarin.Forms.Page view, Boolean animated) [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596):   at Xamarin.Forms.Platform.Android.NavigationRenderer.PushViewAsync (Xamarin.Forms.Page page, Boolean animated) [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596):   at Xamarin.Forms.Platform.Android.NavigationRenderer.OnPushed (System.Object sender, Xamarin.Forms.NavigationRequestedEventArgs e) [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596):   at Xamarin.Forms.NavigationPage+<PushAsync>d__c.MoveNext () [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596): --- End of stack trace from previous location where exception was thrown ---
I/MonoDroid( 1596):   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596):   at System.Runtime.CompilerServices.TaskAwaiter.GetResult () [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596):   at ShoppingCart.Services.AppNavigation+<ShowAbout>d__4.MoveNext () [0x0001e] in c:\code\Repos\spikes\XamarinSpikes\ShoppingCart\ShoppingCart\ShoppingCart\Services\AppNavigation.cs:35 
I/MonoDroid( 1596): --- End of stack trace from previous location where exception was thrown ---
I/MonoDroid( 1596):   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596):   at System.Runtime.CompilerServices.TaskAwaiter.GetResult () [0x00000] in <filename unknown>:0 
I/MonoDroid( 1596):   at ShoppingCart.ViewModels.CategoriesListViewModel+ctor>b__2>d__a.MoveNext () [0x0001b] in c:\code\Repos\spikes\XamarinSpikes\ShoppingCart\ShoppingCart\ShoppingCart\ViewModels\CategoriesListViewModel.cs:39 

The stack trace doesn’t say it, but this error is raised when you call AddView if the ShoppingCart.AboutPage has already had the Content property set.  So, I go back to the AboutPage, and pull out the Content property:

namespace ShoppingCart.Views
{
    public class AboutPage : ContentPage
    {
        public AboutPage()
        {
            Title = "About";
        }
    }
}

Back to the DroidAboutPageRenderer, the OnLayout override is there to make sure that the view is sized to fit the whole screen.  From the top left (0, 0)  to the very bottom right (r-l, b-t)

Don’t forget to register it.  Again, I decided to add this to the top of DroidSetup.cs.

[assembly: ExportRenderer(typeof(ShoppingCart.Views.AboutPage), 
                          typeof(ShoppingCart.Droid.Renderers.DroidAboutPageRenderer))]

Running this up, we get a wonderful (if not pretty) native layout:

droid good

iOS About Page (A Default)

Don’t get too excited.  I still don’t have access to an iDevice.  But I wanted to at least try and make sure that the app wouldn’t crash on iOS.  I’ve updated the core definition of the AboutPage to at least show a label explaining that this page wasn’t available.

public class AboutPage : ContentPage
{
    public AboutPage()
    {
        Title = "About";

        if (Device.OS != TargetPlatform.Android)
        {
            Content = new Label
            {
                Text = "This page is not available for your platform",
            };
        }
    }
}

Since we saw that Android get’s really upset if you set the content in the core version of the page and then try to use a PageRenderer in the platform (at least with my implementation of the renderer), I make sure that we aren’t running on an Android device before setting the content.  The content could have been set to something much more complicated than just a simple label.  It could have even used data bindings like any other page.

Since I don’t have an iPhone, here’s what it looks like on a Droid.

droid unavailble

 

And now we have native views on 2 out of 3 platforms.

Happy Coding

Xamarin.Forms: Dependency Injection

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 added a product search that uses a barcode scanner using the ZXing component.  Today I plan on cleaning up app startup by incorporating a dependency injection framework.

Recap and Code

This is the sixth 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)
  • Day 4:  Search and Barcode Scanner (blog / code)
  • Day 5:  Dependency Injection (blog / code)

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

Cleanup

Before starting up, I’d like to perform a little bit of clean up.  There is a bug in the XF template.  When the reference for the platform project (e.g., ShoppingCart.WinPhone) to the core project (i.e., ShoppingCart) is created the project id is missing.

This causes a few subtle problems, the most annoying of which is that renames in the core project are not propagated to the dependent projects.  For instance, if I rename the FirstPage property of the App class, the reference in ShoppingCart.WinPhone.MainPage will not be updated.

image

This can easily be solved by

  1. Removing the reference to ShoppingCart from each of the platform projects
  2. Adding the reference back

As a reminder, to remove the reference, just expand the References node under ShoppingCart.WinPhone in Solution Explorer, select the reference, and hit the delete key.

image

To add the reference back, right click on the References node, select Add Reference, and tick the checkbox next to ShoppingCart in the Projects tab.

image

Doing a quick diff of your CSPROJ after this you can see that the Project element has been added to the reference.

<!-- Before -->
<ProjectReference Include="..\ShoppingCart\ShoppingCart.csproj">
    <Name>ShoppingCart</Name>
</ProjectReference>

<!-- After -->
<ProjectReference Include="..\ShoppingCart\ShoppingCart.csproj">
    <Project>{8eb80e50-ef54-451b-9768-3d38e2a3e122}</Project>
    <Name>ShoppingCart</Name>
</ProjectReference>

Dependency Service vs. Injection

Last week I used XF’s DependencyService class to resolve application specific dependencies.  This worked by registering the dependency in the Android and WinPhone projects by adding a Dependency attribute like this:

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

and then resolving it in the core project with a call to DependencyService.Get

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

While this worked for what I needed last week, it does have the requirement that resolved type (i.e., DroidScanner in my example) has a default constructor.  This means that unlike a dependency injection framework, you cannot rely on the framework to create all of your dependencies in one go.  You have to manually resolve and compose them yourself.

Dependency Injection on PCL

There are many .Net DI frameworks out there.  My go to DI framework is Ninject but as of now it doesn’t support PCL.  So, after a little digging for PCL frameworks.  The key features that I look for are

  • Constructor injection
  • Registration with generics

I decided to give AutoFac a try.  I’ve never used it before, but by looking at the documentation it looked like it meets my requirements.

Starting Up AutoFac

The first thing I need to do is to get AutoFac.  In my main project (ShoppingCart) I right click to add a nuget package, search for AutoFac, and cross my fingers that it installs with out complaint.  Somewhat to my surprise, NuGet downloaded and added it to my project with out complaint.  I know it claimed to be PCL friendly, but I’m always surprised when installing something just works the first time.

Before I remove the usage of DependencyService, I need to configure the rest of the services and view models to be created by the DI framework.  I’ll do this in a dedicated class called ContainerCreator.  This is a habit I got from Ninject where you can create module classes to do all your bindings.  It keeps everything in one place and I like it in Ninject so I’ll see how well it works in AutoFac land.

public class ContainerCreator
{
  public static IContainer CreateContainer()
  {
    ContainerBuilder cb = new ContainerBuilder();

    // Services
    cb.RegisterType<LoginService>().As<ILoginService>().SingleInstance();
    cb.RegisterType<ProductLoader>().As<IProductLoader>().SingleInstance();
    cb.RegisterType<ProductService>().As<IProductService>().SingleInstance();
    cb.RegisterType<NavigationService>().As<INavigationService>().SingleInstance();
    cb.RegisterInstance(DependencyService.Get<IScanner>()).As<IScanner>().SingleInstance();

    // View Models
    cb.RegisterType<CategoriesListViewModel>().SingleInstance();
    cb.RegisterType<LoginViewModel>().SingleInstance();
    cb.RegisterType<ProductsListViewModel>().SingleInstance();
    cb.RegisterType<ProductViewModel>().SingleInstance();
    cb.RegisterType<WelcomeViewModel>().SingleInstance();

    return cb.Build();
  }
}

I’m setting up all of my registrations for my services which implement interfaces and my view models which are registered to themselves.  I’m also registering everything as singletons which will probably change later, but this should get me going.  Again, I’m still using the DependencyService to get my IScanner, this is just an intermediary step for now.  The goal is to remove that by the end of this post.

Next, I’ll clean up App to make use of the AutoFac container

private static readonly IContainer _container;

static App()
{
    _container = ContainerCreator.CreateContainer();

    NaviService = _container.Resolve<INavigationService>() as NavigationService;

    // Pages
    WelcomePage = new WelcomePage();
    LoginPage = new LoginPage();
    CategoriesListPage = new CategoriesListPage();
}

The view model properties now just resolve their values directly from the container when called.  They are all pretty much the same, here’s one as an example.

public static CategoriesListViewModel CategoriesListViewModel
{
    get
    {
        return _container.Resolve<CategoriesListViewModel>();
    }
}

Starting the app up, it should work exactly like it did before.  And it does.  Success.

Removing DependencyService

Now we can get to work on removing the DependencyService.  Technically though there is no reason just yet to remove it.  It works just fine for my app because DroidScanner and WinPhoneScanner both have parameterless constructors.  So I’m going to break it now by adding some parameters.  The most common parameter in most of my apps is a logger, so I’ll add one here.

I’ll add ILogger, an interface for my logger:

public interface ILogger
{
    void Info(string message);
    void Info(string format, params object[] args);
    void Info(Exception ex, string message);
    void Info(Exception ex, string format, params object[] args);

    void Error(string message);
    void Error(string format, params object[] args);
    void Error(Exception ex, string message);
    void Error(Exception ex, string format, params object[] args);
}

And then a Droid and WinPhone specific implementations each using their platform specific methods for logging:  Log and Debug.WriteLine respectively.   They are kind of long and boring, so here’s just a simple example from both:

public class DroidLogger : ILogger
{
    private const string APP_NAME = "ShoppingCart";

    public void Info(string message)
    {
        Log.Info(APP_NAME, message);
    }
}
public class WinPhoneLogger : ILogger
{
    public void Info(string message)
    {
        Console.WriteLine("INFO:  " + message);
    }
}

Next we add logging to each of the IScanner implementations.  Here’s the WinPhone implementation, the Droid version is identical

private readonly ILogger _logger;

public WinPhoneScanner(ILogger logger)
{
    _logger = logger;
}

public async Task<ScanResult> Scan()
{
    _logger.Info("Starting the barcode scanner.  Stand back.");

    var scanner = new MobileBarcodeScanner(MainPage.DispatcherSingleton);
    // ...
}

Hooray, the call to DependencyService.Get now fails with this error:  System.MissingMethodException:  No parameterless constructor defined for this object.  Time to fix it.

First I’m going to change the name of the ContainerCreator to AppSetup and make it a non-static class.  My plan is to enable custom app setup by overriding methods in this object.  Here’s what it looks like now:

public class AppSetup
{
    public IContainer CreateContainer()
    {
        ContainerBuilder cb = new ContainerBuilder();
        RegisterDepenencies(cb);
        return cb.Build();
    }

    protected virtual void RegisterDepenencies(ContainerBuilder cb)
    {
        // Services
        cb.RegisterType<LoginService>().As<ILoginService>().SingleInstance();
        cb.RegisterType<ProductLoader>().As<IProductLoader>().SingleInstance();
        cb.RegisterType<ProductService>().As<IProductService>().SingleInstance();
        cb.RegisterType<NavigationService>().As<INavigationService>().SingleInstance();

        // View Models
        cb.RegisterType<CategoriesListViewModel>().SingleInstance();
        cb.RegisterType<LoginViewModel>().SingleInstance();
        cb.RegisterType<ProductsListViewModel>().SingleInstance();
        cb.RegisterType<ProductViewModel>().SingleInstance();
        cb.RegisterType<WelcomeViewModel>().SingleInstance();
    }
}

The registrations have been moved to a dedicated method RegisterDependencies which takes the ContinerBuilder to use to add your registrations.  I’ve removed the instance registration that was was using the XF DependencyService (that was the goal the whole time).  If you notice, the method is virtual.  The idea is to override this for each platform that needs to. Here’s the WinPhone override (the Droid version is very similar):

public class WinPhoneSetup : AppSetup
{
    protected override void RegisterDepenencies(ContainerBuilder cb)
    {
        base.RegisterDepenencies(cb);

        cb.RegisterType<WinPhoneLogger>().As<ILogger>().SingleInstance();
        cb.RegisterType<WinPhoneScanner>().As<IScanner>().SingleInstance();
    }
}

All that I’m doing here is registering the WinPhone logger and scanner.  The two bits of platform specific implementations that I have.  Notice that I call into my base implementation to handle all of the rest of the registrations.

There’s a small problem though.  The WinPhoneSetup class won’t compile.  It doesn’t know anything about AutoFac.  Right now only my core project references AutoFac.  I could just add a reference for the WinPhone project, but I know that all of the other projects will need one as well, so I’ll add a reference for each of the projects in one go.  The easiest way to do this is to right click on the Solution (not the projects) and select Manage NuGet Packages for Solution.  From there you can select the Installed Packages tab and click the “Manage” button for AutoFac.  Then just tick all of the check boxes and click OK.

image Next in order to have App be able to use different versions of AppSetup I change the static constructor to a static method called Initialize that takes the AppSetup object to use.

public static void Init(AppSetup appSetup)
{
    _container = appSetup.CreateContainer();

    NaviService = _container.Resolve<INavigationService>() as NavigationService;

    WelcomePage = new WelcomePage();
    LoginPage = new LoginPage();
    CategoriesListPage = new CategoriesListPage();

    // Startup Page
    StartupPage = WelcomePage;// CategoriesListPage;
}

Now all I need to do is to call Init from the Droid and WinPhone projects.  In MainActivity.OnCreate we add the call Init with the DroidSetup object

protected override void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);
    Xamarin.Forms.Forms.Init(this, bundle);
    App.Init(new DroidSetup());
    SetPage(App.StartupPage);
}

And in the constructor for MainPage.xaml.cs we call Init with an instance of WinPhoneSetup.

public MainPage()
{
    InitializeComponent();
    Forms.Init();

    App.Init(new WinPhoneSetup());
    Content = ShoppingCart.App.StartupPage.ConvertPageToUIElement(this);
}

With AutoFac in place I can delete the two ServiceRegistration classes I had that registered my classes with the DependencyService.  And now all I have to do is to spin up the app and see that the log messages in the console.

Happy Coding

A specified communication resource (port) is already in use by another application.

Trying to debug a new Windows Phone 8 application today I came across this problem.  Whenever I hit F5, the emulator would start up like normal but Visual Studio would prompt me with a message box saying:

A specified communication resource (port) is already in use by another application.

Then the debugger would fail to start.  After trying all of the obvious options and restarting everything in sight, I stumbled across a bit of advice which worked for me.  Right click on your project and select Deploy.  This will copy all necessary code to the emulator.  After that you are free to hit F5 like normal.

image