Ready, set, action

I just had the need to style the search box in the actionbar of my Xamarin Android application. For those of you who don’t know, Xamarin lets you write native Android apps in .Net, specifically C#.

Here’s the search box before styling:

image

Here’s the look I was going for:

image

I was using ActionBarSherlock to create the actionbar.  After searching for a while, I came to the conclusion that the only way to customize the actionbar using the Android style system was to switch to AppCompat.  So now the steps seem pretty clear and easy:

  1. Migrate from ActionBarSherlock to AppCompat
  2. Create a style for my search box
  3. Apply style in just the right place.

I ran into more bumps along the way than expected, so I wanted to write down exactly what I had to do to get this working.

Migrating to from Sherlock to AppCompat

Before really considering using AppCompat, I checked to see how well it was supported by Xamarin and found a useful post on their blog with some sample code.  This looked promising and I was able to get it to compile locally, so full steam ahead.  Back in my project, I deleted the ActionBarSherlock Xamarin component and added in the AppCompat component.  I then walked through my code changing all code referencing Sherlock to AppCompat.  Wolram Rittmeyer has an excellent post on the step by step process to do this.

My first concern was that I also use MvvmCross, which requires that all Activity classes implement IMvxEventSourceActivity and IMvxAndroidView.  So months ago (almost a year ago according to my commit history) I created the  MvxActionBarSherlockFragmentActivity base class that inherits from SherlockFragmentActivity and implements the MvvmCross interfaces.  Not remembering what went into creating the class I was concerned it would be tedious to replace it with an AppCompat version.  Turns out it was trivial.  All I had to do was inhert from ActionBarActivity instead.  It was literally a one word change. Here’s my new MvxActionBarActivity:

using Android.App;
using Android.Content;
using Android.OS;
using Android.Support.V7.App;
using Cirrious.CrossCore.Core;
using Cirrious.CrossCore.Droid.Views;
using Cirrious.MvvmCross.Binding.BindingContext;
using Cirrious.MvvmCross.Binding.Droid.BindingContext;
using Cirrious.MvvmCross.Droid.Views;
using Cirrious.MvvmCross.ViewModels;
using System;

namespace Masterdevs.Droid.Views
{
    public class MvxActionBarActivity : ActionBarActivity, IMvxEventSourceActivity, IMvxAndroidView
    {
        protected MvxActionBarActivity()
        {
            BindingContext = new MvxAndroidBindingContext(this, this);
            this.AddEventListeners();
        }

        public event EventHandler<MvxValueEventArgs<MvxActivityResultParameters>> ActivityResultCalled;
        public event EventHandler<MvxValueEventArgs<Bundle>> CreateCalled;
        public event EventHandler<MvxValueEventArgs<Bundle>> CreateWillBeCalled;
        public event EventHandler DestroyCalled;
        public event EventHandler DisposeCalled;
        public event EventHandler<MvxValueEventArgs<Intent>> NewIntentCalled;
        public event EventHandler PauseCalled;
        public event EventHandler RestartCalled;
        public event EventHandler ResumeCalled;
        public event EventHandler<MvxValueEventArgs<Bundle>> SaveInstanceStateCalled;
        public event EventHandler<MvxValueEventArgs<MvxStartActivityForResultParameters>> StartActivityForResultCalled;
        public event EventHandler StartCalled;
        public event EventHandler StopCalled;

        public IMvxBindingContext BindingContext { get; set; }

        public object DataContext
        {
            get { return BindingContext.DataContext; }
            set { BindingContext.DataContext = value; }
        }

        public IMvxViewModel ViewModel
        {
            get { return DataContext as IMvxViewModel; }
            set
            {
                DataContext = value;
                OnViewModelSet();
            }
        }

        public void MvxInternalStartActivityForResult(Intent intent, int requestCode)
        {
            base.StartActivityForResult(intent, requestCode);
        }

