Templated Controls in Xamarin.Forms

Contents

Sometimes you want to create that awesome control, but also want the user to be able to totally customize it… What do you do? Create a templated control!

Xamarin.Forms has a pretty cool view that we can use: TemplatedView (API docs).

More information on how to use templated controls and page can be found in the docs: https://docs.microsoft.com/xamarin/xamarin-forms/app-fundamentals/templates/control-template

So, on this journey to create the best templated control, let us create a cool and exciting “confetti view” like this:

In most cases we would just create a single view, no need to template anything. But, we would also like to try out templated views… and the user may want to place borders, backgrounds or some other fancy thing. If we can support this, then why not?

The Control

So, how does this work? Very simple! All you need is a new type that is going to be the control. Make sure it derives form TemplatedView:

public class SKConfettiView : TemplatedView
{
}

There we go! All done! Thanks for reading.

Just kidding, we need to show how to use it!

This is just like a normal control, so we can add it to our page as we would any other control:

<ContentPage ...
    xmlns:controls="clr-namespace:SkiaSharp.Extended.Controls">

    <controls:SKConfettiView />

</ContentPage>

When we run the app, we will have an invisible control in the app. We can’t see anything, but now we can get started on building up the control.

The Template

Now that we have the files we need, we can add the template. This is straight forward, and we just add a new <ControlTemplate> to the ControlTemplate property:

<ContentPage ...
    xmlns:controls="clr-namespace:SkiaSharp.Extended.Controls"
    xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms">

    <controls:SKConfettiView>
        <controls:SKConfettiView.ControlTemplate>
            <ControlTemplate>
                <skia:SKCanvasView x:Name="PART_DrawingSurface" />
            </ControlTemplate>
        </controls:SKConfettiView.ControlTemplate>
    </controls:SKConfettiView>

</ContentPage>

So, let us have a look at all the moving parts in this XAML.

  1. First, there is the ControlTemplate property. This is just where we place the new template that we want to use for the control.
  2. Next, there is the <ControlTemplate> element. Just like with a data template, the actual views we want to add to the new control must go inside one of these.
  3. Now, there is <skia:SKCanvasView> element. This is just what we are going to use as our control. Not much now, but when a user overrides this template, they may want to wrap this is a frame to get a nice border.
  4. Finally, there is the x:Name="PART_DrawingSurface" attribute. This is what we will use to talk to the actual controls inside that we care about. In our control, we just care about the drawing surface, so we give that a name. If the user adds some cool frame, then we don’t need to worry about that.

As a demonstration of all this and a customization, I take this XAML (the red background is just so that we can see the surface):

<StackLayout>
    <Label Text="Plain control" Margin="20" />

    <controls:SKConfettiView Margin="20">
        <controls:SKConfettiView.ControlTemplate>
            <ControlTemplate>
                <skia:SKCanvasView x:Name="PART_DrawingSurface"
                                   BackgroundColor="Red" />
            </ControlTemplate>
        </controls:SKConfettiView.ControlTemplate>
    </controls:SKConfettiView>

    <Label Text="Custom control" Margin="20" />

    <controls:SKConfettiView Margin="20">
        <controls:SKConfettiView.ControlTemplate>
            <ControlTemplate>
                <Frame Padding="20">
                    <skia:SKCanvasView x:Name="PART_DrawingSurface"
                                       BackgroundColor="Red" />
                </Frame>
            </ControlTemplate>
        </controls:SKConfettiView.ControlTemplate>
    </controls:SKConfettiView>
</StackLayout>

And it renders like this:

Even though we have two different controls with two different view structures, we still have the same essential control because both have the core element in the template:

<skia:SKCanvasView x:Name="PART_DrawingSurface" />

The Code Behind

Because we are making a control that we actually want to do something, we need some code. To do this, we hop on over to our C# code file.

The way templated controls work is very similar to other controls, but with one additional feature – they allow the actual control to use a template. But, this means that anyone can go and put anything in that template. So, how do we get access to those views in the template? By listening for when the template is applied.

