Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

How we can write on C# in Linux: Implementing PanelApplet to Gnome Desktop

4.86/5 (48 votes)
8 Mar 2010CPOL12 min read 1   384  
This article describes how we can use our programming skills on the .NET framework to implement platform-independent code
. CenterWindow.png

Table of content 

Introduction

This article describes how we can use our programming skills on the .NET framework to implement Gnome panel applet. The whole development process was conducted in the Ubuntu operating system with usage of IDE MonoDevelop, so you can find some information about this OS and IDE.

The first part of the article is information collected from various sources (such as books and sites).The second part is a description of applet creation that display current weather in selected location.

The main idea of the article is to interest the developers in working over the platform-independent code. All the references you can find in the end of the article.

Part I – Theory

The Platform-Independent Nature of .NET

I think many of .NET developers, which have experience in previous development tools and technologies such as Microsoft Foundation Classes, Active Template Library, Component Object Model, may be surprised by the fact that .NET Framework is platform-independent . We can compile and execute .NET assemblies not only under Microsoft Windows OS. Using of open source .NET implementation such as Mono, Portable.NET. makes it possible.

Cross-platform applications, written with the help of these .NET implementations, are not necessarily simple console applications. We can use ADO.NET, ASP.NET, XML web service and many other advantages of the core namespaces and language features.

The way in which .NET’s cross-platform nature is achieved is different from the approach taking by Sun Microsystems with the handling of the Java programming platform. Unlike Java,Microsoft itself does not provide installers of .NET for Mac, Linux, etc. Rather, Microsoft has released a set of formalized specifications that other entities can use as a road map for building.NET distributions for their platform of choice:

  • ECMA-334, which defines the syntax and semantics of the C# programming language
  • ECMA-335, which defines numerous details of the .NET platform

CLI Distributions

CLIWebsiteDescription
Monohttp://www.mono-project.com Mono is an open source and commercially supported distribution of .NET sponsored by Novell Corporation.
Portable .NET  http://www.dotgnu.org Portable .NET is distributed under the GNU General Public License.;

Each of the CLI implementations provide a fully function C# compiler, numerous command-line development tools, a global assembly cache implementation, sample code, useful documentation, and dozens of assemblies that constitute the base class libraries.

Beyond implementing the core libraries defined by Partition IV of ECMA-335, Mono and Portable .NET provide Microsoft-compatible implementations of mscorlib.dll, System.Data.dll, System.Web.dll, System.Drawing.dll, and System.Windows.Forms.dll (among many others). Furthermore, the Mono and Portable .NET distribution also ship with a handful of assemblies specifically targeted at Unix/Linux and Mac OS X operating systems.

In my sample application I use Mono implementation of the CLI so we’ll examine what it is.

What’s Mono?

