Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Windows Phone Mango Push Notification Changes

The basic concept of push notifications for Windows Phone doesn’t change for Mango. There are still three types (Push Notifications in Windows Phone), tile, toast and raw, and your application still needs to register with the Microsoft Push Notification Service in order to make use of this feature. The differences are that there are extensions to the service, which both plug holes in the initial implementation and provide support for multiple tiles and the back of tiles. Let’s go through the different types of notifications and I’ll point out some of the differences.

Toast Notifications

One of the issues with toast notifications in WP7 was that when the user clicked on the toast they were taken into the application as if they’d clicked on the application from the apps list. There was no arguments or information passed into the application to indicate where it had been launched from. This made it rather painful to do anything useful with toast notifications, other than trying to encourage users back into the application.

In Mango there is an additional (optional) Param element that can be specified in the toast message payload. This is essentially a navigation uri, or a query string, that is passed into the application. The payload message structure looks similar to the following:

string toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                                              "<wp:Notification xmlns:wp=\"WPNotification\">" + 
                                                  "<wp:Toast>" +
                                                      "<wp:Text1>First String</wp:Text1>" +
                                                      "<wp:Text2>Second String</wp:Text2>" + 
                                                      "<wp:Param>/PageTwo.xaml</wp:Param>" + 
 
                                                 "</wp:Toast>" + 
                                              "</wp:Notification>";

The bold line illustrates the new Param element. In this case if the user taps on this toast, they will be taken directly to PageTwo.xaml. Note that PageTwo.xaml will be the first page in the back stack, bypassing the normal entry point for the application.

The Param element can include a query string either in addition to, or instead of, a navigation uri. For example:

     /PageTwo.xaml?query1=value1
     ?query2=value2

In both cases the query key-value pairs can be extracted via the NavigationContext for the page that is navigated to. In the first case, this would be PageTwo.xaml, whereas in the second (where there is no page defined in the Param element) the page would be the default launch page for the application.

 

Tile Notifications

Live tiles get a major uplift in Mango. You still of course have the front tile which consists of the background image, a badge/count in the top right corner and the title. With Mango you can use the back of the tile as well. The back of the tile also has a background image and a title but also has a content attribute which can be used to display more text content (you can’t modify the font size or layout though). If that wasn’t enough, Mango also gives you the ability to pin multiple tiles for your application to the Start (in much the same way as web pages and contacts can be pinned to the Start today).

The new message structure for sending a tile notification is:

string tileMessage =     "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                                          "<wp:Notification xmlns:wp=\"WPNotification\">" +
                                              "<wp:Tile Id=\"{0}\">" +
                                                  "<wp:BackgroundImage>Background.png</wp:BackgroundImage>" +
                                                  "<wp:Count>6</wp:Count>" + 
                                                  "<wp:Title>My App Title</wp:Title>" + 
                                                  "<wp:BackTitle>Back Tile Title</wp:BackTitle>" + 
                                                  "<wp:BackContent>Some additional content to be displayed</wp:BackContent>" + 
                                                  "<wp:BackBackgroundImage>BackOfTileImage.png</wp:BackBackgroundImage>" +
                                              "</wp:Tile> " +
                                         "</wp:Notification>";

The new elements are:

BackTitle – The title to be displayed on the back of the tile

BackContent – The additional content to be displayed on back of the tile

BackBackgroundImage – The image 173x173 to use on the back of the tile. Can be local (as in this example) or from a remote server, assuming you specify a url that is on one of the permitted domains (see setup of push notifications)

Id – The Id attribute of the Tile element is used to identify tiles in the case where there are multiple tiles pinned to the Start for a given app. The default tile (which is created when the user pins the app from the apps list, as per current version of wp7) has an Id of “” or you can omit this attribute. The Id of a tile also determines the page, and corresponding query string, to be navigated to when the user clicks on the tile.

Raw Notifications

I’m not aware of any changes to raw notifications themselves. However, the service that is used to send notifications can register for a callback when the device becomes inactive – particularly if you are sending large numbers of raw messages out to a phone. More information is available via MSDN on Setting up a Callback Registration Request

Build Mobile: Windows Phone Push Notifications

The fourth part of a series on Windows Phone Push Notifications has been posted on Build Mobile. If you’re interested, check out the direct links below:

- Push Notifications in Windows Phone
- WP7 Push Notifications Part 2

In the Mobile Corner at the Visual Studio Magazine my latest article on using some of the system styles in Windows Phone has been published:

Style Your Application with Windows Phone 7 System Resources

A full listing of articles on both Build Mobile and Visual Studio Magazine is available here

Windows Phone Developer Training: Are You Interested?

We’re looking to run Windows Phone developer training….. but need to know whether you’re interested! Complete the super-quick survey http://bit.ly/remixwp7 to let us know what you want. The quicker you respond, the quicker we’ll make a decision on when, where and how to run developer training.

Note: We’ve purposely dropped the “7” from the Windows Phone title in light of the announcement/SDK for Mango. If you’re skilled with WP7 but want to learn the ins and outs of Mango then make sure you add this to the comments so we can focus on that.

Facelift for the Windows Phone 7 Social Viewer Template

I’ve just checked in the latest Social Viewer template to http://socialviewer.codeplex.com which among a number of bug fixes (including the fix to get Twitter auth working again) also includes a significant facelift.

Previously you defined a number of sources (RSS, Atom, Facebook, Twitter or your own) then grouped them into lists. These lists would then appear in a panorama. This sort of worked for a small number of lists, say 2-4, but you ended up with list after list after list – probably a pivot would be better suited to displaying all the lists.

I’m not a big fan of using a pivot as the starting point for an application so I wanted to craft a better experience that demonstrated the use of both the panorama and pivot controls, as well as using more of the features of the social viewer template itself. What we ended up with is a template that is broken into a main panorama which shows highlights, a more detailed pivot showing a number of on-demand lists and then of course the reading page. Let’s walk through them.

Main Panorama

The main panorama is essentially the launching point for the application. The first pane includes a vertical list of items that is entitled “what’s new” in keeping with the what’s new lists across the core platform. This is a simple list allowing you to easily scan the list for items of interest. Items appear dimmed/greyed to indicate it has been read.

Next we have the recent panorama item where we introduce a bit of a change in pace – you’ll again notice this across the core platform, where you’ll see the panorama broken up into different styles of panes. Here we’re including items that have got images/enclosures to add some life to the panorama.

Lastly we have a pane that is sort of a menu in that it links to different parts of the application. As the template can take advantage of trial mode, there is also a Purchase menu item that is not visible here, linking the user off to the Marketplace link for the application to be purchased.

image image image image

On-Demand Pivot

Clicking on the Developers link on the main panorama navigates the user to a page which contains the pivot control. The control is data bound to a collection of lists. As the user flicks between the lists, the corresponding feeds are downloaded and processed on-demand (as you can see in the second image with the progress bar still visible).

image image image image

Reading

Clicking on any of the items from any of the lists on the Main Panorama or the On-Demand Pivot will navigate the user to the reading page. Here the full post is available, along with any links (see second image) that the user may want to navigate to. The user can also opt to share the post via Facebook, email the post or tweet a reference to the post.

image image image

Settings and About

The last two pages of the template are the Settings and About pages. These are provided out of the box to allow the user to manage their Facebook and Twitter credentials, and to list relevant information about the sources of the data for the application. The About page also provides a mechanism whereby the user can review (via Marketplace) or provide feedback (via email) on the application.

image image

I would love feedback on the latest version of the social viewer template – http://socialviewer.codeplex.com (the updated version is still on the source code tab until I finish testing the updates)

Display PDFs in your Windows Phone 7 application

I just downloaded and was experimenting with the CTP of ComponentOne Studio for Windows Phone. Normally I don’t give these third party control libraries much interest as they’re usually expensive and generally just add another dependency to your application that you have no control over. This is particularly true with mobile applications where performance is so critical.