When a template is applied to a control, there is a method (OnApplyTemplate) that we can override and start to access the things in the template by name. For example, in our control template, we have a SkiaSharp SKCanvasView that is named PART_DrawingSurface. Now, in our code file, we can use the GetTemplateChild to get that child view:

public class SKConfettiView : TemplatedView
{
    protected override void OnApplyTemplate()
    {
        // get the child
        var templateChild = GetTemplateChild("PART_DrawingSurface");
        if (templateChild is SKCanvasView canvasView)
        {
            // subscribe to the paint even of the child so we can redraw
            canvasView.PaintSurface += OnPaintSurface;
        }
    }

    private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
    {
        e.Surface.Canvas.Clear(SKColors.Green);

        // TODO: draw the real thing
    }
}

When the framework determines it is time to build the actual control we see, it will invoke the OnApplyTemplate method, which we can then override to do things, such as subscribe to events or set properties. In our confetti control we are making, we just want to subscribe to the PaintSurface event and draw the confetti.

The GetTemplateChild method should only be called after the OnApplyTemplate method has been called.

If we run the code now, you can see that in both controls, the canvas has drawn a nice green background – even the one that has some weird frame around it! And this green is drawn from the event that we just subscribed to in the OnApplyTemplate method.

The Library

All done! Even though the confetti is not being drawn, our control is ready to package up. So, how do we do that? Again, very simple! We just create a new Xamarin.Forms class library and move the control there.

One this to remember is that when moving a control out of the main app project, we will have to update the XML namespaces and add the assembly= part:

<!-- from -->
xmlns:controls="clr-namespace:SkiaSharp.Extended.Controls"

<!-- to -->
xmlns:controls="clr-namespace:SkiaSharp.Extended.Controls;assembly=SkiaSharp.Extended.Controls"

Once the control is moved, we add a reference to this new library in our app, and make sure that it all works again.

However, if we package this library up and push to NuGet, every single developer out there will have to create their own control template. This is not how we want anyone to live! So, we ship our own template in the library!

The Resources

We could do a few things and place templates in a few places, but we want the control to use our default theme, but also allow for custom templates. So, in order to do this nicely, we can create a custom resource dictionary in our library. I decided to follow the pattern of UWP and WPF and place our “generic” control template in the “Themes\Generic.xaml” file.

Because there is no “resource dictionary” item template in Visual Studio, I just created a new XAML page and replaced the contents of both files.

In the XAML file, I specify the dictionary:

<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    xmlns:local="clr-namespace:SkiaSharp.Extended.Controls"
                    x:Class="SkiaSharp.Extended.Controls.Themes.Generic">
</ResourceDictionary>

In the C# code-behind, I change the base type:

public partial class Generic : ResourceDictionary
{
    public Generic()
    {
        InitializeComponent();
    }
}

Next, we need to move the template from the first control in our page into the resource dictionary XAML file. I went with three new entries:

  1. The new control template.
  2. The explicit style that applies that control template. This explicit style allows for extensions using the BasedOn property of styles.
  3. The implicit style that will apply our explicit style to all of the instances of our control.
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    xmlns:local="clr-namespace:SkiaSharp.Extended.Controls"
                    xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
                    x:Class="SkiaSharp.Extended.Controls.Themes.Generic">

    <!-- the control template for SKConfettiView -->
    <ControlTemplate x:Key="SKConfettiViewControlTemplate">
        <skia:SKCanvasView x:Name="PART_DrawingSurface" />
    </ControlTemplate>

    <!-- the explicit style that allows for extension -->
    <Style x:Key="SKConfettiViewStyle" TargetType="local:SKConfettiView">
        <Setter Property="ControlTemplate"
                Value="{StaticResource SKConfettiViewControlTemplate}" />
    </Style>

    <!-- the implicit style that applies to all controls -->
    <Style TargetType="local:SKConfettiView"
           BasedOn="{StaticResource SKConfettiViewDefaultStyle}" />

</ResourceDictionary>

This is a bit more XAML as we could have just created a single implicit style that also has the control template, but the multiple parts allow for multiple extension points and for reuse of parts if need be.

In order to test all this fancy new XAML, we can update our page to only provide a custom template for the first control:

<StackLayout>
    <Label Text="Plain control" Margin="20" />

    <controls:SKConfettiView Margin="20" />

    <Label Text="Custom control" Margin="20" />

    <controls:SKConfettiView Margin="20">
        <controls:SKConfettiView.ControlTemplate>
            <ControlTemplate>
                <Frame Padding="20">
                    <skia:SKCanvasView x:Name="PART_DrawingSurface" BackgroundColor="Red" />
                </Frame>
            </ControlTemplate>
        </controls:SKConfettiView.ControlTemplate>
    </controls:SKConfettiView>
</StackLayout>

When we run the app now, the first control is missing everything! What has happened? Is all lost? No! This is because the app does not know about this fancy new style we just created.

The Registration

There are a couple of ways to solve this. First we could just import the resource dictionary into the page or into the app. Because I want this style to apply to all parts of my app, I went with the app resources:

<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:themes="clr-namespace:SkiaSharp.Extended.Controls.Themes;assembly=SkiaSharp.Extended.Controls"
             x:Class="SkiaSharpDemo.App">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <themes:Generic />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

We are basically done now. No matter what we do, we are importing our styles and templates, and we also allow for total customization.

But.

We had to do work to get the styles to apply! We never want to do work. If we ship this out to NuGet, every single developer will forget to add the styles, we will get a new issue on our repository, we will be sad! No! We want magic!

In UWP and WPF, the path that we used for our XAML resources (Themes\Generic.xaml) is actually pretty special. So special in fact they have a docs page on this:

Theme-level dictionaries are stored in a subfolder named Themes. The files in the Themes folder correspond to themes. For example, you might have Aero.NormalColor.xaml, Luna.NormalColor.xaml, Royale.NormalColor.xaml, and so on. You can also have a file named Generic.xaml. When the system looks for a resource at the themes level, it first looks for it in the theme-specific file and then looks for it in Generic.xaml.

Now, it would be wonderful if Xamarin.Forms also was able to do something like this, wouldn’t it? Well, I agree! So I just opened an issue for this exact reason. I will try get it implemented.

But.

There is always a but! What about today? We can’t wait for some feature to ship! We need that confetti, now! We could throw up our hands and say “oh, well, ’tis what it is”. But we won’t! We come up with solutions!

The Hack

One way that seems to work just fine is to register the theme inside the control. And, we can do this by inserting our theme in the very same MergedDictionaries property that we just used.

Before we start hacking, we can remove that pesky code we just added to the App.xml and we will see our control disappear again. But not to worry, we will use magic to fix it.

This magic is pretty straight forward, in the Generic resource dictionary with out styles, we add a nice method to the code-behind:

public partial class Generic : ResourceDictionary
{
    private static bool registered;

    public Generic()
    {
        InitializeComponent();
    }

    internal static void EnsureRegistered()
    {
        // don't do extra work
        if (registered)
            return;

        // get the dictionary if we can
        var merged = Application.Current?.Resources?.MergedDictionaries;
        if (merged != null)
        {
            // check to see if we are added already
            foreach (var dic in merged)
            {
                if (dic.GetType() == typeof(Generic))
                {
                    registered = true;
                    break;
                }
            }

            // if we are not added, add ourselves
            if (!registered)
            {
                merged.Add(new Generic());
                registered = true;
            }
        }
    }
}

Finally, we can call this method in our control’s constructor:

public SKConfettiView()
{
    Generic.EnsureRegistered();
}

Only now we are done! We now have a very nice library that works automatically and is awesome for customization.

Embedding Xamarin.Forms in Native Apps

Xamarin.Forms is really cool! It allows you to almost write your app once and then compile for many platforms. With Xamarin.Forms, you can quickly produce multiple apps, but, you can also take advantage of some awesome features – such as cross-platform UIs in XAML, data binding, sharing code and much more.

Xamarin.iOS, Xamarin.Android and UWP are also fantastic! You can get 100% of the platform APIs and you can produce apps that are super fast and polished.

