Monday, June 30, 2014

Xamarin.Forms Custom Controls ImageSource

Xamarin.Forms has a great way to handle images cross platform in controls with the ImageSource.  The ImageSource allows you to specify the file name or URI of an image and a Xamarin.Forms handler will figure out how to load them correctly on the appropriate platform.  The following Xamarin site explains how to use the ImageSource for the stock controls that come with Xamarin.Forms

Working with Images

What if you want to make your own custom control, can you write it to use an ImageSource property?  This was the question I faced when creating an ImageButton control for the Xamarin Forms Labs project.  As it turns out it isn't as straightforward as I hoped, but it is possible.

To make this work first I created an ImageButton class that derived from the normal Button as so:

public class ImageButton : Button
{
    public static readonly BindableProperty SourceProperty =
        BindableProperty.Create<ImageButton, ImageSource>(
        p => p.Source, null);
        
    [TypeConverter(typeof(ImageSourceConverter))] 
    public ImageSource Source
    {
        get { return (ImageSource)GetValue(SourceProperty); }
        set { SetValue(SourceProperty, value); }
    }
}

One thing to note is that there is a TypeConverter on the Source property.  That is because the ImageSource cannot be created through a normal constructor passing in the string.  Instead there are factory methods, FromFile and FromUri to create instances of the ImageSource class.  Xamarin.Forms has a ImageSourceConverter class as a TypeConverter for this purpose; unfortunately this class is internal and can't be used directly.  Instead I made my own implementation as below.

public class ImageSourceConverter : TypeConverter
{
    public override bool CanConvertFrom(Type sourceType)
    {
        return sourceType == typeof(string);
    }

    public override object ConvertFrom(CultureInfo culture, object value)
    {
        if (value == null)
        {
            return null;
        }

        var str = value as string;
        if (str != null)
        {
            Uri result;
            if (!Uri.TryCreate(str, UriKind.Absolute, out result) || !(result.Scheme != "file"))
            {
                return ImageSource.FromFile(str);
            }
            return ImageSource.FromUri(result);
        }
        throw new InvalidOperationException(
            string.Format("Conversion failed: \"{0}\" into {1}",
                new[] { value, typeof(ImageSource) }));
    }
}

We need one more thing before we can create our custom renderers.  There are three handlers that convert our ImageSource to the proper platform specific image depending on if a UriImageSource, FileImageSource or StreamImageSource is being used.  Internally Xamarin.Forms uses the Xamarin.Forms.Registrar to resolve out the proper handler.  Unfortunately this class is also internal and can't be used by our custom renderers.  To solve this I created a class and linked it to my platform specific projects where my custom renderers will reside.  This is the class I used to resolve out the proper handler:

#if __Android__
using Xamarin.Forms.Platform.Android;

namespace Xamarin.Forms.Labs.Droid.Controls.ImageButton
#elif __IOS__
using Xamarin.Forms.Platform.iOS;

namespace Xamarin.Forms.Labs.iOS.Controls.ImageButton
#elif WINDOWS_PHONE
using Xamarin.Forms.Platform.WinPhone;

namespace Xamarin.Forms.Labs.WP8.Controls.ImageButton
#endif
{
    public partial class ImageButtonRenderer
    {
        private static IImageSourceHandler GetHandler(ImageSource source)
        {
            IImageSourceHandler returnValue = null;
            if (source is UriImageSource)
            {
                returnValue = new ImageLoaderSourceHandler();
            }
            else if (source is FileImageSource)
            {
                returnValue = new FileImageSourceHandler();
            }
            else if (source is StreamImageSource)
            {
                returnValue = new StreamImagesourceHandler();
            }
            return returnValue;
        }
    }
}

Then I implemented my custom renderers.  I'm not going to show all of their code here and if you want to see the full code check out the Xamarin.Forms.Labs project on Github.  For the iOS platform renderer i resolved out the ImageSource into a iOS UIImage like this:

private async static Task SetImageAsync(ImageSource source, int widthRequest, int heightRequest, UIButton targetButton)
{
    var handler = GetHandler(source);
    using (UIImage image = await handler.LoadImageAsync(source))
    {
        UIGraphics.BeginImageContext(new SizeF(widthRequest, heightRequest));
        image.Draw(new RectangleF(0, 0, widthRequest, heightRequest));
        using (var resultImage = UIGraphics.GetImageFromCurrentImageContext())
        {
            UIGraphics.EndImageContext();
            using (var resizableImage =
                resultImage.CreateResizableImage(new UIEdgeInsets(0, 0, widthRequest, heightRequest)))
            {
                targetButton.SetImage(
                    resizableImage.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal),
                    UIControlState.Normal);
            }
        }
    }
}

On the Android platform's custom renderer for the ImageButton I resolved it out like this:

private async Task<Bitmap> GetBitmapAsync(ImageSource source)
{
    var handler = GetHandler(source);
    var returnValue = (Bitmap)null;

    returnValue = await handler.LoadImageAsync(source, this.Context);

    return returnValue;
}

On the Windows Phone platform I resolved out the ImageSource like this:

private async static Task<System.Windows.Controls.Image> GetImageAsync(ImageSource source, int height, int width)
{
    var image = new System.Windows.Controls.Image();
    var handler = GetHandler(source);
    var imageSource = await handler.LoadImageAsync(source);

    image.Source = imageSource;
    image.Height = Convert.ToDouble(height / 2);
    image.Width = Convert.ToDouble(width / 2);
    return image;
}

That's how I implemented the Source property on my custom ImageButton control and now I get all the cross platform image handling goodness.  I hope this helps if you need to make a Xamarin.Forms custom control that needs to implement a cross platform image.

Wednesday, June 11, 2014

Xamarin - Xamarin.Forms Renderer Reference

Now that I've spent some time working with writing custom renderers with Xamarin.Forms I've found that one of the easiest ways to extend an existing control is to also extend the an existing platform renderers.  For example, when I created an ImageButton for the Xforms-Toolkit, I extended the existing ButtonRenderers for the three platforms.  This seemed easier to me than extending the base renderers for the three platforms and starting from scratch.  What I found as I continued to work creating custom renderers is that I was being frustrated by not always knowing what renderer was associated with a particular Xamarin.Forms control for a given platform and what native control or controls were being created under the covers by the renderer.

To help with this I put together this guide to list what renderers are associated with what Xamarin.Forms controls and what native control or controls are being created by the platform specific renderers.

This guide was update using Xamarin.Forms version 1.1.1.6206, which is the latest version at the time of this page's last edit.