Anyhow, the one thing that caught my eye in this control library is that there is a PDF Viewer control. There is full documentation available online, including details of this control. What interested me was the following:

View and Save PDF Files

The C1PdfViewer control can be used to view and save PDF files on the Windows Phone device. C1PdfViewer has no external dependency on Adobe Reader to view or save files. Content is parsed and rendered as native XAML elements.

That it awesome that they’re rendering the PDF as native XAML.

After downloading the CTP I was a little lost to start with as I couldn’t see the controls appear within Visual Studio. However they ship a cool Sample Explorer which I was able to use to play around with their samples.

image

After taking a look at the sample I decided to see how easy it was to create a simple application that displays a PDF. Rather than loading the PDF from a resource within the application which is what the sample demonstrates, I decided to load a test PDF located at  http://www.blhr.org/media/documents/test.pdf

The XAML for MainPage is:

<phone:PhoneApplicationPage
    x:Class="PDFViewer.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:c1Pdf="clr-namespace:C1.Phone.PdfViewer;assembly=C1.Phone.PdfViewer"
    Loaded="MainPageLoaded">

    <Grid>
            <c1Pdf:C1PdfViewer x:Name="pdfViewer"
                               ViewMode="FitWidth"
                               Visibility="Collapsed" />
    </Grid>
</phone:PhoneApplicationPage>

And the code to load the PDF is quite simple:

public partial class MainPage : PhoneApplicationPage {
    public MainPage() {
        InitializeComponent();
    }
    private WebClient wc = new WebClient();
    private void MainPageLoaded(object sender, RoutedEventArgs e) {
        wc.OpenReadCompleted += WcOpenReadCompleted;
        wc.OpenReadAsync(new Uri(http://www.blhr.org/media/documents/test.pdf));
    }
    void WcOpenReadCompleted(object sender, OpenReadCompletedEventArgs e) {
        pdfViewer.LoadDocument(e.Result);
        pdfViewer.Visibility = Visibility.Visible;
    }
}

Running this displays the pdf in the application:

image

Warning: There are some definite limitations with this control that are outlined both on the website and within the documentation. For example my first test PDF didn’t render (http://www.education.gov.yk.ca/pdf/pdf-test.pdf) throwing an exception relating to do with the compression format. The guidance is currently to only use this control for rendering PDFs that you have control over.

 

Windows Phone 7 Social Viewer Template v2 Published

If you want to jump start your Windows Phone application development, why not download the Social Viewer template available at http://socialviewer.codeplex.com. You can use this to simplify the creation of apps that read data from Facebook, Twitter, Blogs and any other data feed you might have.

The template integrates directly into Visual Studio 2010 (which you’ll need to do Windows Phone 7 development – http://create.msdn.com) and appears in the New Project dialog.

image

Like most Visual Studio 2010, once you’ve created a new project you can simply hit F5 to run it in the emulator or on a device. Pan left-right to see the different lists. Select an item to read the full item.

image  image  image  image

You can easily change the feeds and the lists that are displayed to the user in the configuration.xaml file. Add api keys for Facebook, Twitter and AdGACto allow the user to post to Facebook or Twitter, and to integrate Ads into your application.

Get the Social Viewer Template v2 from http://socialviewer.codeplex.com today and get building your WP7 applications.

Fix: Calling WCF Methods with Parameters Synchronously on Windows Phone 7

In my previous post, Tidying up Windows Phone 7 + WCF Synchronous Programming Model I proposed a base class called SyncClientBase which would make it much easier to call WCF services from a background thread in a synchronous fashion. Unfortunately, whilst this works well for methods that don’t take any arguments, it completely fails when trying to call methods which take parameters. The first step in the Invoke method was to pull apart the Expression parameter in order to determine the async method that was to be invoked and the corresponding event that would be raised.  ie:

    public TResult Invoke<TResult>(Expression<Action> asyncMethod) 
                          where TResult : AsyncCompletedEventArgs {
        var memberExpression = asyncMethod.Body as MethodCallExpression;
        var method = memberExpression.Method;
        var eventName = method.Name.Replace("Async","Completed");
        var evt = Proxy.GetType().GetEvent(eventName);

 

This works, however, when it comes to actually invoking the async method I was passing in an empty object array – clearly not correct when the method takes parameters. On the desktop we could pull the Expression apart even more and actually pull out the arguments that were supplied in the expression tree. However, after playing around a bit I couldn’t work out how to do this without the .NET CF raising a FieldAccessException or two.

I decided to rework the Invoke method a little: Instead of accepting an Action Expression, it now takes a Func<Delegate> Expression (ie a function that returns the method we want to invoke). It also accepts an array of objects which will be the parameters to the async method to be invoked. The full Invoke method looks like:

public TResult Invoke<TResult>(Expression<Func<Delegate>> asyncMethod, params object[] args) where TResult : AsyncCompletedEventArgs
{     var body = asyncMethod.Body as UnaryExpression;     var operand = body.Operand as MethodCallExpression;     var arg = operand.Arguments[2] as ConstantExpression;     var method = arg.Value as MethodInfo;     var eventName = method.Name.Replace("Async", "Completed");     var evt = Proxy.GetType().GetEvent(eventName);     lock (downloadLock)     {         TResult data = default(TResult);         EventHandler<TResult> handler = (s, e) =>         {             data = e;             downloadWait.Set();         };         evt.AddEventHandler(Proxy, handler);         method.Invoke(Proxy, args);         downloadWait.WaitOne();         evt.RemoveEventHandler(Proxy, handler);         if (data.Error != null)         {             throw data.Error;         }         return data;     }
}
 

The proxy class structure still looks very similar. The only difference is that instead of a method call eg Proxy.Step1Async() we’re passing a delegate eg (Action)Proxy.Step1Async. For methods that require a parameter (eg Step3) the parameters are passed in as additional parameters to the Invoke method.

public class Service1Proxy : SyncClientBase<Services.Service1Client, ServiceTester.Services.IService1>, DemoService.IService1 {     public int Step1() {
        return base.Invoke<Step1CompletedEventArgs>(() => (Action)Proxy.Step1Async).Result;
    }
 
    public int Step2() {
        return base.Invoke<Step2CompletedEventArgs>(() => (Action)Proxy.Step2Async).Result;
    }
 
    public int Step3(int xyz) {
        return base.Invoke<Step3CompletedEventArgs>(() => (Action<int>)Proxy.Step3Async,xyz).Result;
    }
}
 

The only issue with this structure is that there is not validation that the correct number and type of parameters are passed into the Invoke method. Other than that, it’s still quite an elegant wrapper.

Tidying up Windows Phone 7 + WCF Synchronous Programming Model

In my previous post, Windows Phone 7 and WCF Don’t Play Nicely, I talked about how you could overcome some of the shortcomings of working with the generated WCF proxy classes in Windows Phone 7. I wasn’t particularly happy with the workaround I posted so I gave it some more thought and decided to go back a step.

If we look at our WCF service that we’re connecting to it is made up of two parts: the interface definition and then the service implementation. In our case they’re very simple:

[ServiceContract]
public interface IService1 {
    [OperationContract]
    int Step1();
 
    [OperationContract]
    int Step2();
}
 
public class Service1 : IService1{
    public int Step1() {
        return 1;
    }
 
    public int Step2() {
        return 2;
    }
}
 

When we generate the service reference in our Windows Phone 7 application we end up with the following methods and events:

    void Step1Async();
    void Step2Async();
    public event EventHandler<Step1CompletedEventArgs> Step1Completed;
    public event EventHandler<Step2CompletedEventArgs> Step2Completed;
 

And just to remind you, we want to get to code that looks as simple as:

public void RunInBackground(Action callback) {
    var proxy = new Service1Proxy();
    var r1 = proxy.Step1();
    var r2 = proxy.Step2();
}
 

Here’s the trick – we’re going to start with another base class that is going to do the synchronous wrapping of our service method request. Essentially this follows the same pattern as what you would have seen with the ServiceHelper class from my previous post. However, in this case the parameter to the Invoke method is an Expression that references the asynchronous service call (eg “Invoke<Step1CompletedEventArgs>(()=>Proxy.Step1Async())” ) . From this expression we can pull out the name of the method to be invoked (eg “Step1Async”) and use that to determine the name of the corresponding event that will get raised when the service request completes (eg “Step1Completed”). The event name can be used to get a reference to the event itself, which we can then wire an event handler to in the same way that the ServiceHelper worked.

public class SyncClientBase<TClientBase, TServiceInterface> : IDisposable
    where TClientBase : ClientBase<TServiceInterface>, TServiceInterface, new()
    where TServiceInterface : class {
 
    public TClientBase Proxy { get; private set; }
    public SyncClientBase() {
        Proxy = new TClientBase();
    }
 
 
    private static object downloadLock = new object();
    private static AutoResetEvent downloadWait = new AutoResetEvent(false);
 
    public TResult Invoke<TResult>(Expression<Action> asyncMethod) 
                          where TResult : AsyncCompletedEventArgs {
        var memberExpression = asyncMethod.Body as MethodCallExpression;
        var method = memberExpression.Method;
        var eventName = method.Name.Replace("Async","Completed");
        var evt = Proxy.GetType().GetEvent(eventName);
        lock (downloadLock) {
            TResult data = default(TResult);
            EventHandler<TResult> handler = (s, e) => {
                data = e;
                downloadWait.Set();
            };
            evt.AddEventHandler(Proxy,handler);
            method.Invoke(Proxy, new object[] {});
            downloadWait.WaitOne();
            evt.RemoveEventHandler(Proxy, handler);
            if (data.Error != null) {
                throw data.Error;
            }
            return data;
        }
    }
 
    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
 
    protected virtual void Dispose(bool disposing){
        if (disposing) {
            if (Proxy != null) {
                downloadWait.Set();
                Proxy = null;
            }  
        }
    }
}
 

But how does this complex bit of code get us any closer to where we want to be. Well firstly this code is boilerplate code which doesn’t need to be modified for each new service reference we add. However, we do need to create another proxy class which inherits from SyncClientBase for each of our service references:

public class Service1Proxy: SyncClientBase<Services.Service1Client, 
                                           ServiceTester.Services.IService1>, 
                            DemoService.IService1 {
    public int Step1() {
        return base.Invoke<Step1CompletedEventArgs>(()=>Proxy.Step1Async()).Result;
    }
 
    public int Step2() {
        return base.Invoke<Step2CompletedEventArgs>(()=>Proxy.Step2Async()).Result;
    }
}
 

There are some points to note about this proxy:

- Service1Proxy inherits from SyncClientBase where the type arguments are Service1Client, which is the proxy that is generated when you do Add Service Reference, and IService1, which is also generated when you add the service reference.

- Service1Proxy implements DemoService.IService1 which is the original service interface definition, not Services.IService1 which is generated when you Add Service Reference. You’ll need to add this file to your Windows Phone 7 application (if you add it as a link then when you change your service definition it will automatically be updated in your Windows Phone 7 project).

- For each method defined in the DemoService.IService1 the implementation follows the same pattern:

public int Step1(){     return base.Invoke<Step1CompletedEventArgs>(()=>Proxy.Step1Async()).Result;
}

>> “Step1CompletedEventArgs” – This is the type parameter for the event that will be raised when the service request completes. In the case of Step1Async the corresponding event is Step1Completed which is of type EventHandler<Step1CompletedEventArgs>
>> “() => Proxy.Step1Async()” – This is an Action delegate that invokes the service request you want to call. In this case the Step1Async method is going to be called.
>> “.Result” – This just extracts the actual result data out of the event args and the type should match the return type of the method. In this case the Step1 method returns an integer so the Result property returns an integer value.

Wrapping this all up we get the following simple code to run on our background thread to invoke a sequence of service requests:

public void RunInBackground(Action callback) {
    using (var proxy = new Service1Proxy()) {
        var r1 = proxy.Step1();
        var r2 = proxy.Step2();
    }
}

All you have to do is make sure the Service1Proxy class stays up to date (ie add/modify/delete appropriate methods to make sure it matches IService1) and you have a nice wrapper for calling your services in a synchronous manner on a background thread without blocking the UI thread at all.

Windows Phone 7 and WCF Don’t Play Nicely

There area a number of ways you can access data from within your Windows Phone 7 application. These include using base level classes such as HttpWebRequest and the WebClient but you can also use WCF from within your application. The advantage of using WCF is of that Visual Studio 2010 can generate the necessary strongly typed proxy classes for you. All you need to do is click “Add Service Reference”, enter the url for the service you want to import and off you go. You can now write code that connects to a WCF service… happy days…. fail

Unfortunately it attempting to make life easier for noobie developers the Silverlight/Windows Phone 7 teams have managed to create a rod for almost any serious developer who wants to work with WCF Services. I’ll cover this in two parts as follows:

Async Methods Suck!

If you’ve worked with Silverlight on the desktop or WP7 then you’ll probably have noticed that nearly all methods that perform a network operation are asynchronous. This is true of the base HttpWebRequest class, the WebClient and WCF proxy classes. Conceptually this is quite a good idea as it forces developers to think asynchronously and write code that doesn’t block the UI thread. This in theory should lead to responsive applications.

What this doesn’t take into consideration is that developers who have done a programming 101 course understand the use of multiple threads. If you spin up another thread to do background tasks such as synchronisation then all of a sudden you’re going to find yourself fighting the async programming model that’s been thrust on you.

What we want to be able to do is something like:

Service1Client client = new Service1Client();
public void RunInBackground(){
    client.Step1();
    client.Step2();
}
 

What we end up with instead is:

Service1Client client = new Service1Client();
public void RunInBackground(Action callback) {     
    client.Step1Completed += client_Step1Completed;     
    client.Step1Async(callback);
}
 
void client_Step1Completed(object sender, Step1CompletedEventArgs e) {     
    client.Step1Completed -= client_Step1Completed;     
    client.Step2Completed += client_Step2Completed;     
    client.Step2Async(e.UserState);
}
 
void client_Step2Completed(object sender, Step2CompletedEventArgs e) {     
    client.Step2Completed -= client_Step2Completed;     
    var callback = e.UserState as Action;     
    callback.Invoke();
}
 

Note that the RunInBackground takes an Action as a callback. This is passed along with the various WCF method calls so that it can be invoked when all of the services have completed. Notice how untidy this code is and just how difficult it is to read and maintain.

There are ways to beat Silverlight into shape to make it easier to do a sequence of service calls on a background thread. For example the following ServiceHelper class means that I can call the sequence of Step1 and Step2 in a synchronous fashion. Note that this is done on a background thread.

void MainPage_Loaded(object sender, RoutedEventArgs e) {         
    ThreadPool.QueueUserWorkItem(async => RunInBackground());
}
 
Service1Client client = new Service1Client();
public void RunInBackground(){
    ((Action)client.Step1Async).ServiceCall<Step1CompletedEventArgs>(             
            handler => client.Step1Completed += handler,             
            handler => client.Step1Completed -= handler);
    ((Action)client.Step2Async).ServiceCall<Step1CompletedEventArgs>( 
            handler => client.Step2Completed += handler, 
            handler => client.Step2Completed -= handler); 
}

 
public static class ServiceHelper {     
    private static object downloadLock = new object();     
    private static AutoResetEvent downloadWait = new AutoResetEvent(false);     
    public static T ServiceCall<T>(this Action serviceCall, 
                                     Action<EventHandler<T>> addHandler, 
                                     Action<EventHandler<T>> removeHandler) where T : AsyncCompletedEventArgs {         
        lock (downloadLock){             
             T data = default(T);             
             EventHandler<T> handler = (s, e) =>
             {
                 data = e;
                 downloadWait.Set();
             };
          addHandler(handler);             
             serviceCall();             
             downloadWait.WaitOne();        
             removeHandler(handler);             
             if (data.Error != null) {                 
                 throw data.Error;             
             }             
             return data;         
        }     
    }
}

 

WCF Implementation Sucks!

A nasty surprise awaits those attempting to use WCF on Windows Phone 7. What it little understood is that the proxy classes that Visual Studio generates (which inherit from ClientBase) has some awful code behind it that will flick execution back onto the Main/UI thread. As you saw previously each WCF service call is made up of an asynchronous method call, with a completed event that is raised when the service call returns. This event is raised on the Main/UI thread. This means that if you’re doing any sort of processing (eg writing the response to disk before displaying it) you will accidentally block the UI thread!!!

I’m sorry to say this is one of the worst architectural decisions I’ve seen in a long time and is a hidden cause for so many applications performing badly. There are a couple of solutions:

1) Use HttpWebRequest and use a REST API on the server

2) Jump off the UI thread in each event handler, do the processing and then jump back to UI thread to update the UI.

3) The ServiceHelper shown earlier automatically passes back off to the background thread.

Windows Phone 7, Android and iOS with Mono VII: Database

Previous posts in this series:

- Mono I: Getting Started
- Mono II: Basic Navigation
- Mono III: Shared Libraries
- Mono IV: Webservices
- Mono V: Content and Resource Files
- Mono VI: File Access

Not only do you want to be able to write to the file system, for more complex applications you will want to write to a relational database.

Windows Phone 7

There is currently no support for Microsoft’s own lightweight database engine, SQL CE. However, there is a port of Sqlite that will work nicely with Isolated Storage.

- Go to CodePlex and download either the source code or just the assemblies for the Sqlite project (http://wp7sqlite.codeplex.com). If you download the assemblies you just need to add them as a reference to your WP7 application project. I prefer to have the source code handy in case something goes wrong. In this case simply add the two Sqlite projects to your solution and add a reference to both projects to your WP7 application project.

- Once you’ve added the sqlite references, all you need to do is write a bit of code to create the database, tables and insert records. Of course you’ll want to validate that the data exists. The following code was taken, and modifies slightly, from the documentation on Codeplex but illustrates all of these operations.

private void CreateSqlLite(string connectionString)
{
     using (SqliteConnection conn = new SqliteConnection(connectionString)) {
         conn.Open();
         using (SqliteCommand cmd = conn.CreateCommand()) {
             cmd.CommandText = "CREATE TABLE test ( [id] INTEGER PRIMARY KEY, [col] INTEGER UNIQUE, [col2] INTEGER, [col3] REAL, [col4] TEXT, [col5] BLOB)";
             cmd.ExecuteNonQuery();
             cmd.Transaction = conn.BeginTransaction();
             cmd.CommandText = "INSERT INTO test(col, col2, col3, col4, col5) VALUES(@col, @col2, @col3, @col4, @col5);SELECT last_insert_rowid();";
cmd.Parameters.Add("@col", DbType.Int32);
             cmd.Parameters.Add("@col2", DbType.Int32);
             cmd.Parameters.Add("@col3", DbType.Double);
             cmd.Parameters.Add("@col4", DbType.AnsiString);
             cmd.Parameters.Add("@col5", DbType.Object);
             for (int i = 0; i < 100; i++)
             {
                 cmd.Parameters["@col"].Value = i;
                 cmd.Parameters["@col2"].Value = i;
                 cmd.Parameters["@col3"].Value = i * 0.515;
                 cmd.Parameters["@col4"].Value = "สวัสดี な. あ · か · さ · た · な · は · ま · や · ら · わ. 形容詞 hello " + i;
                 cmd.Parameters["@col5"].Value = Encoding.UTF8.GetBytes("สวัสดี");
                 object s = cmd.ExecuteScalar();
             }
             cmd.Transaction.Commit();
             cmd.Transaction = null;
             cmd.CommandText = "SELECT * FROM test";
             using (SqliteDataReader reader = cmd.ExecuteReader())
             {
                 while (reader.Read())
                 {
                     //var bytesValue = (byte[])reader.GetValue(5);
                     //var intValue reader.GetInt32(0);
                     //var doubleValue = reader.GetDouble(3);
                     //var stringValue = reader.GetString(4);
                 }
             }
             conn.Close();
         }
     }
}
 

The only thing left for you is to call this method supplying the connection string to use. For example the following will create a database file called test.db within IsolatedStorage on the device.

CreateSqlLite("Version=3,uri=file:test.db");

 

 

iOS

- The MonoTouch sdk includes support for Sqlite so all you need to do is reference Mono.Data.Sqlite (right-click the Android application project and select Add References) and System.Data.

- Now from your code you can pass in the full path (within the Personal special folder) of the database file you want to create and work with.

string dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "test.db"); 
if (File.Exists(dbPath)) File.Delete(dbPath);
CreateSqlLite("Data Source=" + dbPath);

 

Note I: that this uses exactly the same CreateSqlLite method that the WP7 application uses.

 

Android

- The MonoDroid sdk includes support for Sqlite so all you need to do is reference Mono.Data.Sqlite (right-click the Android application project and select Add References) and System.Data.

- Now from your code you can pass in the full path (within the Personal special folder) of the database file you want to create and work with.

string dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "test.db");  if (File.Exists(dbPath)) File.Delete(dbPath);
CreateSqlLite("Data Source=" + dbPath);
 

Note I: that this uses exactly the same CreateSqlLite method that the WP7 application uses.

Note II: Be careful if you’re using “clever” Visual Studio addins like resharper as they can be overly helpful. When I added a reference to System.Data it decided to add a reference to System.EnterpriseServices which is a .NET 4 library and clearly not designed to work with MonoDroid. This caused me no end of frustration  - I ended up going through all the references in my MonoDroid projects looking for references that were not MonoDroid specific.

Windows Phone 7, Android and iOS with Mono VI: File Access

Previous posts in this series:

- Mono I: Getting Started
- Mono II: Basic Navigation
- Mono III: Shared Libraries
- Mono IV: Webservices
- Mono IV: Content and Resource Files

In the previous post we covered including files in your application that could be accessed at runtime. The flipside of this is being able to write to files, and as you may have expected there are some subtle differences between WP7 and the two Mono platforms.

Windows Phone 7

- WP7 applications are limited to writing to files located within Isolated Storage. The following code illustrates creating the User Store and then opening a file.

using (var store = IsolatedStorageFile.GetUserStoreForApplication())
using(var file = store.OpenFile("test.txt", FileMode.Create,FileAccess.Write))
using(var strm = new StreamWriter(file))
{     strm.Write("Contents to write to the test file");
}

 

iOS

- Writing to files via MonoTouch is done via the Personal special folder, as per the following example

string path = Environment.GetFolderPath(Environment.SpecialFolder.Personal); 
string filePath = Path.Combine(path, "test.txt");
using (var file = File.Open(filePath, FileMode.Create, FileAccess.Write))
using (var strm = new StreamWriter(file))
{
strm.Write("Contents to write to the test file");
}

Android

- Writing to files on MonoDroid should also be done via the Personal special folder, as per the following example

string path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);  string filePath = Path.Combine(path, "test.txt");
using (var file = File.Open(filePath, FileMode.Create, FileAccess.Write))
using (var strm = new StreamWriter(file))
{     strm.Write("Contents to write to the test file");
}

