What is MonoCross?
MonoCross is an open source cross-platform mobile framework designed to maximize the amount of code shared while maintaining the native look and feel of each platform.
It is used in conjunction with the following tools:
- MonoTouch – iOS (iPhone, iPad, iPod Touch)
- Mono for Android – Android
- Windows Phone SDK – Windows Phone 7
I’ve used the sample application BestSellers to explain MonoCross concepts in this document.
A MonoCross Application
The code in a MonoCross application is divided into three architecture layers.
- Presentation Layer – contains platform-specific views
- Application Layer – contains business logic and controls workflow
- Data Layer – contains data models and controls data access
New platform support in an application can be added by writing a new view in the presentation layer for that platform. This new view will take full advantage of a native device control capabilities.
The separation of business and data logic from the presentation layer is done by combining two enterprise development patterns – Model-View-Controller (MVC) and Separated Interface.
The MonoCross MVC pattern builds upon the traditional MVC pattern but adds a Separated Interface between the view and the controller to facilitate the separation of platform-specific views from cross-platform application code, fostering Dependency Inversion (link 1, link 2).
As long as the views implement the Separated Interface definition, developers can build custom views for any supported platform without worrying how these views will be bound to the model and rendered by the application
MonoCross Project Structure
A MonoCross solution for a particular platform contains both platform-specific code and shared code.
The BestSellers.MD project contains the application’s shared code – models (green), controllers (yellow), and registration (blue).
The Container project contains platform-specific presentation code – views (yellow), container initialization (green), and additional assets required to render the views (blue).
The Framework project contains the MonoCross framework components (yellow) required for the target platform.
Creating A MonoCross Model
A MonoCross model represents any business objects that your application performs CRUD actions on. It contains all the necessary properties to describe entities and all the necessary methods to provide business or processing logic for those entities.
When creating a MonoCross model, it is recommended that the developer take an “lightweight” view of your data, delivering only the information your user needs at the time they need it. The goal is to enable mobile users to quickly and easily browse the information on their device and zero in on the specific information they need to do their job.
The BookSellers application has an extremely simple model.
Categories -> Book List -> Book
Represented by three basic POCOs or Plain Old CLR Objects – CategoryList,
Note: Some of the helper methods in these four classes have been removed for clarity.
Building A Shared Application
Controllers handle user input from the user interface and translate each input into an appropriate user action.
You need at least one controller for each model type that you want to display in your views. In BestSellers the first view is of the category list.
In BestSellers’ CategoryListController.Load() method, the Model property, a CategoryList, is initialized with data by calling a NY Times API and populating the list. The Load() methods in the two other controllers – BookListController and BookController – follow the same basic pattern, the Model is populate by either calling an API or retrieving data from a parent.
In MonoCross, all controllers are derived from the abstract MXController<T> class. This class defines the base functions required to register the controllers in your application and initialize model objects for delivery to the application’s containers. You accomplish both these actions by overriding the MXController.Load() method.
Initializing An Application
After defining the model and creating the controllers, the controllers need to be registered in the shared application.
The MonoCross MXApplication class is an abstract class with the contructs needed to define your workflow and register controllers used by your container.
Which the BestSellers.MD App.cs class inherits from.
To initialize your shared application the OnAppLoad() method of MXApplication is overridden. This method is called when your application is initialized and contains the code necessary to establish the appliation’s workflow.
Registering MonoCross Controllers
The most important part of application initialization is the registration of your controllers using the NavigtionMap property. This property is a collection of all the controllers necessary to render your application’s workflow.
The NavigationMap is keyed using a URI template model with defines the application workflow. Each user interaction with the interface initiates an action based on the URI patterns defined in your NavigationMap so it should include any parameters (defined using curved bracket syntax) that are needed to properly initialize the model. These parameters will be processed by the MXApplication.Navigate() method and passed to the specified MXController<T>.Load() method.
In the BestSellers application the navigation map contains three controllers.
- When a category is selected, the category value will be passed as the navigation URI and will load the BookListController from the NavigationMap. The application will then pass the category value to the controller on the parameters argument of the Load() method where it will be used to initialize the BookList object for that category using the NY Times API.
- When a book is selected from the book list, the category and book values will be passed to the navigation map, loading the BookController and retrieving the book information using the API.
The URI-based navigation map concept gives the developer flexibility in construction their application. Controller-View combinations can be reused at different points in the workflow by defining multiple endpoints in the NavigationMap with unique URI definistions.
Building Platform Containers
A MonoCross container is specific to a supported platform (deployment to multiple platforms is done by adding additional containers, one for each new platform). Each container holds one or more views designed to render the application’s model for the targeted platform and take full advantage of the user interface capabilities of their specific platform’s user interface.
The Console.Container does not target a specific mobile platform but it is a good introduction to how MonoCross views are created.
A MonoCross view must implement the IMXView interface, either directly or by inheriting the implementation from the MXView<T> base class. On platforms where the view class must inherit another base class an alternative strategy is for the view class to hold a local object that implements IMXView and delegate all IMXView calls to the local object (composition instead of inheritance).
The Console view for BestSellers CategoryList model inherits from MXConsoleView<T>.
The view’s Render() method must be overriden to implement any actions necessary to initialize and present model information for the target platform. If composition is using to implement IMXView it’s necessary to handle Render() using an event or override an alternative method on the local IMXView implementer because you cannot override Render() itself.
For the Console platform, the Model is iterated and a menu of options is created.
A menu which is initially displayed when the Console version of the program runs.
The BookListView and BookView classes are defined in a similar manner.
The Android containers in BestSellers inherit from the MXListActivityView and MXDialogActivityView classes to display the same model data and handle the same user interactions as Console but tailored for the Android platform. The Android screenshots below illustrate how an existing models and controllers can be migrated to a new platform simply by creating new views and recompiling the solution.
The iPhone containers in BestSellers inherit from the MXTouchTableViewController<T> and MXTouchDialogView<T> classes (both part of MXTouchView.cs) to display the same model data and handle the same user interactions as Console but tailored for the iPhone platform. The iPhone screenshots below illustrate how an existing models and controllers can be migrated to a new platform simply by creating new views and recompiling the solution.
Binding The Application To The Container
Once all platform views are defined, what remains is to initialize the application and bind it to your container. There are three steps in this process that must be done in the Main() method (or equivalent entry point to the platform-specific container application).
- Initialize Application – call the MXContainer.Initialize() method and pass in a new instance of the application
- Initialize Views – register the views with the container by populating the MXApplication.Instance.Views collection using the MXContainer.AddView() method
- Navigate To First View – perform an MXContainer.Navigate() navigation to the default view of your application
Deploying & Running The Application
Having created shared and platform-specific code the application can now be built and deployed.
- Cross-Platform Mobility: The Rise of Mono in the Enterprise – Scott Olson, MonoSpace 2011
- Vagabond Revolution – Scott Olson’s blog
- Things This Coder Thinks – Ben Horgan’s blog
- MonoCross Developers – Google Groups
- MonoSpace 2011 – recorded sessions
- MonoSpace 2012 – October 17-19, 2012
Some questions for future investigation.
- Is it possible to combine multiple platform containers in a single solution?
- What other views are available for the Android and iPhone platforms? Is the iPad supported as well?