Monday, February 20, 2017

Configuring Xamarin.Forms.UWP to use Caliburn Micro

Update 2017/10/15: This walk through is now available as a Visual Studio Template! More details here: https://marketplace.visualstudio.com/items?itemName=BryanBCook.XamarinFormsCaliburnMicroStarterKit

This is the fourth post in this series where we’re configuring a Xamarin.Forms application to use my favourite MVVM framework, Caliburn.Micro. Today’s post will focus on configuring a Universal Windows Application (UWP).

Here’s links to the previous posts:

  1. Getting Started with Xamarin.Forms and Caliburn.Micro
  2. Configuring Xamarin.Forms.Droid to use Caliburn.Micro
  3. Configuring Xamarin.Forms.iOS to use Caliburn.Micro

If you’ve ever built a WPF application, it may seem strange that we’re not going to build XAML pages for our app. Instead, we’re going to leverage the XAML defined in our PCL so that we can reuse as much of the user-interface logic as possible. The pages that we’re adding here act purely as an entry point into our Xamarin.Forms application.

Change App.xaml

Caliburn provides a base application class that we’re going to extend. In our App.xaml, we’ll change the root element to CaliburnApplication, like so:

<cm:CaliburnApplication
    x:Class="XF.CaliburnMicro1.UWP.App"
    xmlns:cm="using:Caliburn.Micro"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    RequestedTheme="Light">
    
</cm:CaliburnApplication>

Configure IoC Container

Next we’ll add the plumbing logic that we’ve added to the other applications to initialize our platform-specific IoC container. Caliburn.Micro has a customized container for UWP, called WinRTContainer, that contains a few helpful registration methods.

I’ve replaced the default App.xaml.cs with this simplified version:

namespace XF.CaliburnMicro1.UWP
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using Windows.ApplicationModel.Activation;
    
    using Caliburn.Micro;
    
    using XF.CaliburnMicro1.ViewModels;

    sealed partial class App
    {
        private WinRTContainer _container;

        public App()
        {
            InitializeComponent();
        }

        protected override void Configure()
        {
            _container = new WinRTContainer();
            _container.RegisterWinRTServices();

            _container.Singleton<XF.CaliburnMicro1.App>();
        }

        protected override void OnLaunched(LaunchActivatedEventArgs args)
        {
            if (args.PreviousExecutionState == ApplicationExecutionState.Running)
                return;

            Xamarin.Forms.Forms.Init(args);

            // loads our MainPage as the root frame
            DisplayRootView<MainPage>();
        }

        #region IoC Overrides
        protected override void BuildUp(object instance)
        {
            _container.BuildUp(instance);
        }

        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return _container.GetAllInstances(service);
        }

        protected override object GetInstance(Type service, string key)
        {
            return _container.GetInstance(service, key);
        }

        protected override IEnumerable<Assembly> SelectAssemblies()
        {
            return new[]
            {
                GetType().GetTypeInfo().Assembly,
                typeof(MainViewModel).GetTypeInfo().Assembly
            };
        } 
        #endregion
    }
}

Eventually, you’ll want to provide additional logic for initialization and suspension specific to UWP, but for now OnLaunched simply launches MainPage as the root window for our Xamarin.Forms app.

Customize MainPage

The Xamarin.Forms template includes a MainPage that is optimized for Xamarin.Forms, we simply need to load our App from the Singleton defined in Caliburn’s IoC Container.

namespace XF.CaliburnMicro1.UWP
{
    using Caliburn.Micro;

    public sealed partial class MainPage
    {
        public MainPage()
        {
            this.InitializeComponent();

            LoadApplication(IoC.Get<XF.CaliburnMicro1.App>());
        }
    }
}

Build and Run

Finally, before we give our UWP a spin, we need to make a minor change the deployment to the Solution.

  1. Change the start-up application to XF.CaliburnMicro.UWP (Universal Windows) and set the target platform to Any CPU
  2. Open the Configuration Manager for the solution

    uwp_configuration
  3. Specify that the solution should Deploy for Debug configuration

    image
  4. Specify Local machine as the target Device
  5. Compile and Run (F5)

You should now see our familiar View / ViewModel from the PCL should be displayed.

Windows 10:

image

Windows 10 Mobile:

image

Next Steps

We’re now set to handle iOS, Android and Universal Windows Apps using Caliburn.Micro, so that’s all for this series. Follow my Xamarin.Forms label to see more posts in this space.

Until then… Happy Coding.

submit to reddit

Configure Xamarin.Forms iOS to use Caliburn.Micro

Update 2017/10/15: This walk through is now available as a Visual Studio Template! More details here: https://marketplace.visualstudio.com/items?itemName=BryanBCook.XamarinFormsCaliburnMicroStarterKit