Platform Specific renderers can be found in the following namespaces:

iOS: Xamarin.Forms.Platform.iOS
Android: Xamarin.Forms.Platform.Android
Windows Phone: Xamarin.Forms.Platform.WinPhone

With the 6201 release many of the inconsistencies in the controls have been address but some still exist.  Sometimes the Windows Phone renderers were creating the native controls by loading an Xaml file or pulling the native control out of Application.CurrentResources.  In these cases I was not able to tell exactly what native controls were being created.

Base Renderer
To start with, all renders usually derive from a platform specific base renderer class that in turn inherit from a platform specific control.  If you want to create platform specific renderers from scratch you are probably going to be deriving them from the correct platform's base renderer class.  These have been substantially standardized in version 6201 with all controls inheriting from a generic version of ViewRenderer.  There is also a non-generic version that appears to be used sometimes by navigation controls.  The methods on these classes are also more in line with each other than they had been but still not the same.  For example the iOS version has a method called ViewOnUnfocusRequested while the Android's version of the same method is called OnUnfocusRequested.

iOS:
Renderer: Xamarin.Forms.Platform.iOS.ViewRenderer<TView, TNavitveView>
Platform Specific Control: types deriving from MonoTouch.UIKit.UIView
Android
Renderer: Xamarin.Forms.Platform.Android.ViewRenderer<TView, TNavitveView>
Platform Specific Control: types deriving from Android.Views.ViewGroup
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.ViewRenderer<TElement, TNativeElement>
Platform Specific Control: types deriving from System.Windows.FrameworkElement

Layout Renderers
ContentPage
iOS
Renderer: Xamarin.Forms.Platform.iOS.PageRenderer
Platform Specific Control: MonoTouch.UIKit.UIView
Android
Renderer: Xamarin.Forms.Platform.Android.PageRenderer
Platform Specific Control: Android.Views.ViewGroup
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.PageRenderer
Platform Specific Control: System.Windows.Controls.Panel

MasterDetailPage
iOS
Renderer: Xamarin.Forms.Platform.iOS.PhoneMasterDetailRenderer for iPhone and Xamarin.Forms.Platform.iOS.TabletMasterDetailRenderer for iPad
Platform Specific Control: custom flyout on iPhone and MonoTouch.UIKit.UISplitViewController for iPad
Android
Renderer: Xamarin.Forms.Platform.Android.MasterDetailRenderer
Platform Specific Control: Android.Support.V4.Widget.DrawerLayout
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.MasterDetailRenderer
Platform Specific Control: System.Windows.Controls.Panel

NavigationPage
iOS
Renderer: Xamarin.Forms.Platform.iOS.NavigationRenderer
Platform Specific Control: MonoTouch.UIKit.UIToolbar and derives from MonoTouch.UIKit.UINavigationController
Android
Renderer: Xamarin.Forms.Platform.Android.NavigationRenderer
Platform Specific Control: No View of its own, deals with switching content derived from Android.Views.View
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.NavigationPageRenderer
Platform Specific Control: No View of its own, deals with switching content derived from System.Windows.FrameworkElements

TabbedPage
iOS
Renderer: Xamarin.Forms.Platform.iOS.TabbedRenderer
Platform Specific Control: MonoTouch.UIKit.UIViewController with contained MonoTouch.UIKit.UIView
Android
Renderer: Xamarin.Forms.Platform.Android.TabbedRenderer
Platform Specific Control: No View of its own, deals with switching content derived from Android.Views.View
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.TabbedPageRenderer
Platform Specific Control: inherits from Microsoft.Phone.Controls.Pivot

CarouselPage
iOS
Renderer: Xamarin.Forms.Platform.iOS.CarouselPageRenderer
Platform Specific Control: MonoTouch.UIKit.UIScrollView
Android
Renderer: Xamarin.Forms.Platform.Android.CarouselPageRenderer
Platform Specific Control: Android.Views.View
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.CarouselPageRenderer
Platform Specific Control: Microsoft.Phone.Controls.PanoramaItem

Frame
iOS
Renderer: Xamarin.Forms.Platform.iOS.FrameRenderer
Platform Specific Control: MonoTouch.UIKit.UIView
Android
Renderer: Xamarin.Forms.Platform.Android.FrameRenderer
Platform Specific Control: Android.Graphics.Drawables.Drawable
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.FrameRenderer
Platform Specific Control: System.Windows.UIElement

ScrollView
iOS
Renderer: Xamarin.Forms.Platform.iOS.ScrollViewRenderer
Platform Specific Control: MonoTouch.UIKit.UIScrollView
Android
Renderer: Xamarin.Forms.Platform.Android.ScrollViewRenderer
Platform Specific Control: Android.Widget.ScrollView
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.ScrollViewRenderer
Platform Specific Control: System.Windows.Controls.ScrollViewer

AbsoluteLayout
iOS
Renderer: Unsure
Platform Specific Control: Unsure
Android
Renderer: Unsure
Platform Specific Control: Unsure
Windows Phone
Renderer: Unsure
Platform Specific Control: Unsure

Grid
iOS
Renderer: Unsure
Platform Specific Control: Unsure
Android
Renderer: Unsure
Platform Specific Control: Unsure
Windows Phone
Renderer: Unsure
Platform Specific Control: Unsure

RelativeLayout
iOS
Renderer: Unsure
Platform Specific Control: Unsure
Android
Renderer: Unsure
Platform Specific Control: Unsure
Windows Phone
Renderer: Unsure
Platform Specific Control: Unsure

StackLayout
iOS
Renderer: Unsure
Platform Specific Control: Unsure
Android
Renderer: Unsure
Platform Specific Control: Unsure
Windows Phone
Renderer: 
Platform Specific Control: Unsure

TableView
iOS
Renderer: Xamarin.Forms.Platform.iOS.TableViewRenderer
Platform Specific Control: MonoTouch.UIKit.UITableView
Android
Renderer: Xamarin.Forms.Platform.Android.TableViewRenderer (Table),  Xamarin.Forms.Platform.Android.TableViewModelRenderer (Rows)
Platform Specific Control: Android.Widget.ListView (Table), Android.Views.View (Row)
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.TableViewRenderer
Platform Specific Control: Microsoft.Phone.Controls.LongListSelector

Control Renderers
ActivityIndicator
iOS
Renderer: Xamarin.Forms.Platform.iOS.ActivityIndicatorRenderer
Platform Specific Control: MonoTouch.UIKit.UIActivityIndicatorView
Android
Renderer: Xamarin.Forms.Platform.Android.ActivityIndicatorRenderer
Platform Specific Control: Android.Widget.ProgressBar
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.ActivityIndicatorRenderer
Platform Specific Control: System.Windows.Controls.ProgressBar