        public override void SetContentView(int layoutResId)
        {
#if DEBUG // This try catch is super useful when debugging bad layouts.  No real need for it in prod.
            try
            {
#endif
                var view = this.BindingInflate(layoutResId, null);
                SetContentView(view);
#if DEBUG
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);  // Because of the JNI layers, this is the easiest way to reliably get the message from the exception when debugging.  The watch window isn't as reliable/timely
                throw;
            }
#endif
        }

        public override void StartActivityForResult(Intent intent, int requestCode)
        {
            StartActivityForResultCalled.Raise(this, new MvxStartActivityForResultParameters(intent, requestCode));
            base.StartActivityForResult(intent, requestCode);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                DisposeCalled.Raise(this);
            }
            base.Dispose(disposing);
        }

        protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            ActivityResultCalled.Raise(this, new MvxActivityResultParameters(requestCode, resultCode, data));
            base.OnActivityResult(requestCode, resultCode, data);
        }

        protected override void OnCreate(Bundle bundle)
        {
            CreateWillBeCalled.Raise(this, bundle);
            base.OnCreate(bundle);
            CreateCalled.Raise(this, bundle);
        }

        protected override void OnDestroy()
        {
            DestroyCalled.Raise(this);
            base.OnDestroy();
        }

        protected override void OnNewIntent(Intent intent)
        {
            base.OnNewIntent(intent);
            NewIntentCalled.Raise(this, intent);
        }

        protected override void OnPause()
        {
            PauseCalled.Raise(this);
            base.OnPause();
        }

        protected override void OnRestart()
        {
            base.OnRestart();
            RestartCalled.Raise(this);
        }

        protected override void OnResume()
        {
            base.OnResume();
            ResumeCalled.Raise(this);
        }

        protected override void OnSaveInstanceState(Bundle outState)
        {
            SaveInstanceStateCalled.Raise(this, outState);
            base.OnSaveInstanceState(outState);
        }

        protected override void OnStart()
        {
            base.OnStart();
            StartCalled.Raise(this);
        }

        protected override void OnStop()
        {
            StopCalled.Raise(this);
            base.OnStop();
        }

        protected virtual void OnViewModelSet()
        {
        }
    }
}

With that done, all my MvvmCross worries were over and my app should compile.  Not quite.  On either score.  It turns out that the version of MvvmCross I was using was referencing the old Mono.Android.Support.v4.dll while the AppCompat library referenced the new Xamarin.Android.Support.v4.dll.  These are essentially the same library, but with different names.  There is an excellent summary on Xamarin’s bugzilla.  Finally after carefully reading through all of the bug report, at the very bottom, I found Stuart’s comment saying that he’d already released a fixed version.  All I had to do was update to the latest version of MvvmCross in NuGet.  And now my code actually compiled and my MvvmCross concerns were over.

Fixing the Null SearchView

While my code happily compiled, it wasn’t quite as happy about actually running.

public override bool OnCreateOptionsMenu(IMenu menu)
{
    MenuInflater.Inflate(Resource.Menu.ManageUsers, menu);
    var searchItem = menu.FindItem(Resource.Id.action_search);

    var view = MenuItemCompat.GetActionView(searchItem);
    var searchView = view.JavaCast<Android.Support.V7.Widget.SearchView>();

    searchView.QueryTextChange += (s, e) => ViewModel.Filter = e.NewText;

    return base.OnCreateOptionsMenu(menu);
}

Whenever I tried to get the action view from the search menu item, it was null.   My first instinct was to double check my menu definition:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto" >
  <item android:id="@+id/action_search"
        android:title="Search Friends"
        android:icon="@android:drawable/ic_menu_search"
        app:showAsAction="ifRoom|collapseActionView"
        app:actionViewClass="android.support.v7.widget.SearchView" />
</menu>

It looked right.  I had remembered to use the AppCompat search view.  After some digging on the inter-tubes, I found a post on StackOverflow explaining that my themes had to specifically derive from AppCompat themes.  Ok, so a trip to the style generator and I’m using the correct themes.

Styling Search

So now it’s been a while, and I have a lot of checked out code.  But I’ve finally gotten back to where I was when I started.  An app that compiles, runs, and has an ugly search box.