Windows Phone 7, Android and iOS with Mono V: Content and Resource Files

Previous posts in this series:

- Mono I: Getting Started
- Mono II: Basic Navigation
- Mono III: Shared Libraries
- Mono IV: Webservices

Quite often you’ll want to include files within your application as resources or assets that can be loaded at runtime. For example images, fonts, strings or text files. In this post we’ll cover some of the ways that you can do this across the three platforms.

Windows Phone 7

There are two main ways to include files with your Windows Phone 7 application, as resources or as content. The primary difference is that files included as resources are compiled into the assembly, where as content files are packaged into the xap and deployed alongside your application on the device. Upshot is that resources load slightly quicker, because they’re already in memory, but that they can adversely affect the load time of your application. General rule of thumb on the .NET Compact Framework (which SL for WP7 sits over) is that the larger the assemblies, the longer it takes for the application to load.

Resource files

- Right-click on the wp7 application project (ie NicksNetTravels) in Solution Explorer. Select Add > Existing Item.

- Locate the file you want to add as a resource (in this case SimpleTextFile.txt located in the root folder of the application) and click Add

- Press F4 or open the Properties window and make sure the Build Action is set to Resource (some files default to Content, whilst others will default to Resource).

- In the MainPage.xaml.cs add the following code to read out the contents of the resource file:

using (var strm = App.GetResourceStream(
   new Uri("/NicksNetTravels;component/simpletextfile.txt",UriKind.RelativeOrAbsolute)).Stream)
using(var reader = new StreamReader(strm)) {
var txt = reader.ReadToEnd();
}
 

Note the format of the Uri: The first bit is the assembly name (ie NicksNetTravels) and then the second bit, after component,  is the path to the resource file (ie /simpletextfile.txt”). If you can’t work out the path to the resource I’d suggest using Reflector to open the application assembly and check to make sure your resource is there.

image

Open the .g.resources node and view the resources. Double-click to open them in notepad and view their contents.

image

Content files

- Right-click on the wp7 application project in Solution Explorer. Select Add > Existing item.

- Locate the file you want to add as content (in this case SecondTextFile.txt again located in the root folder of the application) and click Add

- This time make sure the Build Action is set to Content.

In a lot of cases you can reference content files directly by name (for example you can set the Source property of an Image control to “new Uri(“SecondTextFile.txt”, UriKind.Relative)” and it will be able to locate the file. However, if you want to read directly from the file you’ll need to reference the Microsoft.Xna.Framework assembly so that you can make use of the TitleContainer class.

- Right-Click on the WP7 application project, select Add Reference. Find and select Microsoft.Xna.Framework and click Add.

- In the MainPage.xaml.cs add the following code to read out the contents of the file:

using(var strm = TitleContainer.OpenStream("SecondTextFile.txt"))
using (var reader = new StreamReader(strm)) {
    var txt = reader.ReadToEnd();
}
 

You will probably need to add “using Microsoft.Xna.Framework;” to the top of this file too in order to get it to compile.

 

iOS

I’m sure there are probably other alternative ways to package files with your iOS application but this is the way that I’ve found/used so far (please add a comment if you know of another way):

- Right-click the iOS application project in the Solution window of MonoDevelop. Select Add > Add Files. Locate and select the file you want to include and click Open (SimpleTextFile.txt).

- Right-click the newly added file and make sure the Build Action is set to content.

Screen shot 2011-04-09 at 10.03.26 AM

- Now, in your code you can access this file as follows:

var name = Environment.CurrentDirectory + "/SimpleTextFile.txt";
using(var file = System.IO.File.Open(name,System.IO.FileMode.Open,System.IO.FileAccess.Read))
using(var reader = new System.IO.StreamReader(file)) {
    var txt = reader.ReadToEnd();
}

Note that we’re just opening a file using standard file IO commands. The file we added as content gets placed into the CurrentDirectory of the application.

 

Android

In your Android application there is quite a nice model for accessing resources. By default when you create a new project with MonoDroid you will see a Resources folder with a number of sub-folders. Each of these sub folders represents a different type of resource. For example the Drawable folder is used for images – when you access them you will use a Drawable object so that the image can be rendered to the screen. Similarly the Layout folder is used to store your Views for your application. One sub-folder that appears to be missing from the default project structure was the Raw folder. This is particularly useful as it is used for any files that you want to be able to access as a raw stream.

image

- If the Raw sub-folder does exist under the Resources node, right-click the Resources node and select Add > New Folder and call it Raw.

- Right-click the Raw sub-folder and select Add > Existing Item. Find and select the file you wish to add and click the Add button.

If you look at the Properties window for the newly added item you should see that the Build Action is set to AndroidResource. Build your solution – this is required to generate the designer wrapper for accessing the resource from code.

- Add the following code to your application to read the contents of this file

using (var strm= Resources.OpenRawResource(Resource.Raw.SimpleTextFile))
using (var reader = new StreamReader(strm)) {
    var content = reader.ReadToEnd();
}
 

Note that the reference to Resource.Raw.SimpleTextFile is checked at compile time to ensure the resource exists (rather than having to specify a string literal and hope you’ve got the name right).

And there you have it, content/resource file access across the three platforms.

MonoDroid v1 and MonoTouch v4 Released

Miguel has posted a great summary of the newly released versions of MonoTouch and MonoDroid (officially release for production now!). This is a major step forward in being able to target multiple platform with roughly the same code base. I’ve done a couple of posts covering the basics of getting started across three mobile platforms, wp7, Android and iOS. Check them out:

- Mono I- Getting Started
- Mono II- Basic Navigation
- Mono III: Shared Libraries
- Mono IV: Webservices

Feedback always appreciated

Windows Phone 7 Template for Social Reading Applications

Over the past couple of weeks I’ve been working with Dave Gloveron a Visual Studio template that you can use to create simple reader style applications. You can combine RSS/Atom, Facebook Pages and Twitter feeds into a single application. Mix and match how you want the data to be combined, and customise the UI to your hearts content.

Anyhow, the full details have been posted on Dave’s post Windows Phone 7 Social Viewer Application Template Released

Windows Phone 7, Android and iOS with Mono IV: Webservices

In this series so far:
- Mono I- Getting Started
- Mono II- Basic Navigation
- Mono III: Shared Libraries

Now that you’ve got a simple application up and running it’s time to think about accessing data. We’re going to jump in the deep end and look at how to consume a web service. In this case I’ve created a vanilla WCF service with the following definition (just using the WCF Service Application template in Visual Studio):

[ServiceContract]
public interface IService1 {    
    [OperationContract]
    string GetData(int value);
}
 

Note I: I’ve created this service in a new solution in an instance of Visual Studio 2010 running as administrator. I did this because I wanted to run the service in IIS so that it can be accessed from multiple computers (so that the iphone/simulator on my Mac can access it). See the following image to configure the service to run on IIS.

image

Note II: Because I’m running this service in IIS 7 with .NET 4 there is almost no configuration for this service in the web.config file. What this does mean is that because I’m publishing this service over HTTP the default binding is the basicHttpBinding. As with Silverlight and the .NET Compact Framework, there is only support for basicHttpBinding (or the webHttpBinding if you want to roll your own web requests).

 

Windows Phone 7

Adding this service to your Windows Phone 7 application is unbelievably simple. However, to make it slightly more complex we’re going to add the service into our shared library. This way we can have our service logic in a common place across all three platforms.

- Right-click the shared library (BuiltToRoam.General) and select Add Service Reference

- Enter the address of the wcf service (eg http://localhost/SimpleService/Service1.svc” and hit Go. This should return the service information. Give it a namespace (eg Services) and click OK.

image

This will generate a folder called Service References, a number of files that contain the proxy classes for the referenced service and a ServiceReferences.ClientConfig file, which includes endpoint configuration information about the service.

- Add the following code to the MainPage.xaml.cs code behind file.

Service1Client client=new Service1Client(); 
public MainPage() {
     InitializeComponent();
     client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(client_GetDataCompleted);
     client.GetDataAsync(5);
}

void client_GetDataCompleted(object sender, GetDataCompletedEventArgs e) {
     var result = e.Result;
}

Put a breakpoint on the last line of the above code and run the application. What you should notice is that the application will fail throwing an exception. This is because we haven’t told the application the endpoint or binding to use for the service request. Normally this would be specified in the ServiceReferences.ClientConfig file which would be automatically added to the application when adding the service reference. However in this case because we added the service reference to a class library we need to make sure we include the ClientConfig file in the application.

- Right-Click the WP7 application project node (ie BuiltToRoam) and select Add >Existing Item. Navigate to the shared library (ie BuiltToRoam.General) and select the ServiceReferences.ClientConfig file. Do NOT click the centre of the Add button. Instead click the down arrow on the right side of the Add button. Then select Add as Link.

image

Note: This is important because if we update the service reference in the future we don’t want to have to remember to update additional copies of this file.

- F5 to build and run – this should run smoothly (if not, make sure IIS and the service application are running smoothly).

 

iOS

From the iOS application to call our wcf service we can reuse the proxy classes that we just generated.

- Right-click on the shared library (BuiltToRoam.General.iOS) and select Add > Add Files. Select all the files in the Service References\Services folder and click Open. This will add the proxy files to the project with the appropriate build actions.

- Check that the shared library builds – I had to add a reference to the System.ServiceModel assembly at this point (right-click References and select Edit References; from the Packages tab select System.ServiceModel and click OK). You will probably want to add this assembly and System.Xml.Linq to the References for the iOS Application project too.

- Right-click the iOS application project and select Add > Add Files. Locate the ServiceReferences.ClientConfig file that is situated in the shared library project folder. Click Open to add this file – this will display a dialog prompting you to add as a Link or Copy the file. You want to ensure we only have a single copy of this file, so select the Link option

- You need to set the Build Action of the ServiceReferences file you’ve just linked to Content. Do this by right-clicking the file and setting the Build Action directly in the context menu.

 

Screen shot 2011-04-08 at 12.01.01 PM

- Now, all you need to do is write some code to read the configuration file, setup the proxy client and call the service.

Service1Client client;
public MainPageController (IntPtr p) : base (p) {
    EndpointAddress endpoint;
    var name = Environment.CurrentDirectory + "/ServiceReferences.ClientConfig";
    using(var file = System.IO.File.Open(name,System.IO.FileMode.Open,System.IO.FileAccess.Read))
    using(var reader = new System.IO.StreamReader(file)) {
        var xml =XElement.Load(reader);
        endpoint = (from ep in xml.Descendants("endpoint")
                             select new EndpointAddress(ep.Attribute("address").Value)).FirstOrDefault();
    }
    client = new Service1Client(new BasicHttpBinding(),endpoint);
    client.GetDataCompleted+=new EventHandler<GetDataCompletedEventArgs>(client_GetDataCompleted);
    client.GetDataAsync(5);  
}

private void client_GetDataCompleted(object sender, GetDataCompletedEventArgs e) {
    var result = e.Result;
}

Note: The Environment.CurrentDirectory is a reference to the location on disk where Content files get copied to when the application gets installed on the device. From there you can open it and use a bit of Xml parsing to read out the endpoint configuration value

Set a break point in the client_GetDataCompleted method to confirm the service call works

 

Android

To make use of the same wcf proxy classes we generated earlier we need to a) add the appropriate files to the shared library and b) add the service configuration to the Android application project.