BoxView
iOS
Renderer: Xamarin.Forms.Platform.iOS.BoxRenderer
Platform Specific Control: MonoTouch.CoreGrahics.CGContext
Android
Renderer: Xamarin.Forms.Platform.Android.BoxRenderer
Platform Specific Control: Android.Views.ViewGroup
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.BoxViewRenderer
Platform Specific Control: System.Windows.Shapes.Rectangle

Button
iOS
Renderer: Xamarin.Forms.Platform.iOS.ButtonRenderer
Platform Specific Control: MonoTouch.UIKit.UIButton
Android
Renderer: Xamarin.Forms.Platform.Android.ButtonRenderer
Platform Specific Control: Android.Widget.Button
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.ButtonRenderer
Platform Specific Control: System.Windows.Controls.Button

DatePicker
iOS
Renderer: Xamarin.Forms.Platform.iOS.DatePickerRenderer
Platform Specific Control: MonoTouch.UIKit.UIToolbar with MonoTouch.UIKit.UIBarButtonItems
Android
Renderer: Xamarin.Forms.Platform.Android.DatePickerRenderer
Platform Specific Control: Android.App.DatePickerDialog
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.DatePickerRenderer
Platform Specific Control: Microsoft.Phone.Controls.DateTimePickerBase

Editor
iOS
Renderer: Xamarin.Forms.Platform.iOS.EditorRenderer
Platform Specific Control: MonoTouch.UIKit.UITextView
Android
Renderer: Xamarin.Forms.Platform.Android.EditorRenderer
Platform Specific Control: Android.Widget.EditText
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.EditorRenderer
Platform Specific Control: System.Windows.Controls.TextBox

Entry
iOS
Renderer: Xamarin.Forms.Platform.iOS.EntryRenderer
Platform Specific Control: MonoTouch.UIKit.UITextField
Android
Renderer: Xamarin.Forms.Platform.Android.EntryRenderer
Platform Specific Control: Android.Widget.EditText
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.EntryRenderer
Platform Specific Control: Microsoft.Phone.Controls.PhoneTextBox or System.Windows.Controls.PasswordBox

Image
iOS
Renderer: Xamarin.Forms.Platform.iOS.ImageRenderer
Platform Specific Control: MonoTouch.UIKit.UIImageView
Android
Renderer: Xamarin.Forms.Platform.Android.ImageRenderer
Platform Specific Control: Android.Widget.ImageView
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.ImageRenderer
Platform Specific Control: System.Windows.Controls.Image

Label
iOS
Renderer: Xamarin.Forms.Platform.iOS.LabelRenderer
Platform Specific Control: MonoTouch.UIKit.UILabel
Android
Renderer: Xamarin.Forms.Platform.Android.LabelRenderer
Platform Specific Control: Android.Widget.TextView
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.LabelRenderer
Platform Specific Control: System.Windows.Controls.TextBlock

ListView
iOS
Renderer: Xamarin.Forms.Platform.iOS.ListViewRenderer
Platform Specific Control: MonoTouch.UIKit.UITableView
Android
Renderer: Xamarin.Forms.Platform.Android.ListViewRenderer (currently internal in scope and not derivable)
Platform Specific Control: Android.Widget.ListView
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.ListViewRenderer
Platform Specific Control: Microsoft.Phone.Controls.LongListSelector

OpenGLView
iOS
Renderer: Xamarin.Forms.Platform.iOS.OpenGLViewRenderer
Platform Specific Control: MonoTouch.GLKit.GLKView
Android
Renderer: Xamarin.Forms.Platform.Android.OpenGLRenderer (currently internal in scope and not derivable)
Platform Specific Control: Android.Opengl.GLSurfaceView
Windows Phone
Renderer: Unsure
Platform Specific Control: Unsure

Picker
iOS
Renderer: Xamarin.Forms.Platform.iOS.PickerRenderer
Platform Specific Control: MonoTouch.UIKit.UIPickerView, MonoTouch.UIKit.UIPickerViewModel, MonoTouch.UIKit.UIToolBar, Two MonoTouch.UIKit.UIBarButtonItems, MonoTouch.UIKit.UITextField
Android
Renderer: Xamarin.Forms.Platform.Android.PickerRenderer
Platform Specific Control: Android.Widget.TextView, Android.App.AlertDialog and Android.Widget.NumberPicker
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.PickerRenderer
Platform Specific Control: Microsoft.Phone.Controls.ListPicker

ProgressBar
iOS
Renderer: Xamarin.Forms.Platform.iOS.ProgressBarRenderer
Platform Specific Control: MonoTouch.UIKit.UIProgressView
Android
Renderer: Xamarin.Forms.Platform.Android.ProgressBarRenderer
Platform Specific Control: Android.Widget.ProgressBar
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.ProgressBarRenderer
Platform Specific Control: System.Windows.Controls.ProgressBar

SearchBar
iOS
Renderer: Xamarin.Forms.Platform.iOS.SearchBarRenderer
Platform Specific Control: MonoTouch.UIKit.UISearchBar
Android
Renderer: Xamarin.Forms.Platform.Android.SearchBarRenderer
Platform Specific Control: Android.Widget.SearchView
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.SearchBarRenderer
Platform Specific Control: Microsoft.Phone.Controls.PhoneTextBox

Slider
iOS
Renderer: Xamarin.Forms.Platform.iOS.SliderRenderer
Platform Specific Control: MonoTouch.UIKit.UISlider
Android
Renderer: Xamarin.Forms.Platform.Android.SliderRenderer
Platform Specific Control: Android.Widget.SeekBar
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.SliderRenderer
Platform Specific Control: System.Windows.Controls.Slider

Stepper
iOS
Renderer: Xamarin.Forms.Platform.iOS.StepperRenderer
Platform Specific Control: MonoTouch.UIKit.UIStepper
Android
Renderer: Xamarin.Forms.Platform.Android.StepperRenderer
Platform Specific Control: Android.Widget.LinearLayout with four Android.Widget.Buttons
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.StepperRenderer
Platform Specific Control: System.Windows.Controls.Border with four System.Windows.Controls.Buttons

Switch
iOS
Renderer: Xamarin.Forms.Platform.iOS.SwitchRenderer
Platform Specific Control: MonoTouch.UIKit.UISwitch
Android
Renderer: Xamarin.Forms.Platform.Android.SwitchRenderer
Platform Specific Control: Android.Widget.Switch
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.SwitchRenderer
Platform Specific Control: System.Windows.Controls.Primitives.ToggleButton

