Mono Mania

Balazs Fejes


Table of Contents

How I Went Mono
The Solution
Establishing The Development Environment
A Basic Architecture
Persistency Framework
Unit Tesing Framework
User Interface Framework
Early Conclusions

Abstract

Cross-platform application development does not neccessarily mean Java. This article will document how I explored the Mono Framework and developed a cross-desktop application using Glade, the Gnome libraries, the Db4o embedded database, and the C# language.

How I Went Mono

In the last 5-6 years I've been working on the Java/J2EE platform, delivering enterprise applications for various customers. By "enterprise" I mean applications which have to accomodate a large number of concurrent users, have to integrate with various legacy back-end services, have to fit an end-to-end architecture envisioned by the customer, and has to be manageable and configurable in a consistent, straight-forward way. I have found Java and J2EE the perfect platform for this. While there has been a bit of a backlash against the perceived complexity of the various frameworks associated with J2EE, I think the enterprise space with its inherent constraints is really an area which benefited the most from the pluggable, scalable, and manageable aspects of the Java stack.

That being said, I do love to develop small projects in my free time, to learn new technologies, and to explore new tools and frameworks. My latest project involved a desktop application. While I find server-side development enjoyable on Java, I do not enjoy developing GUI applications with Swing or even SWT. The flexibility and pluggability is not something I am looking for when I want to develop something for my enjoyment - I want something quick and productive.

I had some other requirements:

  1. The application must be cross-platform. While I am mostly using Windows XP as a development platform, I am quite bored with it, so if I ever decide to switch platforms (SUSE 9.3 or Ubuntu, I am not willing to replace my trusty ThinkPad X31 with an Apple box), my applications must be able to come with me...

  2. The application must look reasonably well. This automatically disqualifies most cross-platform toolkits, as they tend to look like Windows 3.1 at best.

  3. There has to be a somewhat WYSIWYG GUI designer available. I am not too good in UI design, there has to be a tool to help me visualize the application.

  4. The GUI forms and components should be defined declaratively. I am not fond of building up the UI from code. It's error-prone, difficult to maintain(with generated do-not-touch code segments), and gets in the way for the actual UI logic and business logic development. I find myself more productive with HTML and MXML for example than Swing or even Windows Forms, and I am quite willing to give up the fine-grained control if I get simplicity in return.

The Solution

After looking at various GUI toolkits accessible from Ruby, which is my preferred language for home hacking, I have found that the only one which would work for me is the GTK/Gnome toolkit. The others looked terrible or had no designer tools. After some investigation, I decided not to use Ruby for this, for the following main reasons:

  • For me it looked like the Windows builds for the GTK/Gnome bindings are really just an afterthought.

  • The Libglade integration was also not as mature as I had hoped - most examples, tutorials just focus on using GTK from the code, rather than using the Glade.XML files and binding them runtime.

  • I was not sure about how the distribution for the application would work, ie. how to create a nice executable, installer for Windows, which would include the runtime, the executable, etc.

Overall I have not found a strong message that the Ruby+Gnome solution is indeed the proper combination for cross-platform desktop applications. I do think it could be and should be that, but I will revisit this in a year or so.

The framework and the toolset which did have a very strong message in terms of being a cross-platform, productive, actively supported and maintained environment with a strong focus on GTK bindings is the Mono framework.

This is how the Mono page defines the platform:

Mono is a platform for running and developing modern applications, based on the ECMA/ISO Standards. Mono can run existing programs targeting the .NET or Java frameworks. Mono is an open source effort led by Novell and is the foundation for many new applications.

I've been impressed by the reenergized Novell recently. The SUSE Linux acquisition, and the strong focus on the Linux platform and Mono gives a new fresh direction to the company I think.

The UI framework recommended for cross-platform development is called GtkSharp. I have found that it is actively maintained, has a reasonable level of documentation, seems to be integrated well with the .NET runtime, and has up-to-date Windows builds.

The GTK Glade designer tool is reasonably productive, and the declarative approach with runtime binding seems to be supported and documented reasonably well(more about this later).