- The first step is relatively straight forward: In Solution Explorer select the shared project (BuiltToRoam.General.Android) and toggle the Show All Files button in the toolbar of the Solution Explorer window, so that files that aren’t currently part of the project are shown.

- Right-click on the Service References folder and select Include in Project. You can now toggle Show All Files to hide the excluded files again. You should end up with a structure in the shared library similar to the following.

image

The Android application project should already have a reference to the shared library from what we did in the previous posts. However, it’s missing the wcf service configuration file, ServiceReferences.ClientConfig. Unfortunately you can’t just add this to the project and expect it to work, which is what we did in the wp7 case. It’s a little more involved…

- Right-click the shared library project and select Properties. Go to the Build Events tab and add the following Post-Build event:

xcopy "$(ProjectDir)ServiceReferences.ClientConfig" "$(ProjectDir)..\..\NicksNetTravelsAndroid\NicksNetTravelsAndroid\Resources\Raw\" /y

To break this down the Post-Build event commands will get run after this project has been built. In this case we’re copying the ServiceReferences file from the shared library project directory to the Resources\Raw sub-folder of the Android application project directory.

- Build the solution, and then toggle the Show All Files option for the Android application project. This should reveal the ServiceReferences.ClientConfig file within the Raw directory. If an error is thrown during the build to do with the xcopy command, you may have either got the path wrong (source or destination), or the Raw folder may not exist, in which case you need to create it.

- Right-click the ServiceReferences.ClientConfig file and select Include In Project. Make sure that the Build Action (in the Properties window) is set to AndroidResource

This has included this file as a raw resource that can be accessed from within the Android application. Now in the body of Activity1 we’ll need to open this resource, read out the wcf service configuration which we can use when creating the instance of the proxy.

Service1Client client;
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);

EndpointAddress endpoint;
using (var inputStream = Resources.OpenRawResource(Resource.Raw.ServiceReferences))
using(var reader = new StreamReader(inputStream))
{
var xml = XElement.Load( inputStream));

endpoint = (from ep in xml.Descendants("endpoint")
select new EndpointAddress(ep.Attribute("address").Value)).FirstOrDefault();
}
client = new Service1Client(new BasicHttpBinding(), endpoint);
client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(client_GetDataCompleted);
client.GetDataAsync(5);
}


void client_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
{
var result = e.Result;
}
 

We could have just created the EndpointAddress in code but that would mean that we’ve got it hard coded within the application. By reading the contents of the ServiceReferences file we’re at least keeping the wcf service configuration information in a single file for all three platforms.

- Set a breakpoint in the client_GetDataCompleted method to see the result returned from the service.

Android Frustrations

This evening I spent a number of hours chasing my tail.  All I wanted to do was to debug the very simple Android application. I figured all I would have to do is go ahead and purchase MonoDroid (you can only debug using the emulator with the evaluation license, which is painfully slow and periodically breaks). Unfortunately this was not the case.

It turns out that my Samsung Galaxy S which is less than a year old is running Android v2.1 which is too old for the MonoDroid toolkit. I then spent the next hour fighting Samsungs poor excuse for a desktop application, Kies. It doesn’t help that the new version (v2) isn’t compatible with their less than a year old devices. I then managed to locate an older version (v1.5) which was compatible but for the life of me I couldn’t get my device to connect to the software. Finally after tweaking all sorts of things I got it to connect only to discover that the software didn’t have an update for my Galaxy S. ARGH.

Ok, back to basics – lets just get a cooked ROM and apply that. Sure enough I found instructions on how to a) root my Galaxy S and then to b) install Android 2.3.3:

How to Root Samsung Galaxy S

http://www.pathikshah.com/blog/root-samsung-galaxy-s/

Install Android 2.3.3 Gingerbread on Samsung Galaxy S

http://www.pathikshah.com/blog/install-android-2-3-3-gingerbread-on-samsung-galaxy-s/

After doing all that I now have a device I can debug to from Visual Studio 2010 using MonoDroid. This is way way way too painful, but to be expected from Google and Android.

Windows Phone 7, Android and iOS with Mono III: Shared Libraries

We’ve got the basics functioning across all three platforms but at the moment we’ve written all our code within the confines of three different projects with no code shared between them. Of course, one of the reasons for using MonoTouch/MonoDroid is to try to reuse and share as much code as possible between the platforms. That and hopefully we don’t need to spend too much time writing obnoxious Java or Objective-C code.

Now we could simply share files between the three projects, after it’s just .NET code but I want to be a little more adventurous. I’m going to create a class library that each of my three platform projects can access. Now I’m actually going to have platform specific class library project files, but the rest of the files contained within the project will be common across all projects.

Unfortunately I don’t know of an easy way to do this, so I’ll show you the long way – if someone does know an easier way to do this, please let me know because this way is painful.

Windows Phone 7

- Add a new project to your Windows Phone 7 application based on the Windows Phone Class Library template. In my case my shared library is going to be called BuiltToRoam.General but rather than leave it in the solution folder, I’m going to go up a level, create a folder called Shared and create the project in there. This will give me a folder structure as follows:

\CrossPlatform
    \Shared
        \BuiltToRoam.General
    \NicksNetTravels
    \NicksNetTravelsAndroid
    \NicksNetTravelsiOS

- In the new project rename Class1.cs to Utility.cs and add the following simple extension method:

namespace BuiltToRoam.General {     
  public static class Utility {         
    public static T EnumParse<T>(this string enumValue, bool ignoreCase = true) where T : struct {
             try {
                 if (string.IsNullOrEmpty(enumValue)) return default(T);
                 return (T)Enum.Parse(typeof(T), enumValue, ignoreCase);
             }
             catch {
                 return default(T);
             }
         }
     }
}

As you can probably work out all this method does is parse a string and return the appropriate enumeration value (strongly typed of course!).

- Right-click your Windows Phone 7 application project in the Solution Explorer window and select Add Reference; Select the BuiltToRoam.General project and add it as a reference.

- Now, just to test that this works, modify the MainPage constructor (in MainPage.xaml.cs) as follows:

public enum Wp7EnumTest {
First,
Second
}
 
public MainPage() {
InitializeComponent();

var test = ("First").EnumParse<Wp7EnumTest>();
}
 

- Build and run. Set a breakpoint after the EnumParse line to check that this works.

 

iOS

This is where it starts to get messy. Essentially you want to create an empty project stub for both iOS and Android. You can’t call them the same as the WP7 project because then you’ll end up with a file conflict; yet if you give them a different name they’ll create a new project folder which won’t contain the same files. There are actually two options here: Option 1 is that you just live with the fact that you’ll have multiple project folders – Visual Studio allows you to add files as links which means that you can have the files in one project (eg the Windows Phone 7 project you just created) and then as links in the iOS and Android projects. Option 2, which I prefer, is to create the project stubs with a slightly different name, and then move them into the same directory as the Windows Phone 7 project file. Then you can add literally the same files as the projects will be in the same folder. Here I’m going to go with Option 2, so if it didn’t make sense when I described it, then hopefully it will by the end of this.

- In MonoDevelop, right-click the Solution node in the Solution window and select Add > Add New Project. Select the MonoTouch Library Project from under the C# > iPhone and iPad node. Give you project a name, in this case BuiltToRoam.General.iOS, and click Forward to create the new project.

- Right-click the newly created project and select Delete (this might seem odd since what we actually want to do is to remove it from the solution, rather than deleting the project). When prompted, select “Remove” from the dialog (if you select “Delete from Disk” it will do just that, which is not what we want).

- In Finder, locate the newly created project file (in this case BuiltToRoam.Genera.iOS.csproj) and move it from where it was created into the shared project folder (in this case \Shared\BuiltToRoam.General).

- Back in MonoDevelop, right-click the Solution node. This time select Add > Add Existing Project and navigate to the shared project folder and select the project you just moved.

- Right-click the References folder of your iOS application project (ie NicksNetTravelsiOS) and select Edit References. Select the Projects tab and check the box alongside the shared project you just added (ie BuiltToRoam.General.iOS).

- Right-click the shared project and select Add > Add Files. Select Utilities.cs and click Open to add the file to your project

- Modify the code in Main.cs to include the following code to test the code in the referenced assembly works.

public enum iOSEnumTest{
    First,
    Second
}
 
public class Application  {
  static void Main (string[] args) {
   var test = ("First").EnumParse<iOSEnumTest>();

   UIApplication.Main (args);           
  }
}

- Again, set a breakpoint after the EnumParse line to validate that this code works.

Note: You may find that your breakpoints aren’t hit for some reason. This may be because you’re running without attaching the debugger. To run with the debugger select Run > Run With > Mono Soft Debugger for iPhone

 

Android

Ok, last one. We’re going to follow basically the same process over again. Perhaps I should make a project template for this….. volunteers?

- File > Add > New Project. Select the Mono for Android Class Library from the Mono for Android node. Give your project a name, in this case BuiltToRoam.General.Android, and click OK to create the class library.

- Like we did in the iOS case: Right-click on the newly created project and click Remove. Find the project file (ie BuiltToRoam.General.Android.csproj) in Windows Explorer and move it into the shared project folder.

- Right-click the solution node in Solution Explorer and select Add > Existing Project. Locate the shared project file and click Open to add it to the solution.

- Right-click the Android application project and select Add Reference. Select the Projects tab, select the BuiltToRoam.General.Android project and add it as a reference.

- Right-click the shared project file and select Add > Existing Item. Find Utilities.cs and click Add.

- To verify that this works, add the following code to Activity1.cs

public enum AndroidEnumTest {
    First,
    Second
}

protected override void OnCreate(Bundle bundle) {
    var test = ("First").EnumParse<AndroidEnumTest>();
    ...
}

- Set a break point after the EnumParse line to verify this works.

So there you have it, the ability to share whole projects between WP7, Android and iOS. It wasn’t that hard after all.

Windows Phone 7, Android and iOS with Mono II: Basic Navigation

When I’m building a mobile application one of the first things I want to get right is the flow of navigation. My demo cross platform application is no different. So the first thing to do is to work out how each of the navigation systems work, how to control it from C# code and of course wire up a simple navigation.

Windows Phone 7

Yeh, so this one should be easy since I’m so familiar with it. Essentially the navigation model is that an application is made up of a series of pages. To proceed within the application you make a call to Navigate and supply the relative Uri of the page that you want to navigate to. When the user presses the Back button the default behaviour is for the application to return to the previous page. If they press the Back button on the first page of the application, the application will be terminated.

- Add a new page to the application (Right-click the project in Solution Explorer > Add > New Item)

image

- Select the Windows Phone Portrait Page, give it a name, SecondPage.xaml, and click OK

- Add a button to the first page of the application (ie to MainPage.xaml)
  > Double-click MainPage.xaml in solution explorer to open it
  > Find "<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"></Grid>” and replace with

<StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Button Content="Go To Second Page"
            Click="SecondPageClick" />
</StackPanel>

- Right-click anywhere in the MainPage.xaml code/design window and select View Code

- Add the following code into the MainPage class. This is the event handler for the button you just added and causes the application to navigate to the second page.

private void SecondPageClick(object sender, RoutedEventArgs e) {
    this.NavigationService.Navigate(new Uri("/SecondPage.xaml", UriKind.Relative));
}

- F5 to run the application. Click the “Go To Second Page” button an note that a new page is displayed. Click the Back Button (bottom left of emulator) to return to the main page. Click Back again to exit the application.

image image

 

 

iOS

- Double-click MainWindow.xib to open it in Interface Builder

- From the Library window (Tools > Library if not already visible) drag a Navigation Controller element onto the MainWindow.xib

image

In order to reference this Navigation Controller from within your code you’re going to need to create an Outlet. You can think of this as a backing variable (although not strictly the same) to allow you to reference this element from code.

- In the Library window, select the Classes tab; Select AppDelegate from the list; Select Outlets from the AppDelegate sub-window.

- Click the + button, and give the new outlet a name, rootNavigation

Screen shot 2011-04-05 at 4.21.26 PM

- Lastly you need to wire the Navigation Controller to the outlet you just created. Select the App Delegate node in the MainWindow.xib window – the Inspector window (Tools > Inspector if not already visible) will update to display information about the App Delegate. Select the App Delegate Connections tab (second one from left) to display the list of outlets available and then drag the empty ring from alongside rootNavigation across onto the Navigation Controller node in MainWindow.xib.

image

We’ve wired up the navigation controller, the last thing to do in order to get the application to display something when we run it (if you recall from when we initially created this application there was nothing displayed on screen when we ran it) is to add the Navigation Controller as a sub-view of the main application window.

- Save changes in Interface Builder and return to MonoDevelop

- Open Main.cs and uncomment and update the line that starts // window.AddSubView (~line 25) to the following:

window.AddSubview(rootNavigation.View);

