Getting started with Xamarin, Prism, Unity, and UWP – Part 1

Xamarin, Prism, Unity, and UWP are technologies that have been on my short list of technologies to embrace for a while. I initially tried to ease into UWP, and I met problem after problem which deterred me from progressing. After struggling to find libraries I needed from the traditional .Net collection, and struggling to make progress with the incomplete status of Prism 6 in UWP generally (coming from Prism 5 and WPF), I made the decision to shelve my project temporarily and move onto something else. Xamarin has a lot to offer, and Prism 6 seems to really complete the experience, but my first attempts to make progress were riddled with broken templates, superseded instructions, inaccurate information,  and unusual issues. I put this guide together to help anyone else that may find themselves in the same situation, lost in the current climate of these exciting technologies. Over the next couple of weeks, I hope to cover:

  • Project Setup
  • Navigation & Eventing
  • SQLite
  • Cross-Platform Deployment

Of course, all code below can be found in my public bitbucket blog repository under ‘GettingStartedWithXamarinPart1’.

Creating a Project

I am going to assume a complete Visual Studio 2015 installation is present, and Xamarin is installed, and I will be closely following this guide from the Prism Xamarin documentation to get started.

getting-started-with-xamarin-01

First of all, we need the Prism Template Pack. This is easy enough to procure from the Online Extensions Library. In Visual Studio, open ‘Tools’ > ‘Extensions and Updates’, select ‘Online’, and then search for “Prism Template Pack” and click ‘Download’ with it selected. You will need to restart visual studio afterwards.

getting-started-with-xamarin-02

Now you will be able to use the template pack to create a new solution. If you are as lucky as me, you will be greeted with these lovely messages:

getting-started-with-xamarin-03 getting-started-with-xamarin-04 getting-started-with-xamarin-05

…and your solution explorer will look rather incomplete, with only iOS and Android projects; if this is not the case for you however, skip to the next section.

getting-started-with-xamarin-06

To clean up this mess, we need to open the solution folder and delete the project folders of the projects that failed to load. In this case, “GettingStartedWithXamarin.UWP”, “GettingStartedWithXamarin.Windows”, and “GettingStartedWithXamarin.WinPhone”.

 

getting-started-with-xamarin-07a

Once this is complete, we can create a new Blank UWP project following the same naming convention. You will also need to select a target, and minimum supported platform, which can most likely be left as default.

To prepare this UWP project so that it is compatible with our Xamarin landscape, we will need to make some minor changes, as detailed in the Xamarin documentation here.

First, we need to add references to the new project. The Prism and Xamarin references can be added by right-clicking on the solution and selecting the “Xamarin.Forms” package from the list of installed packages, then selecting the new project on the right and clicking install. This process should be repeated for “Prism.Unity.Forms” which will also add the other dependencies it requires.

getting-started-with-xamarin-09

The final reference needed is to the shared project created by the template pack. Right-click on the UWP project’s references section in the solution explorer, and ‘Add Reference…’ to the shared project.

getting-started-with-xamarin-10

The solution build configurations should be updated to build and deploy this project when we build the solution, setting the build configuration up to do this now reduces an irritating nag screen later, and an additional steps of building and deploying before debugging. Right-click on the solution in the solution explorer and select ‘Properties’. Under ‘Configuration Properties’ > ‘Configuration’, ensure the adjacent ‘Build’ and ‘Deploy’ boxes are ticked for our new project.

getting-started-with-xamarin-11

In the UWP project, we now need to make some code changes to complete integration with Xamarin forms and Prism. In the App.xaml.cs file, locate the ‘OnLaunched’ method, and below ‘rootFrame.NavigationFailed += OnNavigationFailed;’ add:

Xamarin.Forms.Forms.Init (e);

In MainPage.xaml, add the following namespace to the Page:

xmlns:forms="using:Xamarin.Forms.Platform.UWP"

then delete the grid element contained within the page’s body, and finally, change the page tag to instead be ‘forms:WindowsPage’. This should look something like this:

<forms:WindowsPage
 x:Class="GettingStartedWithXamarin.UWP.MainPage"
 xmlns:forms="using:Xamarin.Forms.Platform.UWP"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:local="using:GettingStartedWithXamarin.UWP"
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 mc:Ignorable="d">;
</forms:WindowsPage>;