The trick, (thanks to Andreas Nilsson for explaining it) is to set your own style  searchViewAutoCompleteTextView in the Theme.AppCompat style.

<resources>
    <style name="AppTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar">
        <item name="android:actionBarWidgetTheme">@style/ActionBarWidget.actionbar</item>
    </style>

    <style name="ActionBarWidget.actionbar" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="searchViewAutoCompleteTextView">@style/AutoCompleteTextView.actionbar</item>
    </style>

    <style name="AutoCompleteTextView.actionbar" parent="Widget.AppCompat.Light.AutoCompleteTextView">
        <item name="android:textColor">#FFFFFF</item>
        <item name="android:textCursorDrawable">@null</item>
    </style>
</resources>

Thanks and Resources

Thanks to Wolfram Rittmeyer for his methodical description on how to migrate from Sherlock to AppCompat.  Another thanks to Andreas Nilsson for his blog post showing that it was even possible to customize the the search box using styles.  I encourage you to read both blog posts, since they go into far greater detail.

Happy Coding.

Xamarin.Forms: Attached Behaviors

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 styled the app so it looked slick.  This week I am going to revisit a problem I had in my Day 2 post, namely the lack of an EventToCommand behavior.  A developer named Corrado created a Behaviors library specifically for Xamarin.Forms.  This library comes with an EventToCommand behavior out of the box, and lets you create your own.

Recap and Code

This is the seventh 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)

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

Getting Behaviors

First off, you can check out Corrado’s own blog post about this library.  You can also take a look at his code on GitHub, or just grab the library from nuget.

The first thing I did was install the nuget package in my core project (ShoppingCart).  I did two things wrong here.  First, there are two nuget packages to choose from:  Xamarin.Behaviors and Xamarin.Forms.Behaviors.  Unintuitively, the correct one to choose is Xamarin.Behaviors.  The next mistake I made was that I installed it in just the core project.  When I ran up the solution, I saw this error immediately:

System.IO.FileNotFoundException was unhandled by user code
Message=Could not load file or assembly 'Xamarin.Behaviors, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

I realized that the platform projects also need to reference the package.  Easy enough.

TL;DR

To install behaviors install the nuget package in your shared project as well as all platform projects:

PM> Install-Package Xamarin.Forms.Behaviors

Using Behaviors

My first use case for behaviors is to remove the ugly event to command code I have in my code behind.  Here’s the xaml that I want to get rid of:

<ListView ItemsSource="{Binding Categories.Result}" 
    IsGroupingEnabled="false" 
    ItemSelected="OnItemSelected">
  <ListView.ItemTemplate>
    <DataTemplate>
      <ViewCell>
        <Label Text="{Binding .}" />
      </ViewCell>
    </DataTemplate>
</ListView>

Specifically, I don’t want the ItemSelected property set to the OnItemSelcted method in the code behind file:

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 method casts the context to the view model, grabs the command, casts the SelectedItem into a string to act as the parameter, checks to see if it can call execute, and then calls execute.

First things first, I delete the OnItemSelected method.  Gone.  No more.  Next, I add an EventToCommand behavior in my xaml:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ShoppingCart.Views.CategoriesListPage"
             xmlns:b="clr-namespace:Xamarin.Behaviors;assembly=Xamarin.Behaviors"
             xmlns:local="clr-namespace:ShoppingCart;assembly=ShoppingCart"
             BindingContext="{x:Static local:App.CategoriesListViewModel}"
             BackgroundColor="White">
  <ListView ItemsSource="{Binding Categories.Result}">
    <ListView.ItemTemplate>
      <DataTemplate>
        <TextCell Text="{Binding Name}"
                  Detail="{Binding Count}">
          <b:Interaction.Behaviors>
            <b:BehaviorCollection>
              <b:EventToCommand EventName="Tapped"
                                Command="{Binding NavigateToCategory}"
                                CommandParameter="{Binding Category}" />
            </b:BehaviorCollection>
          </b:Interaction.Behaviors>
        </TextCell>
      </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