Most of the time you have to choose between one or the other. Sure, you can do custom renderers or effects in Xamarin.Forms to get more platform API access. And sure, you can use frameworks that help you share code with native Xamarin and Windows apps. Before today, you had one of two choices:

  • Xamarin.Forms for 100% shared code and UI
  • Xamarin/Windows Native for polished, platform-specific APIs and UIs

But… why not have both? Recently Xamarin/Microsoft shared on the blog that Xamarin.Forms can be embedded into native apps. This means that you can take your existing (or new) native app and just drop in a Xamarin.Forms page without doing anything special. You lose nothing, and gain a whole lot more. You can still use XAML, data binding, converters, effects and everything else that comes with Xamarin.Forms – but now from within you native app code and UI.

I have a nice sample app that you can pull from GitHub. Be sure to check it out and let me know what you think of this new functionality. I will try and keep that repository up to date with any new developments.

Xamarin.Forms embedded in native apps

Getting the Bits

To embed Xamarin.Forms into your native app, you just need to install a preview NuGet from the Xamarin.Forms pre-release feed:

https://www.myget.org/F/xamarinforms-dev/api/v3/index.json

Then, you want to install the 3.0.0.100-embeddingpreview NuGet into any Xamarin.Forms projects as well as into the native app projects.

That is it! Now you should be able to create your shared page.

If you have any questions or comments you can do this using the Xamarin discussion (Preview: Xamarin.Forms Embedding) on the forums.

It is important to note that this is a pre-release and functionality may change with subsequent releases.

Creating Shared Pages

Let’s take an example of an existing app that you want to add the ability to register and/or login to save user preferences on some cloud service – such as Azure. Your app is out and in the wild, users are loving it, and, you have just received feedback that they want to login across devices and keep their preferences.

What do you do? Well you could create a few screens for each platform and then share some code. But, as all the login screens are going to look the same and work the same, why not do everything in shared XAML?

So, the first thing we can do is to either create a shared project, a .NET Standard library or even a PCL. We then just need to install the pre-release Xamarin.Forms NuGet. Finally, we can go ahead an add a new XAML page. Just right-click and do “Add” > “New Item…” > “Content Page (XAML)” and then you have a new page.

In this page below, I am using data binding, converters, resource dictionaries and good old XAML markup:

<?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:app="clr-namespace:EmbeddedFormsDemo.Converters"
             x:Class="EmbeddedFormsDemo.Views.LoginPage">
    <ContentPage.Resources>
        <ResourceDictionary>
            <app:NegateConverter x:Key="Negate" />
            <app:NullInvisibilityConverter x:Key="NullInvisibility" />
        </ResourceDictionary>
    </ContentPage.Resources>
    <ScrollView>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <StackLayout Spacing="12" Grid.Row="1">
                <StackLayout.Padding>
                    <OnPlatform x:TypeArguments="Thickness" 
                                Android="24" iOS="24" WinPhone="0" />
                </StackLayout.Padding>
                <Label Text="Enter any username/password:"
                       HorizontalTextAlignment="Center"
                       FontSize="12" />
                <Entry Keyboard="Email" 
                       Placeholder="username or email"
                       Text="{Binding UserName}" 
                       HorizontalTextAlignment="Center"
                       IsEnabled="{Binding IsBusy, Converter={StaticResource Negate}}" />
                <Entry IsPassword="True" 
                       Placeholder="password" 
                       Text="{Binding Password}" 
                       HorizontalTextAlignment="Center"
                       IsEnabled="{Binding IsBusy, Converter={StaticResource Negate}}" />
                <Button Text="Log In" 
                        Command="{Binding LoginCommand}"
                        IsEnabled="{Binding IsBusy, Converter={StaticResource Negate}}" />
                <Label Text="{Binding ErrorMessage}" 
                       HorizontalTextAlignment="Center"
                       FontSize="12"
                       TextColor="Red" 
                       IsVisible="{Binding ErrorMessage, Converter={StaticResource NullInvisibility}}" />
                <ActivityIndicator IsRunning="{Binding IsBusy}" 
                                   IsVisible="{Binding IsBusy}" />
            </StackLayout>
        </Grid>
    </ScrollView>
</ContentPage>