TimePicker
iOS
Renderer: Xamarin.Forms.Platform.iOS.TimePickerRenderer
Platform Specific Control: MonoTouch.UIKit.UIDatePicker, MonoTouch.UIKit.UITextField, MonoTouch.UIKit.UIToolbar, MonoTouch.UIKit.UIBarButtonItems
Android
Renderer: Xamarin.Forms.Platform.Android.TimePickerRenderer
Platform Specific Control: Two Android.Widget.TextViews and a Android.App.AlertDialog
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.TimePickerRenderer
Platform Specific Control: Microsoft.Phone.Controls.DateTimePickerBase

WebView
iOS
Renderer: Xamarin.Forms.Platform.iOS.WebViewRenderer
Platform Specific Control: MonoTouch.UIKit.UIWebView
Android
Renderer: Xamarin.Forms.Platform.Android.WebRenderer
Platform Specific Control: Android.Webkit.WebView
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.WebViewRenderer
Platform Specific Control: Microsoft.Phone.Controls.WebBrowser

Cell Controls
EntryCell
iOS
Renderer: Xamarin.Forms.Platform.iOS.EntryCellRenderer
Platform Specific Control: MonoTouch.UIKit.UITableViewCell with a MonoTouch.UIKit.UITextField
Android
Renderer: Xamarin.Forms.Platform.Android.EntryCellRenderer
Platform Specific Control: Android.Widget.LinearLayout contianing Android.Widget.TextView and Android.Widget.EditText
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.EntryCellRenderer
Platform Specific Control: Pulls from Application.Current.Resources for name "EntryCell" of type System.Windows.DataTemplate

SwitchCell
iOS
Renderer: Xamarin.Forms.Platform.iOS.SwitchCellRenderer
Platform Specific Control: MonoTouch.UIKit.UITableViewCell with a MonoTouch.UIKit.UISwitch
Android
Renderer: Xamarin.Forms.Platform.Android.SwitchCellRenderer
Platform Specific Control: Android.Widget.Switch
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.SwitchCellRenderer
Platform Specific Control: Pulls from Application.Current.Resources for name "SwitchCell" of type System.Windows.DataTemplate

TextCell
iOS
Renderer: Xamarin.Forms.Platform.iOS.TextCellRenderer
Platform Specific Control: MonoTouch.UIKit.UITableViewCel
Android
Renderer: Xamarin.Forms.Platform.Android.TextCellRenderer
Platform Specific Control: Android.Widget.LinearLayout contianing two Android.Widget.TextViews and Android.Widget.ImageView
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.TextCellRenderer
Platform Specific Control: Pulls from Application.Current.Resources for name "TextCell", or "ListViewHeaderTextCell" or "ListViewTextCell" of type System.Windows.DataTemplate depending on parent/context of the cell.

ImageCell
iOS
Renderer: Xamarin.Forms.Platform.iOS.ImageCellRenderer
Platform Specific Control: MonoTouch.UIKit.UITableViewCell with a MonoTouch.UIKit.UIImage
Android
Renderer: Xamarin.Forms.Platform.Android.ImageCellRenderer
Platform Specific Control: Android.Widget.LinearLayout contianing two Android.Widget.TextViews and Android.Widget.ImageView
Windows Phone
Renderer: Xamarin.Forms.Platform.WinPhone.ImageCellRenderer
Platform Specific Control: Pulls from Application.Current.Resources for name "ListImageCell", or "ImageCell" of type System.Windows.DataTemplate depending on parent/context of the cell.

I hope this helps people trying to understand what these renderers are creating on the native platforms and gives a hint at how they can be extended.

Thursday, May 29, 2014

Xamarin 3.0 and Xamarin.Forms - Getting Started

A while back I blogged about the possibility of Xamarin using something like XAML as a standard way to write UX because it isn't the platform specific syntax that's important; it's the platform specific look and behavior that's important.  Today Xamarin introduced Xamarin 3.0 and with it Xamarin.Forms that appears to do just that; we write in XAML and we get native controls out the other side.  The promise of Xamarin.Forms is that (for the most part) we need to know one UI syntax, XAML.

Please note, this does not mean that we want to make the same exact UI for Android, iOS and Windows Phone.  In fact that would produce a substandard user experience.  But to the extent possible, it's isn't the syntax that matters, only appearance and behavior.  Xamarin.Forms gives us a much greater possibility of sharing UI code across platforms than ever before while still maintaining a native experience.

For the most part I don't think we are going to be using Xamarin.Forms for a lot of applications that we would have considered using complex native UIs a few months ago because there are going to be some platform specific tweaks that are still going to demand a native design experience.  But that's OK because in such an application you can mix Xamarin.Forms for the more generic native experiences in the application with native forms where we need to do something very platform specific.

So where does that leave Xamarin.Forms?  Its sweet spot would seem to be when we may have considered using a hybrid tool like PhoneGap or Appcellerator for cross platform development.  Because Xamarin.Forms still produces a native UI it will produce a much better native experience for the user while giving us all the cross platform UI code sharing that we would get from these tools.  It would seem to be a far superior choice and extremely compelling.

I wanted to try this out.  Xamarin has an introduction that I found useful here:
http://developer.xamarin.com/guides/cross-platform/xamarin-forms/introduction-to-xamarin-forms/

It is day two of playing around with this for me and let me tell you Xamarin.Forms seems vast, very vast.  There is a lot to learn here and I look forward to attending next weeks Xamarin University class on Xamarin.Forms.  It is going to take a bit of time to really learn how to use it well.  For my first project I wanted to use actual XAML files and also use the new Universal Application Shared Projects.  For many reasons I believe this will be a better approach for most cross platform projects than using a PCL.

Here's what I did:

Step 1: Install Xamarin 3.0.  Just use the normal update process, no magic here.

Step 2: Create a new solution with projects for the platform specific UIs we want to support.  For my solution I selected an Android Application and an iOS empty Universal Application.




Step 3: Add a Xamarin.Forms project.  A Xamarin.Forms project is a project that provides shared code to the Android and iOS projects we just created.  This can be accomplished by using a portable class library or the new universal shared application.  Unfortunately in Visual Studio there are only three templates currently available under the Mobile Apps section.  One is used to create a multi-project solution with shared code through a portable class library, one to create a multi-project solution using a universal application shared project and one to create a new Xamarin.Form portable class library on it's own.  There is currently no template to create a Universal Application Shared Xamarin.Forms project on its own.  Luckily this is possible using the Universal Application Extension.  With this extension I created a new Universal Application Shared Project for my Xamain.Forms that I want to share with both projects and then put my Xamarin.Forms code in there.


