Monday, March 13, 2017

Applying MVVM to Xamarin.Form’s TabbedPage (Updated)

My last post showed how to setup a TabbedPage with separate View/ViewModels for each tab using Caliburn.Micro. The post used a DataTemplateSelector to resolve the tab content which isn’t the preferred technique when working with Caliburn.Micro. Today we’ll use a strategy that is more aligned to Caliburn’s philosophy and is a tiny bit more extensible.

As per the previous post, we used a DataTemplateSelector because the default TabbedPage.ItemTemplate expects a ContentPage and we wanted to use existing ContentPage items as tabs. In reality, this was a situation that occurred because our team had originally developed these pages independently and then wanted to consolidate them into a TabbedPage after the fact. If you don’t plan on navigating to these pages outside of the TabbedPage, we can build up our ContentPage inside the DataTemplate and use Caliburn.Micro’s binding syntax. This approach will require us to change our existing ContentPage(s) into ContentView(s) and since we depended on the Page objects to provide us with Title and Icon information, we’ll need to push this information into our ViewModels.

Modify ViewModels

Let’s introduce a simple interface for our ViewModels that will be tabs in our TabbedPage:

public interface ITabViewModel
{
   string Title { get; }
   string Icon { get; }
   int SortOrder { get; }
}

The changes to the ViewModel are pretty trivial. We simply implement the ITabViewModel interface:

public class Tab1ViewModel : Screen, ITabViewModel
{
   public Tab1ViewModel()
   {
      Tab1Content = "Tab 1 Content";
   }

   public string Tab1Content { get; set; }

   public string Icon => "Tab1.png";

   public int SortOrder => 0;

   public string Title => "Tab1";
}

public class Tab2ViewModel : Screen, ITabViewModel
{
   public Tab2ViewModel()
   {
      Tab2Content = "Tab 2 Content";
   }

   public string Tab2Content { get; set; }

   public string Icon => "Tab2.png";

   public int SortOrder => 0;

   public string Title => "Tab 2";
}

Next, we register our ViewModels in the App using the ITabViewModel signature:

public class App : FormsApplication
{
   private readonly SimpleContainer container;

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

      // TODO: Register additional viewmodels and services
      container
         .PerRequest<Main2ViewModel>()
         .PerRequest<ITabViewModel,Tab1ViewModel>()
         .PerRequest<ITabViewModel,Tab2ViewModel>()
         ;

      Initialize();

      DisplayRootViewFor<Main2ViewModel>();
   }
	
   // snip...
}

Lastly, we can change our Screen Conductor to be less coupled to the specific ViewModels:

public class Main2ViewModel : Conductor<Screen>.Collection.OneActive
{
   public Main2ViewModel(IEnumerable<ITabViewModel> tabs)
   {
      if (tabs.Any())
      {
         foreach(var tab in tabs.OrderBy(i => i.SortOrder))
         {
            Items.Add((Screen)tab);
         }

         ActivateItem(Items[0]);
      }
   }
}

Modify Views

The changes to the view are also very trivial. We’re simply changing them from ContentPage to ContentView. There are a few attributes that aren’t available (Title, Icon) so we’ll simply remove them if present.

And then finally, we can remove our DataTemplateSelector and use the View.Model attached property to resolve our ContentView:

Main2View.xaml
<?xml version="1.0" encoding="UTF-8"?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" 
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
            xmlns:cm="clr-namespace:Caliburn.Micro.Xamarin.Forms;assembly=Caliburn.Micro.Platform.Xamarin.Forms"
            x:Class="XF.CaliburnMicro1.Views.Main2View"
            ItemsSource="{Binding Items}"
            SelectedItem="{Binding SelectedItem}"
            >
    <TabbedPage.ItemTemplate>
        <DataTemplate>
            <ContentPage Title="{Binding Title}" Icon="{Binding Icon}">
                <ContentView cm:View.Model="{Binding}" />
            </ContentPage>
        </DataTemplate>
    </TabbedPage.ItemTemplate>
</TabbedPage>

Easy peasy.

Happy coding.

submit to reddit

Sunday, March 05, 2017

Applying MVVM to Xamarin.Forms's TabbedPage

For this post I thought we'd dig into an example that came up recently with Caliburn.Micro and the TabbedPage view, specifically how to apply MVVM using Caliburn.Micro.

The TabbedPage view that ships with Xamarin.Forms is a great cross-platform page structure that presents sets of content in different tabs. The layout is surprisingly very similar between iOS, Android and Windows 10. The biggest layout difference is seen in iOS where each tab optionally has an icon.

xamarinforms_tabbedpage

The above image, taken without permission from Xamarin’s documentation, shows iOS, Android and Windows 8.1 Phone. The Windows 10 version is closer to the Android version.