There’s a little more going on here than just the behavior, so I’ll explain that first.  First off, on line 4, I add the reference to the Behaviors namespace.  I also change the DataTemplate from the generic ViewCell to the TextCell.  This is mostly just to simplify my layout and because I only recently learned about the TextCell after reading a recent blog on the Xamarin Newsletter.  The TextCell lets you create a row in a ListView with a main text field, and a description underneath.  I also just realized that the ViewCell and TextCell both already have Command and CommandParameter properties that I could have bound to directly. Evidently I don’t need behaviors for this at all.  I’m still going to use behaviors, just so I can play with them a bit.  But, if you want to see how to do this without behaviors, check out my list view in the ProductsListPage.

So, now that I have my TextCell, I can use the Interaction.Behaviors attached property and add an EventToCommand behavior.  The EventToCommand maps an event on the UI control to an ICommand on the view model.  In this case, when the Tapped event of the TextCell is raised, the NavigateToCategory command will be executed.  But which NavigateToCategory command?  Originally this command existed on the CategoriesListViewModel, but that was when we were in the code behind and our BindingContext was the CategoriesListViewModel.  By the time our EventToCommand is created, we are in the DataTemplate and only have access to the individual members of Categories.Results which was originally a list of strings.  If we were using WPF, we would have been able to bind to our parent’s context using RelativeSource binding and access the command.  RelativeSource binding is not an option in XF.  The easiest way around this for me is to change my categories list from strings to CategoryViewModels.  Here’s my new view model:

public class CategoryViewModel : BaseViewModel
{
    private readonly Category _category;

    public CategoryViewModel(Category category, ICommand navigateCommand)
    {
        _category = category;
        Name = _category.Name;

        NavigateToCategory = navigateCommand;
    }

    public Category Category { get { return _category; } }

    public string Count { get; private set; }

    public string Name { get; private set; }

    public ICommand NavigateToCategory { get; private set; }
}

The  CategoriesListViewModel creates these instances, and just passes the navigate command in.  The implementation of the command itself isn’t changed. Truth be told, passing the command in like this is a bit of a hack.  It would be cleaner to use the Message Center.  That’s a bit out of the scope for this article, perhaps I’ll clean this up next week.

Another thing I’m doing that’s not strictly necessary, is passing in a CommandParameter.  I’m just using it here just to show how it can be done.  Currently, you can’t pass in the EventArgs as the parameter.  There are times when that is useful, so hopefully it’s added some time in the future before I really need it.

What Did I Do Wrong?

Typo in the EventName

At one point in my testing, I had a typo in my EventToCommand where I was trying to bind to a nonexistent event.

<b:EventToCommand EventName="OnTapped"
                  Command="{Binding NavigateCommand}"
                  CommandParameter="{Binding Category}" />

“OnTapped” doesn’t exist.  The correct event name is “Tapped”.  This is the error you’ll see if/when you make that mistake:

System.FormatException: Index (zero based) must be greater than or equal to zero and less than the size of the argument list

The exception is confusing until you look at the EventToCommand code and see that there is a small bug in it when it is trying to throw what would be a much more helpful exception.

Typo in the Command

I also had some trouble with typos where I misspelled the name of the command.  This was worse.  It just silently doesn’t work.  Typos are bad.

Creating Behaviors

Xamarin.Forms.Behaviors comes with two behaviors out of the box:  EventToCommand which we discussed earlier, and TextChangedBehavior.  Even better though, it gives you all the building blocks you need to create behaviors of your own.  Suppose you want to have your Entry (text box) animate when you click in it.  Something like this:

WP_Animation      Droid_Animation

Here’s the behavior that handles this:

using System;
using Xamarin.Behaviors;
using Xamarin.Forms;

namespace ShoppingCart.Behaviors
{
    public class AnimateSizeBehavior : Behavior<View>
    {
        public static readonly BindableProperty EasingFunctionProperty = BindableProperty.Create<AnimateSizeBehavior, string>(
            p => p.EasingFunctionName,
            "SinIn",
            propertyChanged: OnEasingFunctionChanged);

        public static readonly BindableProperty ScaleProperty = BindableProperty.Create<AnimateSizeBehavior, double>(
            p => p.Scale,
            1.25);