Step 4: Add a reference to the Xamarin.Forms project to our iOS and Android projects.  This can be done by right clicking on references and selecting Add Nuget Package.  Search for the Xamarin.Forms project and add it.  Also add a Shared Project Reference for the new Universal Application Shared Project in the iOS and Android projects.


Step 5: Add a new Forms XAML Page to the Shared Project we created.  I called mine SharedForm.xaml.


Note: When I try to open the XAML file I get a designer error but I am still able to edit the XAML directly. From the Xamain.Forms forum on the Xamarin site it looks like there is currently no visual designer for this.  It was strongly hinted that one is in the works however.  I added a label and a button to make a very simple form that I called SharedForm.xaml.  The following is the code in my XAML file:


<?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="XamarinFormsTest.Shared.SharedForm">
    <StackLayout Spacing="10">
        <Label Text="Foo" VerticalOptions="Center" HorizontalOptions="Center" />
        <Button Text="Bar" x:Name="btnBar" />
    </StackLayout>
</ContentPage>

Step 6: I want to show a popup window of some sort when they click on my new "bar" button.  It wouldn't surprise me to learn there is some sort of cross platform library for this in Xamarin.Forms but I don't know what it is.  Instead I used a Toast for Android and a UIAlertView for iOS.  This is where using a Universal Application Shared Project really shines.  Using the a conditional compilation symbol of __Android__ in the Android project and __IOS__ in the iOS project I was able to do the following:


   1:  public partial class SharedForm : ContentPage
   2:  {
   3:      public SharedForm ()
   4:      {
   5:          InitializeComponent();
   6:          btnBar.Clicked += btnBar_Click;
   7:      }
   8:   
   9:      private void btnBar_Click(object sender, EventArgs e)
  10:      {
  11:  #if __ANDROID__
  12:          Android.Widget.Toast.MakeText(Android.App.Application.Context, "Test 123", Android.Widget.ToastLength.Short).Show();
  13:  #elif __IOS__
  14:          new MonoTouch.UIKit.UIAlertView("Test", "Test 123", null, "Ok", null).Show();
  15:  #endif
  16:      }
  17:  }
  
First I made the SharedForm partial class inherit from ContentPage.  This shouldn't be necessary because the generated version of SharedForm also inherits from ContentPage but there seems to be some bug in the Universal Shared Projects where this wasn't recognized in the Visual Studio designer.  It was just easier to add it.

I also tied the click event of the button to a new method, btnBar_Click.  If we are compiled into the Android application the Toast message is used in the method, otherwise the UIAlertView is used for iOS.

Step 7: I went into the android project's main activity and changed it so it inherits from AndroidActivity instead of Activity.  I also made a call to Forms.Init() and created a new instance of my SharedFrom.  A quick call to SetPage and my XAML form is used.


   1:  [Activity(Label = "XamarinFormsTest", MainLauncher = true, Icon = "@drawable/icon")]
   2:  public class MainActivity : AndroidActivity
   3:  {
   4:      protected override void OnCreate(Bundle bundle)
   5:      {
   6:          base.OnCreate(bundle);
   7:  
   8:          Xamarin.Forms.Forms.Init(this, bundle);
   9:   
  10:          var form = new SharedForm();
  11:          SetPage(form);
  12:      }
  13:  }

When I run the application and click on the "Bar" button this is what I see:


Step 8:  I want to try the same thing for iOS.  So I go to the iOS project's AppDelegate.  In the FinishedLaunching event I again call Forms.Init() and then create an instance of my XAML form.  On the form there is a CreateViewController() method that I call to set the window's RootViewController.


   1:  public override bool FinishedLaunching(UIApplication app, NSDictionary options)
   2:  {
   3:      // create a new window instance based on the screen size
   4:      window = new UIWindow(UIScreen.MainScreen.Bounds);
   5:   
   6:      // If you have defined a view, add it here:
   7:      // window.RootViewController  = navigationController;
   8:      Forms.Init();
   9:   
  10:      var form = new SharedForm();
  11:      window.RootViewController = form.CreateViewController();
  12:   
  13:      window.MakeKeyAndVisible();
  14:   
  15:      return true;
  16:  }

I then run the project on the iOS simulator, click on the "Bar" button and this is what I see there:



Similar but different.  I used the same UI syntax for a button and in one case I got an android button and here I got an iOS 7 style button, nice and flat.  Granted this was a simple example and it looks like my label got a bit mixed up with text at the top of the iOS simulator.  There is clearly more experimentation and learning I need to do.  But the idea seems to work.  Almost all of my code was shared and I was able to do it with little fuss or muss.

Very exciting and I'm ready to learn more.

Tuesday, May 6, 2014

Universal Application Extension - Working with Xamarin

One thing I mentioned in my blog post last month was how to use the new universal shared application concept in your Xamarin applications.  At the time (all of a month ago) the only way to do add them to our Xamarin projects was through directly editing the project files to add a reference to the universal shared project.  OK, not too difficult but still a bit of a PITA.  Now there is a new extension for Visual Studio 2013 Update 2 that makes this easy and it can be obtained here:

http://visualstudiogallery.msdn.microsoft.com/315c13a7-2787-4f57-bdf7-adae6ed54450

After installing the extension here is how to use it:

  • Have a new or existing solution with two different projects where you want to share code, such as a solution that contains an iOS project and an Android project.
  • Add a new project for code sharing, use the "Shared Project (Empty)" template:
           Note: even if you don't have a Windows 8.1 project in your solution, adding a universal shared project will require you to get a Windows 8.1 developer license, at least it did for me.
  • In the universal project add any shared modules, classes, constants, etc that you want.
  • In each of the platform specific projects under references right click and select the new "Add Shared Project Reference"


  • A list of all shared projects in the solution will be displayed, select the one you want:


That's all there is to it.  You can directly create empty shared universal projects and then through the Visual Studio UI add references to them to platform specific projects.  No direct editing of the projects required.  It would be nice if adding the Universal shared project didn't require a Windows 8.1 developer license if you don't have a Windows 8.1 platform specific project in your solution, but that's a minor nit.

Thursday, April 10, 2014

Universal Apps: What Are They And Can I Use Them With Xamarin?