We’re continuing with our series of getting started with Xamarin.Forms and Caliburn.Micro, check out these previous posts if you’re interested:

Today, we’ll set up our Xamarin.Forms iOS project. Unlike the Droid project, we can get our project up and running with very few changes to the default template. So we'll start this post with configuring our Visual Studio instance to talk to our Mac.

And before you ask, unfortunately, although it's C# you absolutely need a Mac to compile and run the solution. On the plus side, you can do most of your development on your PC.

Verify Prerequisite Software on your Mac

You will need the following installed on your Mac:

  • XCode 7+
  • Xamarin Studio
  • Xamarin.iOS SDK

Setup your Mac for Remote Access

Before you can configure Visual Studio to use your Mac as a Build Host, you'll need to configure your Mac to allow users to remotely login to your machine. You can get the full details following this post, but in short we can simply set up our mac using the following:

  • Press Command ⌘ + Space to bring up Spotlight Search
  • Type Remote Login to open Sharing
  • Enable Remote Login and then specify Allow access for: All Users 

Configure your Build Host

Now that your Mac will accept remote logins, we can now configure Visual Studio on the PC to use your Mac as a build host.

  • From your PC, open our XF.CaliburnMicro project.
  • In the toolbar, click on the Xamarin Mac Agent icon (also Tools –> iOS –> Xamarin Mac Agent):

    mac_agent_toolbar

  • If your mac is running, it should appear in the dialog:

    image

  • Select your Mac, click Connect…
  • You’ll be prompted to login, so provide the credentials you use when logging into the Mac
  • Once connected, the toolbar changes colour and provides the option to launch the simulator:

    mac_agent_connected_toolbar

Note this assumes that both the Mac and the PC are on the same network and that all required software is installed and configured for use.

Create and configure IoC Container

Now that we have our environment dependencies out of the way, we can turn our attention to modifying the template code to leverage Caliburn.Micro.

Similar to our Droid project, we need to set up our IoC container. For iOS, we do this by using a CaliburnAppDelegate which looks almost identical to the Application class we created for Android.

namespace XF.CaliburnMicro1.iOS
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    
    using Caliburn.Micro;
    
    using XF.CaliburnMicro1.ViewModels;

    class CaliburnAppDelegate : CaliburnApplicationDelegate
    {
        private SimpleContainer _container;

        public CaliburnAppDelegate()
        {
            Initialize();
        }

        protected override void Configure()
        {
            _container = new SimpleContainer();
            _container.Instance(_container);

            _container.Singleton<App>();

            // TODO: Register all platform services here
        }

        protected override void BuildUp(object instance)
        {
            _container.BuildUp(instance);
        }

        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return _container.GetAllInstances(service);
        }

        protected override object GetInstance(Type service, string key)
        {
            return _container.GetInstance(service, key);
        }

        protected override IEnumerable<Assembly> SelectAssemblies()
        {
            return new[]
            {
                GetType().Assembly,
                typeof(MainViewModel).Assembly
            };
        }
    }
}

We then modify the AppDelegate to leverage our CaliburnAppDelegate, and change the start-up routine to use our App singleton.

namespace XF.CaliburnMicro1.iOS
{
    using Foundation;
    using UIKit;
    
    using Caliburn.Micro;
    using Xamarin.Forms.Platform.iOS;

    [Register("AppDelegate")]
    public partial class AppDelegate : FormsApplicationDelegate
    {
        private readonly CaliburnAppDelegate appDelegate = new CaliburnAppDelegate();

        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();

            LoadApplication(IoC.Get<App>());

            return base.FinishedLaunching(app, options);
        }
    }
}

Build and Run

To run your application, you’ll need to change the start project, select a target and the Device. Here I’ve selected XF.CaliburnMicro.iOS project, specified the iPhoneSimulator target and the iPhone 6s iOS 10.x simulator.

build_and_run

If everything worked, you should now see our Xamarin.Forms app backed by our Caliburn.Micro ViewModel.

Next Steps

So there you go! We’ve got both Android and iOS projects running. Our next post we’ll continue down this path and our Xamarin.Forms project to leverage the Universal Windows Platform which will target tablets, phones and PCs.

submit to reddit

Sunday, February 05, 2017

Configure Xamarin.Forms Droid to use Caliburn.Micro

Update 2017/10/15: This walk through is now available as a Visual Studio Template! More details here: https://marketplace.visualstudio.com/items?itemName=BryanBCook.XamarinFormsCaliburnMicroStarterKit

In my last post, we created a Xamarin.Forms project and added necessary components for taking advantage of my favourite MVVM framework, Caliburn.Micro. However, the solution didn't compile because we needed to make some platform specific changes. Today we'll address this.

Add an Application