As it was mentioned earlier, Mono is a software platform designed to allow developers to create cross platform applications easily. It`s an open source implementation of Microsoft's .Net Framework based on the ECMA standards for C# and the Common Language Runtime.

Components

There are several components that make up Mono:
  • C# Compiler - The C# compiler is feature complete for compiling C# 1.0 and 2.0 (ECMA), and also contains many of the C# 3.0 features.
  • Mono Runtime - The runtime implements the ECMA Common Language Infrastructure (CLI). The runtime provides a Just-in-Time (JIT) compiler, an Ahead-of-Time compiler (AOT), a library loader, the garbage collector, a threading system and interoperability functionality.
  • Base Class Library - The Mono platform provides a comprehensive set of classes that provide a solid foundation to build applications on. These classes are compatible with Microsoft's .Net Framework classes.
  • Mono Class Library - Mono also provides many classes that go above and beyond the Base Class Library provided by Microsoft. These provide additional functionality that are useful, especially in building Linux applications. Some examples are classes for Gtk+, Zip files, LDAP, OpenGL, Cairo, POSIX, etc.

Compatibility

The current release version of Mono is 2.6. (Released December 2009) The easiest way to describe what Mono currently supports is: Everything in .NET 3.5 except WPF and WF, limited WCF.

If you already have an application written in .Net, you can scan your application with the Mono Migration Analyzer (MoMA) to determine if your application uses anything not supported by Mono.

The upcoming version of Mono currently only available from AnonSVN to users and not yet released additionally has:

  • C# 4.0
  • LINQ 4.0
  • ASP.NET 4.0 (Partially implemented)

Development

Available IDE

We can develop application on Mono using the following IDE:

  • Microsoft Visual Studio with add-in (http://go-mono.com/monotools/)
  • SharpDevelop (http://www.icsharpcode.net/OpenSource/SD/)
  • MonoDevelop (http://monodevelop.com/)

In this article I describe using MonoDevelop

MonoDevelop

MonoDevelop is an IDE primarily designed for C# and other .NET languages. MonoDevelop enables developers to quickly write desktop and ASP.NET Web applications on Linux, Windows and Mac OSX. MonoDevelop makes it easy for developers to port .NET applications created with Visual Studio to Linux and to maintain a single code base for all platforms.

MonoDevelopOnWindows.PNG

Also you can open MonoDevelop project with Microsoft Visual Studio. MonoDevelop project file with csproj extension is a valid MSBuild file.

Current released version of MonoDevelop is 2.2.1

Ubuntu

In our country (I live in Belarus) there are some problems with the culture of using licensed software. Therefore my main home operation system is Ubuntu 9.10 Karmic. Ubuntu is a computer operating system based on the Debian GNU/Linux distribution. It is built by a worldwide team of expert developers and contains all the applications you need: a web browser, office suite, media apps, instant messaging and much more. Ubuntu is an open-source alternative to Windows and Office.

The Ubuntu ideology:

  • Ubuntu will always be free of charge, along with its regular enterprise releases and security updates
  • Ubuntu comes with full commercial support from Canonical and hundreds of companies from across the world
  • Ubuntu provides the best translations and accessibility features that the free software community has to offer
  • Ubuntu core applications are all free and open source.

GNOME is the default user desktop in Ubuntu . This is the screenshoot of my desktop:

Screenshot.jpg

Gnome

The GNOME project provides two things: The GNOME desktop environment, an intuitive and attractive desktop for users, and the GNOME development platform, an extensive framework for building applications that integrate into the rest of the desktop.

As developers, we are interested in GTK+ library . GTK+ is the primary library which is used to construct user interfaces in GNOME applications. Let's conside the portable version of this library.

Gtk#

Gtk# is a Graphical User Interface Toolkit for Mono and .Net. The project binds the gtk+ toolkit and assorted GNOME libraries, enabling fully native graphical Gnome application development using the Mono and .Net development frameworks. More information can be found here.

Summary

In this part of the article we`ve considered some theoretical aspects of the application development with Mono usage. I also shortly considered Ubuntu, Gnome and Gtk#. 

Part II – Practice

In this part I’ll consider the implementation of PanelApplet for Gnome that will display the current weather in selected location. 

What's An Applet?

An applet is a small application, designed to sit in the Gnome panel, providing quick and easy access to a control, such as a volume control or a weather gauge. Technically, applets are Bonobo controls embedded in the Gnome panel. This means that there are a few slight differences to stand-alone Gnome programs.

Bonobo configuration file

Each applet requires a 'server' file, which contains a description of the Bonobo capabilities.

Let's have a look at our .server file:
XML
<oaf_info>
<oaf_server iid="OAFIID:appletWeather_Factory" type="exe"
            location="/home/aidan/Projects/appletGDK/appletSource1/bin/Debug/weatherApplet.exe">

        <oaf_attribute name="repo_ids" type="stringv">
                <item value="IDL:Bonobo/GenericFactory:1.0"/>
                <item value="IDL:Bonobo/Unknown:1.0"/>
        </oaf_attribute>
        <oaf_attribute name="name" type="string" value="Weather Applet Factory"/>
        <oaf_attribute name="description" type="string" value="Factory to create the weather applet"/>
</oaf_server>

<oaf_server iid="OAFIID:appletWeather" type="factory"
            location="OAFIID:appletWeather_Factory">

        <oaf_attribute name="repo_ids" type="stringv">
                <item value="IDL:GNOME/Vertigo/PanelAppletShell:1.0"/>
                <item value="IDL:Bonobo/Control:1.0"/>
                <item value="IDL:Bonobo/Unknown:1.0"/>
        </oaf_attribute>
        <oaf_attribute name="name" type="string" value="appletWeather_Factory"/>
        <oaf_attribute name="description" type="string" value="Demo weather applet on Mono"/>
        <oaf_attribute name="panel:category" type="string" value="Amusements"/>
        <oaf_attribute name="panel:icon" type="string" value="myicon.png"/>
