MW

C#/Gtk#/Glade# Tutorial on Debian Sid

Home -> Programming -> CSharp Tutorial 1

Index

Introduction
Tools
Tutorial
GUI Design
Coding
Compiling
Conclusion

Introduction

This tutorial is aimed to provide up-to-date information on how to develop a graphical application using C# with the Gtk# toolkit on Debian Sid, and to highlight some of the pitfalls for new developers.

The sample demonstrates the following features of C#:

  • Basic application startup, including parameter parsing.
  • RAD GUI design using glade.
  • Multiple Windows defined in Glade#.
  • File loading.
  • Dialog handling.
It has been put together out of snippets found online, various tutorials, and my own hacking. The code is nothing special, but the process of getting this program running illustrated various issues I had with the various toolchains currently available for Debian Sid.

Development Environment

The development environment was initially MonoDevelop, however due to a bug in Stetic, the visual GUI designer, the project was restarted using the more traditional tools of Glade (visual GUI designer), mcs (compiler), and an editor of your choice.

Issues

MonoDevelop: The current version shipped with Debian Sid is 0.14 and it crashes when designing the interface. The bug has been fixed upstream in MonoDevelop 0.16, but this will likely take a little while to filter down into Sid. In the meantime the fixed version can be built directly from the latest svn sources.

Tutorial

Step 1 - Build the GUI

Tools

I used glade-3 to build the GUI, although if you use the latest svn build of MonoDevelop you should be able to build the GUI using the built-in GUI designer. YMMV. Regardless, the output must be an XML file which can be loaded with the glade# library.

Tasks

First, create the main window. Add a VBox. Into the top section of the VBox put a Menu. Into the bottom part put a scrollable window and into that place a Text View. In the menu, add File->Open, File->Quit, View->Word Wrap, and Help->About.

Next add event handlers for the menu items.

Finally, create an AboutDialog and fill it out suitably.

The final XML file should be similar to this one.

Issues

Glade: The current version shipped with Debian Sid is 3.2.2, and during my work stopped loading the menu items added to the interface. Incidentally the menu items caused the crash in MonoDevelop/Stetic as well, so possibly the real problem is in the Gtk+ 2.12.0 libraries...

My workaround was to simply edit the XML file directly with vim, although that kindof defeats the purpose of having a RAD tool..

Step 2 - Write the Code

Tools

I used gvim to write the pre. Use the editor of your choice - MonoDevelop is quite good as it gives you pre-completion and online help.

Tasks

First we need the skeleton of the program:

using System;
using Gtk;
using Glade;

class SimpleViewer2
{
  [Widget] Window Window1;
  [Widget] AboutDialog aboutdialog1;
  [Widget] TextView textview1;
  [Widget] CheckMenuItem menuWordWrap;

  public static void Main(string[] args)
  {
    new SimpleViewer2(args);
  }

  public SimpleViewer2(string[] args)
  {
    try
    {
      Application.Init();
      Glade.XML guiXml = new Glade.XML("SimpleViewer2.glade", "Window1", null);
      guiXml.Autoconnect(this);
      if( args.Length > 0 )
      {
        loadFile(args[0]);
      }
      Application.Run();
    }
    catch( Exception e )
    {
      Console.WriteLine("Error running application:\n\n" + e);
    }
  }
}
  
What this does is set up the application, binds the GUI to the event handlers, and start the main loop.