        private Easing _easingFunction;

        public string EasingFunctionName
        {
            get { return (string)GetValue(EasingFunctionProperty); }
            set { SetValue(EasingFunctionProperty, value); }
        }

        public double Scale
        {
            get { return (double)GetValue(ScaleProperty); }
            set { SetValue(ScaleProperty, value); }
        }

        protected override void OnAttach()
        {
            this.AssociatedObject.Focused += OnItemFocused;
        }

        protected override void OnDetach()
        {
            this.AssociatedObject.Focused -= OnItemFocused;
        }

        private static Easing GetEasing(string easingName)
        {
            switch (easingName)
            {
                case "BounceIn": return Easing.BounceIn;
                case "BounceOut": return Easing.BounceOut;
                case "CubicInOut": return Easing.CubicInOut;
                case "CubicOut": return Easing.CubicOut;
                case "Linear": return Easing.Linear;
                case "SinIn": return Easing.SinIn;
                case "SinInOut": return Easing.SinInOut;
                case "SinOut": return Easing.SinOut;
                case "SpringIn": return Easing.SpringIn;
                case "SpringOut": return Easing.SpringOut;
                default: throw new ArgumentException(easingName + " is not valid");
            }
        }

        private static void OnEasingFunctionChanged(BindableObject bindable, string oldvalue, string newvalue)
        {
            (bindable as AnimateSizeBehavior).EasingFunctionName = newvalue;
            (bindable as AnimateSizeBehavior)._easingFunction = GetEasing(newvalue);
        }

        private async void OnItemFocused(object sender, FocusEventArgs e)
        {
            await this.AssociatedObject.ScaleTo(Scale, 250, _easingFunction);
            await this.AssociatedObject.ScaleTo(1.00, 250, _easingFunction);
        }
    }
}

This is a big file, but not that much is really going on.  AnimateSizeBehavior inherits from Behavior<View>.  This means that we can apply it to any type of control.  It also means that the AssociatedObject property will be of type View.  The key methods to look at are the OnAttach and OnDetach.

protected override void OnAttach()
{
    this.AssociatedObject.Focused += OnItemFocused;
}

protected override void OnDetach()
{
    this.AssociatedObject.Focused -= OnItemFocused;
}

OnAttach is called when the behavior is added to the the control and OnDetach is called when it is removed from the control.  This is where I registered to receive the Focused event.  Now it’s a simple matter that whenever the control gains focus my animation code in OnItemFocused will be called.

private async void OnItemFocused(object sender, FocusEventArgs e)
{
    await this.AssociatedObject.ScaleTo(Scale, 250, _easingFunction);
    await this.AssociatedObject.ScaleTo(1.00, 250, _easingFunction);
}

The animation is very straight forward.  I use the ScaleTo method on View to scale the control up, then I call ScaleTo a second time to return it to its original size.  If I didn’t want to provide any flexibility with my behavior, I could stop there.  The rest of the code is just there to let me pass in parameters and configure how to perform the scale.  Let’s look at Scale.

public static readonly BindableProperty ScaleProperty = BindableProperty.Create<AnimateSizeBehavior, double>(
    p => p.Scale,
    1.25,
    propertyChanged: OnScaleChanged);

public double Scale
{
    get { return (double)GetValue(ScaleProperty); }
    set { SetValue(ScaleProperty, value); }
}

First I set up a static BindableProperty called ScaleProperty.  This is what lets me bind to properties on the behavior.  The first parameter ties it to the double Scale instance property.  The second parameter sets the default value to 1.25.

The EasingFunction property is a little more complicated.  It requires validation when it is set.  This is accomplished by setting the propertyChanged parameter in the factory method.

public static readonly BindableProperty EasingFunctionProperty = BindableProperty.Create<AnimateSizeBehavior, string>(
    p => p.EasingFunctionName,
    "SinIn",
    propertyChanged: OnEasingFunctionChanged);

private Easing _easingFunction;

public string EasingFunctionName
{
    get { return (string)GetValue(EasingFunctionProperty); }
    set { SetValue(EasingFunctionProperty, value); }
}