The main entry point into our Android application is the Application class as defined in the AndroidManifest.xml. By default, the Xamarin.Forms template does not include an Application class, but we’ll need one in order to hold some global state.

To register our custom application, we subclass Caliburn’s CaliburnApplication class and mark it with an [Application] attribute:

namespace XF.CaliburnMicro1.Droid
{
    using System;
    using Android.App;
    using Android.Runtime;
    using Caliburn.Micro;

    [Application]
    public class Application : CaliburnApplication
    {
        public Application(IntPtr javaReference, JniHandleOwnership transfer)
              : base(javaReference, transfer)
        {
        }

        public override void OnCreate()
        {
            base.OnCreate();

            Initialize();
        }
    }
}

Create and configure IoC

One of the main features of the CaliburnApplication is to create our platform-specific Inversion of Control (IoC) container and register any platform-specific abstractions.

There are four primary methods that we need to implement in our Application (SelectAssemblies, BuildUp, GetAllInstances, GetInstance). These methods are the backing implementation for Caliburn's IoC:

It's important to note that we register the Xamarin.Forms App as a singleton before we activate it. This is largely because we want the App to have the same lifetime as our Android Application, which avoids a few scenarios where the Android application lifecycle causes the App class to be reinitialized more than once.

Our Application class now looks like this:

namespace XF.CaliburnMicro1.Droid
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    
    using Android.App;
    using Android.Runtime;
    
    using Caliburn.Micro;
    
    using XF.CaliburnMicro1.ViewModels;

    [Application]
    public class Application : CaliburnApplication
    {
        private SimpleContainer _container;

        public Application(IntPtr javaReference, JniHandleOwnership transfer)
              : base(javaReference, transfer)
        {
        }

        public override void OnCreate()
        {
            base.OnCreate();

            Initialize();
        }

        protected override void Configure()
        {
            _container = new SimpleContainer();

            // make the container available for resolution
            _container.Instance(_container);

            // CRITICAL! make sure our Xamarin.Forms App 
            // can only be initilized once!
            _container.Singleton<App>();

            // TODO: Register any Platform-Specific abstractions here
        }

        protected override IEnumerable<Assembly> SelectAssemblies()
        {
            // Get a list of all assemblies that will be used
            // by the IoC container
            return new[]
            {
                GetType().Assembly,
                typeof (MainViewModel).Assembly
            };
        }

        protected override void BuildUp(object instance)
        {
            _container.BuildUp(instance);
        }

        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return _container.GetAllInstances(service);
        }

        protected override object GetInstance(Type service, string key)
        {
            return _container.GetInstance(service, key);
        }
    }
}

Modify MainActivity

The last modification we need to make is a slight change to our existing MainActivity. Rather than instantiating our App class, we resolve it from the IoC container.

namespace XF.CaliburnMicro1.Droid
{
    using Android.App;
    using Android.Content.PM;
    using Android.OS;
    
    using Xamarin.Forms.Platform.Android;
    
    using Caliburn.Micro;

    [Activity(
        Label = "XF.CaliburnMicro1", 
        Icon = "@drawable/icon", 
        MainLauncher = true, 
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : FormsApplicationActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);

            // Resolve the App class to use the singleton we created
            LoadApplication(IoC.Get<App>());
        }
    }
}

Compile and Run

At this point, the android project should be good to go. Set the project as the default target, and run (F5). The project should automatically be configured to deploy the app to the emulator.

Next Steps

So we have our Android application running our Caliburn.Micro flavoured Xamarin.Forms app. The next step is to configure our iOS project with similar customizations.

submit to reddit

Saturday, February 04, 2017

Getting Started with Xamarin.Forms and Caliburn.Micro

Update 2017/10/15: This walk through is now available as a Visual Studio Template! More details here: https://marketplace.visualstudio.com/items?itemName=BryanBCook.XamarinFormsCaliburnMicroStarterKit

Let me say this: Xamarin.Forms is awesome.

Xamarin.Forms is a great way to build native iOS and Android applications using C# and XAML. But if you’re just starting out, you’ll find that the most of the documentation out there, including Charles Petzold’s fantastic book, are lacking great examples on how to do MVVM, which is XAMLs strongest feature.

Last year when Caliburn.Micro finally released version 3.0 with support with Xamarin.Forms I was disappointed that there wasn’t much documentation to support it as well.

So with that said, lets build a Xamarin.Forms app using Caliburn.Micro.

Create Project

Good news here is that Visual Studio will do most of the work for us. To create a blank Xamarin.Forms project, choose File –> New Project. Then select the template Visual C# –> Cross Platform –> Blank App (Xamarin.Forms Portable).

image

