Tuesday, December 23, 2014

Mobile Development Platform Performance (Native, Cordova, Classic Xamarin, Xamarin.Forms)

Last month I published my Mobile Technology Decision Making White Paper and I feel that goes a long way to helping choose what technology to use in the mobile space.  One question I get asked frequently is how do the different mobile development platforms compare to each other from a performance perspective. While I've heard a lot of anecdotal information I have not seen a lot of formal comparisons.  At Magenic we normally work with native (iOS, Android, WP), Cordova and Xamarin so I wanted to compare these.  I plan on this being the first post on this topic.

For some background on testing methodology using Android and iOS.

The Development Platforms
- Native (Objective-C 64 bit and Java)
- Cordova (Multi-Device Hybrid Apps) using Intel's App Framework for the UI
- Classic Xamarin (64 bit unified beta for iOS)
- Xamarin.Forms (64 bit unified beta for iOS with beta version of Xamarin.Forms, note latest version of the unified API in the beta/alpha channels could not be used as it is not supported by Xamarin.Forms Note: 1.3.1 pre 1 was released Dec 24th so Xamarin.Forms may now work with the version of the unified iOS API in the alpha and beta channel)

The Devices
- iPad Mini (non Retina) running iOS 8.1.1 (12B435)
- ASUS K00F running Android 4.2.2

The Test Apps
Applications were made for each of the development platforms that are functionally similar. There was little (if no) effort to make them look exactly the same or even look "good".  But they looked about the same.  There were some differences such as Java, Classic Xamarin and Xamarin.Forms rendered the tabs on the top in Android as expected while the JavaScript library showed them on the bottom.

The Timing Methodology
Due to difficulties in knowing when things are "done", particularly with JavaScript, timings were handled via stopwatch.  Each timing was taken ten times and the results were averaged.  It should noted that hand timings have an accuracy of about 2/10 of a second so that does give us an approximate margin of error.

Test 1: Test App Size
The size of the application can impact how much bandwidth it takes to deploy and also have some impact on load times.  For Android the size of the APKs was examined.  For iOS I looked at Settings to find out how much space the apps took up on disk.


Development PlatformSize
Android
Java166kb
Cordova433kb
Classic Xamarin3.5mb
Xamarin.Forms4.7mb
iOS
Objective-C (64 bit)644kb
Cordova2.7mb
Classic Xamarin12.1mb
Xamarin.Forms16.9mb

When it comes to application size Xamarin shows the extra size involved in the overhead of the .Net framework.  There was an attempt to reduce the size of the deployed Xamarin application by using the "Link SDK assemblies only" setting.  I am surprised in a very small application how large the difference is.  However, from experience in "real" applications the difference is much less consequential as graphics and frameworks get added to the projects.

Test 2: Load Times
I wanted to see how long it took the application to load into memory.  While the initial load time is important, many mobile applications tend to stay in memory so it tends to have a limited impact.  For this test I made sure to close all applications before each timing.

Development PlatformTest Avg.
Android
Java1.085
Cordova3.978
Classic Xamarin1.704
Xamarin.Forms2.764
iOS
Objective-C1.221
Cordova1.715
Classic Xamarin1.28
Xamarin.Forms1.813

In all cases the vendor native technologies loaded the fastest.  Classic Xamarin loaded nearly as fast as the native languages.  Xamarin.Forms and Cordova had the slowest load times.  The Cordova load time on Android was particularly bad while on iOS the load times were close enough to not be a huge factor.

Test 3: Loading a List from Azure Mobile Services
In this test I wanted to look at getting data from an external service so I loaded 1000 records from Azure Mobile Services.  For Xamarin iOS 64 bit I had to modify the Azure Mobile Services to be compatible with the unified API.  The timings were taken from pushing the button to load the list until the results visibly came back and were displayed on a list on the screen.

Java:
public  void addRecord(String firstName, String lastName, int index, String misc) throws Exception {
    if (dbConn == null) {
        openConnection();
    }

    ContentValues values = new ContentValues();
    values.put("firstName", firstName);
    values.put("lastName", lastName + index);
    values.put("misc", misc);
    dbConn.insertOrThrow(TABLE_NAME, null, values);
}