If you run at this point you will now see a heading of Root View Controller at the top of your application. We now need to add a button which will invoke the navigation to a new view.

- Go back to Interface Builder and find the Navigation Controller window. From the Objects tab of the Library window, drag a View object into the main area of the Navigation Controller window. The newly created View should take up the majority of the window, displacing the “View” placeholder.

- Again from the Library window, drag on a Round Rect Button. Resize the button across the top of the view area, and in the Inspector window change the Title property to read “Go To Second View”

We’ve created both the initial view and a button within the view. Now we need to be able to reference the button from code so that we can wire up an event handler to when the user taps the button. To do this we extend the view controller (if you look in the MainWindow.xib you will see that nested under the Navigation Controller node is a View Controller node) so that when an instance is created, we can wire up an event handler for the button click.

- In the MainWindow.xib window. Select the View Controlller (Root View Controller) node and then switch to the Identity tab of the Inspector window.

- Set the Class value to be MainPageController (we need to give the controller a name here so that we can write code in a code-behind file for a partial class with the same name).

- In the Library window, select the MainPageController on the Classes tab. Then select Actions from the MainPageController dropdown.

- Click the + button to create a new Action (think of actions as event handlers) and call it navigationButtonClick

Screen shot 2011-04-05 at 5.30.35 PM

- Switch over to the MainWindow.xib window; Select the Rounded Rect Button and find the Touch Down connector on the Connections tab of the Inspector window.

- Drag the circle alongside the Touch Down connector across onto the Main Page Controller node of the MainWindow.xib window. When prompted click on the navigationButtonClick action to complete the wiring up process. This has wired up the Touch Down event on the button to the navigationButtonClick action.

image

So now all we need is some code to be invoked when that action occurs. This is where Mono comes in – it’s already created a stub for us. All we need to do is to fill in the code.

- Switch back to MonoDevelop and create a new class file, MainPageController.cs, based on the Empty Class file template. Replace the contents of the file with the following.

using System;
using MonoTouch.Foundation;
using MonoTouch.UIKit;

namespace NicksNetTravelsiOS {
  public partial class MainPageController: UIViewController {
    public MainPageController (IntPtr p) : base (p) { }
 
    partial void navigationButtonClick (UIButton sender) {
    }
  }
}

Note that the MainPageController is a partial class, and the navigationButtonClick method is a partial method. The class and method definitions are in the MainWindow.xib.designer.cs file and are automatically generated by MonoDevelop (don’t hack around in the designer generated files!!!)

Ok, to round out this we’re going to need another view to switch in when the button is clicked

- In MonoDevelop select File > New > File; Select the iPhone View with Controller template; give it a name, SecondPageController and click New.

- Open the MainPageController.cs file and modify the navigationButtonClick event handler you created earlier.

private SecondPageController secondPage;
partial void navigationButtonClick (UIButton sender) {
  if(secondPage==null){
    this.secondPage = new SecondPageController();
  }

this.NavigationController.PushViewController(secondPage,true);
}

- Build and run this and when you click on the button, the second view will animate in.

Screen shot 2011-04-05 at 5.50.06 PM Screen shot 2011-04-05 at 5.50.15 PM

 

 

Android

The initial application that we created already comes with a button on the first view in the application. All we need to do is add a second view and navigate to it. Ok, just to be clear on the terminology here: In an Android application there are views and activities – and you should think of them in the context that an activity manages the current view and that you can either transition views within the current activity, or you can transition between activities. In this case we’re actually going to do the latter which will involve creating both an additional activity and a corresponding additional view.

- In the Solution Explorer window, right click on the Layout node; Select Add > New Item; Select the View template and give it a name, SecondPage; Click Add

- Next we need to add an additional activity, so right-click the project node and select Add > New Item; Select the Activity template and give it a name, Activity2

- Update the Activity2 class in Activity2.cs as follows. This sets the SecondPage view as the current view for Activity2

[Activity(Label = "My Activity")]
public class Activity2 : Activity
{
protected override void OnCreate(Bundle bundle){
base.OnCreate(bundle);     SetContentView(Resource.Layout.SecondPage);   }
}
 

- The last thing to do is to add the navigation from Activity1 to Activity2, which is in Activity1.cs. Change the delegate for the Click event to the following:

button.Click += delegate { StartActivity(typeof(Activity2)); };
 

- Press F5 to build and run

image image

 

And there you have it – basic navigation across all three platforms. Not that they all use slightly different conventions but all support some level of “back” operation. On Windows Phone 7 it’s a dedicated hardware back button, Android has a back button in the on screen menu area (or I believe it can be a hardware button) and iOS has it as part of the navigation controller on screen infrastructure.

Windows Phone 7, Android and iOS with Mono I: Getting Started

Over the past couple of months I’ve been thinking more about how to share resources between applications written for WP7, Android and iOS. Increasingly companies that I consult with are asking how they can build an application once and have it run across multiple platforms. My belief is that this isn’t easily achievable and even if it is would lead to a poor user experience on one or  more platforms.

The reality is that whilst functionality the platforms all offer similar capabilities and services, they don’t all share the same user experience. This means that we’d want to be able to customise how our applications look and behave on the different platforms. Enter the world of Mono.

MonoTouch and MonoDroid not only allow you to write c# code to address iOS and Android respectively, they also provide wrappers for a large proportion of the core apis and controls. Essentially this means that I could write my business logic once in C# and then have the UI component done in a way that is native to the platform I’m targeting. With iOS this would be via Interface Builder, WP7 using Blend and then for Android my understanding is that I’m left writing UI markup in XML (how surprising….).

In this series I’m going to try to share some of the pain/success/difficulties I experience in building the same application across three platforms.

Let’s get started, so what do we need:

Development and Design Tools

Windows Phone 7

- Development tools (Free): Visual Studio 2010 + Expression Blend (http://create.msdn.com)
- Register for developer program/marketplace ($100AUD): Register

iOS

- Note: Make sure you following the instructions on the MonoTouch website re installation
- Development tools (Free): XCode + Interface Builder (http://developer.apple.com/devcenter/ios)
- MonoTouch ($399USD):  http://monotouch.net/
- Register for developer program/app store ($99USD): Register

Android

- Note: Make sure you following the instructions on the MonoDroid website re installation
- Development tools (Free): Java JDK, Android SDK
- MonoDroid (public beta): monodroid-download
- Register for developer program/market ($25USD): Register

Don’t forget for the iOS component you’re going to need to go buy a Mac to use.

First Applications

 

Windows Phone 7

- Launch Visual Studio 2010

- File > New > Project
  > Select Windows Phone Application template under Silverlight for Windows Phone node
  > Enter name of project: NicksNetTravels
  > Click Ok

image

- Make sure Windows Phone Emulator is selected as deployment device (in Standard toolbar)

- F5 to build and run. This will launch the Windows Phone 7 Emulator, deploy the app to it and run it.

image

 

iOS

- Launch MonoDevelop

- File > New > Solution
  > Select  iPhone Window-Based Project under the iPhone and iPad node
  > Enter name of solution: NicksNetTravelsiOS (my applications are going to reside in the same folder, so it’s important that they are named slightly differently).

Screen shot 2011-04-05 at 3.16.50 PM

- (Apple key, or Windows key if using an MS keyboard) + Alt + Enter – build and run application. This will launch the simulator, deploy and run the application

Screen shot 2011-04-05 at 3.23.06 PM

Android

- Launch Visual Studio 2010

- File > New > Project
> Select Mono for Android Application template under Mono for Android node
> Enter name of project: NicksNetTravelsAndroid
> Click Ok

image

- F5 to build and run. This will launch the “Select Device” window.

image

- Click “Start emulator image”

image

- Click OK to launch the MonoDroid emulator (this should have been created during installation – see MonoDroid installation instructions. If not, you’ll just need to follow those instructions now by clicking the Create new emulator image link).

Note: The first time I did this the process failed with some cryptic error. I pressed F5 again, selected the existing emulator instance and it seemed to work fine.

image