Then, in our code-behind, we can write out code to work with the UI. Here we have made use of several features such as binding, commands, messaging and async/await:

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LoginPage : ContentPage
{
    public const string LoginMessage = "login";

    private readonly JsonPlaceholderApi api;
    private string errorMessage;

    public LoginPage()
    {
        InitializeComponent();

        // initialize our fake services
        api = new JsonPlaceholderApi();

        // set up the data binding
        LoginCommand = new Command(OnLogin);
        BindingContext = this;
    }

    private async void OnLogin()
    {
        IsBusy = true;

        // reset errors
        ErrorMessage = "";
        // try logging in with our fake services
        var user = await api.LoginAsync(UserName, Password);
        if (user == null) {
            // there was an error
            ErrorMessage = "There was a problem logging in.";
        } else {
            // let the app know we are finished
            MessagingCenter.Send(user, LoginMessage);
        }

        IsBusy = false;
    }

    public string ErrorMessage
    {
        get { return errorMessage; }
        set { errorMessage = value; OnPropertyChanged(); }
    }

    // we don't want to save this as the user types
    public string UserName { get; set; }

    // we don't want to save this at all
    public string Password { get; set; }

    // the login button uses this
    public ICommand LoginCommand { get; }
}

That is basically all we need to do in order to create our UI. The JsonPlaceholderApi type just makes use of http://jsonplaceholder.typicode.com to get some random data, the User type is just a simple POCO object and the converters are basic IValueConverter implementations.

Displaying Shared Pages in Native Apps

Once we have created our shared Xamarin.Forms page, we can just instantiate out Xamarin.Forms page and then ask for the native version.

On Android, the CreateFragment extension method will return a native Android Fragment, which we can then use as we would any other fragment. On iOS, we use the CreateViewController extension method and we will get out a native UIViewController that, again, we can use as we would any other view controller. And finally, on Windows, we use the CreateFrameworkElement extension method to get out a native FrameworkElement that we can also uses as any other element.

There are two steps to obtaining this native container, first we make sure Xamarin.forms is initialized:

// Android
Xamarin.Forms.Forms.Init(this, null);

// iOS
Xamarin.Forms.Forms.Init();

// Windows
// the `e` from `Application.OnLaunched(LaunchActivatedEventArgs e)`
Xamarin.Forms.Forms.Init(e);

Once that is done, we can then get the native page:

// create the Xamarin.Forms page for all platforms
var formsPage = new MyFormsPage();

// Android - get the native Fragment
var nativeFragment = formsPage.CreateFragment(this);

// iOS - get the native UIViewController
var nativeController = formsPage.CreateViewController();

// Windows - get the native FrameworkElement
var nativeElement = formsPage.CreateFrameworkElement();

Now that we have the native view, we can just go ahead and use it as if it was created using the traditional mechanisms.

Xamarin.Android and Xamarin.Forms

When using Xamarin.Forms with an Android app, we are going to need to handle the native Fragment that represents the page. To this end, our Android app is using a native .axml layout with a FrameLayout that will hold all our fragments:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <!-- the other bits of UI -->
    <FrameLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/frameLayout" />
    <!-- some more UI -->
</RelativeLayout>

Then, in our main activity code, we load this UI as normal and everything works as it usually does. When we want to show our new XAML page at some point, all we need to do is to just make sure Xamarin.Forms is initialized and then instantiate the page before navigating to it:

using Xamarin.Forms.Platform.Android;

public void DisplayLogin()
{
    if (!Xamarin.Forms.Forms.IsInitialized)
    {
        // initialize Xamarin.Forms before we use it
        Xamarin.Forms.Forms.Init(this, null);

        // we want to listen to the messaging center
        Xamarin.Forms.MessagingCenter.Subscribe(this, LoginPage.LoginMessage, (User user) =>
        {
            // update the app
            CurrentUser = user;

            // go back to the main page
            FragmentManager.PopBackStack();
        });
    }

    // create the login page (Xamarin.Forms ContentPage)
    var loginPage = new LoginPage();

    // get hold of the native Android fragment that represents the Xamarin.Forms page
    var loginFragment = loginPage.CreateFragment(this);

    // show the login screen (native Xamarin.Android)
    FragmentManager
        .BeginTransaction()
        .AddToBackStack(null)
        .Replace(Resource.Id.frameLayout, loginFragment)
        .Commit();
}