At Build this year the new Universal App type was announced that allows you to write apps on for both the Windows Store and Windows Phone 8.1 platforms with one code base.  There were also hints that this would be supported in Xamarin as well.  Exactly how these work was a little unclear to me so I downloaded the Visual Studio 2013 update 2 RC and did some experimentation, at least what is possible in the release candidate.  The Visual Studio 2013 Update 2 RC can be found here:

http://www.microsoft.com/en-us/download/details.aspx?id=42307

I opened up the new project window and with a little searching around this is what I found:



There are four new project types:

  • Blank App (Universal App) - Very simple solution to create two projects (Windows Store and Windows Phone 8.1) and one shared universal project with a nifty new icon
  • Hub App (Universal App) - Slightly more complex than the Blank app, it still creates a Windows Store and Windows Phone 8.1 projects both using the same shared project
  • Class Library (Portable for Universal Apps) - Creates a project to create a Portable Class Library (DLL) in the profile that supports Windows Store 8.1 and Windows Phone 8.1 applications.  Currently there are no profiles that support Windows Phone 8.1 and Xamarin so I was not able to make one of these that also supports Xamarin but I suspect this will be updated soon enough.  This project type is intended to create libraries that can be shared outside the solution.
  • Windows Runtime Component (Portable for Universal App) - Similar to the Class Library type except compiles into a winmd file instead of a dll.

Blank App (Universal App) - This creates a new universal app solution with three projects, One for a Windows Phone 8.1 UI, one with a Windows Store Application and one shared universal application which would seem to be where the interesting bits are.


The new shared app type just shows up with an xaml file and is referenced by both the Windows Store application and the Windows Phone 8.1 application.  This is a project type unlike what you may be used to, in fact it appears to be only a repository for shared code that can be used by other projects in the solution.  Here are some things that are of interest:

  • If you right click on the shared universal project and select properties all you can change is the root namespace.  That's it.
  • When compiled no DLL is generated for this project, all code in it is compiled directly into the Windows Store and Windows Phone 8.1 applications.  These projects cannot be independently be shared or used outside of the projects that directly reference them in a solution.
  • You cannot compile only the Shared project on the build menu, it cannot be built independently.  This makes sense give that it has no output and is compiled directly into a referencing project.
  • As of the time of the writing there is no references nor ability to add references to the shared project through the Visual Studio UI.  What is available to you within code of these projects is the subset of the .NET framework for this project and that's it.  That means no referencing Autofac or any third party library in code through the Visual Studio UI. 
  • EDIT TO PREVIOUS VERSION - The shared project *WILL* pickup references in the projects that reference them.  You have to select the proper project in the upper left dropdown when editing a file in the shared project and it will pickup the proper references.

Here is a screenshot of the project selection dropdown where you can select the right platform specific project and get the proper references and conditional compilation symbols:


I wasn't satisfied with the last point as it would be extremely limiting so I decided to do some experimentation.  I went out to Nuget and added the latest stable version of Windows Azure Mobile Services to the Windows Store and Windows Phone 8.1 projects.  I then opened up the Windows Store project file in Notepad and copied the following code to the shared project file from the Windows Store project file:

  <Import Project="..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\..

\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
  <Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
    <Error Condition="!Exists('..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This 

project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more 

information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
    <Error Condition="Exists('..\..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build 

restored NuGet packages. Build the project again to include these packages in the build. For more information, see 

http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
  </Target>

When I refreshed the shared project I still had no references section visible in the project on the Visual Studio UI.  However, I then created a new class in the Shared project with the following code:

using Microsoft.WindowsAzure.MobileServices;

namespace App1
{
    class ReferenceTest
    {
        private MobileServiceClient Test1()
        {
            return new MobileServiceClient("");
        }
    }
}

Granted this code is rather useless but Visual Studio now recognized that Microsoft.WindowsAzure.MobileServices is valid in the shared project.  I was able to compile both projects using the shared project just fine.  So there may not be support in the Visual Studio UI for this at the present time but it does appear possible to reference other assemblies in your shared project.  Of course when I removed the Windows Azure Mobile Services reference from my Windows Phone 8.1 project, it no longer compiled though the Windows Store project continued to compile so any references added to the shared project also need to be added to any project that reference it, which makes perfect sense.

How about Xamarin?  As I stated before I couldn't create a Portable Class Library for Universal Apps that also referenced either Xamarin iOS or Android as there did not seem to be a profile for that combination.  What about the shared project?  I went ahead and added Xamarin iOS and a Xamarin Android UI projects to the solution.  I then went to the references section of the Android project and tried to add a reference to App1.Shared like there was in the Windows Store and Windows Phone 8.1 projects.  No luck, that wasn't an option when looking at the solution nor could I browse out to a DLL because one does not exist.



I did, however, find the following blog post that states how to modify the project file:

https://medium.com/p/141c2b7bae4f

I took the advice and added the following code to my Android project file in Notepad (As noted in the blog, be careful about the path being correct or your project will not load):

<Import Project="..\App1\App1.Shared\App1.Shared.projitems" Label="Shared" />

When I then reloaded the Android project as the blog post indicated I suddenly has a reference to the shared universal app project.


When I tried to compile I couldn't for two reasons.  First the default Blank App added a Xaml file to the shared app, App.xaml. Currently xaml isn't going to be usable by the Android or iOS projects and so can't exist in App1.Shared.  I moved this to the individual Windows Store and Windows Phone 8.1 applications (I could also have used pre-compilation directives so it wouldn't be visible to the Android project to the same effect). 

The second reason is because the shared project uses the Windows Azure Mobile Services component and my Android project does not reference it.  I added this and the problem was resolved (Note: sometimes when getting the Windows Azure Mobile Services package for Xamarin from Nuget it drags along some extra system DLLs that I have to delete: System.IO, System.Runtime and System.Threading.Tasks).

With these issues resolved I am able to use the shared project in Xamarin Android.  By following the same steps I am able to do it with the Xamarin iOS project as well.

In conclusion, what have I learned about the present state of Universal Apps and Xamarin?

  • Universal PCLs and winmd projects won't work with Xamarin at the present time because there is no profile that supports Windows Phone 8.1 and Xamain project types.  I expect that to be resolved.
  • Creating a Universal Blank App or Hub app creates a shared project type
    • The shared project cannot be created directly through the Visual Studio UI and instead must be created through one of those two project types
    • Code in the shared project does not create a DLL or winmd file but instead is compiled directly into the projects that reference the project
    • There is no way through the Visual Studio UI to add a reference to an external dll to a shared universal project but references can still be added by editing the project file directly
    • You do not need references in the shared project because it picks up references form the projects that use it.  When in a code file you can switch between projects to pick up their references and conditional compilation constants
    • All projects that reference the shared universal project must reference and be able to use any code in the shared project or measures such as pre-compilation directives must be used
    • Xamarin iOS and Android UI projects can use the shared project but only if their project files are edited directly as there is not currently any way to do it through the Visual Studio UI