Objective-C:
- (void)addRecord:(NSString*)firstName withLastName:(NSString*)lastName withIndex:(int)index withMisc:(NSString*)misc withError:(NSError**)error {
    NSString *sqlStatement = NULL;
    char *errInfo;
    *error = nil;

    if (dbConn == nil) {
        [self openConnection:error];
        return;
    }
    
    sqlStatement = [NSString stringWithFormat:@"%@%@%@%@%d%@%@%@", @"INSERT INTO testTable (firstName, lastName, misc) VALUES ('", firstName, @"', '", lastName, index, @"', '", misc, @"')"];
    
    int result = sqlite3_exec(dbConn, [sqlStatement UTF8String], nil, nil, &errInfo);
    
    if (result != SQLITE_OK) {
        NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
        [errorDetail setValue:[NSString stringWithFormat:@"%@%s", @"Error writing record to database: ", errInfo] forKey:NSLocalizedDescriptionKey];
        *error = [NSError errorWithDomain:@"testDomain" code:101 userInfo:errorDetail];
    }
}

JavaScript:
db.executeSql("INSERT INTO testTable (firstName, lastName, misc) VALUES (?,?,?)", ["test", lastName, "12345678901234567890123456789012345678901234567890"], function (res) {
    successCount++;
    if (successCount === maxValue) {
        $.ui.popup({
            title: "Success",
            message: "All records written to database",
            doneText: "OK",
            cancelOnly: false
        });
        $.ui.unblockUI();
    }
}, function (e) {
    $.ui.popup({
        title: "Error",
        message: "An error has occurred adding records: " + e.toString(),
        doneText: "OK",
        cancelOnly: false
    });
    $.ui.unblockUI();
    return;
});

Xamarin (All Versions):
public void AddRecord(string fName, string lName, int i, string m)
{
    if (dbConn == null)
    {
        OpenConnection();
    }

    var testRecord = new TestTable {firstName = fName, id = 0, lastName = lName + i, misc = m};

    dbConn.Insert(testRecord);
}

Xamarin Classic Android Alternate:
public  void AddRecord(string firstName, string lastName, int index, string misc) 
{
    if (dbConn == null) 
    {
        OpenConnection();
    }

    ContentValues values = new ContentValues();
    values.Put("firstName", firstName);
    values.Put("lastName", lastName + index);
    values.Put("misc", misc);
    dbConn.InsertOrThrow(TABLE_NAME, null, values);
}


Development PlatformTest 1Test 2Test 3Test 4Test 5Test 6Test 7Test 8Test 9Test 10Test Avg.
Android
Java22.7117.518.0417.718.6320.332.682.422.162.342.369
Cordova25.9924.7627.0523.324.0622.862.122.021.942.482.149
Classic Xamarin34.0727.3832.0538.7729.2734.631.611.631.841.851.738
Xamarin.Forms1.991.762.321.911.91.581.932.022.031.641.908
iOS
Objective-C2.382.442.242.32.342.322.322.352.22.272.316
Cordova3.572.182.071.951.972.052.041.932.21.962.192
Classic Xamarin21.871.882.061.741.91.811.941.751.961.891
Xamarin.Forms2.112.012.231.961.952.072.122.162.082.12.079
*results in seconds

In many ways this test is showing how well the Azure Mobile Services libraries perform on the different platforms.  Unsurprisingly Xamarin, with it's underpinnings of a .Net implementation, performs the best in this test.  I was surprised to see the libraries for the native technologies perform the worst, both on Android and iOS.

I have heard that the Xamarin.Forms lists can perform poorly with large data sets.  These results did not show that, at least with lists of up to 1000 records.

Test 4: Prime Number Calculation
In the final of my first series of tests I wanted to try out a CPU intensive operation.  I created a Sieve of Eratosthenes on each of the platforms.  My first plan was to calculate all prime numbers up to 50,000,000.  This required some special handling of the method's array for both Objective-C and JavaScript.  In the case of Objective-C I had to malloc memory to support arrays that large.  Also for Objective-C and JavaScript I had to initialize the array items to 0.  To keep the timings the same I did the array item initialization to 0 on all platforms even though it could have been left out for .Net (and Java I believe).  It that was done, the .Net timings would have been even better.