private static void OnEasingFunctionChanged(BindableObject bindable, string oldvalue, string newvalue)
{
    (bindable as AnimateSizeBehavior).EasingFunctionName = newvalue;
    (bindable as AnimateSizeBehavior)._easingFunction = GetEasing(newvalue);
}

The propertyChagned parameter is set to the static OnEasingFunctionChanged method.  The instance of the behavior is passed in as the bindable parameter, along with the old and new values being set.  In my example, we ignore the old value and just set the new value to the string property of EasingFunctionName.  We also parse the new value to determine what type of easing function to use in the GetEasing method.  If the easing function supplied is not an expected value, an exception is thrown.  This happens not when we try to run the animation, but as soon as we set the value.

Now all I need to do is add the behavior to my text boxes.  I’ll do this for the login page because there are two text boxes on the page so we can see how to tweak it.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:bLocal="clr-namespace:ShoppingCart.Behaviors;assembly=ShoppingCart"
             xmlns:b="clr-namespace:Xamarin.Behaviors;assembly=Xamarin.Behaviors"
             x:Class="ShoppingCart.Views.LoginPage" >
  <StackLayout VerticalOptions="FillAndExpand" Padding="50">

    <Entry Text ="{Binding Username}" Placeholder ="User name goes here" >
      <b:Interaction.Behaviors>
        <b:BehaviorCollection>
          <bLocal:AnimateSizeBehavior />
        </b:BehaviorCollection>
      </b:Interaction.Behaviors>
    </Entry>

    <Entry Text ="{Binding Password}"
      Placeholder ="Password goes here"
      HorizontalOptions="FillAndExpand">
      <b:Interaction.Behaviors>
        <b:BehaviorCollection>
          <bLocal:AnimateSizeBehavior EasingFunction="BounceIn"
                                      Scale="1.50" />
        </b:BehaviorCollection>
      </b:Interaction.Behaviors>
    </Entry>

  </StackLayout>
</ContentPage>

I stripped a lot out of the xaml here for clarity (like the submit button).  The username textbox has the default behavior set.  The password box changes the scale size to 1.5 and selects a different easing function.  It would be possible to bind to those values as well, but it didn’t make sense in my already pointless example.

And that’s all there is to it.  A quick thanks to lobrien for a useful sample on how to do animations in XF.  This sped up my coding quite a bit.  And of course a thanks to Corrado for the Xamarin.Forms.Behaviors library.

Happy Coding

Xamarin.Forms: Styling

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 an dependency injection framework.  This week I want to make the app look a little less blah so I’ll be adding themes and styling.

Recap and Code

This is the seventh 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)

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

Styling Options in XF

The guidance in Xamarin’s documentation is to create custom renders for each control you want to style for each platform.  This seemed like an unintuitive and complicated way to go about solving the problem so I avoided trying it as long as possible.

@TheRealJoeRall suggested an interesting idea to try: use the native theming options from each platform.  On WindowsPhone add styles with a TargetType but no key to the App.xaml.  On Android create a theme in xml and apply it to the top level activity.  On iOS use the UIAppearance class.  Like the content render solution this approach requires writing a different theme for each platform, but it uses the native approach meaning that there will be more documentation and tooling around it.  Now, let’s just hope it works.

Quick Reminder about iOS

Before I go any further, I want to put out a reminder that since I don’t have access to iOS these articles won’t cover it.  Which means that I was unable to determine if the UIAppearance class would be able to help in styling a XF application.

Styling Windows Phone

There’s lots of documentation on how to style and theme a native Windows Phone app.  Most of it written by people who know a lot more about the topic than I do.  I’m going to keep mine to the basics.

The first thing to know is that any styles you create in the App.xaml file will be accessible from all of your content pages by default.  But how do you create a style in the first place?

<Application>
  <Application.Resources>
    <ResourceDictionary>
      <Style TargetType="TextBlock" >
        <Setter Property="Foreground" Value="Black"/>
      </Style>
    </ResourceDictionary>
  </Application.Resources>
</Application>