The MainPage.xaml.cs file now must no longer inherit from ‘Page’, and in its constructor it needs the following added below ‘InitializeComponent’:

LoadApplication(new GettingStartedWithXamarin.App());

This should now look as follows:

 public sealed partial class MainPage
{
 public MainPage()
 {
 this.InitializeComponent();
 LoadApplication(new GettingStartedWithXamarin.App());
 }
}

Congratulations! You should now be able to build and run your new UWP project!

getting-started-with-xamarin-12

Adding Data Binding

I won’t go into detail explaining how the shared project hangs together, as this is very well covered in the initially linked tutorial from the Prism library. To demonstrate binding with a little more complexity, we will extend our MainPage now to allow a user to type two numbers into two text fields, and show the sum of the numbers in a label. Although this is petty, it is of value because the Xamarin markup is different to XAML, and the provided elements have different names to those of typical Xaml. We will do this work inside the project which is shared with all the platform-specific projects. Open MainPage.xaml. We can start by adding some labels and inputs.

A Label is expressed as follows:

<Label Text="Number A:" />

An Input:

<Entry Text="{Binding NumberA}" />

I have added two number inputs, and a result field with bindings to properties in the view model which are not yet implemented. This once complete should look something similar to this:

<?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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
 prism:ViewModelLocator.AutowireViewModel="True"
 x:Class="GettingStartedWithXamarin.Views.MainPage"
 Title="MainPage">;
 <StackLayout HorizontalOptions="Center" VerticalOptions="Center">;
 <Label Text="{Binding Title}" />;
 <Label Text="Number A:" />;
 <Entry Text="{Binding NumberA}" />;
 <Label Text="Number B:" />;
 <Entry Text="{Binding NumberB}" />;
 <Label Text="Sum:" />;
 <Label Text="{Binding Result}" />;
 </StackLayout>;
</ContentPage>;

The view model needs fields to support these bindings, and a method to perform the summing operation on the two values.

A value for binding should be a property, even if it simply uses a backing field. The ‘SetProperty’ method is an important part of Prism, as it takes the burden out of notifying the view that a change has occurred.

private string _numberA;

public string NumberA
{
 get { return _numberA; }
 set 
 {
 SetProperty(ref _numberA, value);
 Sum();
 }
}

My ‘sum’ method is pretty simple too:

private void Sum()
{
 try
 {
 var a = int.Parse(_numberA);
 var b = int.Parse(_numberB);
 Result = $"{a} + {b} = {a + b}";
 }
 catch
 {
 Result = "Invalid Input.";
 }
}

The MainPageViewModel.cs looks like this with these mechanisms accounted for:

public class MainPageViewModel : BindableBase, INavigationAware
{
 private string _numberA;
 private string _numberB;
 private string _result;

 private string _title;

 public string NumberA
 {
 get { return _numberA; }
 set 
 {
 SetProperty(ref _numberA, value);
 Sum();
 }
 }
 public string NumberB
 {
 get { return _numberB; }
 set
 {
 SetProperty(ref _numberB, value);
 Sum();
 }
 }
 public string Result
 {
 get { return _result; }
 set { SetProperty(ref _result, value); }
 }

 public string Title
 {
 get { return _title; }
 set { SetProperty(ref _title, value); }
 }

 public MainPageViewModel()
 {

 }

 private void Sum()
 {
 try
 {
 var a = int.Parse(_numberA);
 var b = int.Parse(_numberB);
 Result = $"{a} + {b} = {a + b}";
 }
 catch
 {
 Result = "Invalid Input.";
 }
 }

 public void OnNavigatedFrom(NavigationParameters parameters)
 {

 }

 public void OnNavigatedTo(NavigationParameters parameters)
 {
 if (parameters.ContainsKey("title"))
 Title = (string)parameters["title"] + " and Prism";
 }
}

Our app now runs, and adds up numbers!

getting-started-with-xamarin-13

In the next part, we will expand on this implementation, to add navigation between pages and events.

Visual Studio 2015 Remote Debugging

When developing with expensive and unusual hardware in a team environment, debugging and testing can quickly become a chore; swapping devices with other developers and making concessions to complete individual components can quickly cripple a projects momentum, and may even lead to integration issues from oversights often made when a full set of hardware isn’t available to all developers for end-to-end debugging. Remote Debugging offers a clean solution to this, and Visual Studio 2015 makes this easy across multitude of different devices.