I did end up having to settle for only calculating primes up to 5,000,000.  The reason for this is that the JavaScript performed so poorly that I was unwilling to wait for it to complete 10 times.

Java:
private int getPrimesFromSieve(int maxValue)
{
    byte[] primes = new byte[maxValue + 1];
    for (int i = 0; i <=maxValue; i++)
    {
        primes[i] = 0;
    }
    int largestPrimeFound = 1;

    for (int i = 2; i <=maxValue; i++)
    {
        if (primes[i - 1] == 0)
        {
            primes[i - 1] = 1;
            largestPrimeFound = i;
        }

        int c = 2;
        int mul = i*c;
        for (; mul <= maxValue;)
        {
            primes[mul - 1] = 1;
            c++;
            mul = i*c;
        }
    }
    return largestPrimeFound;
}

Objective-C:
- (int) getPrimesFromSieve: (int) maxValue {
    Byte *primes;
    primes = (Byte *) malloc(maxValue * sizeof(Byte));
    for (int i=1; i<=maxValue; i++)
    {
        primes[i-1] = 0;
    }    

    int largestPrimeFound;
    largestPrimeFound = 1;

    for (int i=2; i<=maxValue; i++)
    {
        if(primes[i-1] == 0)
        {
            primes[i-1] = 1;
            largestPrimeFound = i;
        }

        int c=2;
        int mul = i*c;
        for(; mul <= maxValue;)
        {
            primes[mul-1] = 1;
            c++;
            mul = i*c;
        }
    }
    return largestPrimeFound;
}

JavaScript:
function getPrimesFromSieve(maxValue) {
    var primes = new Uint8Array(new ArrayBuffer(Number(maxValue)));
    for (var i = 0; i <=maxValue; i++) {
        primes[i] = 0;
    }
    var largestPrimeFound = 1;

    for (i = 2; i <= maxValue; i++) {
        if (primes[i - 1] == 0) {
            primes[i - 1] = 1;
            largestPrimeFound = i;
        }

        var c = 2;
        var mul = i * c;
        for (; mul <= maxValue;) {
            primes[mul - 1] = 1;
            c++;
            mul = i * c;
        }
    }
    return largestPrimeFound;
}

Xamarin (All Versions):
public static int GetPrimesFromSieve(int maxValue)
{
    var primes = new byte[maxValue + 1];
    for (var i = 0; i <=maxValue; i++)
    {
        primes[i] = 0;
    }
    var largestPrimeFound = 1;

    for (var i = 2; i <=maxValue; i++)
    {
        if (primes[i - 1] == 0)
        {
            primes[i - 1] = 1;
            largestPrimeFound = i;
        }

        var c = 2;
        var mul = i*c;
        for (; mul <= maxValue;)
        {
            primes[mul - 1] = 1;
            c++;
            mul = i*c;
        }
    }
    return largestPrimeFound;
}

Development PlatformTest 1Test 2Test 3Test 4Test 5Test 6Test 7Test 8Test 9Test 10Test Avg.
Android
Java4.314.314.24.334.394.374.324.454.344.44.342
Cordova91.699594.3194.494.7394.194.191.893.6397.7594.151
Classic Xamarin4.274.254.154.324.514.414.224.124.144.194.258
Xamarin.Forms4.214.174.314.34.24.344.294.364.224.194.259
iOS
Objective-C5.045.495.384.864.85.025.034.834.844.855.014
Cordova66.9667.3667.2267.367.1767.4467.1367.1167.5867.6467.291
Classic Xamarin4.414.424.354.344.494.374.174.274.394.284.349
Xamarin.Forms4.514.334.314.314.334.44.414.44.334.464.379
*results in seconds

While I was expecting that JavaScript would be slower, I was unprepared for how much worse it was for this type of operation.  This would make Cordova problematic for highly CPU bound work were performance is important (I wonder what that means for server side Node.js...).  CPU bound work is not as important in many of today's mobile applications but given the history of increased performance for mobile CPUs and what happened in the PC market in the 1990's, it is likely that in the future CPU bound work will be more prevalent in mobile applications.

Having said that, using native HTML commands can be very fast.  In the past I've testing loading JSON using HTML commands vs. the JSON.Net library on Xamarin and found them nearly comparable.