Above is a very simple style that sets the Foreground of all TextBlocks to black.  Again, this style can get much more complex, but I’ll leave it here for now.

You could make this an explicit style by giving it a key, x:key=”BlackTextBlock” for example.  This way it would only apply to TextBlocks that specifically reference the style.  But since all of our pages are currently defined in the common Xamarin Forms layer, we don’t have TextBlocks at all, and can’t reference explicit styles.  So while explicit styles are great when writing native Windows Phone pages, they are not very interesting in XF.

Again, I could rant on this a lot longer, but I’m not the best source.  My style for WP is quite long but not much more than a basic example, so I won’t bother with the snippet here.  If you’re interested, you can view my full App.xaml on GitHub.

Limitations With Using Windows Phone Styling

My goal with the styling was to create a white background.  I could not figure out a way to set the background color from within the Windows Phone project.  I tried several techniques that did not work.

First, I tried explicitly setting the color in the MainPage.xaml (the following code is stripped down for size and clarity):

<phone:PhoneApplicationPage
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    Background="White" />

This had no effect so I tried setting it programmatically in MainPage.xaml.cs.  I tried setting both the MainPage background as well as the navigation page returned from the common layer.  Neither worked, either separately or together.  I’ll show them all at once.

public MainPage()
{
    this.Background = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.White);

    var startupPage = ShoppingCart.App.StartupPage;
    startupPage.BackgroundColor = Xamarin.Forms.Color.White;

    Content = startupPage.ConvertPageToUIElement(this);
}

Digging down into the child page of the navigation page did work, but that had two drawbacks.  First it only worked for that one page, so I’d have to change the color of all the pages.  Second, it pushed me up into the common layer.  Since I was in the common layer anyway it would just be easier to set the background page color directly in the XAML definitions of the pages.  This is ultimately what I did.  Here’s an example from the Welcome page.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ShoppingCart.Views.WelcomePage"
             BackgroundColor="White" >
   <!-- Layout Removed -->
</ContentPage>

Note that I could have used Xamarin Form’s OnPlatform mechanism to selectively set the property only for Windows Phone.  I elected not to since I did want the background to be white on all platforms anyway, even if I did not need to set the property explicitly for Android to work.

Styling Android

Android also has a very simple way to set the overall style for all Activities.  They also have much better documentation than I could mimic.  The key take away here is to define your android theme in an xml file in the values folder of your project.  Then reference it in your AndroidManifest.xml file.  The quickest way I’ve found to create a theme is to use the online Holo Colors Generator.

Holo COlor Generator Screen Shot

You just enter the color you want to use in your theme and tick off the checkboxes of the controls you want this theme to apply to.  It generates a zip that you can extract directly into your resources directory.  Just include all of the files and set your theme in the AndroidManifest like this where AppTheme is the the name of the theme in the themes_apptheme.xml file:

<application android:theme="@style/AppTheme" />

That’s it for styling droid.  It’s pretty straight forward and took me about 15 minutes.  The color I picked was a nice light green:  #afcca6.

Problems with This Approach

One major limitation with this approach is that it treats all things equally  All text boxes will look the same.  If for example you wanted the text box at the top of the product page to be a color based on the theme, you can’t easily achieve that.  You’d have to fallback to manually setting the various properties correctly on each page.

Solution 1:  Theme Class

For this specific case, I created an interface IThemer with a default implementation that could be overriden for specific platforms.  For now, the only property exposed is AccentColor, but it could be expanded to provide other style related properties.

public interface IThemer
{
    Color AccentColor { get; }
}

public class DefaultThemer : IThemer
{
    public Color AccentColor { get { return Color.Accent; } }
}
public class AppSetup
{
    protected virtual void RegisterDepenencies(ContainerBuilder cb)
    {
        // ... Code removed for clarity
        cb.RegisterType<DefaultThemer>().As<IThemer>().SingleInstance();
    }
}