Xamarin.iOS and Xamarin.Forms

If we want to use Xamarin.Forms in our iOS app, we will be showing a new UIViewController. We first need to make sure that Xamarin.Forms is initialized and then we can just go ahead and create the desired page. We then get the native view controller that can be presented using all the existing means:

using Xamarin.Forms;

partial void OnLoginClicked(UIButton sender)
{
    if (!Xamarin.Forms.Forms.IsInitialized)
    {
        // initialize Xamarin.Forms before we use it
        Xamarin.Forms.Forms.Init();

        // we want to listen to the messaging center
        Xamarin.Forms.MessagingCenter.Subscribe(this, LoginPage.LoginMessage, (User user) =>
        {
            // update the app
            User = user;

            // go back to the main page
            NavigationController.PopViewController(true);
        });
    }

    // create the login page (Xamarin.Forms ContentPage)
    var loginPage = new LoginPage();

    // show the login screen
    var viewController = loginPage.CreateViewController();
    NavigationController.PushViewController(viewController, true);
}

Windows UWP and Xamarin.Forms

When we want to show a Xamarin.Forms page within our native UWP app, we will work with a FrameworkElement. Like with all the other platforms, we have to make sure that Xamarin.Forms is initialized and then just create the page. Once we have obtained the native element, we can just place it anywhere we need it to be. In this example, I am placing my view in a Flyout as the it is being opened:

using Xamarin.Forms;

private void OnLoginFlyoutOpening(object sender, object e)
{
    var flyout = sender as Flyout;

    if (!Xamarin.Forms.Forms.IsInitialized)
    {
        // initialize Xamarin.Forms before we use it
        Xamarin.Forms.Forms.Init(App.LastLaunchEventArgs);

        // we want to listen to the messaging center
        Xamarin.Forms.MessagingCenter.Subscribe(this, LoginPage.LoginMessage, (User user) =>
        {
            // update the app
            User = user;
            // show some message for some random reason
            WelcomeText = $"Welcome back {user.Name}!";

            // hide the login screen
            flyout.Hide();
        });
    }

    // create the login page (Xamarin.Forms ContentPage)
    var loginPage = new LoginPage();

    // set the native dialog to contain the shared login
    var loginElement = loginPage.CreateFrameworkElement();
    flyout.Content = new Frame
    {
        Content = loginElement,
        Width = 300,
        Height = 200
    };
}

Summary

In this fairly long post, we looked at how to quickly embed a Xamarin.Forms page into an existing native app. The process is easy and painless, and will only get better.

You can download the full code from my GitHub repository and try it out for yourself. And maybe have a look at your app and see if some of those screens that are exactly the same could use Xamarin.Forms.

You can also check out some links:

Some other people also have blogs:

And, then there is code:

Cross-Platform Mobile Apps

One of my current projects at Jam Warehouse is to port the app, You & Your Puppy, from the iPhone/iPad to the Android devices (Smart-phones/Tablets). Over the month of November, I have completed the first version and is now undergoing review at our client, DogsTrust.

The App