I was also surprised that Xamarin performed better than Objective-C by a noticeable amount.  It also performed better than Java on Android but by a very marginal amount, well within the .2 second margin of error that manual timings give us.

That's it for my first installment of performance tests.  Much of this code was made more difficult for Xamarin due to the flux around the 64 bit Unified iOS API.  For next month I'll take a look at loading large JSON strings and perhaps something else.

I hope this is useful or you.  If you have any ideas for performance tests I can perform, I'd love to hear them.

Source code for the tests can be found here: Performance Tests Source

Thursday, October 30, 2014

Xamarin.Forms Circle Images

It is a common pattern in mobile applications to display an image in the shape of a circle.  Xamarin.Forms does not have any functionality for this right out of the box but the Xamarin.Forms team showed us how this can be done at Evolve 2014.  That implementation can be found here:

Xamarin.Forms Team Circle Image

This implementation by +James Montemagno is good for making an exact circle with a white border.  It uses masking and it's really fast.  One thing it doesn't do is handle what happens when the height and width of the control are different or what happens when you set the Aspect property.  I'm ignoring the catch(Exception ex) statements James, but just don't let +Jason Bock see them! (not that my code is perfect either, I try to do better each and every day.)

So here is an example of the renderers when using with controls that have a different height and width with the Aspect property set:



OK, we can see that the Android and Windows Phone implementations in this scenario are about the same.  They always draw a round image though the two implementations show a slightly different portion of the picture.  It probably with have displayed fine had the control's height and width been the same.  These are generally behaving as would be expected under the Aspect Fit setting. 

The iOS implementation is doing something very different.  It is behaving as though the Aspect property were set to Fill in all scenarios.  The upshot of the deal is that in most cases the programmer is going to set the RequestedHeight and RequestedWidth the same and these renderers are going to be great (and fast).  But how about those other cases, what would we expect to see then?





This is what you would expect to see.  Aspect Fill fills the entire control and what portion of the circle image is outside of that area is cropped off.  The top and bottom in this case.  The fill setting takes the circle and spreads it out to fill the entire control area resulting in an oval.  Aspect Fit makes a perfect circle in the center of the control.  Since the control area is wider than it is tall a letterbox situation is created with blank space on either side of the circle.

Note: My Windows Phone implementation is now yet complete.  I'm currently using a renderer in the style that +James Montemagno created.

The following implementation is now in the open source XLabs library for Xamarin.Forms.  It can be found here XForms Labs.  As of the time of this writing it has not yet made it to the nuget package.

The first thing I did was create a custom view that derived from the standard Image view as so:

public class CircleImage : Image
{
}

Ok, I know what you may be thinking... Where's the beef?  As it turns out at the present time I don't need to change anything intrinsic about the view itself, just how it is rendered.  To do this I subclass from the Image view so I can create a custom renderer for that without impacting base Image view.

To use this in Xaml it is as simple as:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:Xamarin.Forms.Labs.Controls;assembly=Xamarin.Forms.Labs"
             x:Class="Xamarin.Forms.Labs.Sample.Pages.Controls.CircleImagePage">
        <controls:CircleImage HeightRequest="75" WidthRequest="100" HorizontalOptions="Fill" Aspect="AspectFill">
            <controls:CircleImage.Source>
                <OnPlatform x:TypeArguments="ImageSource">
                    <OnPlatform.iOS>
                        <FileImageSource File="panic.jpg" />
                    </OnPlatform.iOS>
                    <OnPlatform.Android>
                        <FileImageSource File="panic.jpg" />
                    </OnPlatform.Android>
                    <OnPlatform.WinPhone>
                        <FileImageSource File="Images/panic.jpg" />
                    </OnPlatform.WinPhone>
                </OnPlatform>
            </controls:CircleImage.Source>
        </controls:CircleImage>
</ContentPage>

Other than calling it a CircleImage and referencing it the line "xmlns:controls="clr-namespace:Xamarin.Forms.Labs.Controls;assembly=Xamarin.Forms.Labs"" it is just like working with a standard Image view.

So let's take a look at what I did with iOS to make a custom renderer:

[assembly: ExportRenderer(typeof(CircleImage), typeof(CircleImageRenderer))]
namespace Xamarin.Forms.Labs.iOS.Controls.CircleImage
{
    public class CircleImageRenderer : ImageRenderer

The header is exactly what you would expect.  The new renderer for the CircleImage is registered as part of the  namespace and the custom renderer derives from the ImageRenderer.

One of the things that I need to think about is how will I accomplish the Aspect property working.  This requires considering a few things, do I need to manipulate the image, if I do how do I know the image is loaded, ensure there are no memory leaks and finally use masking as the Xamarin.Forms implementation when possible because it performs better.

So right of the bat I want to do masking for Fill because that's what works by default.  First we'll look at the OnElementChanged method:

protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
    base.OnElementChanged(e);

    if (Control == null || e.OldElement != null || Element == null || this.Element.Aspect != Aspect.Fill )
        return;

    var min = Math.Min(Element.Width, Element.Height);
    Control.Layer.CornerRadius = (float)(min / 2.0);
    Control.Layer.MasksToBounds = false;
    Control.ClipsToBounds = true;
}

This isn't normally where I would look for manipulating images as this is where the base class's ImageRenderer will start loading them asynchronously from the source.  I can however set a mask, which is what I do if the Aspect is Fill.

Where the real interesting code comes in is during OnElementPropertyChanged.  For my masking I want to know that the control is drawing and that means that the height and width have changed.  For the bitmap manipulation I need to know when the image is loaded from the source.  Luckily the Xamarin.Forms team sets an IsLoading property to true when the image starts loading and turns it to false when complete.  That means when the IsLoading property changes to false I know when the load operation is complete.

NOTE: On Windows Phone changes to the IsLoading property are not raised to the renderer's OnElementPropertyChanged method as they are in iOS and Android.  I suspect this is an oversight but for now other methods need to be used.

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    base.OnElementPropertyChanged(sender, e);

    if (Control == null) return;

    if (this.Element.Aspect == Aspect.Fill)
    {
        if (e.PropertyName == VisualElement.HeightProperty.PropertyName ||
            e.PropertyName == VisualElement.WidthProperty.PropertyName)
        {
            DrawFill();               
        }
    }
    else
    {
        if (e.PropertyName == Image.IsLoadingProperty.PropertyName
            && !this.Element.IsLoading && this.Control.Image != null)
        {
            DrawOther();
        }
    }
}

The code here is pretty simple.  Base on the right phase in the lifecycle and what Aspect we are using we either draw for our masking (DrawFill) or re-size the image (DrawOther).  For now I'll ignore the masking and focus on the DrawOther method.

private void DrawOther()
{
    int height = 0;
    int width = 0;
    int top = 0;
    int left = 0;

    switch (this.Element.Aspect)
    {
        case Aspect.AspectFill:
            height = (int)this.Control.Image.Size.Height;
            width = (int)this.Control.Image.Size.Width;
            height = this.MakeSquare(height, ref width);
            left = (((int)this.Control.Image.Size.Width - width) / 2);
            top = (((int)this.Control.Image.Size.Height - height) / 2);
            break;
        case Aspect.AspectFit:
            height = (int)this.Control.Image.Size.Height;
            width = (int)this.Control.Image.Size.Width;
            height = this.MakeSquare(height, ref width);
            left = (((int)this.Control.Image.Size.Width - width) / 2);
            top = (((int)this.Control.Image.Size.Height - height) / 2);
            break;
        default:
            throw new NotImplementedException();
    }

    UIImage image = this.Control.Image;
    var clipRect = new RectangleF(0, 0, width, height);
    var scaled = image.Scale(new SizeF(width, height));
    UIGraphics.BeginImageContextWithOptions(new SizeF(width, height), false, 0f);
    UIBezierPath.FromRoundedRect(clipRect, Math.Max(width, height) / 2).AddClip();

    scaled.Draw(new RectangleF(0, 0, scaled.Size.Width, scaled.Size.Height));
    UIImage final = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();
    this.Control.Image = final;
}

A couple of things to notice.  I look at the aspect to figure out the size of the image I need.  If it is Aspect Fill I expect that the image may be higher or wider than the control's requested width or height.  For Aspect Fit I expect that neither the height nor width of the image will be larger than the requested amounts but that at least one will be equal.  In both cases I expect the image to be square.