In general the shared universal projects would seem to be a new alternative to the old method of simple linking a shared code file between two or more different projects.  As is common with that method platform specific differences can be handled via pre-compilation directives.  Any sharing of the UX may work on projects that use xaml but of course such code is not currently workable in Xamarin Android and Xamain iOS and would have to be hidden from those project types.

UPDATE: If you want to create a PCL today that targets Windows Phone 8.1 and Xamarin Android and/or Xamarin iOS @danrigby has a great blog post that explains how to do it until an update is released:

http://danrigby.com/2014/04/10/windowsphone81-pcl-xamarin-fix/

Friday, April 4, 2014

The .NET Foundation and Xamarin

Miguel put this post on his blog discussing the joint announcement of The .NET Foundation:

http://blog.xamarin.com/xamarin-and-the-.net-foundation/

The .NET Foundation is an attempt to create cross platform language syntax for the features of the platforms where they operate similarly.  This is similar to what Microsoft has done with Azure's Mobile Service's Identity.  With Mobile Services Identity Microsoft realized that as a developer I don't care if I'm trying to authenticate against a Live account, Google, Twitter or Facebook.  All I care about is what service the user is authenticated with and the token returned.  The differences in syntax were not of interest to me, in fact as a developer they created a harmful knowledge barrier.  So Microsoft takes care of the differences in syntax and I just write against one unified syntax.

The .NET Foundation is bringing us more of the same for mobile platforms.  Do I care about the syntactical differences in Geolocation or the contact systems?  No probably not, so we have Xamarin Mobile.   Do we care about differences in how mail is sent out?  No probably not, so we have MailKit and MimeKit.  Ultimately anything that has common functionality between systems can be abstracted out behind a common language library to give common syntax.  This is big, huge in fact, for cross platform development.  The more knowledge barriers that can be removed between platforms, the less expensive and more maintainable native cross platforms solutions can be.

This got me thinking, what can't easily be abstracted out across platforms to a common syntax for a truly engaging and flexible native applications?  Two things quickly come to mind:

- The user experience.  Applications running on iOS that look like Android applications or Windows Phone apps do not create an engaging user experience and vice-versa.  The users on those platforms expect things to look a act in certain ways.  This isn't just the look and feel of the controls, it may be the general design of the screens themselves and the flow between screens, how menus operate, etc.

- Platform specific features and flow.  It's important to know that you can pass reference types between controllers on iOS but not between activities in Android.  Sometimes you need access to those properties, methods and events that exist on one platform and not the other.  It is similarly important to understand the lifecycle differences between the platforms, the fact that JIT code won't work on iOS, etc.  This type of platform specific knowledge will always be needed to write an application that looks, feels and acts like a native application and performs well on that platform.

If we look at these one at a time what do they mean?  For the user experience what is important is that we can create difference views on each platform with different layouts and a different look and feel.  What isn't as important is the syntactical differences in what the controls are.  Most of the time when I want to display some text I don't care if what I need to create is called a TextView or a UILabel.  I just want some control to display some text.  Similarly when I'm laying out the view I know that I want controls to appear in a certain order and usually be responsive to changes in the size of the UI.  Do I care if I'm using the XCode designer, some controls created and positioned in code or a LinearLayout for that?  Probably not.  So I can envision some sort of common designer with common syntax being created that will have a different look and feel (i.e. the correct native controls being used) when I'm in a Windows Phone project, an Android project or an iOS project.  But that will only take you so far which brings me to platform specific features and flow.

The platforms are different.  Whether it be fragments in Android or the TouchUpOutside event in iOS.  Activities and iOS controllers behave differently.  Those types of behavioral and feature differences will be hard to get away from and still create a native application that behaves and runs well on the individual platforms.  To the extent that they may be able to make a common syntax name for a button that actually creates the correct button on the native platform, that's great.  To the extent that they can use a common layout scheme like XAML that creates the appropriate native specific controls and layout, that's even better.  A single designer that works the same but looks differently on Andoird, iOS, Windows Phone and WinRT, that would be better still.  But at the end of the day we are still likely to need some sort of platform specific extensions to get at the control features and layout capabilities that are unique to one platform or the other.

I don't even know if common control syntax or designer while preserving native look, feel and flow is truly feasible.  Even then, we will likely still be creating multiple versions of the views that look/behave correctly on each platform even if the syntax is mostly the same.  It will be fun and interesting to see how this plays out; what things can have standardized language syntax while still preserving the platform specific look, feel, features and flow and what things can't.  I'd love to see that single UX designer that is emitting the correct native platform code.  Sure I still may be creating platform specific layouts but my tool and syntactic knowledge will be similar; that is, the platform specific knowledge barriers will be lowered. 

We will still need to know about how the individual platforms work, I don't think we will be able to move away from that and make native applications that look, feel and operate like native applications.  The .NET Foundation is clearly moving in the right direction to lower many of those barriers.  In the final analysis this clearly strengthens the value proposition of Xamarin and C#/.NET in cross platform development, which is kind of interesting in that it was one of the original thought processes behind .NET.  It just took us a while (and some people with vision and perseverance) to make it happen.

Sunday, March 23, 2014

Conditional Deployment of Resources in Xamarin Android


I was recently out at a client site and they asked me how to conditionally deploy resources in Xamarin Android.  They have a situation where they have one base application that they are creating and depending on the brand deploy a completely different set of resources.  It is important to note that they only want to deploy the resources that would be particular to that brand so as not to bloat the overall application with a bunch of images/strings that would never be used.  At the time this seemed like an interesting problem but one that I had never tried.  With a little digging I found an answer on the Xamarin site: 

Xamarin - Build Process

The Xamarin instructions are at a pretty high level and sometimes they need some experimentation to figure out all the wrinkles.  I decided to try this out and I wanted a solution that would meet the following criteria:

- It should allow for one set of resources to be shared for all brands and some resources to be brand specific
- It should be easy to specify building for one brand or the other
- It should support all types of resources, strings, views, images, etc
- It should be easy to setup and maintain

From the above article this will require some manual editing of the project file in something like notepad.  This means right off the bat my fourth requirement of "It should be easy to setup and maintain" is going to be a bit tenuous (note to self: perhaps a Visual Studio plugin could be made for this so it can be done through the UX?). 