It is my first ever Android app as well as my first ever mobile application. The current version that is being tested is written in Java. It is also my first Java application as well. (Lots of firsts πŸ™‚ I then re-created the app using MonoDroid using C# – this is still only been tested in the emulator as I haven’t bought the MonoDroid licence yet. I am currently porting the app to the Windows Phone as well. I opted to use the MonoDroid/MonoTouch instead of native languages for several reasons:

  • I am a total .NET addict – as you can see by the title of this blog πŸ™‚
  • Code re-usability – Write once, test once, deploy n times, get n money!
  • Automatic memory management – who wants to dispose objects manually anyway?
  • Make use of advanced C# features and short-cuts – I can churn out some serious code…
  • And who wants to learn Objective-C or use Eclipse – C# and Visual Studio for me!

The Code

Code re-usability is really cool if you are having to write apps for 4 or more different platforms:
  1. Java for Android
  2. Objective-C for iPhone
  3. C# for Windows Phone
  4. Java for Blackberry
  5. HTML 5 for everything else
We could just use PhoneGap or something similar for all the devices, but why make it too easy? Lets go native! C# for Android/iPhone/Windows Phone and HTML5 for all the other players in the game πŸ™‚
We all know (or at least I hope we do) that the iPhone has a “slightly” different layout to the Android phone. So, how do I get this “write once” thing in the list? Well, I didn’t tell the whole story as you can’t really πŸ™‚

The Database

I do have an element of “write once”, but not for the entire app, just the non-visual part. As all the versions are going to be reading from a database and showing images/videos, we can use the same code for this part.
But there’s a catch, as always, what database do we use? We can use xml files for the data, but who wants to? We are advanced guys here, we use a database when a database is “needed”, just now we have to expand this app. As the original database was a SQLite one, so I used that. No complaints there as it is one of the best (if not the best) for mobile development. But now there’s another catch – this time with Microsoft: Windows Phones don’t have SQLite. And you can’t put the C++ version on the device either. So what do we do? Do we use a different database especially for this? No! we use the C# version called System.Data.Sqlite – much easier than maintaining two databases πŸ™‚ – Don’t we all love this thing called C#?

The Data

OK, so we have the database. Now we need to read this data. We can of course write SQL queries and use the SqliteCommand classes, but who wants to use strings in a strongly typed language? Not me. After a little bit of research a found SQLite-NET a very small ORM for the iPhone. I was saved from strings! It also works on the Android phones! But maybe you noticed that I didn’t say anything about the Windows Phone, That’s right it doesn’t work! – Yup, Microsoft is making things hard for me, but I will not yield, not even to the global monopolistic software company that churns in the billions hourly! No! I re-write the SQLite-NET library (or just tweak it slightly). SQLite-NET was originally using the [DllImport] attributes (P/Invoke) to gain access to the sqlite3.dll that is stored somewhere on the filing system on each device. But for those who don’t know, no P/Invoke on the Windows Phone.
I converted the ORM to use the native SqliteCommand and SqliteConnection instead of the P/Invokes. I don’t actually know why the guys recreated those two classes when they could have just used them instead. Maybe there’s something I don’t know πŸ™‚ I practically halved the code and reduced the amount of possible bugs.
So, now we are up and running with the database, a strongly typed ORM and I can use LINQ to SQLite as well!

The UI

Now we get to the basic functionality of the app, showing the information to the user! This is where the not-so-well-documented MonoCross library comes into play. (I’m hoping to buy this book Professional Cross-Platform Mobile Development in C# that’s supposed to show me the best way to use the framework). All the navigation between screens is taken care of and all getting the data ready for showing is done. Now, the user wants to click (or touch) on something, that’s why they bought the app in the first place :). Here’s the “snag”. We want cross-platform, but we also want the app to look like it was especially designed for each device – although it wasn’t. So we recreate each GUI screen for each platform, but we don’t use any non-platform specific code and if we have to, we abstract it!
In order to get the UI updates and things to happen, I subscribe to events that are raised on the Model, and then perform the necessary tasks to show each page to the user. I also catch all the events from the UI and perform the respective actions on the Model. For example, when the user clicks a link/button the command is sent to my middle-ground library. This library checks to see if the command is a navigation command. If it is, it then notifies the MonoCross framework that we want to navigate to wherever. If it is an action command, we inform the Model of the action we want to perform. The model is then updated, and the Model then fires an event that the UI will know that it has to change.
When we navigate, each screen has an abstracted helper class that receives the command as a parameter, which is then processed and used to inform the Model of the actions required (if any). When the actions are finished, the new screen is displayed.

The Diagram

Here is a basic diagram of the flow of the app from the time the user presses start and just how little code is actually needed for the platform specific areas.
As this diagram shows, most of the code is re-used.
As this diagram shows, most of the code is re-used.
And to prove it (mostly to myself), I was able to port the basic functionality of the Android version of the app to the Windows Phone in about 2-3 hours. There was almost no need for changes to the shared code. A bit of tweaking did improve it, but all the platforms – and future ones – will benefit.