Nick's .NET Travels

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

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, 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.

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

Going Occasionally Connected with Orcas

Nick's .NET Travels

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

Going Occasionally Connected with Orcas

Visual Studio vNext (aka Orcas) has a number of features that is set to make building occasionally connected applications really really easy.  Since downloading the Beta 1 bits I have been trying to get my head around all the improvements and changes that Microsoft have added.  I must admit the overall stability of Visual Studio is quite good - I've only had it crash once or twice and that was actually the compiler that seemed to die rather than the IDE itself.

As a number of people (Amanda and Daniel, twice) have mentioned one of the things you will immediately notice is that Intellisense has gone absolutely nuts, it's everywhere.  As soon as you type, it's there - sometimes infuriatingly so ;-).  In [VS2005] I thought that C# did a better job with their intellisense support as it appeared more frequently without have to manually invoke it (ie through Ctrl-Space).  In Orcas VB Intellisense rock - in particular I love the support for both Linq and working with XML. Oh, and did I mention JavaScript support too?

The first thing about building an Occasionally Connected application (OC App) is that you need to think about how you are going to work with the data.  Unlike traditional applications which typically connect directly to a central database (whether they be a winforms or web app) OC Apps need to have data available when they are offline.  This means having a local datastore - with the release of SQL Server Compact Edition (v3.1) earlier this year this decision is now quite easy.  Orcas will ship with v3.5 of SQL Server CE which has some minor improvements around synchronisation.

This leads me to the next problem - how do I synchronise data from the central database to the local datastore (ie my Sql Server CE database)? In the past we had to choose between building this ourselves (eg using webservice with some data structure, perhaps datasets), using RDA or Merge Replication.  Last year Steve Lasker started talking about OCS or, as it is now called, the Microsoft Synchronisation Services.  This is a framework for building logic that will synchronise data between a "client" and a "server" - although the first release will focus on supporting the SQL Server CE (client) to SQL Server (server) scenario, there is no reason why you can't do SQL Server Express to SQL Server, or even SQL CE or SQL CE.

MS Sync Services, as I mentioned in a previous post, also has designer support within Orcas.  Although the designer in Beta 1 has changes somewhat (the "Synchronisation" group box is no longer there - you have to build your own logic around when synchronisation is triggered) it still builds the majority of the functionality you need to be able to get syncing (more on what works/doesn't work in a later post).  This designer can output both the client and server code.  Of course building an occasionally connected application we want to put the client code in our application and the server code in a webservice or WCF project (more on configuring this in a further post).

Now that we have a local datastore (SQL Server CE) that is setup to synchronise with the central database (using MS Sync Services) we now need to be able to read and write to the database, and to bind data to UI controls.  This can all be done using Linq to Entities.  As a few people have noted in Beta 1 there are a few issues with the Entities designer but if you are happy to work with the command line tool this can save you a lot of work accessing your data.  Once you have setup the Entity models you can then use Linq to pull back a set of objects (strongly typed of course) from the database.  These can then be bound to your UI using a binding source (the same as you would have done in VS2005).  You can even add/remove items using the Navigation control.  When you are done making changes all you need to do is call SaveChanges on the data context and you are done - this commits changes to the local database, you need to synchronise for these changes to be persisted to the server!

This gives you an overview of how Orcas can facilitate building occasionally connected applications.  I will follow this up with posts that go into more detail about some of these features - in particular the areas that are currently broken and some gotchas!

Comments are closed