Originally, the DefaultThemer I created just returned a randomly generated dark color and the Windows Phone implementation overrode it with the AccentColor pulled from the app’s resource dictionary.  Then while playing around I found the Xamarin.Forms.Color.Accent property.  On Windows Phone this returns the phone’s accent color.  On Droid it returns black.  This is great in that it gets me half way there, but I want to use my nice light green color I defined in my droid styles:  #afcca6.  So in the droid project I created an implementation of IThemer and registered it with my dependency injection container from last week:

public class DroidThemer : IThemer
{
    public DroidThemer()
    {
        var resources = Forms.Context.Resources;
        int colorId = Resource.Color.apptheme_color;
        var color = resources.GetColor(colorId);

        AccentColor = Color.FromRgba(color.R, color.G, color.B, color.A);
    }

    public Color AccentColor { get; private set; }
}
public class DroidSetup : AppSetup
{
    protected override void RegisterDepenencies(ContainerBuilder cb)
    {
        base.RegisterDepenencies(cb);

        cb.RegisterType<DroidLogger>().As<ILogger>().SingleInstance();
        cb.RegisterType<DroidScanner>().As<IScanner>().SingleInstance();
        cb.RegisterType<DroidThemer>().As<IThemer>().SingleInstance();
    }
}

The code grabs the color as it is defined in my app’s theme and sets the AccentColor property.  Now if the color is ever changed in the theme, it will propagate to my special theming class.  Again, I simply override the existing registration for the IThemer with the DroidThemer.  AutoFac is smart enough to remove the prior registration made in AppSetup.

Now the question becomes, where do I put this class?  I could make the BaseViewModel take an instance of it so that all of the views can easily know where to access it.  I don’t like this for a number of reasons.  First and foremost this is not view model information.  It has nothing to do with the state of the app or data, it’s purely view.  Secondly, adding constructor parameters on a base class is a headache to mange long term, especially when you wind up having a lot of inheriting classes.  You need to touch each child class whenever those parameters change.  This is definitely not the solution for me.

My second idea was to expose the AccentColor property on the App class.  This really is a system wide app setting so the App class does make a certain amount of sense.  Besides, all of my views are already aware of it since that’s where they are getting their view models.  Also, it’s really quick to add it in this one place.  It’s not an ideal solution since I’m trying to NOT have App turn into a dumping ground for properties (hence my clean up last week) but it’s so quick and easy now, that I’ll go with it.

public static class App
{
    private static IContainer _container;

    public static void Init(AppSetup appSetup)
    {
        _container = appSetup.CreateContainer();
        AccentColor = _container.Resolve<IThemer>().AccentColor;
    }

    public static Color AccentColor { get; private set; }
}

The init method pulls out the IThemer implementation and grabs the AccentColor from it.  After this I bind to the property from within the WelcomePage.

<Label Text="Welcome to The Store"
       Font="Bold, Large" 
       HorizontalOptions="Center" 
       TextColor="{x:Static local:App.AccentColor}" />

I only used this solution in one place:  WelcomePage.  While it is good for keeping all of the style definitions in one place I really only had one style that I wanted to use:  accent color.  As it turned out Xamarin already did most of the heavy lifting for me.  Isn’t that always the case?

Solution 2:  Color.Accent and OnPlatform

After finding out that I could just use the Color.Accent static color (I really did find that very late in the game), I realized that there was a much simpler way to solve the problem.  On platforms that support the Color.Accent, use it, on other platforms use my alternate color.  This is easy to accomplish with the OnPlatform class.  Here’s an example from my ProductsPage:

<Label Text="{Binding Product.Name}"
       HorizontalOptions="Center"
       Font="Bold,Large">
  <Label.TextColor>
    <OnPlatform x:TypeArguments="Color"
                iOS="Accent"
                WinPhone="Accent"
                Android="#afcca6" />
  </Label.TextColor>
</Label>

On WinPhone and iOS, the Accent color is used.  On Droid, I fallback to my custom light green color.

Pics or It Didn’t Happen

I was mostly concerned with the how-to aspect of this write up and avoided screen shots throughout the post.  But, now that the post is over, here’s what the app looks like after my styling efforts.

Welcome Screen

Both_Welcome

Categories Screen

Both_Categories

It’s starting to look just a little more polished.

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