If you specify the project name XF.CaliburnMicro, Visual Studio will create several projects:

  • XF.CaliburnMicro (Portable) – This is the PCL library that will hold our Views and ViewModels and core services and interfaces
  • XF.CaliburnMicro.Droid – Our Android application
  • XF.CaliburnMicro.iOS – Our iPhone / iPad application
  • XF.CaliburnMicro.UWP (Universal Windows) – Our Windows 10 desktop/tablet/mobile application.
  • XF.CaliburnMicro.Windows (Windows 8.1) – A Windows 8.1 app, for those of us that didn’t take the free upgrade.
  • XF.CaliburnMicro.WinPhone (Windows Phone 8.1) – A Windows Phone 8.1 Silverlight/XAML application.

Note: You’ll likely be prompted to specify a Windows 10 version. For now, don’t worry about this, just click OK.

Also note that I’m only going to focus on Android, iOS and UWP, so feel free to delete XF.CaliburnMicro.Windows and XF.Caliburn.WinPhone.

Compile and Run

Now would be a good time to make sure everything compiles and your machine is good to go. Xamarin.Forms requires a few dependencies to get up and running, so before we start messing things up this would be a good time for a sanity test.

For simplicity sake, let’s focus only on getting our Android emulator running. If you’re able to see the default Forms page, we’re good.

Moving along…

Reference NuGet Packages

It’s time to add Caliburn.Micro to the project. There are three key NuGet packages we’re interested in:

  • Caliburn.Micro
  • Caliburn.Micro.Core
  • Caliburn.Micro.XamarinForms

What seems a bit counter intuitive is that the Caliburn.Micro package is platform specific, so it cannot be referenced by the PCL library.

The best way to add these to your project is to right-click the solution and choose Manage NuGet Packages for Solution. Search for “Caliburn.Micro.XamarinForms” and add it to all projects. It’ll bring in the Caliburn.Micro.Core dependency.

Search for “Caliburn.Micro.Core” and add it to the PCL “XF.CaliburnMicro (Portable)” project.

Setup basic View and ViewModel

Caliburn uses conventions to automatically map Views to ViewModels and vice-versa, so you’ll need to create two folders Views and ViewModels in the XF.XamarinForms (Portable) project.

The ViewModels\MainViewModel is pretty straight forward:

using Caliburn.Micro;
using System.Runtime.CompilerServices;

namespace XF.CaliburnMicro1.ViewModels
{
    public class MainViewModel : Screen
    {
        private string _mainText;

        public MainViewModel()
        {
           MainText = "Hello World!";
        }         

        public string MainText
        {
            get { return _mainText; }
            set
            {
                SetField(ref _mainText, value);
            }
        }

        protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            if (!object.Equals(field, value))
            {
                field = value;
                NotifyOfPropertyChange(propertyName);
                return true;
            }
            return false;
        }
    }
}

And the Views\MainView.xaml is an empty screen created using Project –> Add New Item –> Forms Xaml Page:

<?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="XF.CaliburnMicro1.Views.MainView">
  <Label Text="{Binding MainText}" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

Introduce Caliburn.Micro

If you’ve worked with Caliburn.Micro in the past, you would normally configure a Bootstrapper as the primary entry point for the application. In Xamarin.Forms, the closest equivalent is the App class that is created in the PCL project. We’ll use it to setup our Inversion of Control (IoC) container and register ViewModels, services and abstractions.

Caliburn.Micro ships with a simple IoC container, aptly named SimpleContainer, which offers simple functionality to register and resolve dependencies. It is possible to replace this container with a different one if you prefer, but in my experience the SimpleContainer does exactly what we need.

namespace XF.CaliburnMicro1
{
    using Caliburn.Micro;
    using Caliburn.Micro.Xamarin.Forms;
    using Xamarin.Forms;
    using XF.CaliburnMicro1.ViewModels;

    public class App : FormsApplication
    {
        private readonly SimpleContainer container;

        public App(SimpleContainer container)
        {
            this.container = container;

            // TODO: Register additional viewmodels and services
            container
                .PerRequest<MainViewModel>();

            Initialize();

            DisplayRootViewFor<MainViewModel>();
        }

        protected override void PrepareViewFirst(NavigationPage navigationPage)
        {
            container.Instance<INavigationService>(new NavigationPageAdapter(navigationPage));
        }

        protected override void OnStart()
        {
            // Handle when your app starts
        }

        protected override void OnSleep()
        {
            // Handle when your app sleeps
        }

        protected override void OnResume()
        {
            // Handle when your app resumes
        }
    }
}

Next Steps

At this point, the solution doesn’t compile, and that’s because we’ve changed the signature of the App class and we’ll have to adjust our iOS, Android and UWP projects to suit. Admittedly, it’s an awkward place to stop a blog post, but there’s a lot more to cover. Over the next few blog posts, we’ll setup the platform specific projects with their Caliburn parts and we’ll update the links below.

submit to reddit