When looking at Xamarin’s documentation, most examples show a series of Pages defined as inline XAML, and applying an ItemTemplate assumes that each tab will have the same layout; there doesn’t seem to be a great way to swap in different pages per tab. Regardless of these short-comings, the most important point regarding these examples is that the TabbedPage view displays the the Title and Icon from the contained Page in the tab headers.

Setting up the ViewModel

The first step in setting up a TabbedPage view is to represent it as its own ViewModel. Caliburn.Micro offers a unique solution to this problem using a pattern it refers to as a Screen Conductor. To understand how this pattern works, Caliburn.Micro treats pages of your application as Screens and the base Screen class contains abstractions for the view lifecycle: OnInitialize, OnActivated, OnDeactivated. These methods, respectively, make it really easy to defer work that shouldn’t be in the constructor, to ensure the view always has the latest data, and to clean-up or prevent navigating away without saving changes. With regards to the TabbedPage, the ScreenConductor provides a simple mechanism to activate and deactivate ViewModels as you navigate between tabs.

Here we define our ScreenConductor as our Main2ViewModel, and Tab1ViewModel and Tab2ViewModel as the contained tabs.

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

    public class Main2ViewModel : Conductor<Screen>.Collection.OneActive
    {
        public Main2ViewModel(Tab1ViewModel tab1, Tab2ViewModel tab2)
        {
            Items.Add(tab1);
            Items.Add(tab2);

            ActivateItem(Items[0]);
        }
    }

    public class Tab1ViewModel : Screen
    {
        public Tab1ViewModel()
        {
            DisplayName = "Tab 1";
            Tab1Content = "Tab 1 Content";
        }

        public string Tab1Content { get; set; }
    }

    public class Tab2ViewModel : Screen
    {
        public Tab2ViewModel()
        {
            DisplayName = "Tab 2";
            Tab2Content = "Tab 2 Content";
        }

        public string Tab2Content { get; set; }
    }
}

For completeness sake, these new ViewModels are registered in the App class (defined in my previous post):

public class App : FormsApplication
{
   private readonly SimpleContainer container;

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

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

       Initialize();

       DisplayRootViewFor<Main2ViewModel>();
    }

    // snip...
}

The XAML definitions for these views are represented as a TabbedPage and two instances of ContentPage:

Main2View.xaml
<?xml version="1.0" encoding="UTF-8"?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" 
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
            xmlns:converters="clr-namespace:XF.CaliburnMicro1.Converters"
            x:Class="XF.CaliburnMicro1.Views.Main2View"
            ItemsSource="{Binding Items}"
            SelectedItem="{Binding SelectedItem}"
            >

</TabbedPage>
Tab1View.xaml
<?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.Tab1View"
             Title="{Binding DisplayName}">
  <Label Text="{Binding Tab1Content}" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>
Tab2View.xaml
<?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.Tab2View"
             Title="{Binding DisplayName}">
  <Label Text="{Binding Tab2Content}" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

Fixing View / ViewModels for Tabs

If you run the solution as is, you’ll be disappointed. The reason for this is that while Caliburn.Micro can find the View/ViewModel for our MainPage, it doesn’t know how to resolve the View/ViewModels for the tabs. Now the solution I’m going to use leverages a DataTemplateSelector which isn’t something that is traditionally done with Caliburn.Micro. The correct approach with Caliburn.Micro is to take advantage of conventions and special attached properties (eg View.Model). However in this case, the approach I’m using allows you to use your ContentPage inside the TabbedPage or as a standalone page that you can navigate to directly. I’ll cover the other approach in an upcoming post.

We’ll define a DataTemplateSelector that can do the work of finding the View for our ViewModel:

namespace XF.CaliburnMicro1.Converters
{
    using System;
    using System.Collections.Generic;
    using Caliburn.Micro;
    using Caliburn.Micro.Xamarin.Forms;
    using Xamarin.Forms;    

    public class TabbedPageDataTemplateSelector : DataTemplateSelector
    {
        private readonly Dictionary<Type, DataTemplate> _selectors;

        public TabbedPageDataTemplateSelector()
        {
            _selectors = new Dictionary<Type, DataTemplate>();
        }

        protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
        {
            Type viewModelType = item.GetType();

            DataTemplate template = null;

            // check if we've already found the datatemplate for this view
            if (!_selectors.TryGetValue(viewModelType, out template))
            {
                // use caliburn to find the View for this viewmodel
                Type viewType = ViewLocator.LocateTypeForModelType(viewModelType, null, null);

                template = new DataTemplate(() =>
                {
                    var view = Activator.CreateInstance(viewType);

                    var bindableObject = view as BindableObject;

                    if (bindableObject != null)
                    {
                        // when the view's content changes...
                        bindableObject.BindingContextChanged += (sender, args) =>
                        {
                            var page = sender as Page;

                            // leverage a caliburn view lifecyle event
                            // if the viewmodel supports it
                            var viewAware = page?.BindingContext as IViewAware;
                            viewAware?.AttachView(page);
                        };
                    }

                    return view;
                });

                // cache the datatemplate
                _selectors.Add(viewModelType, template);
            }

            // return the correct view for the viewmodel
            return template;
        }
    }
}