</oaf_server>
</oaf_info>

NB. The oaf_server tag define the location of our executable file where type is “exe. In this example, our executable file is called weatherApplet.exe and is placed in "/home/aidan/Projects/appletGDK/appletSource1/bin/Debug/”. Also we define the name of our applet 'factory', weatherApplet-Factory. This is the name of the .server file, and is usually placed in /usr/lib/bonobo/servers/.

PanelApplet class from GTK#

PanelApplet class is used to create an applet using Mono. Official documentation about this class is available here and a full sample can be seen here.

To create applet we must create our own class, that will inherit the PanelApplet class and implement the following method and properties:

  • void Creation() – This method is called when an applet is being created
  • string IID – This property return OAFIID-string, which is used to bind with bonobo server-file 
  • string FactoryIID - This property return OAFIID-string, which define bonobo-factory

Let’s have a look at our Creation() implementation.

First we define the GTK# user element control named as Label and add it to the canvas:

C#
testLabel = new Label ("Update..");
	this.Add (testLabel);

Then we define a context menu. We add two menu items, give them names that appear in the menu when the right mouse button is clicked:

C#
string menuItemTemplate = "<menuitem name=\"{0}\" verb=\"{1}\" _label=\"_{2}\" pixtype=\"stock\" pixname=\"gtk-properties\"/>";
StringBuilder menuBuilder = new StringBuilder();
menuBuilder.Append("<popup name=\"button3\">");
		menuBuilder.AppendFormat(menuItemTemplate,"Properties","LabelChange","Change label");
			menuBuilder.AppendFormat(menuItemTemplate,"Properties","Preferences","Preferences");
	menuBuilder.Append("</popup>");

Finally, we add this menu description to the PanelApplet:

C#
this.SetupMenu (menuBuilder.ToString(), new BonoboUIVerb [] 
		{ 
		new BonoboUIVerb ("LabelChange", new ContextMenuItemCallback (LabelChangeCB)),
		new BonoboUIVerb ("Preferences", new ContextMenuItemCallback (PreferenceMenuClicked))
		}
	);

Implementation of the properties is trivial: we simply return strings, which were previously defined in the server-file:

C#
public override string IID {
	get { 
		return "OAFIID:appletWeather";
	    }
	}

public override string FactoryIID {
			get { 
				return "OAFIID:appletWeather_Factory"; 
			}
		}
 

Also we will add additional functionality on the mouse click. To do this, we can use OnButtonPressEvent:

C#
protected override bool OnButtonPressEvent (EventButton evnt)
{
//see http://www.go-mono.com/docs/index.aspx?link=P%3AGdk.EventButton.Time
			//1 - left
			//2 - middle
			//3 - right
	if(evnt.Button == 1)
	{
		if(w!=null)
		
w.Destroy();
		w = new MyWindow();
	}
			
	return base.OnButtonPressEvent (evnt);
}

In this method we create MyWindow object when the left mouse button is pressed.

Register applet in application

To register our applet we must add the following code in Main method:

C#
PanelAppletFactory.Register(typeof (PanelAppletClass));

Using weather web-service

I use the web-service from http://www.webserviceX.NET. To get the information about weather. It provides the following methods:

  • GetCitiesByCountry - Get all major cities by country name(full / part)
  • GetWeather - Get weather report for all major cities around the world.

WSDL schema is available on this site.

WSDL

Mono provide specific tool that allows us to generate corresponding C# source file from wsdl-file . This file contains the class that can call web-service methods. To generate this file, run the wsdl-utility in command line:

WsdlUsing.png

The utility will generate the GlobalWeather class that contains the GetCitiesByCountry and GetWeather methods.The example of returned data from GetWeather method:

XML
<currentweather>
  <location>Minsk, Belarus (UMMS) 53-56N 027-38E 231M</location>
  <time>Mar 04, 2010 - 08:00 AM EST / 2010.03.04 1300 UTC</time>
  <wind> from the SW (230 degrees) at 9 MPH (8 KT) gusting to 16 MPH (14 KT) (direction variable):0</wind>
  <visibility> greater than 7 mile(s):0</visibility>
  <skyconditions> mostly cloudy</skyconditions>
  <temperature> 30 F (-1 C)</temperature>
  <wind>Windchill: 21 F (-6 C):1</wind>
  <dewpoint> 17 F (-8 C)</dewpoint>
  <relativehumidity> 58%</relativehumidity>
  <pressure> 29.80 in. Hg (1009 hPa)</pressure>
  <status>Success</status>