As I had some previous experience with the preferred language for the platform (C#), and I have a couple of friends who are really .NET and C# experts, it looked like a good solution to go ahead with.

Establishing The Development Environment

I have chosen to continue using my Windows environment for the actual development, as moving to Linux would have meant spending a lot of my (scarce) free time tweaking the environment instead of actually building the solution. However I really wanted to make sure that the application runs well on Linux, so I decided to use LiveCD-based distributions to check the portability from time to time.

The first component for the environment is the actual .NET runtime and SDK. I have downloaded the latest Mono for Windows build from the Mono website, but I also wanted to make sure that the solution works well with the Microsoft .NE T framework.

Download the combined Windows Installer here:

Mono Project website.

The second component (which is not included in the combined package) is the Glade UI Designer tool, which must be downloaded separately.

Glade for Windows website

For some reason the combined package did not make the Gtk libraries accessible for the MS .NET environment, so I downloaded the separate standalone GtkSharp package from the NovellForge website.

GtkSharp for Microsoft .NET

Overall I did spend a lot time installing and configuring various components. For example I had to experiment with various GtkSharp and Glade builds, because they've used an incompatible set of Gtk runtimes.Since I've started this project, there have been a number of releases for Gtk, Glade, and GtkSharp, so I will try again to test the whole platform with the most up-to-date components at some point, but here's the combination that works reasonably well for me:

  • mono-1.1.4-gtksharp-1.9.2-win32-0.2.exe

  • gtksharp-1.9.2-win32-0.5.exe

  • glade-2.6.0-rc1.zip

I will update this section when I do a clean install. Annoyingly the distribution does not contain the API docs in a browseable form. There's a MonoDoc for Windows solution being built using the Mozilla/Gecko intregration, but I wasn't able to get it to work reliably so far. It's not too bad as the .NET platform documentation is available from Microsoft, and the Mono-specific component documentation is available online, but this does make it a bit complicated to work offline.

As for the IDE itself, I was looking forward to try MonoDevelop, but it does not work currently on Windows. Thruth to be told, maybe it's not even needed in Windows, as Visual Studio is a fine development environment, and with the upcoming Visual CSharp Express edition, the price is rumoured to be very low ($50), so it'll be even affordable for home hacking.

Visual C# Express Edition can be downloaded from here:

MSDN VC# express site

A Basic Architecture

As I wanted to develop something quickly, I really tried to keep the design of the application simple. On the other hand, I wanted to make sure that I don't end up with a mass of unmaintainable code, so I have kept some of the principles I use on my day job.

Figure 1. A simple design

A simple design

These are the key principles I've used:

  • There should be a reasonable separation between the application layers, ie. the Application controller and UI handler classes should not depend on the namespaces defined by the persistency framework, and the "business logic" classes should not import any GtkSharp namespaces. This allows me to independently test the classes, and to change the frameworks if I find issues with them without impacting the whole codebase.

  • I should try to use .NET/Mono best practices and naming conventions, and should fully utilize the framework features. I've seen many applications where you can see that the developer's experience is primarily with Java for example, and instead of doing the best possible solution, tries to mimic the frameworks and patterns associated with his/her "native" programming language. I tried to use CSharp's additional features, like properties, enums, annotations, instead of falling back to Java or Ruby principles.

  • I should minimize the use of external components, to ensure easy portability, packaging and deployment for the application. On the other hand, I should not reinvent the wheel, and should reuse existing functionality when it saves time.

  • Because the frameworks, components, and even the language itself is new to me, each class should be easy to test in isolation, instead of trying to figure out for example database problems by interacting with the User Interface.

Persistency Framework

The application has a need for local data storage. One simple solution is the usage of using files to persist/retrieve the application-specific data, and the other end of the spectrum is using proper database services (like Microsoft's MS SQL server, MySQL etc.) to persist and retrieve the data in a relational manner.

For this application, I wanted something that is simple (ie. it does not require a service, and has simple deployment), but still has an API that allows me to focus on the object model rather than the storage semantics. It was also important to make sure that the persistency framework has cross-platform support. I've looked at various options, like SQLite.NET, and BerkeleyDB, but since the Mono website had a couple of announcements about the new GPL-licenced Db4o framework, I expected that it had been properly integrated with the rest of the environment, so I went ahead with it.

The Db4Object homepage says:

Embed db4o's native Java and .NET open source object database engine into your products and store complex object structures with ease. db4o provides superior performance and is simple to install.

The Db4o package can be downloaded from the following location:

Db4objects Download page

Overall I am happy with this framework, as it has numerous benefits:

  • It has a simple API to persist and retrieve objects (using Query By Example)

  • It just requires one DLL assembly, which greatly simplifies the deployment

  • The API documentation and the tutorial is reasonably good (but not great)

I did find some shortcomings, but I believe these could be addressed in subsequent releases.

  • The solution is generated from the Java version of the framework. It is not thoroughly integrated with the .NET environments. Java is peeking through the API, in the form of method names, iteration, collection handling.

  • The tutorial and the docs focus on the "recipes" on how to achieve something, but I think it should also describe the concepts and the architectural decisions as well.

I think a good desktop application platform should contain a simple persistency solution, so maybe one of the available libraries should be integrated more closely with Mono. Microsoft obviously has the Desktop MSSQL thing, and Apple just introduced the Core Data framework (using SQLite).

In terms of the framework usage, I've decided to use a DbManager facade which holds a reference to the DB4o ObjectContainer. The constructor on this Manager configure the cascading levels for the various entities. The methods on this facade return the required object hierarchies, or arrays for the application Controller.

Unit Tesing Framework

As Visual C# Express does not contain unit testing tools (which has a bad message - not only "professional" developers should unit test), I have separately downloaded the NUnit framework and toolset.

NUnit Download page

As I've worked with unit testing tools in Java and Ruby before, the API was straight forward for me. What I was quite happy about is instead of just porting the Java Junit framework, the developers actually used .NET features (like declarative attributes) to mark test setup, teardown, and actual test methods, instead of relying on the naming conventions as in Java.

Figure 2. NUnit Graphical Runner

NUnit Graphical Runner

The Test Runner tool itself was simple and good enough. I had a bit of a difficulty to get it configured properly, but after some googling around, I realized that I need to configure the nunit .config files to the exact version of my .NET runtime:

<requiredRuntime version="v2.0.50215" /> 

The other challenge was to decide how I will structure the unit tests in the solution. Eventually I decided to separate the Unit Tests into their own project. This meant that the tests themselves did not see the private classes in the other package. There's a "Friend Assemblies" feature in .NET 2.0 which enables access for designated assemblies, but for the purposes of this little project I could have just set the classes to public.

Here's how a simple test looks like:

namespace BroadCatcherTest 
{ 
    using System; 
    using NUnit.Framework; 
    using com.db4o; 
    using Bc; 
    using System.IO; 
 
    [TestFixture] 
    public class DbManagerTest 
    { 
        private DbManager DbMan; 
        [TestFixtureSetUp] 
        public void Init() 
        { 
            string path = @"c:\bc.db"; 
            Console.WriteLine("deleting now.."); 
            File.Delete(path); 
            DbMan = new DbManager(); 
        } 
 
        [Test] 
        public void Add() 
        { 
  /* actual test code with asserts */

User Interface Framework

The User Interface is one of the most challening aspects of desktop applications. The look-and-feel and the behaviour of the application really separates hobby programs from professional development. Although the application I'm building is really something that is only useful for me (I use it to catalog and track my recorded/acquired TV shows and episodes), I wanted to make sure that I can use it as a reference.

I decided to follow the Gnome Human Interface Design guidelines for development, primarily because I am a crap designer and this has several recommendations which hopefully helps me to design something that looks OK and works consistently. As the Gnome environment works on the same GUI toolkit, the guidelines there should be achievable with this framework.

The HIG document can be browsed online, or downloaded as a PDF document.

HIG homepage

To actually design the Windows and Dialogs of the application, I've used the Glade tool. Now I'm not sure if it's just because of my Windows background, but I find the multiple small-windows-flying-around layout of the tool extremely inconvenient. For a while I thought it's just something that does not work very well on the Windows port, but after testing it under Linux I can now tell you that it sucks pretty badly on Linux, too.

Figure 3. I'm Glade You Like It

I'm Glade You Like It

On a functional level, it's also not great, as it does not have Undo support, but it works pretty reliably.

I have experience building VB/MFC/Swing user interfaces with various tools, and compared to them, there's no surprise in Glade. You need to define containers (windows, dialogs) and drag-and-drop widgets in layouts, plus define events raised by the widgets. What I did find very positive is that I was able to lay things out very quickly, and have found the alignment and sizing process rather painless compared to Swing for example.

When the windows and dialogs are laid out propery, there are two additional things that need to be done:

  • The developer needs to select which events (raised by the widget) needs to be raised to the application. It's a good idea to name the events in a consistent way, as the same names will be used for methods in the Controller class to catch the events.

  • The elements which will be manipulated programmatically from the application (ie. dynamically filled treeviews/lists, entry fields) need to be named again in a consistent way, as they will be referenced from the Controller class of the application.

Glade persists all this information in an XML file. The GtkSharp assemblies contain functionality to read this XML file, build up the windows runtime, and connect the event handling code in the custom Controller class.

namespace Bc 
{ 
    using System; 
 
    using Gtk; 
    using Glade; 
    using GtkSharp; 
 
    public class BroadCatcher 
    { 
        [Glade.Widget] 
        TreeView seriesView; 
   /* the same attribute to any widget we want to bind. The name is the same
    * as in the Glade XML file.*/

        public BroadCatcher(string[] args) 
        { 
            Application.Init(); 
 
   /* This loads the glade file glade.glade,  
    *  selects MainWindow and connects it to the current Controller object*/

            Glade.XML gxml = new Glade.XML("bc.glade", "MainWindow", null); 
            gxml.Autoconnect(this); 

Unfortunately with the current MS .NET runtime I have, the "Autoconnect" method crashes with a NullPointer exception with my current glade file, while it works when executing with the Mono Framework. I will investigate this further with the latest releases, but as it works with Mono, I just decided to continue working with the Mono runtime.

The Gtk components themselves work in a "Model-View-Controller" pattern. This pattern is not exactly how I envisioned it. As an example, there's a TreeView component, which uses a Model property to store the elements for the tree/listview. I expected that I can just use my typed CSharp classes as the Model, however Gtk only works with simple arrays containing the elements rather than a collection of typed objects. This caused me a lot of mapping and iterating over my Model objects.

Other than this, I have found the API quite straight forward. Some of the components would need much more documentation, but overall I managed to hack it together. When I ran into a problem (I could not find a property on the ComboBox which controls the selected item), I got a response on IRC pretty quickly. What I'm lacking most is a set of guidelines about how to do an end-to-end application rather than just simple tutorials with one window and one simple widget. I have found numerous tutorials about how to autoconnect one window with one Controller class, but there are no tutorials on how to structure a complex application.

Currently I am using the following pattern:

I have one class per window/dialog. When a new dialog should be raised, the main controller instantiates the controller class for the specific dialog, which in its constructor uses autoconnect to bind to the new dialog. I'm not entirely sure about what mechanism to use when I want to enable communication between the controllers.

Early Conclusions

While I did experience some frustration during the development effort, primarily related to the lack of end-to-end integration of the components, I enjoyed the project. I'd certainly avoid using this combination for anything commercial (targeting Windows) that needs to be shipped like within 6 months, I will continue to use this configuration for my home projects.

Figure 4. Screenshot on Windows

Screenshot on Windows

Perhaps the one thing that baffled me most is that the Mono framework is not fully embraced by the Linux ecosystem. When I tried my application with the latest Ubuntu LiveCD, I realised that not only Mono was not in the default configuration, I could not even install a consistent set of components for Mono and GtkSharp. I think that the productivity that this environment provides, and the cross-platform focus of the project should really grab the attention of the developers.