The application that I encountered that lead me on the quest to setting up a debugging environment was a work project involving a kiosk, which requires a number of external machines to be connected to it to accept and dispense money, print, and conduct cashless payments. I had heard of remote debugging previously, and had a few hesitations and questions:

  • Will it work without domain membership on all computers?
  • Does it use authentication?
  • Can I make it difficult to use the debugging machine for anything beyond debugging?
  • Is it simple to configure and maintain?

The end result was clean and simple to use, with minimal configuration. Note that all computers are using Windows 10, build 10586 or higher, and Visual Studio 2015 Update 1.

VS2015-remote-debugging-01
Development Hardware on Remote Debugging enabled computer.

Client Preparation

Firstly, the client should be prepared. This includes installing the debugging tools, securing the client machine, and configuring options to ease use and maintainability. I used this MSDN article (Set Up the Remote Tools on the Device) to get started.

  1. Install the Remote Debugging Tools, obtainable from Microsoft’s download site. You may need to restart afterwards.
  2. On the first run, you will need to configure some preferences. If you aren’t presented with the following window upon attempting to run the program, don’t worry – just enable a firewall exception on the nominated port which we will cover later.
    VS2015-remote-debugging-02
  3. Set up a user account with a password if you don’t already have one, which can be used to access the file share which will hold the debugable application, and connect to the remote debugging tools.
  4. Set up a Windows share, with full permissions for the nominated user. VS2015-remote-debugging-08Remember to set full access in both the security and sharing pages. VS2015-remote-debugging-09I use hidden shares, as denoted by the ‘$’ following the share name. VS2015-remote-debugging-07
  5. Set a static IP address. In a domain environment, I prefer to achieve the same outcome via the DHCP server with a reservation instead. This isn’t strictly necessary, but simplifies things.
  6. Run the remote debugging utility from the start menu if it is not already running. VS2015-remote-debugging-03Permissions, authentication type, and port must now be set. Within the utility, click Tools, ‘Options’ to open the Options dialogue.VS2015-remote-debugging-04
  7. Set the default port of 4020, Windows Authentication, and add the desired user as the debug user in permissions.VS2015-remote-debugging-05
  8. Note the computer name if not a domain member, for remote authentication later.