First thing I did was set up a new project called MyResourceTest and renamed Activity1.cs to MainActivity.  That creates an Android project that looks like this:














I want to have a shared image and a shared string.  I use the default icon as my shared image and in the default strings.xml I change the string resources to contain a single value, SharedString:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="SharedString">Shared string used by all</string>
</resources>

Then I modify the Main.axml to display the shared string:
 


<?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">
  <TextView
      android:id="@+id/lblShared"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="@string/SharedString" />
</LinearLayout>

Finally I remove the code for the button that was created by default in the MainActivity.cs:

using Android.App;
using Android.OS;

namespace MyResourceTest
{
    [Activity(Label = "", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);
        }
    }
}

When I run this I get a very unexciting application, nothing new here:


























This is where the fun starts.  I want to add an image and a couple of strings that vary as far as what gets deployed based on what configuration I choose.  My application brands are going to be named AppOne and AppTwo and so I need to create four new configurations Debug-AppOne, Debug-AppTwo, Release-AppOne and Release-AppTwo.  I copy the default Debug and Release configurations to my new configurations as appropriate.  When I’m done I remove the default Debug and Release configurations because I won’t need them anymore.  The complete configuration setup looks like this:





















Now I’ll set up the directory structure to support my new applications.  In the project I’m going to create two new directories from the root of the project, Resources-AppOne, Resources-AppTwo.  Under each of these projects will be a Drawable and Values folder:


























Note: That there are now three resources directories, one for  our shared resources, one for the brand resources specific to AppOne and one for resources specific to AppTwo.  I could just as easily add a Layout folder to each of the new resources folders to have brand specific views.

To the Resources-AppOne\Drawable folder I add an image for AppOne called MainLogo.png and add an AppTwo MainLogo.png picture to the Resources-AppTwo\Drawable folder.  These will be two different images with the same name.  Be careful, if you add an image to one brand specific folder you will have to have an equivalent version with the same name in the other brand specific folders or have code in your application to handle the case where it might be missing.

To each of the brand specific Values folder I add a new xml file for my strings called strings-app.xml.  This needs to have a different name than any of the files under the default Resources\Values folder or you will have a collision at compile time.  In each of the files I set up two string values, ApplicationName, AppDescription.
File for Resources-AppOne\Values\Strings-App.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="ApplicationName">First Application</string>
    <string name="AppDescription">The First Application</string>
</resources>

File for Resources-AppTwo\Values\ Strings-App.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="ApplicationName">Second Application</string>
  <string name="AppDescription">The Second Application</string>
</resources>


Note: I make sure the build action for all of these files is set to AndroidResource for them to be used as Android Resources by the compiler.

I want to use my new image and strings in my view, Main.xml.  I change it to be as follows:

<?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">
  <TextView
      android:id="@+id/lblShared"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="@string/SharedString" />
  <TextView
      android:id="@+id/lblDescription"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="@string/AppDescription" />
  <ImageView
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:id="@+id/imgMain"
      android:src="@drawable/MainLogo"
      android:scaleType="fitCenter"/>
</LinearLayout>

Finally I also want the application’s title to use my brand specific ApplicationName string resource.  To do this I override the MainActivity’s OnAttachedToWindow event to use the ApplicationName string resource:

public override void OnAttachedToWindow()
{
    base.OnAttachedToWindow();
    this.Window.SetTitle(GetString(Resource.String.ApplicationName));
}

If I decide to try and run this now I’m going to get a couple of errors, the compiler doesn’t know how to deal with the Resources-AppOne or Resources-AppTwo folders.  Errors similar to the following appear:










This is where it gets a little difficult.  We can’t do what we want in the IDE, instead we need to edit the project file directly in a tool like Notepad.  If I open the project in Notepad and view my newly added brand specific files this is what I find:









I want to pull out the AppOne and AppTwo resources into their own ItemGroups that are conditionally included in the project if one of the AppOne or AppTwo configurations we added before are selected.  When done your project file should look similar to this:

<ItemGroup Condition="'$(Configuration)'=='Debug-AppOne'Or'$(Configuration)'=='Release-AppOne'">
    <AndroidResource Include="Resources-AppOne\Drawable\MainLogo.png" />
    <AndroidResource Include="Resources-AppOne\Values\Strings-App.xml">
      <SubType>Designer</SubType>
    </AndroidResource>
</ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Debug-AppTwo'Or'$(Configuration)'=='Release-AppTwo'">
    <AndroidResource Include="Resources-AppTwo\Drawable\MainLogo.png" />
    <AndroidResource Include="Resources-AppTwo\Values\Strings-App.xml">
      <SubType>AndroidResource</SubType>
    </AndroidResource>
</ItemGroup>
<ItemGroup>
    <Content Include="Properties\AndroidManifest.xml" />
</ItemGroup>

There is one more thing we need to do.  If you recall the compiler won’t see the Resources-AppOne or Resources-AppTwo directories as being valid.  We need to map them correctly in the project file.  Add two new conditional PropertyGroups, one for AppOne and one for AppTwo, to correctly map the directories:

<PropertyGroup Condition="'$(Configuration)'=='Debug-AppOne'Or'$(Configuration)'=='Release-AppOne'">
    <MonoAndroidResourcePrefix>Resources;Resources-AppOne</MonoAndroidResourcePrefix>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug-AppTwo'Or'$(Configuration)'=='Release-AppTwo'">
    <MonoAndroidResourcePrefix>Resources;Resources-AppTwo</MonoAndroidResourcePrefix>
</PropertyGroup>

If I save the project file and reopen it, the project will use the correct resources for the configuration I have selected.  If I select Debug-AppOne and run the application this is what I see:
























If I then select the Debug-AppTwo profile I see this:
























Remember, this is doing more than just using the correct resources at run time like when you have language specific resources, it is only compiling in the resources specified by the selected configuration.  To verify this you can examine the obj output folders for the appropriate configuration.  Compiling in an alternate set of resources is as simple as changing the selected configuration from Debug-AppOne to Debug-AppTwo.  In general this satisfies all my conditions other than it does take some time to setup and to maintain.  That is, if new drawable resources are added to the brand specific resource directories you will still need to modify the project file directly and make sure they show up under the correct conditional ItemGroups. 

There are probably other ways to do this such as using your source control solution with some sort of branching strategy but that seems too difficult to manage.  As I indicated earlier at some point a Visual Studio plugin could be added to make this easier to do through the UX but at the present time as indicated on the Xamarin site this would seem to be the easiest way to do it.