Then associate into the view:

<?xml version="1.0" encoding="UTF-8"?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" 
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
            xmlns:converters="clr-namespace:XF.CaliburnMicro1.Converters"
            x:Class="XF.CaliburnMicro1.Views.Main2View"
            ItemsSource="{Binding Items}"
            SelectedItem="{Binding SelectedItem}"
            >
    <TabbedPage.ItemTemplate>
        <converters:TabbedPageDataTemplateSelector />
    </TabbedPage.ItemTemplate>
</TabbedPage>

Now when we run our solution, our tabs are correctly populated.

tabbedpage-content

Happy coding!

submit to reddit

Monday, February 20, 2017

Configuring Xamarin.Forms.UWP to use Caliburn Micro

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:

using System;
using System.Collections.Generic;
using System.Reflection;
using Windows.ApplicationModel.Activation;

using Caliburn.Micro;

using XF.CaliburnMicro1.ViewModels;

namespace XF.CaliburnMicro1.UWP
{
    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.

using Caliburn.Micro;

namespace XF.CaliburnMicro1.UWP
{
    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

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.

using System;
using System.Collections.Generic;
using System.Reflection;

using Caliburn.Micro;

using XF.CaliburnMicro1.ViewModels;

namespace XF.CaliburnMicro1.iOS
{
    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.

using Foundation;
using UIKit;

using Caliburn.Micro;
using Xamarin.Forms.Platform.iOS;

namespace XF.CaliburnMicro1.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

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:

using System;
using Android.App;
using Android.Runtime;
using Caliburn.Micro;

namespace XF.CaliburnMicro1.Droid
{
    [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:

using System;
using System.Collections.Generic;
using System.Reflection;

using Android.App;
using Android.Runtime;

using Caliburn.Micro;

using XF.CaliburnMicro1.ViewModels;

namespace XF.CaliburnMicro1.Droid
{
    [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.

using Android.App;
using Android.Content.PM;
using Android.OS;

using Xamarin.Forms.Platform.Android;

using Caliburn.Micro;

namespace XF.CaliburnMicro1.Droid
{
    [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

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.

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

namespace XF.CaliburnMicro1
{
    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

Saturday, January 21, 2017

This time on a mac

There’s been less activity than normal around here. Looking back, I missed last year completely. That’s more than a blip, that’s a complete year. Not sure what happened, I think it was a perfect storm...

Those that know me know that I started practicing Karate in 2015 and my facebook feed has been filled with belt exams and karate videos. I’m highly motivated to obtain my first degree in under three years, so three times a week in evening classes and weekends makes it difficult to find a few extra cycles to experiment and blog. On the plus side, I’m probably the most fit I’ve ever been, I've dropped nearly twenty pounds, toned up and dangerous.

The other major shift for me was professionally work wasn’t selling the projects that suited my skills. I kinda had a crisis of faith of sorts, where I felt I had to choose a new religion. I switched into managing more people, bought a mac and learned Swift, and explored leveraging my .net and WPF strengths in Xamarin. Work would eventually pick up later in the year, but in true agency fashion it involved dusting off my Unity3D skills, learning how to rock a HoloLens and learn Python and Ruby while I was at it. There’s loads to blog about it.

I want to touch on the macbook reference above. I feel like I’ve made a career out of bashing apple. (I ended many arguments in the 90s with apple fanboys with my favourite argument killer: “...but it’s a mac…”) Buying a mac for me was huge slice of humble pie. I still use a PC at work, but my mac is now my playground and go to device when I want to do something that isn’t “work”. I’m writing this on a mac, trying out blogo today.

One last thing, to accompany my macbook I switched to an iPhone and parted ways with my Windows Phone. I’m no stranger to iOS as I’ve had an iPad and iPhone in the past, but I was on Windows Phone for years with three different devices. I loved the platform, and still do, but I got tired of waiting for apps that the rest of the planet had. Apply Pay and wallet are awesome. I may still buy a lumina to play with UWP with Xamarin.

So what should you expect this year around here? Well, hopefully I can make good on promises to blog more, but expect to see a combination of development and agile delivery practices. You’ll likely see some Xamarin, some UI automation, maybe a HoloLens. From an agile delivery perspective, expect some tips and tricks to bring to your team as well as a focus on DevOps and Test Automation.

Friday, December 04, 2015

List of C# Compiler Warnings

Generally, you want to fix compiler warnings as they come up, but you may need to suppress some of them for legitimate reasons. A list of warnings and errors can be found here, but there’s no high-level list.

Here is a summarized list of warnings.

 

Warning Description More Info

Level 1 Warnings (Severe)

CS0420 ‘identifier’: a reference to a volatile field will not be treated as volatile https://msdn.microsoft.com/en-us/library/4bw5ewxy.aspx
CS0465 Introducing a ‘Finalize’ method can interfere with destructor invocation. https://msdn.microsoft.com/en-us/library/02wtfwbt.aspx
CS1058

A previous catch clause already catches all exceptions

https://msdn.microsoft.com/en-us/library/ms228623.aspx
CS1060 Use of possibly unassigned field 'name'. Struct instance variables are initially unassigned if struct is unassigned. https://msdn.microsoft.com/en-us/library/bb384216.aspx
CS1598 XML parser could not be loaded for the following reason: 'reason'. The XML documentation file 'file' will not be generated. https://msdn.microsoft.com/en-us/library/szfbfb7e.aspx
CS1607 A warning was generated from the assembly-creation phase of the compilation. https://msdn.microsoft.com/en-us/library/4a0640cd.aspx
CS1616 Option 'option' overrides attribute 'attribute' given in a source file or added module https://msdn.microsoft.com/en-us/library/0b104xt8.aspx
CS1658

'warning text'. See also error 'error code'

The compiler emits this warning when it overrides an error with a warning.

https://msdn.microsoft.com/en-US/library/ft0k10fs.aspx
CS1683 Reference to type 'Type Name' claims it is defined in this assembly, but it is not defined in source or any added modules https://msdn.microsoft.com/en-US/library/6b351z64.aspx
CS1685 The predefined type 'System.type name' is defined in multiple assemblies in the global alias; using definition from 'File Name' https://msdn.microsoft.com/en-us/library/8xys0hxk.aspx
CS1690 Accessing a member on 'member' may cause a runtime exception because it is a field of a marshal-by-reference class https://msdn.microsoft.com/en-us/library/x524dkh4.aspx
CS1691 'number' is not a valid warning number https://msdn.microsoft.com/en-us/library/7z6tx3wa.aspx
CS1699 Use command line option "compiler_option" or appropriate project settings instead of "attribute_name" https://msdn.microsoft.com/en-us/library/xh3fc3x0.aspx
CS1762 A reference was created to embedded interop assembly '<assembly1>' because of an indirect reference to that assembly from assembly '<assembly2>'. Consider changing the 'Embed Interop Types' property on either assembly. https://msdn.microsoft.com/en-us/library/ff183282.aspx
CS1956 Member 'name' implements interface member 'name' in type 'type'. There are multiple matches for the interface member at run-time. It is implementation dependent which method will be called. https://msdn.microsoft.com/en-us/library/bb882526.aspx
CS3003 Type of 'variable' is not CLS-compliant https://msdn.microsoft.com/en-us/library/ax5w23a6.aspx
CS3007 Overloaded method 'method' differing only by unnamed array types is not CLS-compliant https://msdn.microsoft.com/en-us/library/3w1d36e0.aspx
CS3009 'type': base type 'type' is not CLS-compliant https://msdn.microsoft.com/en-us/library/tdd79w48.aspx
CS4014 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. https://msdn.microsoft.com/en-us/library/hh873131.aspx

Level 2 Warnings

CS0108 'member1' hides inherited member 'member2'. Use the new keyword if hiding was intended. https://msdn.microsoft.com/en-US/library/3s8070fc.aspx
CS0467 Inherited members from different interfaces that have the same signature cause an ambiguity error. https://msdn.microsoft.com/en-us/library/ms228509.aspx
CS0618 'member' is obsolete: 'text' https://msdn.microsoft.com/en-us/library/x5ye6x1e.aspx
CS1701 Assuming assembly reference "Assembly Name #1" matches "Assembly Name #2", you may need to supply runtime policy https://msdn.microsoft.com/en-us/library/2h4x8b08.aspx

Level 3 Warnings (Less Severe)

CS0675 Bitwise-or operator used on a sign-extended operand; consider casting to a smaller unsigned type first https://msdn.microsoft.com/en-us/library/wdc6717a.aspx
CS1700 Assembly reference Assembly Name is invalid and cannot be resolved https://msdn.microsoft.com/en-us/library/ac36c25f.aspx

Level 4 Warnings (Informational)

CS0429 Unreachable expression code detected https://msdn.microsoft.com/en-us/library/2ch1a3w5.aspx
CS1591 Missing XML comment for publicly visible type or member 'Type_or_Member' https://msdn.microsoft.com/en-us/library/zk18c1w9.aspx
CS1610 Unable to delete temporary file 'file' used for default Win32 resource -- resource https://msdn.microsoft.com/en-US/library/z4aytccf.aspx