With the new width and height I now scale the image to the right size (image.Scale(new SizeF(width, height));).  I can then use the FromRoundedRect function to crop the image and make it round with the rest transparent.  When I am done I make the new image the image for the control's image property and that's what will be used.

Android was a little more complex but in many ways the same.  The first thing was that the masking works correctly with the Aspect Fit scenario instead of Fill like on iOS.  The second is that we want to apply the mask on the DrawChild method override and not OnElementPropertyChanged.

protected override bool DrawChild(Canvas canvas, global::Android.Views.View child, long drawingTime)
{
    if (this.Element.Aspect == Aspect.AspectFit)
    {
        var radius = Math.Min(Width, Height)/2;
        var strokeWidth = 10;
        radius -= strokeWidth/2;

        var path = new Path();
        path.AddCircle(Width/2, Height/2, radius, Path.Direction.Ccw);
        canvas.Save();
        canvas.ClipPath(path);

        var result = base.DrawChild(canvas, child, drawingTime);

        path.Dispose();

        return result;

    }

    return base.DrawChild(canvas, child, drawingTime);
}

I check to see if this is actually an Aspect Fit situation and if not just don't so anything.  If it is I want to clip the existing image.  The AddCircle command works nicely for this.  This command always takes a center X and Y and a radius to draw a circle.  We calculate how large we want the circle to be by looking at the width and height of the control as they have been calculated by this point.  Once this is done successfully we return true and have a nice round image view that fits nicely in the control's bounds.

For the other two we look at OnElementPropertyChanged like we did on iOS and if we are done loading convert the drawable into a bitmap.

protected async override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    base.OnElementPropertyChanged(sender, e);
    if (e.PropertyName == Image.IsLoadingProperty.PropertyName && !this.Element.IsLoading
        && this.Control.Drawable != null)
    {
        //Should only be true right after an image is loaded
        if (this.Element.Aspect != Aspect.AspectFit)
        {
            using (var sourceBitmap = Bitmap.CreateBitmap(this.Control.Drawable.IntrinsicWidth, this.Control.Drawable.IntrinsicHeight, Bitmap.Config.Argb8888))
            {
                var canvas = new Canvas(sourceBitmap);
                this.Control.Drawable.SetBounds(0, 0, canvas.Width, canvas.Height);
                this.Control.Drawable.Draw(canvas);
                this.ReshapeImage(sourceBitmap);
            }
                    
        }
    }
}

Once we have a new source bitmap I call ReshapeImage to manipulate it:

private void ReshapeImage(Bitmap sourceBitmap)
{
    if (sourceBitmap != null)
    {
        var sourceRect = GetScaledRect(sourceBitmap.Height, sourceBitmap.Width);
        var rect = this.GetTargetRect(sourceBitmap.Height, sourceBitmap.Width);
        using (var output = Bitmap.CreateBitmap(rect.Width(), rect.Height(), Bitmap.Config.Argb8888))
        {
            var canvas = new Canvas(output);

            var paint = new Paint();
            var rectF = new RectF(rect);
            var roundRx = rect.Width() / 2;
            var roundRy = rect.Height() / 2;

            paint.AntiAlias = true;
            canvas.DrawARGB(0, 0, 0, 0);
            paint.Color = Android.Graphics.Color.ParseColor("#ff424242");
            canvas.DrawRoundRect(rectF, roundRx, roundRy, paint);
            paint.SetXfermode(new PorterDuffXfermode(PorterDuff.Mode.SrcIn));
            canvas.DrawBitmap(sourceBitmap, sourceRect, rect, paint);

            this.Control.SetImageBitmap(output);
            // Forces the internal method of InvalidateMeasure to be called.
            this.Element.WidthRequest = this.Element.WidthRequest;
        }
    }
}

Android works primarily in rectangles for the area to draw in.  I get two rectangles, a scaled rectangle that is used for the area to resize the image and a target rectangle for where it will display on the control.  A new target bitmap is created that will be used by the control.  I won't go too deeply into the image manipulation commands but one thing to notice is the DrawRoundRect has an x radius and a y radius.  That allows me to draw a nice oval in fill mode as I was not able to do in iOS.

The other thing that may look strange is setting the Element's WidthRequest property to itself.  I did this because I wanted to call the base class's InvalidateMeasure method to let the control take appropriate action with the new image.  Since this is a protected method I can't call it directly.  However, when the WidthRequest is set, InvalidateMeasure is then called.  It was just a way to force an InvalidateMeasure call.