Make Machine more Appliance-Like (Optional)

  1. Disable power saving, and update power preferences. I like to use the high-performance profile, set the power and sleep buttons to shutdown, disable sleep, and ensure the screen never turns off.
  2. Set up auto login. This can be done in accordance with this TechNet article about Setting up a kiosk.
    1. Open ‘RegEdit.exe’.
    2. Locate ‘HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Winlogon’.
    3. Set ‘AutoAdminLogon’ to ‘1’.
    4. Set ‘DefaultUserName’ to the desired username.
    5. Set ‘DefaultPassword’ to the desired password. Note that if either the username or password key is missing, you can simply add a string value at this location.
  3. Replace the shell with the debug client. This can be done in accordance with this superuser question (http://superuser.com/questions/435275/can-you-disable-windows-explorer-from-starting-with-windows).
    1. Open ‘RegEdit.exe’.
    2. Locate ‘HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Winlogon’.
    3. Set ‘Shell’ to the full path of the executable you wish to run. In my case, this was ‘C:\Program Files\Microsoft Visual Studio 14.0\Common7\IDE\Remote Debugger\x64\msvsmon.exe’.
    4. Restart. (Press Ctrl+Shift+Escape to bring up task manager if needed, when explorer isn’t running. From here you can start new tasks like ‘regedit’ or ‘explorer’).

Configure Solution

It’s now necessary to configure the solution to utilise this remote debugging agent. This can be done by modifying the local build configuration rather than making a new build configuration, but this will make it difficult to debug locally, and will prove to be more of a hindrance in a team environment.

  1. Connect to the windows share, and remember the credentials. You may need to connect to the \\COMPUTER and authenticating, before connecting to \\COMPUTER\MYSHARE$. If the remote debug machine is not a domain member, log in with COMPUTER\USERNAME.
  2. Open the solutions configuration manager. This can be found on the tool bar at the top of visual studio or in the solution properties.VS2015-remote-debugging-11
  3. Add a new Solution Configuration, and name it appropriately. You may wish to copy settings from your current profile. It’s likely that you will want to also create project configurations; tick the box.VS2015-remote-debugging-12
  4. Set the Active Solution Platform if desired, or adjust the project platforms against the profile.VS2015-remote-debugging-13
  5. Open the project properties for executable projects, and navigate to the ‘Build’ tab. Here you will need to ensure the ‘Configuration’ is set to the new one you have created, and set the output path to your share.VS2015-remote-debugging-14
  6. In the project properties, navigate to the ‘Debug’ tab and tick “Use remote machine”, and set the field to the IP address of the target computer.VS2015-remote-debugging-15
  7. Save All.

You should now be able to select the solution build configuration from the upper toolbar, and start debugging as usual. You may be re-prompted for authentication, log in as you would a windows file share, and remember credentials.

VS2015-remote-debugging-16

Cash Payment with MEI and C# – Part 1

Although getting off to a shaky start with an MEI bill/note recycler, after some consultation with the supplier, firmware upgrades, and software reprovisioning, development has become a really nice experience in contrast to other hardware I have encountered. The MEI SCR 8327 is a note accepter and recycler; it will accept note/bill payment and can also dispense notes back to customers. A recycler differs from an accepter and dispenser because it can accept a note into its dispensable pool, which this unit stores on a drum internally.

cash-payment-with-mei-and-c-pt1-05

I struggled to get the device to function at first. I explored the MEI websites, downloaded everything that looked relevant, and attempted to install drivers and samples. MEI hardware seems to be of high build quality. The device exhibits nice fabrication, high serviceability and modularity, and is proudly represented at almost every kiosk I have encountered in my life, however their organisation of critical documents online is somewhat poor. My problems started when I attempted to connect it to my development PC (I develop on Windows 10, 64bit, with .Net 4.6), and try out the sample applications. I first attempted with the serial port, accessible through a tail at the rear. This resulted in no response. I then tried to install the USB drivers and connect the unit to my computer. The drivers installed without issue, but the device was in a Code 10 state and wouldn’t start.

After becoming stuck at this point I contacted the supplier who eventually connected me to a representative from Crane Payment Innovations (the MEI parent company). The representative (Glenn) was immensely helpful, and after 5 minutes of email conversations we had a Team Viewer session established and began making progress. The software I had downloaded was actually no longer relevant, the drivers were no longer supported, and the software and drivers I was issued are still not available on their website.

He first connected to the device with an application called “Cashflow STS v7.12”, using a new driver which was digitally signed and functional to connect via the device’s USB port (which is obnoxiously presented on the front of the device). From here he was able to upgrade the devices firmware, and enable the Serial port. The device was stuck in a ‘Stand Alone’ state form the supplier, and the serial port was not responsive nor usable. After moving out of this state, and setting up the note values and profiles to accept, he demonstrated their API through the example program, “CPI Integration Toolkit”.

The application seemed to separate out all the basic functions I would imagine that anyone would expect the platform to provide. You can:

  • Accept notes.
  • Decide what to do with an inserted note.
  • Empty the recycler to the cash box.
  • Return a note from the recycler.
  • Get the status of the hardware.
  • Perform simple configuration.

cash-payment-with-mei-and-c-pt1-03

The workflow of the primary use-case of this device seems pretty straight forward. You put the device in a listening state, which in turn allows a note to be inserted. Once the note is inserted the machine tries to match it to a configured denomination profile. If a match is found it is held in escrow. Escrow is like a buffering system for inserted notes; other models support multi-note escrow, allowing the ‘session’ of inserted notes to be returned to the customer who inserted them which might be desirable if the transaction was cancelled. This particular unit only holds a single note in an escrow state, and it must be either recycled or sent to the cassette, if not manually rejected. The recycler of this specific unit can hold 2 different denominations, and is configurable, with a total capacity of 60 notes (according to the sample application). Once the note is actioned, the device leaves the escrow state and returns to an idle state. The hosting system could then choose to raise some event or perform an action knowing that a note has been received, or the device could simply be told to go back into an accepting state.

cash-payment-with-mei-and-c-pt1-02

Viewing the demo application’s code initially made my hair jump after seeing ReSharper litter the main scrollbar (with the most orange markers I have ever seen) , and observing that all the code driving the demo was stored crudely in the code behind. Despite this, the code was actually nicely laid out, and lent itself well to learning, with well though-out comments, validation, and guidance distributed throughout the methods.

As described above, the machine should first enter an accepting state, this is done by calling the EnableAcceptance method on the device instance.

try
{
// Ensure communications are open
if (!_scr.IsOpen)
{
// Cannot enable acceptance. Communications are not open.
return;
}
// Enable acceptance
_scr.EnableAcceptance();
}
catch
{
// Communications are not open, or unknown/unexpected exception.
}

When a note is inserted, it will fire the event bound to the DocumentStatusReported event. This would allow an implementing system to check that the document is in a state where it can be stacked, and then proceed to stack the note to the recycler or cassette. The system could even decide to return the note.

To stack the note to the recycler:

try
{
// Ensure we are at a point where stacking is allowed.
if (_scr.DeviceState != DeviceState.ESCROWED)
{
           // Cannot Stack to Recycler : Device state != Escrowed
return;
}
// Check to see if this document is enabled for recycling.
var bCanStackToRecycler = false; // Default to False until we are sure we can recycler this document.
// Make sure we are dealing with a Banknote object
if (_document != null && _document is Banknote)
{
// Get a reference as a Banknote
var banknoteTmp = _document as Banknote;
var recyclerNoteTable = _scr.GetRecyclerNoteTable();
// Loop through the table and compare the 'ISO' and the 'Value'
foreach (var entry in recyclerNoteTable)
{
if (entry.ISO == banknoteTmp.ISO && entry.Value == banknoteTmp.Value)
{
// Valid entry- check the 'IsRecyclingEnabled' property
if (entry.IsRecyclingEnabled)
{
bCanStackToRecycler = true;
}
break;
}
}
}
// Abort if we cannot stack this document to the recycler
if (!bCanStackToRecycler)
{
// Cannot Stack to Recycler. Document is not enabled for recycling.
return;
}
// Stack the document to the recycler.
_scr.StackDocumentToRecycler();
}
catch
{
// See documentation for the plethora of issues that could arise.
}

To stack the note to the cassette:

try
{
// Ensure we are at a point where stacking is allowed
if (_scr.DeviceState != DeviceState.ESCROWED)
{
// Cannot Stack to Cassette : Device state != Escrowed
return;
}
// Stack the document to the cassette.
_scr.StackDocumentToCassette();
}
catch
{
// See documentation for the plethora of issues that could arise.
}

To return the note:

try
{
// Ensure we are at a point where stacking is allowed
if (_scr.DeviceState != DeviceState.ESCROWED)
{
// Cannot Return the Document : Device state != Escrowed
return;
}
// Return the document
_scr.ReturnDocument();
}
catch
{
// See documentation for the plethora of issues that could arise.
}

You could now re-enable the device to continue accepting notes. If nothing was to be inserted for a long time, it might be desirable to disable acceptance.

try
{
// Ensure communication is open
if (!_scr.IsOpen)
{
// Cannot disable acceptance. Communications are not open.
return;
}
// Disable acceptance
_scr.DisableAcceptance();
}
catch
{
// communications are not open, or unknown/unexpected exceptions.
}

You could issue change to your customer by dispensing notes like so:

// Set the index and quantity to dispense from the recycler accordingly.
int iIndex = 1;
int iCount = 2;
try
{
// Ensure we are in a state where we can dispense documents.
switch (_scr.DeviceState)
{
case DeviceState.IDLE:
// Ensure we aren't currently accepting.
_scr.DisableAcceptance();
break;
case DeviceState.HOST_DISABLED:
// Ok to proceed.
break;
default:
// Cannot Dispense Documents : Device state == _scr.DeviceState
return;
}
// Input has been validated so dispense the document(s)
_scr.DispenseByCount(iIndex, iCount);
}
catch
{
// Device is not in a valid state, index is invalid, quantity is invalid, comms loss, or other unknown error.
}

Decisions can be made on how to route notes and perform transactions by using the recycler note table. calling the GetRecyclerNoteTable command on the device instance will return an object describing the state of the devices reserves. The cassette should be manually monitored however.

I will follow up this article once I have completed the integration of this device into my own project, and outline any more issues that arise. Despite the obvious limitations that present themselves with the limited core instruction set, the hardware seems well fabricated, the API is easy to follow, and the features sensible to standard applications. I look forward to exploring other MEI/CPI devices in the future.