Next we need to add the event handlers. The really interesting one is the one for opening the About box - as you may notice, we need to load the XML for the About box. This is because Glade# only ever loads a single top-level element out of the XML file at a time.

  public void OnWindow1DeleteEvent(object o, DeleteEventArgs args)
  {
    Console.WriteLine("OnWindow1DeleteEvent start");
    args.RetVal = doCloseApplication();
    Console.WriteLine("OnWindow1DeleteEvent done: {0}", args.RetVal);
  }

  private bool doCloseApplication()
  {
    Console.WriteLine("Application Quitting");
    // TODO: Any checks to determine whether or not the application
    // should stay active..
    //if( test-for-shutdown fails )
    //  return false;

    // By default, quit application and signal that we should
    // close the main window.
    Application.Quit();
    return true;
  }

  public void OnFileOpenEvent(object o, EventArgs args)
  {
    Console.WriteLine("OnFileOpenEvent start");

    FileChooserDialog fc = new FileChooserDialog("Select file to open",
        Window1,
        FileChooserAction.Open,
        "Cancel",ResponseType.Cancel,
        "Open",ResponseType.Accept);

    int resp = fc.Run();
    fc.Hide();
    if( resp == (int)ResponseType.Accept )
    {
      loadFile(fc.Filename);
    }
    fc.Destroy();
    Console.WriteLine("OnFileOpenEvent done");
  }

  private bool loadFile( string filename )
  {
    int err = 0;
    try
    {
      System.IO.StreamReader sr = new System.IO.StreamReader(filename);
      textview1.Buffer.Text = sr.ReadToEnd();
      sr.Close();
    }
    catch( UnauthorizedAccessException )
    {
      showError("You are not authorized to access this file.");
      err++;
    }
    catch( Exception e )
    {
      showError("Unexpected error while loading the file, please contact the " +
                "author with the following information:\n\n" + e);
      err++;
    }
    return err == 0;
  }

  private void showError( string s )
  {
      MessageDialog md = new MessageDialog(Window1,
          DialogFlags.DestroyWithParent,
          MessageType.Error,
          ButtonsType.Close,
          s);
      md.Run();
      md.Destroy();
  }

  public void OnQuitEvent(object o, EventArgs args)
  {
    Console.WriteLine("OnQuitEvent start");
    doCloseApplication();
    Console.WriteLine("OnQuitEvent done");
  }

  public void OnWordWrapActivatedEvent(object o, EventArgs args)
  {
    textview1.WrapMode = menuWordWrap.Active ? WrapMode.WordChar : WrapMode.None;
  }

  public void OnMenuHelpAboutActivatedEvent(object o, EventArgs args)
  {
    try
    {
      Console.WriteLine("OnMenuHelpAboutActivatedEvent start");
      XML aboutXml = new XML("SimpleViewer2.glade", "aboutdialog1", null);
      aboutXml.Autoconnect(this);
      if( aboutdialog1 == null )
        Console.WriteLine("aboutdialog1 is null (?)");
      else
      {
        aboutdialog1.TransientFor = Window1;
        aboutdialog1.Run();
        aboutdialog1.Destroy();
      }
      Console.WriteLine("OnMenuHelpAboutActivatedEvent done");
    }
    catch( Exception e )
    {
      Console.WriteLine("Could not display the About dialog: {0}", e);
    }
  }
}
  

Issues

About Dialog: This caused me a lot of headache early on as I had assumed that the initial XML load had loaded all of the resources out of the XML file - and not just the main window! In general, Glade# requires each top-level resource to be loaded separately out of the XML file.

The final source code file should be similar to this one.

Step 3 - Compile the Code

Tools

I used the mcs compiler directly.

Tasks

This is very straightforward for such a small example:

$ mcs SimpleViewer2.cs /pkg:gtk-sharp-2.0 /pkg:glade-sharp-2.0
$
  
The application can then be run as follows:
$ ./SimpleViewer2.exe 
OnFileOpenEvent start
OnFileOpenEvent done
OnMenuHelpAboutActivatedEvent start
OnMenuHelpAboutActivatedEvent done
OnWindow1DeleteEvent start
Application Quitting
OnWindow1DeleteEvent done: True
$
  
It should look something like this:

Issues

Compiling: Again, this took a very long time to track down due to old tutorials online. Basically the issue was that I used this command-line initially, based on the first tutorial I was following:

$ mcs /r:gtk-sharp /r:glade-sharp SimpleViewer2.cs
error CS0006: cannot find metadata file `gtk-sharp'
error CS0006: cannot find metadata file `glade-sharp'
Compilation failed: 2 error(s), 0 warnings
$
  
I then found a relatively recent online tutorial which suggested the following:
$ mcs SimpleViewer2.cs /pkg:gtk-sharp /pkg:glade-sharp
Package gtk-sharp was not found in the pkg-config search path.
Perhaps you should add the directory containing `gtk-sharp.pc'
to the PKG_CONFIG_PATH environment variable
No package 'gtk-sharp' found
error CS8027: Error running pkg-config. Check the above output.
$
  
Needless to say, that didn't work very well either...

I then added the following to my initial command-line which made the program compile and run, but it just "wasn't right"..:

$ mcs /lib:/usr/lib/mono/gtk-sharp-2.0 /r:gtk-sharp /r:glade-sharp SimpleViewer2.cs
$
  
The final solution was that the names of the package files had changed from gtk-sharp to gtk-sharp-2.0 and glade-sharp to glade-sharp-2.0, which I found in an obscure mailing-list somewhere.

Conclusions

Hopefully this little webpage will save somebody a lot of blood sweat and tears when they get started with .Net and Mono development under Debian Sid. I find there is little which demotivates more when starting to learn a new language/development environment than when the tools don't work.

Serves me right for running Debian Sid rather than some stable Linux Distro!

Top

MW
Mail You can email Micha here.
Last updated: 2007.10.04