I hope you find this useful.  The full code case be found in the XForms labs project and will shortly be coming to our nuget packages.  What's next?  Finishing the Windows Phone renderer and then adding a border, allowing the user to set the color and width.

I'll be speaking in a few weeks at Modern Apps Live in Orlando (strangely enough not on Xamarin).  I hope to see you there.  Modern Apps Live 2014 Orlando

Monday, July 7, 2014

Xamarin.Forms.Labs Version 1.1.0 Released

For the last several weeks several developers from around the globe have been working on a project to extend the functionality given in Xamarin.Forms to deliver more cross platform capabilities for Android, iOS and Windows Phone 8.  This project is called Xamarin.Froms.Labs.  Today we have released version 1.1.0 and it is now in Nuget for your use.

One of the largest improvements is in the structure of the packages themselves.  All platform specific packages are being depreciated in favor of single packages that install correctly for all supported platforms.

The 1.1.0 version includes the following package structure for use with Android, iOS and Windows Phone:
Xamarin.Forms.Labs - Core package containing PCL and platform specific dlls including custom controls, renderers and services.
Xamarin.Forms.Labs.Services.Autofac - Includes PCL with extensions to use with Autofac.
Xamarin.Forms.Labs.Services.Ninject - Includes PCL with extensions to use with Ninject.
Xamarin.Forms.Labs.Services.SimpleContainer - Includes PCL with extensions to use with Simple Injector.
Xamarin.Forms.Labs.Services.TinyIOC- Includes PCL and platform specific DLLs with extensions to use with TinyIOC
Xamarin.Forms.Labs.Services.Serialization.JsonNET - Includes PCL to use JsonNet with Xamarin.Forms.
Xamarin.Forms.Labs.Services.Serialization.ProtoBuf - Includes PCL to use ProtoBuf with Xamarin.Forms.
Xamarin.Forms.Labs.Cryptography - Includes PCL to give cross platform cryptography with Xamarin.Forms.
Xamarin.Forms.Labs.Caching.SQLiteNet - Includes PCL to enable cross platform caching using SQLite with Xamarin.Forms.

The following packages are being depreciated or not part of 1.1.0, primarily because of their platform specific nature:
Xamarin.Forms.Labs.Droid
Xamarin.Forms.Labs.iOS
Xamarin.Forms.Labs.WP
Xamarin.Forms.Labs.Services.Serialization.ServiceStackV3
Xamarin.Forms.Labs.Services.Serialization.ServiceStackV3.Droid
Xamarin.Forms.Labs.Services.Serialization.ServiceStackV3.iOS
Xamarin.Forms.Labs.Services.Serialization.ServiceStackV3.WP8
Xamarin.Forms.Labs.Services.TinyIOC.Droid
Xamarin.Forms.Labs.Services.TinyIOC.iOS
Xamarin.Forms.Labs.Services.TinyIOC.WP8

Information about the contents and features of Xamarin.Forms.Labs can be found on our wiki here:
Xamarin.Forms.Labs Wiki

The following bugs/features are part of 1.1.0:
-Changed structure of Nuget packages to remove platform specific packages.
-Corrected references in Nuget packages.
-XML documentation and source added to Nuget packages.
-ImageButton with no size specified no longer throws an exception.
-WebImage control added with Android and iOS implementations.
-Fixed Json serializer so default settings are no longer null.
-Changed ImageButton to use the standard Xamarin.Forms Source property for the image to display instead of a string Image property.  The Image property was removed as a result.  This is a breaking change.
- Fixed issue with HybridWebView callback not being called.
- Corrected issue where ViewFactory did not support mutliple Views with the same view model.
-Renderer support for Windows Phone added for the ExtendedLabel, ExtendedTextCell and the ExtendedViewCell.
- Renderer support for Android added for the ExtendedViewCell.
- Platform specific font properties on the ExtendedLabel are depreciated.
- RelayCommand depreciated.

If  you would like to know more about the Xamarin.Forms.Labs team, would like to contribute functionality, comment or enter a bug you can always go to our repository on GitHub: Xamarin-Forms-Labs.

Thanks and happy coding!

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.