</currentweather>

Using code

As it is seen from the example the data is returned in xml format. Let's create a class to represent the current weather in any city:
C#
public class CurrentWeather
	{
		public string Location {get;set;}
		public string Time {get;set;}
		public string Wind {get;set;}
		public string Visibility {get;set;}
		public string SkyConditions {get;set;}
		public string Temperature {get;set;}
		public string DewPoint {get;set;}
		public string RelativeHumidity {get;set;}
		public string Pressure {get;set;}
}
To create xml object of this class, we add a factory method Create. In this method we use the XmlSerializer and MemoryStream classes that are provided by System.Xml.Serialization and System.IO namespaces:
C#
public static CurrentWeather Create(string xml)
		{
			 if (String.IsNullOrEmpty(xml))
                return new CurrentWeather();
                        
            try
            {
                XmlSerializer sr = new XmlSerializer(typeof(CurrentWeather));
                MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
                               
                return (CurrentWeather)sr.Deserialize(memoryStream);
            }
            catch
            {
                return new CurrentWeather();
            }
		}
To hide these implementation details, I created a class WeatherService. This class provides all the same two methods:
  • GetWeather – call web-service, parse the response and return the CurrentWeather object which represents the weather in appropriate country and city:
    C#
    public static CurrentWeather GetWeather(string country, string city)
    		{
    			string xml = service.GetWeather(city, country);
    			XElement root = XElement.Parse(xml);
    			
    			return CurrentWeather.Create(root.ToString());
    		}
  • GetCitiesByCountry – return list of cities:
    C#
    private  static GlobalWeather service = new GlobalWeather();
    		private static Dictionary<string, IEnumerable<string>> 
    			citiesOfcountry = new Dictionary<string, IEnumerable<string>>();
    			
    		public static IEnumerable<string> GetCitiesByCountry(string country)
    		{
    			if(!citiesOfcountry.ContainsKey(country))
    			{
    				string xml = service.GetCitiesByCountry(country);
    				
    				XElement root = XElement.Parse(xml);
    				
    				IList<string> cities = new List<string>();
    				
    				citiesOfcountry.Add(country, 
    					    (from city in root.Elements()
    				         orderby city.Element("City").Value
    						 select city.Element("City").Value));
    			}
    			
    			return citiesOfcountry[country];
    		}
    Also here I use Linq capabilities that is available in Mono.
In these two methods XElement class is used to parse xml-file. This class appeared in .NET Framework 3.0 and Mono supports it too.

User interface

Window

To display the current weather of any location I create a special window:
C#
public class MyWindow:Window
	{
		private Label lblLocation = new Label();
		private Label lblTime = new Label();
		private Label lblWind = new Label();
		private Label lblVisibility = new Label();
		private Label lblSkyConditions = new Label();
		private Label lblTemperature = new Label();
		private Label lblDewPoint = new Label();
		private Label lblRelativeHumidity = new Label();
		private Label lblPressure = new Label();		
		public MyWindow():base("Center")
		{
			CreateControls();
        			ShowAll();
		}
		...
}
Here, I declare MyWindow class that is inherited from built-in Gtk.Window class. Also in this class I add Label that will display information about the current weather of selected location. In construction I call CreateControls() method in which controls are added to the window:
C#
private void CreateControls()
{
    SetDefaultSize(400, 200);

    Fixed fix = new Fixed();

    fix.Put(new Label("Location"), 10, 10 );
    fix.Put(lblLocation, 180, 10 );
    fix.Put(new Label("Pressure"), 10, 170 );
    fix.Put(lblPressure, 180, 170 );
    ...
    this.Add(fix);
}

The tree of countries

The Gtk library provides TreeView control. I use it in my applet to display a tree of countries with cities:

TreeViewWindow.png Let’s consider how we can build a tree of countries:
  • Create the scrolled window:
    C#
    ScrolledWindow sw = new Gtk.ScrolledWindow ();
  • Create our TreeView and lable that is displaying current choice
    C#
    Gtk.TreeView tree = new Gtk.TreeView ();
    filterEntry = new Gtk.Label ();
  • Create a box to hold the Entry and Tree
    C#
    Gtk.VBox box = new Gtk.VBox ();
  • Add the widgets to the box
    C#
    box.PackStart(filterBox, false, false, 5);
    sw.Add (tree);
    box.PackStart(sw, true, true, 5);
  • Create column
    C#
    Gtk.TreeViewColumn countriesColumn = new Gtk.TreeViewColumn ();
    countriesColumn.Title = "Countries";
    Gtk.CellRendererText countriesNameCell = new Gtk.CellRendererText ();
    countriesColumn.PackStart (countriesNameCell, true);
    tree.AppendColumn (countriesColumn);
    countriesColumn.AddAttribute(countriesNameCell, "text", 0);
  • Add selection-handler
    C#
    tree.Selection.Changed += OnSelectionChanged; 	
  • Create list of countries
    C#
    tree.Model = BuildTree();
The BuildTree method creates the TreeStore object that represents the tree of countries:
C#
private TreeStore BuildTree()
		{
			Gtk.TreeStore countryListStore = new Gtk.TreeStore(typeof (string));
			
			Gtk.TreeIter europe = countryListStore.AppendValues ("Europe");
		
			SetCountryCities("Belarus", europe, ref countryListStore);
			SetCountryCities("Russia", europe, ref countryListStore);
						
			return countryListStore;
		}
The SetCountryCities method appends cities to single node:
C#
private void SetCountryCities(string country,TreeIter region, ref TreeStore store)
		{
			TreeIter countryIter = store.AppendNode(region);
			store.SetValue(countryIter,0, country);
		
			foreach(var city in WeatherService.GetCitiesByCountry(country))
				store.AppendValues (countryIter, city);
			
		}
Let’s have a look at tree selection handler:
C#
void OnSelectionChanged (object o, EventArgs args)
        {
                TreeIter iter;
                TreeModel model;
 
                if (((TreeSelection)o).GetSelected (out model, out iter))
                {
					if(model.IterHasChild(iter)) return;
						
						TreeIter parent;
						model.IterParent(out parent, iter);
					
						string state = (string) model.GetValue (parent, 0);
                        string city = (string) model.GetValue (iter, 0);
						filterEntry.Text = String.Format("{0}-{1} was selected", state, city);
						
						SettingsStore.Set("state", state);
						SettingsStore.Set("city", city);
                }
        }
Here we get user choice, change label and save it using SettingStore. Where we can save the settings of the applet? For this purpose in gnome environment we can use the GConf configuration system.

Store settings with Gconf

GConf is a system for storing application preferences. It is intended for user preferences; not configuration of something like Apache, or arbitrary data. That is what we need:
C#
using GConf;

public static class SettingsStore
	{
		//path in gconf-editor
		private const string APPLICATION_KEY = "/apps/appletweather/";
		//gconf object
		private static Client client = new Client();

		//set value
		public static void Set(string key, object val)
		{
			client.Set(APPLICATION_KEY+key, val);
		}
		
		//get value
		public static object Get(string key)
		{
			return client.Get(APPLICATION_KEY+key);
		}
	}

Actually I thought it is very similar to the register tool in Windows:

GConfWindow.png

Installing applet

In order to register the applet in the system we must do the following steps:
  • Set the location of the execution-file in the server-file
  • Add server-file to /usr/lib/bonobo/servers directory
  • Add icon file that is referenced by a server-file to /usr/share/pixmaps directory. The icon-file is an image that is shown in «Add to Panel» dialog
  • Click the right mouse button on the toolbar and choice «Add to Panel...» option
  • Select applet from list

    AddToPanelWindow.png

Using applet

The applet is intended for demonstration purposes. Nevertheless it knows how to display information about the selected location. To do that we must click the right mouse button on the applet, select “preference” option and select location on TreeView:

TreeViewWindow.png

To get a detailed view of current weather you should click the left mouse button:

CenterWindow.png

Summary

In this part of article we considered the implementation panel applet for Gnome desktop enviroment. We used standard classes of .NET Framework which could previously be used in the implementation under Windows. Also we used specific Gtk library.

Conclusion

I became intrested in open source technologies not long ago. Therefore, this article may contain errors and inaccuracies. But I`m interested in this technology and would like to participate in its development.

Resources

History

  • 5 March 2010 - First version of the article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)